Alexa can speak with iot devices (bulbs, switches, …) directly without creating any skill. Recently I’ve discoverer the library fauxmoesp to use a ESP32 as a virtual device and use it with Alexa.
I want to create a simple example with my M5Stack (it’s basically one ESP32 with an screen). It’s pretty straightforward to do it.
Here the firmware that I deploy to my device
Then I only need to pair my virtual device (in this case “my device”) to my compatible Alexa Device (In my case one echo spot). We can do it with the Alexa device’s menu or simply saying “Alexa discover devices”. Then I’ve got my iot device paired to my Alexa and I can say something like: “Alexa, switch on my device”, “Alexa, switch off my device” or “Alexa, set my device at 50%”
Today I want to keep on with the previous example. This time I want to create one Alexa skill that listen to external events. The example that I’ve build is the following one:
I’ve got one ESP32 with a proximity sensor (one HC-SR04) that is sending the distance captured each 500ms to a MQTT broker (a mosquitto server).
I say “Alexa, use event demo” then Alexa tell me to put my hand close to the sensor and it will tell me the distance.
The process is similar to the previous example. There’s a GadgetInterceptor to find the endpointId of my Raspberry Pi. But now the Raspberry Pi must emit to the event to the skill, not the skill to the event. Now my rpi’s python script is a little bit more complicated. We need to start the main loop for the Alexa Gadget SDK but also we need to start another loop listening to a mqtt event. We need to use threads as we see in a previous post. That’s the script that I’m using.
We’re keeping on playing with Alexa. This time I want to create one skill that uses a compatible device (for example one Raspberry Pi 3). Here you can see the documentation and examples that the people of Alexa provides us. Basically we need to create a new Product/Alexa gadget in the console. It gives us one amazonId and alexaGadgetSecret.
Then we need to install the SDK in our Raspberry Pi (it install several python libraries). The we can create our gadget script running on our Raspberry Pi, using the amazonId and alexaGadgetSecret. We can listen to several Alexa events. For example: When we say the wakeword, with a timer, an alarm and also we can create our custom events.
I’m going to create one skill that allows me to say something like: “Alexa, use device demo and set color to green” or “Alexa, use device demo and set color to red” and I’ll put this color in the led matrix that I’ve got (one Raspberry Pi Sense Hat)
This is the python script:
import logging
import sys
import json
from agt import AlexaGadget
from sense_hat import SenseHat
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
logger = logging.getLogger(__name__)
sense = SenseHat()
sense.clear()
RED = [255, 0, 0]
GREEN = [0, 255, 0]
BLUE = [0, 0, 255]
YELLOW = [255, 255, 0]
BLACK = [0, 0, 0]
class Gadget(AlexaGadget):
def on_alexa_gadget_statelistener_stateupdate(self, directive):
for state in directive.payload.states:
if state.name == 'wakeword':
sense.clear()
if state.value == 'active':
sense.show_message("Alexa", text_colour=BLUE)
def set_color_to(self, color):
sense.set_pixels(([color] * 64))
def on_custom_gonzalo123_setcolor(self, directive):
payload = json.loads(directive.payload.decode("utf-8"))
color = payload['color']
if color == 'red':
logger.info('turn on RED display')
self.set_color_to(RED)
if color == 'green':
logger.info('turn on GREEN display')
self.set_color_to(GREEN)
if color == 'yellow':
logger.info('turn on YELLOW display')
self.set_color_to(YELLOW)
if color == 'black':
logger.info('turn on YELLOW display')
self.set_color_to(BLACK)
if __name__ == '__main__':
Gadget().main()
One important thing here is the GadgetInterceptor. This interceptor find the endpointId from the request and append it to the session. This endpoint exists because we’ve created our Product/Alexa gadget and also the python script is running on the device. If the endpoint isn’t present maybe our skill must say to the user something like “No gadgets found. Please try again after connecting your gadget.”.
Sometimes when we’re building one Alexa skill, we need to identify the user. To do that Alexa provides account linking. Basically we need an Oauth2 server to link our account within our Alexa skill. AWS provide us a managed Oauth2 service called Cognito, so we can use use Cognito identity pool to handle the authentication for our Alexa Skills.
In this example I’ve followed the following blog post. Cognito is is a bit weird to set up but after following all the steps we can use Account Linking in Alexa skill.
There’s also a good sample skill here. I’ve studied a little bit this example and create a working prototype by my own, basically to understand the process.
This interceptor retrieves the user info from cognito when we provide the accessToken. We can obtain the accessToken from session (if our skill is account linked). Then we inject the user information (in my example the email and the username of the Cognito identity pool) into the session.
Then we can create one intent in our request handlers chain called CheckAccountLinkedHandler. With this intent we check if our skill is account linked. If not we can provide ‘withLinkAccountCard’ to force user to login with Cognito and link the skill’s account.
And basically that’s all. In fact isn’t very different than traditional web authentication. Maybe the most complicated part especially if you’re not used to Oauth2 is to configure Cognito properly.
Today I want to play with Alexa and serverless framework. I’ve created a sample hello world skill. In fact this example is more or less the official hello-world sample.
I’m using Serverless framework to deploy the skill. I know that serverless frameworks has plugins for Alexa skills that help us to create the whole skill, but in this example I want to do it a little more manually (it’s the way that I learn new things).
First I create the skill in the Alexa developer console (or via ask cli). There’re a lot of tutorials about it. Then I take my alexaSkillId and I use this id within my serverless config file as the trigger event of my lambda function.
And I take the ARN of the lambda function and I use this lambda as my endpoint in the Alexa developer console.
Also we can test our skill (at least the lambda function) using our favorite testing framework. I will use jest in this example.
Testing is very important, at least for me, when I’m working with lambdas and serverless. I want to test my script locally, instead of deploying to AWS again and again (it’s slow).
const when = require('./steps/when')
const { init } = require('./steps/init')
describe('When we invoke the skill', () => {
beforeAll(() => {
init()
})
test('launch intent', async () => {
const res = await when.we_invoke_intent(require('./events/use_skill'))
const card = res.response.card
expect(card.title).toBe('Hello world')
expect(card.content).toBe('Welcome to Hello world, you can say Hello or Help. Which would you like to try?')
})
test('help handler', async () => {
const res = await when.we_invoke_intent(require('./events/help_handler'))
console.log(res.response.outputSpeech.ssml)
expect(res.response.outputSpeech.ssml).toBe('<speak>You can say hello to me! How can I help?</speak>')
})
test('Hello world handler', async () => {
const res = await when.we_invoke_intent(require('./events/hello_world_handler'))
const card = res.response.card
expect(card.title).toBe('Hello world')
expect(card.content).toBe('Hello World')
})
})