Last November's Black Friday event led to the delivery of an Amazon Echo Dot which has been sitting under utilised in the kitchen. I had seen that the likes of Hive and Tado were offering Skills to control their systems and not wanting to be left behind, I looked into how I could add similar functionality to my own system.
I was thinking along the lines of
- 'Alexa, boost the kitchen for an hour'
- 'Alexa, turn on the hot water pump'
- 'Alexa, who is calling for heat?'
but after some initial investigation I realised that this would require a custom skill and the building of a voice interaction model. This would be way more effort than the end feature would justify. But all was not lost as Amazon provide a simplified framework for home automation projects, the Smart Home Skill SDK.
Amazon's Smart Home Skill
This takes care of all the extra work by providing a standardized framework of voice commands which would be adequate for my purposes. Some example utterances would be:
- Alexa, discover my devices
- Alexa, set the living room to 20 degrees
- Alexa, turn on the hot water pump
- Alexa, set the <device name> to 50%
It looks like there are some 'get' commands too, but only available in the US at present.
I could certainly make use of 'turn on the hot water pump' and decided this would be a good first command to implement.
Getting Started with Smart Home Skills
Amazon provides a step by step guide to creating a Smart Home Skill, starting with a prerequisites list:
Signing up for the developer account and the AWS account were straightforward. The developer account is free and the AWS account has a free tier option for light use (requires a credit card though).
The OAuth part looked less straightforward and I was not sure how this was going to work out. However, I ploughed on through Amazon's step by step guide and discovered that for my purposes I could use Login with Amazon as the OAuth server. This worked fine.
The Amazon guide is very good and provides a dummy lambda function (in python) to get going with. The following steps were enough to get communication from Alexa through to the Lambda function and back.
It is important to take it slowly and follow every step. I missed the relevance of selecting the correct region for the Lambda function (EU Ireland in my case) and ended up with no obvious errors during testing, but with my Lambda function not getting called once I had enabled the skill. I wasted a lot of time getting to the bottom of this mistake.
After ironing out the region issue I was able to successfully ask Alexa to "Discover Devices" and could see the dummy device "Smart Home Virtual Device" appear in the Alexa App. Asking Alexa to "Turn On the Smart Home Virtual Device" resulted in an "OK" from Alexa.
The next steps in the guide are about submitting your skill for certification. These steps are not necessary for personal Alexa skills and can be ignored.
My next step was to replace the dummy lambda function with something that actually communicated with my raspberry pi. This turned out to be remarkably easy.
Adapting the Lambda Function
After completing the step by step guide I had a Lambda function sitting in the AWS cloud which was able to receive messages (directives) from Alexa via my Smart Home Skill. The lambda function was just sample code written in Python and which reported a single dummy device capable of actioning 'Turn On' and 'Turn Off' directives.
The code was a single inline python script with the main entry point
def lambda_handler(event, context):
I played around with this code for a while to get a feel for which fields were important and what happened when things weren't set up correctly. All of the directives supported by the Smart Home Skill are listed in The Smart Home Skill API Reference another excellent guide from Amazon.
The Directives I was most interested in were:
- DiscoverApplianceRequest / Response
- TurnOnRequest / Response
- TurnOffRequest / Response
- SetTargetTemperatureRequest / Confirmation
The DiscoverApplianceRequest directive is received at the lambda_handler function when I ask Alexa to discover devices (either via the Echo or through the app). The sample lambda_handler returns a DiscoverApplianceResponse with a single dummy device.
The most important fields in the device details are as follows:
The applianceId must be unique and the friendlyName is the name that Alexa will recognise when you communicate via speech.
The actions indicate which directives the device supports. The Smart Home Skill will only route through directives listed in the actions. So, if a device doesn't indicate 'setTargetTemperature' in the actions field no amount of shouting at Alexa to set the temperature will route the message through to the lambda_handler.
Communicating between my Lambda function and my raspberry pi
I altered the sample code to return a friendlyName of 'water pump'. Asking Alexa to 'turn on the water pump' resulted in a TurnOnRequest routing through to the lambda_function and a TurnOnResponse being returned. Now it was time to add the actual action of instructing the raspberry pi / zway to turn on the pump.
I already had a load of python scripts that I had used for testing so just copied a few functions into my lambda handler and hoped for the best.
def set_pumpstatus (cookie,id,payload):
headers={'accept':'application/json','Content-Type':'application/json','Cookie':'ZWAYSession='+cookie}
# now set the pump status
r = requests.post(pumpstatus_url+str(id),data=json.dumps(payload),headers=headers)
return r
Unfortunately the Python requests library is not included in the AWS lambda environment so I was unable to use the 'edit code inline' feature. This turned out to be a minor inconvenience as I just needed to follow the instructions on Creating a Deployment Package (Python) for AWS and upload a zip file with my code in lambda_function.py and the necessary requests library.
# this handles Control directives from Alexa
# it determines which device to control from the id
def handleControl(context, event):
payload = ''
header = ''
device_id = event['payload']['appliance']['applianceId']
message_id = event['header']['messageId']
control_type = event['header']['name']
headerTemplate = {
"namespace":"Alexa.ConnectedHome.Control",
"payloadVersion":"2",
"messageId": message_id
}
# is it a pump request
if device_id == pumpDeviceID:
if (control_type == 'TurnOnRequest') or (control_type == 'TurnOffRequest'):
cookie = login.send()
if control_type == 'TurnOnRequest':
pumpstatus.set_pumpstatus(cookie,1,{"data":"true"})
headerTemplate['name'] = "TurnOnConfirmation"
if event['header']['name'] == 'TurnOffRequest':
pumpstatus.set_pumpstatus(cookie,1,{"data":"false"})
headerTemplate['name'] = "TurnOffConfirmation"
header = headerTemplate
payload = { }
return { 'header': header, 'payload': payload }
# it determines which device to control from the id
def handleControl(context, event):
payload = ''
header = ''
device_id = event['payload']['appliance']['applianceId']
message_id = event['header']['messageId']
control_type = event['header']['name']
headerTemplate = {
"namespace":"Alexa.ConnectedHome.Control",
"payloadVersion":"2",
"messageId": message_id
}
# is it a pump request
if device_id == pumpDeviceID:
if (control_type == 'TurnOnRequest') or (control_type == 'TurnOffRequest'):
cookie = login.send()
if control_type == 'TurnOnRequest':
pumpstatus.set_pumpstatus(cookie,1,{"data":"true"})
headerTemplate['name'] = "TurnOnConfirmation"
if event['header']['name'] == 'TurnOffRequest':
pumpstatus.set_pumpstatus(cookie,1,{"data":"false"})
headerTemplate['name'] = "TurnOffConfirmation"
header = headerTemplate
payload = { }
return { 'header': header, 'payload': payload }
Remarkably, this went smoothly and within minutes I could turn on and off the water pump with my voice.
The 'devices' I require Alexa to manage are basically the water pump and the rooms (my heating zones). I left the water pump details hard coded into the lambda function but dynamically create the room list by requesting the information from my ZWay server.
The directives I am using for the rooms are as follows:
The lambda function can be found on github with my other zway python scripts.
And here is a demo of the Alexa app logging in to the Smart Home Skill, discovering devices and communicating with them. The status is refreshed on my Android app to show that the operation has been carried out.
Adding more devices and features
The 'devices' I require Alexa to manage are basically the water pump and the rooms (my heating zones). I left the water pump details hard coded into the lambda function but dynamically create the room list by requesting the information from my ZWay server.
The directives I am using for the rooms are as follows:
- TurnOn / TurnOff - sets boost mode / returns to previous mode
- SetTargetTemperature - sets boost mode and sets the boost temperature
- SetPercentage - sets boost mode and uses the % value for the boost duration 1%=1 hour etc.
The lambda function can be found on github with my other zway python scripts.
And here is a demo of the Alexa app logging in to the Smart Home Skill, discovering devices and communicating with them. The status is refreshed on my Android app to show that the operation has been carried out.