TL;dr Does anybody have their Alexa Skill working in the new platform?
So one of the first thing I noticed after the migration was my Alexa Skil was broken. I initially assumed the cause to be some required inputs involving my no longer working thermostat devices.
So I created a new SmartApp with all traces of the thermostats removed. Here is what the new app looks like:
definition(
name: "Caspar",
namespace: "scottinpollock",
author: "Scottin Pollock",
description: "Do what Alexa tells us to do",
category: "My Apps",
iconUrl: "http://solutionsetcetera.com/stuff/STIcons/ASK.png",
iconX2Url: "http://solutionsetcetera.com/stuff/STIcons/ASK@2x.png")
preferences {
page(name: "configure")
}
def configure() {
dynamicPage(name: "configure", title: "Choose Devices and Routine", install: true, uninstall: true) {
section("Select your devices") {
input "outTemp","capability.temperatureMeasurement", title: "Select the Outdoor temp sensor", required: true, multiple: false
input "medTemp","capability.temperatureMeasurement", title: "Select the Media temp sensor", required: true, multiple: false
input "frdTemp","capability.temperatureMeasurement", title: "Select the fridge temp sensor", required: true, multiple: false
input "gTemp","capability.temperatureMeasurement", title: "Select the Garage temp sensor", required: true, multiple: false
input "outdoorHu","capability.relativeHumidityMeasurement", title: "Select the Outdoor humidity sensor", required: true, multiple: false
input "gCont","capability.contactSensor", title: "Select the Garage Door contact sensor",required:true, multiple: false
input "stPres","capability.presenceSensor", title: "Select the Sport Trac presence", required: true, multiple:false
input "allBat","capability.battery",title: "Select ALL batteries", required: true, multiple: true
}
section("Optional HAM Bridge commands"){
input "HAMBleaving", "text", title: "Command to send when leaving...", required: false
input "HAMBhome", "text", title: "Command to send when returning...", required: false
input "server", "text", title: "Server IP", description: "IP Address", defaultValue: "192.168.86.12", required: false
input "port", "number", title: "Port", description: "Port number", defaultValue: "8080", required: false
}
section(title: "App ID") {
paragraph "Application ID:\n${app.id}"
}
}
}
def installed() {}
def updated() {}
mappings { path("/:noun/:operator/:operand"){ action: [GET: "centralCommand"] } }
def centralCommand() {
log.debug params
def noun = params.noun
def op = params.operator
def opa = params.operand
log.debug "Central Command ${noun} ${op} ${opa}"
state.talk2me = ""
if (op == "none") { op = "status" } //if there is no op, status request
if (["done","finished"].contains(op)) { op = "status" } //these are status
if (["open","closed","locked","unlocked","about","running"].contains(op)) { op = "status" }
else if (["hot","cold","warm"].contains(op)) { op = "temperature" }
switch (noun) {
case "batteries" : switch(op) { //check the status of all batteries
case "dead" :
case "low" : batteryResponseMulti(allBat,noun,op); break
default : batteryResponseMultiNow(allBat,noun,op)
}
break
case "temperature" : switch (op) { //tell me my temps
case "outdoor" : temperatureMeasurementResponse(outTemp, noun, op); break
case "indoor" : temperatureMeasurementResponse(medTemp, noun, op); break
case "report" :
case "status" : environmentNounResponse(outTemp,medTemp,frdTemp,gTemp,outdoorHu); break
default : defaultResponseUnkOp(noun,op)
}
break
case "good morning" : switch (op) { //Good Morning!
case "status" : goodMorningNounResponse(); break
default : defaultResponseUnkOp(noun,op)
}
break
case "good night" : switch (op) { //Good Night!
case "status" : goodNightNounResponse(); break
default : defaultResponseUnkOp(noun,op)
}
break
case "mode" : switch (op) { //Answer mode
case "status" : theModeNounResponse(); break
default : defaultResponseUnkOp(noun,op)
}
break
case "I am home" : switch (op) { //I am home
case "status" : iamhomeNounResponse(); break
default : defaultResponseUnkOp(noun,op)
}
break
case "I am leaving" : switch (op) { //I am leaving
case "status" : iamleavingNounResponse(); break
default : defaultResponseUnkOp(noun,op)
}
break
case "release the hounds" : switch (op) {
case "status" : houndsNounResponse(); break
default : defaultResponseUnkOp(noun,op)
}
break
case "lighting" : switch (op) {
case "status" : lightingNounResponse(noun,op,opa); break
default : defaultResponseUnkOp(noun,op)
}
break
case "bedroom music" : switch (op) {
case "play" :
case "stop" : uPlaylistNounResponse(noun,op,opa); break
case "status" :
default : defaultResponseUnkOp(noun,op)
}
break
case "media music" : switch (op) {
case "play" :
case "stop" : dPlaylistNounResponse(noun,op,opa); break
case "status" :
default : defaultResponseUnkOp(noun,op)
}
break
case "media system" : switch (op) {
case "on" :
case "off" : mediaNounResponse(noun,op); break
case "status" :
default : defaultResponseUnkOp(noun,op)
}
break
case "bedroom system" : switch (op) {
case "on" :
case "off" : bedroomNounResponse(noun,op); break
case "status" :
default : defaultResponseUnkOp(noun,op)
}
break
case "garage system" : switch (op) {
case "on" :
case "off" : garageNounResponse(noun,op); break
case "status" :
default : defaultResponseUnkOp(noun,op)
}
break
case "none" : defaultResponseWhat()
break
default : defaultResponseUnkNoun(noun,op)
}
return ["talk2me" : state.talk2me]
}
def defaultResponseWhat()
{
state.talk2me = state.talk2me + "Ask me about something, or to do something with something. But at least you're here. "
}
//defaultResponse Unknown Device
def defaultResponseUnkNoun(noun, op)
{
state.talk2me = state.talk2me + "I can't find a person, place, or thing called ${noun} in the smart app. "
}
//defaultResponse Unknown Operator for device
def defaultResponseUnkOp(noun, op)
{
state.talk2me = state.talk2me + "I haven't been told how to do ${op} with ${noun} yet. "
}
//capability.switch ["on", "off"]
def switchResponse(handle, noun, op)
{
def arg = handle.currentValue("switch") //value before change
if (op == "on") { handle.on(); arg = "turning " + op;} //switch flips slow in state, so tell them we did Op
else if (op == "off") { handle.off(); arg = "turning " +op; } //...or it will report what it was, not what we want
else if (op == "status") { } //dont report Op, report the real currentState
state.talk2me = state.talk2me + "The ${noun} is ${arg}. " //talk2me : switch is on (or off)
}
//capability.battery ALL
def batteryResponseMulti(handle, noun, op) //handle All battery check differently
{
state.talk2me = state.talk2me + "SmartThings reports that the following batteries are low: "
handle.each {
def arg = it.currentValue("battery")
if (arg) {
if (arg.toInteger() < 50) {
state.talk2me = state.talk2me + "${it} at ${arg} percent. "
}
}
}
}
def batteryResponseMultiNow(handle, noun, op) //handle All battery check differently
{
state.talk2me = state.talk2me + "SmartThings batteries are as follows: "
handle.each {
def arg = it.currentValue("battery")
if (arg) {
state.talk2me = state.talk2me + "${it} at ${arg} percent. "
}
}
}
//capability.contactSensor ["open", "closed"]
def contactSensorResponse(handle, noun, op)
{
def arg = handle.currentValue("contact") //lookup the current contact status
state.talk2me = state.talk2me + "The ${noun} is ${arg}. " //talk2me : contact is open or closed
}
//capability.temperatureMeasurement ["degrees"]
def temperatureMeasurementResponse(handle, noun, op)
{
def arg = handle.currentValue("temperature")
state.talk2me = state.talk2me + "The ${op} temperature is ${arg} degrees. "
if (op == "outdoor") {
def outHU = outdoorHu.currentValue("humidity")
state.talk2me = state.talk2me + "With a relative humidity of ${outHU} percent. "
}
}
//capability.motionSensor ["active", "inactive"]
def motionSensorResponse(handle, noun, op)
{
def arg = handle.currentValue("motion")
state.talk2me = state.talk2me + "The ${noun} motion is ${arg}. "
}
def environmentNounResponse(outTemp,medTemp,frdTemp,gTemp,outdoorHu)
{
def otemp = outTemp.currentValue("temperature")
def mtemp = medTemp.currentValue("temperature")
def fritemp = frdTemp.currentValue("temperature")
def gtemp = gTemp.currentValue("temperature")
def outHU = outdoorHu.currentValue("humidity")
state.talk2me = state.talk2me + "SmartThings reports the outdoor temperature is ${otemp} degrees, with a relative humidity of ${outHU} percent. "
state.talk2me = state.talk2me + "Indoors, it is ${mtemp} degrees. "
state.talk2me = state.talk2me + "The garage is ${gtemp}. "
state.talk2me = state.talk2me + "The refridgerator is ${fritemp}. "
}
def goodMorningNounResponse()
{
location.helloHome?.execute(settings.GMaction)
def theRoutine = settings.GMaction
def otemp = outTemp.currentValue("temperature")
def mtemp = medTemp.currentValue("temperature")
def outHU = outdoorHu.currentValue("humidity")
def theCOM = "goodMORNING"
doHAMB(theCOM)
state.talk2me = state.talk2me + "Good Morning Scott, SmartThings has executed your morning routine. "
state.talk2me = state.talk2me + "Inside, it is ${mtemp} degrees. "
state.talk2me = state.talk2me + "Outside, it is ${otemp} degrees with a relative humidity of ${outHU} percent. "
state.talk2me = state.talk2me + "I hope you have a great day. "
}
def goodNightNounResponse()
{
location.helloHome?.execute(settings.GNaction)
def theRoutine = settings.GNaction
def theCOM = "goodNIGHT"
doHAMB(theCOM)
state.talk2me = state.talk2me + "Good Night Scott, SmartThings has executed your sleeping routine. "
state.talk2me = state.talk2me + "I hope you Sleep Well. "
}
def theModeNounResponse()
{
def theMode = location.mode
state.talk2me = state.talk2me + "SmartThings has confirmed the mode is set to ${theMode}. "
}
def iamhomeNounResponse()
{
def theMode = location.mode
if (HAMBhome) {
def theCOM = HAMBcommandOn
doHAMB(theCOM)
}
state.talk2me = state.talk2me + "Welcome back Scott, smart things mode is ${theMode}. "
state.talk2me = state.talk2me + "Firing up your media system. "
}
def iamleavingNounResponse()
{
if (HAMBleaving) {
def theCOM = HAMBcommandOff
doHAMB(theCOM)
}
state.talk2me = state.talk2me + "OK scott, enjoy your outing. "
state.talk2me = state.talk2me + "SmartThings will shut down all non essential systems. "
}
def houndsNounResponse()
{
def theCOM = "dogs"
doHAMB(theCOM)
state.talk2me = state.talk2me + " ok "
}
def lightingNounResponse(noun,op,opa)
{
def theCOM = opa
doHAMB(theCOM)
state.talk2me = state.talk2me + "Setting the lighting to ${theCOM}. "
}
def uPlaylistNounResponse(noun,op,opa)
{
def tCmd = op
def tListnum = opa
def theCOM = "PLAYlistUP&"+tListnum
if (tCmd == "stop") {theCOM = "PLAYstopUP"}
doHAMB(theCOM)
state.talk2me = state.talk2me + "ok, ${tCmd}ing music upstairs. "
}
def dPlaylistNounResponse(noun,op,opa)
{
def tCmd = op
def tListnum = opa
def theCOM = "PLAYlistDOWN&"+tListnum
if (tCmd == "stop") {theCOM = "PLAYstopDOWN"}
doHAMB(theCOM)
state.talk2me = state.talk2me + "ok, ${tCmd}ing music downstairs. "
}
def mediaNounResponse(noun,op)
{
def tCmd = op
def theCOM = "mediaAVon"
if (tCmd == "off") {theCOM = "mediaOFF"}
doHAMB(theCOM)
state.talk2me = state.talk2me + "ok, turning downstairs audio video system ${tCmd}. "
}
def bedroomNounResponse(noun,op)
{
def tCmd = op
def theCOM = "AVupstairsON"
if (tCmd == "off") {theCOM = "AVupstairsOFF"}
doHAMB(theCOM)
state.talk2me = state.talk2me + "ok, turning upstairs audio video system ${tCmd}. "
}
def garageNounResponse(noun,op)
{
def tCmd = op
def theCOM = "AVgarageON"
if (tCmd == "off") {theCOM = "AVgarageOFF"}
doHAMB(theCOM)
state.talk2me = state.talk2me + "ok, turning garage system ${tCmd}. "
}
def doHAMB(theCOM)
{
def ip = "${settings.server}:${settings.port}"
sendHubCommand(new physicalgraph.device.HubAction("""GET /?$theCOM HTTP/1.1\r\nHOST: $ip\r\n\r\n""", physicalgraph.device.Protocol.LAN))
}
I also had to edit the Lambda function as the new app has new OAuth info (which is embedded in the function). I could not make this simple edit as the version of nodeJS I created the original in was deprecated, so I created a new function, deleted the trigger from the old one, added a trigger for the new one with the Skil ID, and transferred the ARN of the new function to the current Alexa Skill. I do seem to be getting to the Lambda function from the Alexa Skill, but I am hitting the following line in the Lambda function below:
var speechText = 'The App on SmartThings did not return any message.';
Here is the entire Lamda function:
'use strict';
exports.handler = function( event, context ) {
var https = require( 'https' );
var STprompt = ['How can I help?', 'Smartthings here.', 'Your wish is my command.', 'Smartthings online.', 'Smartthings is listening.', 'Smartthings standing by.'] ;
var askForNext = ['anything else?', 'need something else?', 'still need help?', 'do you have another command?'] ;
var signOff = ['Ok then', 'smartthings out!', 'catch ya later', 'Smartthings has left the building.', 'Smartthings offline.', 'Smartthings signing off.'] ;
var STappID = <Client ID> ; // AppID from Apps Editor
var STtoken = <Client Secret>; //Token from Apps Editor
//var url='https://graph.api.smartthings.com:443/api/smartapps/installations/' + STappID + '/' ;
var areWeDone=false;
if (event.request.type == "LaunchRequest") {
var speech = STprompt[Math.floor(Math.random() * STprompt.length)];
output(speech, context, areWeDone);
}
else if (event.request.type === "SessionEndedRequest"){
}
else if (event.request.intent.name == "command") {
var Operator = event.request.intent.slots.Operator.value;
var Noun = event.request.intent.slots.Noun.value;
var Operand = event.request.intent.slots.Operand.value;
if (!Operator) {Operator = "none";}
if (!Noun) {Noun = "none";}
if (!Operand) {Operand = "none";}
var url = 'https://graph.api.smartthings.com/api/smartapps/installations/' + STappID + '/' +
Noun + '/' + Operator + '/'+ Operand +'?access_token=' + STtoken;
console.log(url);
https.get( url, function( response ) {
response.on( 'data', function( data ) {
var resJSON = JSON.parse(data);
var speechText = 'The App on SmartThings did not return any message.';
if (resJSON.talk2me) { speechText = resJSON.talk2me; }
console.log(speechText);
var nextQ = askForNext[Math.floor(Math.random() * askForNext.length)];
if (areWeDone === false) { speechText = speechText + nextQ; }
output(speechText, context, areWeDone);
console.log("after the fact");
} );
} );
} else if (event.request.intent.name == "exodus") {
areWeDone=true;
var soLong = signOff[Math.floor(Math.random() * signOff.length)];
output(soLong, context, areWeDone);
} else if (event.request.intent.name == "AMAZON.StopIntent") {
areWeDone=true;
output("", context, areWeDone);
} else if (event.request.intent.name == "AMAZON.CancelIntent") {
areWeDone=true;
output("Canceling.", context, areWeDone);
}
};
function output( text, context, areWeDone ) {
var response = {
outputSpeech: {
type: "PlainText",
text: text
},
shouldEndSession: areWeDone
};
context.succeed( { response: response } );
}
So it seems the SmartApp is for some reason returning nothing to the Lambda function, and I have no idea why.
Any suggestions much appreciated.
-SiP