diff --git a/src/main/java/AlexaHandler.java b/src/main/java/AlexaHandler.java index 1ec768f..b369e09 100644 --- a/src/main/java/AlexaHandler.java +++ b/src/main/java/AlexaHandler.java @@ -13,7 +13,7 @@ import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Scanner; import com.amazonaws.services.dynamodbv2.document.DynamoDB; @@ -32,9 +32,10 @@ public class AlexaHandler { public static void handler(InputStream inputStream, OutputStream outputStream, Context context) { - String request; try { - request = getRequest(inputStream); + + String request = getRequest(inputStream); + System.out.println("Request:"); System.out.println(request); @@ -42,31 +43,40 @@ public static void handler(InputStream inputStream, OutputStream outputStream, C JSONObject directive = (JSONObject) jsonRequest.get("directive"); JSONObject header = (JSONObject) directive.get("header"); - AlexaResponse ar; + AlexaResponse alexaResponse; String namespace = header.optString("namespace", "INVALID"); String correlationToken = header.optString("correlationToken", "INVALID"); + switch(namespace) { case "Alexa.Authorization": + System.out.println("Found Alexa.Authorization Namespace"); - ar = new AlexaResponse("Alexa.Authorization","AcceptGrant", "INVALID", "INVALID", correlationToken); + alexaResponse = new AlexaResponse("Alexa.Authorization","AcceptGrant", "INVALID", "INVALID", correlationToken); + break; case "Alexa.Discovery": + System.out.println("Found Alexa.Discovery Namespace"); - ar = new AlexaResponse("Alexa.Discovery", "Discover.Response"); - String capabilityAlexa = ar.CreatePayloadEndpointCapability("AlexaInterface", "Alexa", "3", null); - String capabilityAlexaPowerController = ar.CreatePayloadEndpointCapability("AlexaInterface", "Alexa.PowerController", "3", "{\"supported\": [ { \"name\": \"powerState\" } ] }"); + + alexaResponse = new AlexaResponse("Alexa.Discovery", "Discover.Response"); + + String capabilityAlexa = alexaResponse.createPayloadEndpointCapability("AlexaInterface", "Alexa", "3", null); + String capabilityAlexaPowerController = alexaResponse.createPayloadEndpointCapability("AlexaInterface", "Alexa.PowerController", "3", "{\"supported\": [ { \"name\": \"powerState\" } ] }"); String capabilities = "[" + capabilityAlexa + ", " + capabilityAlexaPowerController + "]"; - ar.AddPayloadEndpoint("Sample Switch", "sample-switch-01", capabilities); + + alexaResponse.addPayloadEndpoint("Sample Switch", "sample-switch-01", capabilities); // For another way to see how to craft an AlexaResponse, have a look at AlexaResponseTest:ResponseDiscovery break; case "Alexa.PowerController": + System.out.println("Found Alexa.PowerController Namespace"); + String endpointId = directive.getJSONObject("endpoint").optString("endpointId", "INVALID"); String token = directive.getJSONObject("endpoint").getJSONObject("scope").optString("token", "INVALID"); String powerStateValue = directive.getJSONObject("header").optString("name", "TurnOn"); @@ -74,33 +84,36 @@ public static void handler(InputStream inputStream, OutputStream outputStream, C // Set the value in the DynamodDB table SampleSmartHome if(sendDeviceState(endpointId, "powerState", value)) { - ar = new AlexaResponse("Alexa", "Response", endpointId, token, correlationToken); - ar.AddContextProperty("Alexa.PowerController", "powerState", value, 200); - } - else { - ar = new AlexaResponse("Alexa", "ErrorResponse"); + + alexaResponse = new AlexaResponse("Alexa", "Response", endpointId, token, correlationToken); + alexaResponse.addContextProperty("Alexa.PowerController", "powerState", value, 200); + + } else { + + alexaResponse = new AlexaResponse("Alexa", "ErrorResponse"); } break; default: + System.out.println("INVALID Namespace"); - ar = new AlexaResponse(); + alexaResponse = new AlexaResponse(); + break; } System.out.println("Response:"); - System.out.println(ar); + System.out.println(alexaResponse); - outputStream.write(ar.toString().getBytes(Charset.forName("UTF-8"))); - } - catch (Exception e) - { + outputStream.write(alexaResponse.toString().getBytes(StandardCharsets.UTF_8)); + + } catch (Exception e) { e.printStackTrace(); } } - static boolean sendDeviceState(String endpoint_id, String state, String value) { + static boolean sendDeviceState(String endpointId, String state, String value) { String attributeValue = state + "Value"; @@ -111,7 +124,7 @@ static boolean sendDeviceState(String endpoint_id, String state, String value) { UpdateItemSpec updateItemSpec = new UpdateItemSpec() - .withPrimaryKey("ItemId", endpoint_id) + .withPrimaryKey("ItemId", endpointId) .withUpdateExpression("set #v = :val1") .withNameMap(new NameMap().with("#v", attributeValue)) .withValueMap(new ValueMap().withString(":val1", value)) @@ -123,7 +136,7 @@ static boolean sendDeviceState(String endpoint_id, String state, String value) { return true; } - static String getRequest(java.io.InputStream is) { + static String getRequest(InputStream is) { Scanner s = new Scanner(is).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; } diff --git a/src/main/java/AlexaResponse.java b/src/main/java/AlexaResponse.java index 0d2606a..a2b11e2 100644 --- a/src/main/java/AlexaResponse.java +++ b/src/main/java/AlexaResponse.java @@ -20,43 +20,36 @@ public class AlexaResponse { - private JSONObject response = new JSONObject("{}"); - private JSONObject event = new JSONObject("{}"); - private JSONObject header = new JSONObject("{}"); - private JSONObject endpoint = new JSONObject("{}"); - private JSONObject payload = new JSONObject("{}"); - - private String CheckValue(String value, String defaultValue) { - - if (value.isEmpty()) - return defaultValue; - - return value; - } + private final JSONObject response = new JSONObject("{}"); + private final JSONObject event = new JSONObject("{}"); + private final JSONObject header = new JSONObject("{}"); + private final JSONObject endpoint = new JSONObject("{}"); + private final JSONObject payload = new JSONObject("{}"); public AlexaResponse() { this("Alexa", "Response", "INVALID", "INVALID", null); } - public AlexaResponse(String namespace, String name) { this(namespace, name, "INVALID", "INVALID", null); } + public AlexaResponse(String namespace, String name) { + this(namespace, name, "INVALID", "INVALID", null); + } public AlexaResponse(String namespace, String name, String endpointId, String token, String correlationToken) { - header.put("namespace", CheckValue(namespace, "Alexa")); - header.put("name", CheckValue(name,"Response")); + header.put("namespace", checkValue(namespace, "Alexa")); + header.put("name", checkValue(name,"Response")); header.put("messageId", UUID.randomUUID().toString()); header.put("payloadVersion", "3"); - if (correlationToken != null) { - header.put("correlationToken", CheckValue(correlationToken, "INVALID")); - } + if (correlationToken != null) + header.put("correlationToken", checkValue(correlationToken, "INVALID")); JSONObject scope = new JSONObject("{}"); scope.put("type", "BearerToken"); - scope.put("token", CheckValue(token, "INVALID")); + scope.put("token", checkValue(token, "INVALID")); endpoint.put("scope", scope); - endpoint.put("endpointId", CheckValue(endpointId, "INVALID")); + endpoint.put("endpointId", checkValue(endpointId, "INVALID")); event.put("header", header); event.put("endpoint", endpoint); @@ -65,68 +58,83 @@ public AlexaResponse(String namespace, String name, String endpointId, String to response.put("event", event); } - public void AddCookie(String key, String value) { + private static String checkValue(String value, String defaultValue) { + + if (value.isEmpty()) + return defaultValue; + + return value; + } + + public void addCookie(String key, String value) { + JSONObject endpointObject = response.getJSONObject("event").getJSONObject("endpoint"); JSONObject cookie; + if (endpointObject.has("cookie")) { cookie = endpointObject.getJSONObject("cookie"); cookie.put(key, value); } else { + cookie = new JSONObject(); cookie.put(key, value); endpointObject.put("cookie", cookie); } - } - public void AddPayloadEndpoint(String friendlyName, String endpointId, String capabilities) { + public void addPayloadEndpoint(String friendlyName, String endpointId, String capabilities) { JSONObject payload = response.getJSONObject("event").getJSONObject("payload"); - if (payload.has("endpoints")) - { + if (payload.has("endpoints")) { + JSONArray endpoints = payload.getJSONArray("endpoints"); - endpoints.put(new JSONObject(CreatePayloadEndpoint(friendlyName, endpointId, capabilities, null))); - } - else - { + endpoints.put(new JSONObject(createPayloadEndpoint(friendlyName, endpointId, capabilities, null))); + + } else { + JSONArray endpoints = new JSONArray(); - endpoints.put(new JSONObject(CreatePayloadEndpoint(friendlyName, endpointId, capabilities, null))); + endpoints.put(new JSONObject(createPayloadEndpoint(friendlyName, endpointId, capabilities, null))); payload.put("endpoints", endpoints); } } - public void AddContextProperty(String namespace, String name, String value, int uncertaintyInMilliseconds) - { + public void addContextProperty(String namespace, String name, String value, int uncertaintyInMilliseconds) { + JSONObject context; JSONArray properties; + try { + context = response.getJSONObject("context"); properties = context.getJSONArray("properties"); } catch (JSONException jse) { + context = new JSONObject(); properties = new JSONArray(); context.put("properties", properties); } - properties.put(new JSONObject(CreateContextProperty(namespace, name, value, uncertaintyInMilliseconds))); - response.put("context", context); + properties.put(new JSONObject(createContextProperty(namespace, name, value, uncertaintyInMilliseconds))); + response.put("context", context); } - public String CreateContextProperty(String namespace, String name, String value, int uncertaintyInMilliseconds) { + public String createContextProperty(String namespace, String name, String value, int uncertaintyInMilliseconds) { JSONObject property = new JSONObject(); property.put("namespace", namespace); property.put("name", name); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'"); - TimeZone tz = TimeZone.getTimeZone("UTC"); - sdf.setTimeZone(tz); - String timeOfSample = sdf.format(new Date().getTime()); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'"); + + TimeZone timeZone = TimeZone.getTimeZone("UTC"); + simpleDateFormat.setTimeZone(timeZone); + + String timeOfSample = simpleDateFormat.format(new Date().getTime()); property.put("timeOfSample", timeOfSample); property.put("uncertaintyInMilliseconds", uncertaintyInMilliseconds); @@ -141,20 +149,25 @@ public String CreateContextProperty(String namespace, String name, String value, return property.toString(); } - public String CreatePayloadEndpoint(String friendlyName, String endpointId, String capabilities, String cookie){ + public String createPayloadEndpoint(String friendlyName, String endpointId, String capabilities, String cookie) { + JSONObject endpoint = new JSONObject(); endpoint.put("capabilities", new JSONArray(capabilities)); endpoint.put("description", "Sample Endpoint Description"); + JSONArray displayCategories = new JSONArray("[\"OTHER\"]"); endpoint.put("displayCategories", displayCategories); + endpoint.put("manufacturerName", "Sample Manufacturer"); if (endpointId == null) endpointId = "endpoint_" + 100000 + new Random().nextInt(900000); + endpoint.put("endpointId", endpointId); if (friendlyName == null) friendlyName = "Sample Endpoint"; + endpoint.put("friendlyName", friendlyName); if (cookie != null) @@ -163,7 +176,7 @@ public String CreatePayloadEndpoint(String friendlyName, String endpointId, Stri return endpoint.toString(); } - public String CreatePayloadEndpointCapability(String type, String interfaceValue, String version, String properties) { + public String createPayloadEndpointCapability(String type, String interfaceValue, String version, String properties) { JSONObject capability = new JSONObject(); capability.put("type", type); @@ -176,7 +189,7 @@ public String CreatePayloadEndpointCapability(String type, String interfaceValue return capability.toString(); } - public void SetPayload(String payload) { + public void setPayload(String payload) { response.getJSONObject("event").put("payload", new JSONObject(payload)); } diff --git a/src/test/java/AlexaHandlerTest.java b/src/test/java/AlexaHandlerTest.java index 4c7c65b..e5f75a6 100644 --- a/src/test/java/AlexaHandlerTest.java +++ b/src/test/java/AlexaHandlerTest.java @@ -17,26 +17,28 @@ import java.io.*; import java.net.HttpURLConnection; import java.net.URL; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import static org.junit.Assert.*; public class AlexaHandlerTest { - private static String sampleUri = "https://raw.githubusercontent.com/alexa/alexa-smarthome/master/sample_messages/"; + private static final String SAMPLE_URI = "https://raw.githubusercontent.com/alexa/alexa-smarthome/master/sample_messages/"; - private JSONObject GetResponse(String json) { + private JSONObject getResponse(String json) { + + InputStream inputStream = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8) ); + + OutputStream outputStream = new OutputStream() { + + private final StringBuilder sb = new StringBuilder(); - InputStream inputStream = new ByteArrayInputStream(json.getBytes(Charset.forName("UTF-8")) ); - OutputStream outputStream = new OutputStream() - { - private StringBuilder sb = new StringBuilder(); @Override - public void write(int b) throws IOException { + public void write(int b) { this.sb.append((char) b ); } - public String toString(){ + public String toString() { return this.sb.toString(); } }; @@ -44,14 +46,18 @@ public String toString(){ AlexaHandler.handler(inputStream, outputStream, null); String responseString = outputStream.toString(); + return new JSONObject(responseString); } - private String GetSample(String url) - { + private String getSample(String url) { + StringBuilder sb = new StringBuilder(); + try { + URL iurl = new URL(url); + HttpURLConnection c = (HttpURLConnection)iurl.openConnection(); c.connect(); int status = c.getResponseCode(); @@ -75,22 +81,21 @@ private String GetSample(String url) } @Test - public void TestAuthorization() - { - JSONObject response = GetResponse(GetSample(sampleUri + "Authorization/Authorization.AcceptGrant.request.json")); + public void testAuthorization() { + + JSONObject response = getResponse(getSample(SAMPLE_URI + "Authorization/Authorization.AcceptGrant.request.json")); String namespace = response.getJSONObject("event").getJSONObject("header").get("namespace").toString(); String name = response.getJSONObject("event").getJSONObject("header").get("name").toString(); assertEquals("Namespace should be Alexa.Authorization", "Alexa.Authorization", namespace); assertEquals("Name should be AcceptGrant", "AcceptGrant", name); - } @Test - public void TestDiscovery() - { - JSONObject response = GetResponse(GetSample(sampleUri + "Discovery/Discovery.request.json")); + public void testDiscovery() { + + JSONObject response = getResponse(getSample(SAMPLE_URI + "Discovery/Discovery.request.json")); String namespace = response.getJSONObject("event").getJSONObject("header").get("namespace").toString(); String name = response.getJSONObject("event").getJSONObject("header").get("name").toString(); @@ -100,9 +105,9 @@ public void TestDiscovery() } @Test - public void TestPowerControllerOff() - { - JSONObject response = GetResponse(GetSample(sampleUri + "PowerController/PowerController.TurnOff.request.json")); + public void testPowerControllerOff() { + + JSONObject response = getResponse(getSample(SAMPLE_URI + "PowerController/PowerController.TurnOff.request.json")); String namespace = response.getJSONObject("event").getJSONObject("header").get("namespace").toString(); String name = response.getJSONObject("event").getJSONObject("header").get("name").toString(); @@ -110,5 +115,4 @@ public void TestPowerControllerOff() assertEquals("Namespace should be Alexa", "Alexa", namespace); assertEquals("Name should be Response", "Response", name); } - } diff --git a/src/test/java/AlexaResponseTest.java b/src/test/java/AlexaResponseTest.java index f6b4755..f091a1d 100644 --- a/src/test/java/AlexaResponseTest.java +++ b/src/test/java/AlexaResponseTest.java @@ -32,7 +32,7 @@ public void ResponseTest(){ @Test public void ResponseCookieTest() { AlexaResponse ar = new AlexaResponse(); - ar.AddCookie("key", "value"); + ar.addCookie("key", "value"); JSONObject response = new JSONObject(ar.toString()); String value = response.getJSONObject("event").getJSONObject("endpoint").getJSONObject("cookie").get("key").toString(); assertEquals("Key value should be value", value, "value"); @@ -41,8 +41,8 @@ public void ResponseCookieTest() { @Test public void ResponseCookieMultipleTest() { AlexaResponse ar = new AlexaResponse(); - ar.AddCookie("key1", "value1"); - ar.AddCookie("key2", "value2"); + ar.addCookie("key1", "value1"); + ar.addCookie("key2", "value2"); JSONObject response = new JSONObject(ar.toString()); String value1 = response.getJSONObject("event").getJSONObject("endpoint").getJSONObject("cookie").get("key1").toString(); @@ -59,7 +59,7 @@ public void ResponseErrorTest() { payloadErrorObject.put("message", "ERROR_MESSAGE"); AlexaResponse ar = new AlexaResponse("Alexa", "ErrorResponse"); - ar.SetPayload(payloadErrorObject.toString()); + ar.setPayload(payloadErrorObject.toString()); JSONObject response = new JSONObject(ar.toString()); @@ -76,17 +76,17 @@ public void ResponseDiscovery() { AlexaResponse ar = new AlexaResponse("Alexa.Discovery", "Discover.Response"); - JSONObject capability_alexa = new JSONObject(ar.CreatePayloadEndpointCapability("AlexaInterface", "Alexa", "3", null)); + JSONObject capability_alexa = new JSONObject(ar.createPayloadEndpointCapability("AlexaInterface", "Alexa", "3", null)); JSONObject propertyPowerState = new JSONObject(); propertyPowerState.put("name", "powerState"); - JSONObject capability_alexa_powercontroller = new JSONObject(ar.CreatePayloadEndpointCapability("AlexaInterface", "Alexa.PowerController", propertyPowerState.toString(), null)); + JSONObject capability_alexa_powercontroller = new JSONObject(ar.createPayloadEndpointCapability("AlexaInterface", "Alexa.PowerController", propertyPowerState.toString(), null)); JSONArray capabilities = new JSONArray(); capabilities.put(capability_alexa); capabilities.put(capability_alexa_powercontroller); - ar.AddPayloadEndpoint("Sample Switch", "sample-switch-01", capabilities.toString()); + ar.addPayloadEndpoint("Sample Switch", "sample-switch-01", capabilities.toString()); JSONObject response = new JSONObject(ar.toString());