From 76338d8e7cc10bb15f1705b7cdb30a2d6471125d Mon Sep 17 00:00:00 2001 From: Karl Hudgell Date: Mon, 19 Dec 2016 16:34:44 +0000 Subject: [PATCH] Initial push --- Alexa-Skill/IntentSchema | 13 +++ Alexa-Skill/SampleUtterances | 6 ++ Alexa-Skill/slots | 8 ++ Lambda/AlexaSkill | 175 +++++++++++++++++++++++++++++++++++ Lambda/index | 88 ++++++++++++++++++ Lambda/index.zip | Bin 0 -> 2820 bytes readme.md | 8 ++ 7 files changed, 298 insertions(+) create mode 100644 Alexa-Skill/IntentSchema create mode 100644 Alexa-Skill/SampleUtterances create mode 100644 Alexa-Skill/slots create mode 100644 Lambda/AlexaSkill create mode 100644 Lambda/index create mode 100644 Lambda/index.zip create mode 100644 readme.md diff --git a/Alexa-Skill/IntentSchema b/Alexa-Skill/IntentSchema new file mode 100644 index 0000000..94671fb --- /dev/null +++ b/Alexa-Skill/IntentSchema @@ -0,0 +1,13 @@ +{ + "intents": [ + { + "intent": "house", + "slots": [ + { + "name": "Control", + "type": "Control_List" + } + ] + } + ] +} \ No newline at end of file diff --git a/Alexa-Skill/SampleUtterances b/Alexa-Skill/SampleUtterances new file mode 100644 index 0000000..79c9138 --- /dev/null +++ b/Alexa-Skill/SampleUtterances @@ -0,0 +1,6 @@ +house to turn {Control} +house can you turn {Control} +house to turn {Control} please +house {Control} +house to turn the {Control} +house to {Control} \ No newline at end of file diff --git a/Alexa-Skill/slots b/Alexa-Skill/slots new file mode 100644 index 0000000..508bf3e --- /dev/null +++ b/Alexa-Skill/slots @@ -0,0 +1,8 @@ +television on +tv on +TV on +t. v. on +television off +tv off +TV off +t. v. off \ No newline at end of file diff --git a/Lambda/AlexaSkill b/Lambda/AlexaSkill new file mode 100644 index 0000000..f9804e4 --- /dev/null +++ b/Lambda/AlexaSkill @@ -0,0 +1,175 @@ +// Alexa SDK for JavaScript v1.0.00 +// Copyright (c) 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. Use is subject to license terms. +'use strict'; +function AlexaSkill(appId) { + this._appId = appId; +} + +AlexaSkill.prototype.requestHandlers = { + LaunchRequest: function (event, context, response) { + this.eventHandlers.onLaunch.call(this, event.request, event.session, response); + }, + + IntentRequest: function (event, context, response) { + this.eventHandlers.onIntent.call(this, event.request, event.session, response); + }, + + SessionEndedRequest: function (event, context) { + this.eventHandlers.onSessionEnded(event.request, event.session); + context.succeed(); + } +}; + +/** + * Override any of the eventHandlers as needed + */ +AlexaSkill.prototype.eventHandlers = { + /** + * Called when the session starts. + * Subclasses could have overriden this function to open any necessary resources. + */ + onSessionStarted: function (sessionStartedRequest, session) { + }, + + /** + * Called when the user launches the skill without specifying what they want. + * The subclass must override this function and provide feedback to the user. + */ + onLaunch: function (launchRequest, session, response) { + throw "onLaunch should be overriden by subclass"; + }, + + /** + * Called when the user specifies an intent. + */ + onIntent: function (intentRequest, session, response) { + var intent = intentRequest.intent, + intentName = intentRequest.intent.name, + intentHandler = this.intentHandlers[intentName]; + if (intentHandler) { + console.log('dispatch intent = ' + intentName); + intentHandler.call(this, intent, session, response); + } else { + throw 'Unsupported intent = ' + intentName; + } + }, + + /** + * Called when the user ends the session. + * Subclasses could have overriden this function to close any open resources. + */ + onSessionEnded: function (sessionEndedRequest, session) { + } +}; + +/** + * Subclasses should override the intentHandlers with the functions to handle specific intents. + */ +AlexaSkill.prototype.intentHandlers = {}; + +AlexaSkill.prototype.execute = function (event, context) { + try { + console.log("session applicationId: " + event.session.application.applicationId); + + // Validate that this request originated from authorized source. + if (this._appId && event.session.application.applicationId !== this._appId) { + console.log("The applicationIds don't match : " + event.session.application.applicationId + " and " + + this._appId); + throw "Invalid applicationId"; + } + + if (!event.session.attributes) { + event.session.attributes = {}; + } + + if (event.session.new) { + this.eventHandlers.onSessionStarted(event.request, event.session); + } + + // Route the request to the proper handler which may have been overriden. + var requestHandler = this.requestHandlers[event.request.type]; + requestHandler.call(this, event, context, new Response(context, event.session)); + } catch (e) { + console.log("Unexpected exception " + e); + context.fail(e); + } +}; + +var Response = function (context, session) { + this._context = context; + this._session = session; +}; + +Response.prototype = (function () { + var buildSpeechletResponse = function (options) { + var alexaResponse = { + outputSpeech: { + type: 'PlainText', + text: options.output + }, + shouldEndSession: options.shouldEndSession + }; + if (options.reprompt) { + alexaResponse.reprompt = { + outputSpeech: { + type: 'PlainText', + text: options.reprompt + } + }; + } + if (options.cardTitle && options.cardContent) { + alexaResponse.card = { + type: "Simple", + title: options.cardTitle, + content: options.cardContent + }; + } + var returnResult = { + version: '1.0', + response: alexaResponse + }; + if (options.session && options.session.attributes) { + returnResult.sessionAttributes = options.session.attributes; + } + return returnResult; + }; + + return { + tell: function (speechOutput) { + this._context.succeed(buildSpeechletResponse({ + session: this._session, + output: speechOutput, + shouldEndSession: true + })); + }, + tellWithCard: function (speechOutput, cardTitle, cardContent) { + this._context.succeed(buildSpeechletResponse({ + session: this._session, + output: speechOutput, + cardTitle: cardTitle, + cardContent: cardContent, + shouldEndSession: true + })); + }, + ask: function (speechOutput, repromptSpeech) { + this._context.succeed(buildSpeechletResponse({ + session: this._session, + output: speechOutput, + reprompt: repromptSpeech, + shouldEndSession: false + })); + }, + askWithCard: function (speechOutput, repromptSpeech, cardTitle, cardContent) { + this._context.succeed(buildSpeechletResponse({ + session: this._session, + output: speechOutput, + reprompt: repromptSpeech, + cardTitle: cardTitle, + cardContent: cardContent, + shouldEndSession: false + })); + } + }; +})(); + +module.exports = AlexaSkill; diff --git a/Lambda/index b/Lambda/index new file mode 100644 index 0000000..600ac22 --- /dev/null +++ b/Lambda/index @@ -0,0 +1,88 @@ +var http = require('http'); // if going through a proxy that uses SSL change to "require('https');" + +// Your external IP. Alexa can only access publically-accessible IPs. No LAN access unfortunately. +// In my case I had to move receiver to DMZ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +var local_ip = 'home.k-world.me.uk'; +var local_port = '7474'; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * App ID for the skill + */ + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +var APP_ID = "amzn1.ask.skill.3a572d72-bbf1-488b-ab9b-34e5947b1f27"; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* The AlexaSkill prototype and helper functions + */ +var AlexaSkill = require('./AlexaSkill'); + +var DTVControl = function () { + AlexaSkill.call(this, APP_ID); +}; + + +// Extend AlexaSkill +DTVControl.prototype = Object.create(AlexaSkill.prototype); +DTVControl.prototype.constructor = DTVControl; + +//Ignore Certificate errors if using HTTPS communication +process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + +DTVControl.prototype.intentHandlers = { + house: function (intent, session, response) { + + //No matter what she wants to tell you her opinion. + + function satisfyAlexa() { + response.tell("House Updated"); + }; + + // Obtain User Intent + switch(intent.slots.Control.value) { + + // List of triggers and URL to goto + case "television on": + case "tv on": + case "TV on": + case "t. v. on": + case "television off": + case "tv off": + case "TV off": + case "t. v. off": + path = 'code/TVPower'; + break; + + // List of triggers and URL to goto + case "test": + path = 'code/TVPower/'; + break; + + default: + + response.tell("Sorry, I didnt understand that house request."); + break; + + + } + var options = { + host: local_ip, + port: local_port, // default port for webserver + path: '' + path, // Modify if path is prefixed + method: 'POST' //, //(remove first comment slashes if using the "auth" option below) + // auth: 'username:password' // this is used if going through authenticated proxy (this is BASIC AUTH) + }; + var req = http.request(options, satisfyAlexa); + req.end(); + } +} + + +exports.handler = function (event, context) { + + var dtvControl = new DTVControl(); + dtvControl.execute(event, context); + +}; diff --git a/Lambda/index.zip b/Lambda/index.zip new file mode 100644 index 0000000000000000000000000000000000000000..db7d7970d1f4b5ab899244ec32a9c401fa66bbb8 GIT binary patch literal 2820 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