Building Bluetooth iot devices compatible with Alexa

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

[sourcecode language=”c”]
#include <M5Stack.h>
#include <WiFi.h>
#include "fauxmoESP.h"

#define SERIAL_BAUDRATE 115200

#define WIFI_SSID "my_SSOD"
#define WIFI_PASS "my_password"

#define DEVICE_1 "my device"

fauxmoESP fauxmo;
bool current_state;
bool change_flag;
int current_value;

void wifiSetup() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);

while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.printf("WIFI coneccted! IP: %s\n", WiFi.localIP().toString().c_str());
}

void setup() {
Serial.begin(SERIAL_BAUDRATE);
Serial.println();
M5.begin();
M5.Power.begin();

wifiSetup();

current_state = false;
current_value = 0;
change_flag = false;

fauxmo.createServer(true);
fauxmo.setPort(80);
fauxmo.enable(true);
fauxmo.addDevice(DEVICE_1);

fauxmo.onSetState([](unsigned char device_id, const char * device_name, bool state, unsigned char value) {
Serial.printf("Device #%d (%s) state: %s value: %d\n", device_id, device_name, state ? "ON" : "OFF", value);
current_value = value * 100 / 254;
current_state = state;
change_flag = true;
});
}

void loop() {
fauxmo.handle();

static unsigned long last = millis();
if (millis() – last > 5000) {
last = millis();
Serial.printf("[MAIN] Free heap: %d bytes\n", ESP.getFreeHeap());
}
if (change_flag) {
M5.Lcd.fillScreen(current_state ? GREEN : RED);
M5.Lcd.setCursor(100, 100);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(6);
M5.Lcd.print(current_state ? current_value : 0);
change_flag = false;
}
}
[/sourcecode]

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%”

Here a small video showing the working example:

Alexa and Raspberry Pi demo (Part 2). Listening to external events

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.

That’s the ESP32 code

[sourcecode language=”c”]
#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "MY_SSID";
const char* password = "MY_PASSWORD";
const char* server = "mqtt.server.ip";
const char* topic = "/alert";
const char* clientName = "com.gonzalo123.esp32";

WiFiClient wifiClient;
PubSubClient client(wifiClient);

int trigPin = 13;
int echoPin = 12;
int alert = 0;
int alertThreshold = 100;
long duration, distance_cm;

void wifiConnect() {
Serial.print("Connecting to ");
Serial.println(ssid);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print("*");
}

Serial.print("WiFi connected: ");
Serial.println(WiFi.localIP());
}

void mqttReConnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection…");
if (client.connect(clientName)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}

void mqttEmit(String topic, String value)
{
client.publish((char*) topic.c_str(), (char*) value.c_str());
}

void setup() {
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);

wifiConnect();
client.setServer(server, 1883);

delay(1500);
}

void loop() {
if (!client.connected()) {
mqttReConnect();
}

client.loop();

digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH) / 2;
distance_cm = duration / 29;

if (distance_cm <= alertThreshold && alert == 0) {
alert = 1;
Serial.println("Alert!");
mqttEmit("/alert", (String) distance_cm);
} else if(distance_cm > alertThreshold && alert == 1) {
alert = 0;
Serial.println("No alert");
}

Serial.print("Distance: ");
Serial.print(distance_cm);
Serial.println(" cm ");

delay(500);
}
[/sourcecode]

And this is my alexa skill:

[sourcecode language=”javascript”]
‘use strict’

const Alexa = require(‘ask-sdk-core’)

const RequestInterceptor = require(‘./interceptors/RequestInterceptor’)
const ResponseInterceptor = require(‘./interceptors/ResponseInterceptor’)
const LocalizationInterceptor = require(‘./interceptors/LocalizationInterceptor’)
const GadgetInterceptor = require(‘./interceptors/GadgetInterceptor’)

const YesIntentHandler = require(‘./handlers/YesIntentHandler’)
const NoIntentHandler = require(‘./handlers/NoIntentHandler’)
const LaunchRequestHandler = require(‘./handlers/LaunchRequestHandler’)
const CancelAndStopIntentHandler = require(‘./handlers/CancelAndStopIntentHandler’)
const SessionEndedRequestHandler = require(‘./handlers/SessionEndedRequestHandler’)
const CustomInterfaceEventHandler = require(‘./handlers/CustomInterfaceEventHandler’)
const CustomInterfaceExpirationHandler = require(‘./handlers/CustomInterfaceExpirationHandler’)

const FallbackHandler = require(‘./handlers/FallbackHandler’)
const ErrorHandler = require(‘./handlers/ErrorHandler’)

let skill
exports.handler = function (event, context) {
if (!skill) {
skill = Alexa.SkillBuilders.custom().
addRequestHandlers(
LaunchRequestHandler,
YesIntentHandler,
NoIntentHandler,
CustomInterfaceEventHandler,
CustomInterfaceExpirationHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
FallbackHandler).
addRequestInterceptors(
RequestInterceptor,
ResponseInterceptor,
LocalizationInterceptor,
GadgetInterceptor).
addErrorHandlers(ErrorHandler).create()
}
return skill.invoke(event, context)
}
[/sourcecode]

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.

[sourcecode language=”python”]
from queue import Queue, Empty
import threading
import logging
import sys
import paho.mqtt.client as mqtt
from agt import AlexaGadget

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
logger = logging.getLogger(__name__)

class Listener(threading.Thread):
def __init__(self, queue=Queue()):
super(Listener, self).__init__()
self.queue = queue
self.daemon = True

def on_connect(self, client, userdata, flags, rc):
print("Connected!")
client.subscribe("/alert")

def on_message(self, client, userdata, msg):
self.on_event(int(msg.payload))

def on_event(self, cm):
pass

def run(self):
client = mqtt.Client()
client.on_connect = self.on_connect
client.on_message = self.on_message

client.connect(‘192.168.1.87’, 1883, 60)
client.loop_forever()

class Gadget(AlexaGadget):
def __init__(self):
super().__init__()
Listener.on_event = self._emit_event

def _emit_event(self, cm):
logger.info("_emit_event {}".format(cm))
payload = {‘cm’: cm}
self.send_custom_event(‘Custom.gonzalo123’, ‘sensor’, payload)

l = Listener()
l.start()

def main():
gadget = Gadget()
gadget.main()

if __name__ == ‘__main__’:
main()
[/sourcecode]

And that’s all. It’d be good if I could wake up my skill directly from my iot device, instead of waking up manually, but AFAIK that’s not possible.

Source code in my github

Alexa and Raspberry Pi demo

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:

[sourcecode language=”python”]
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()
[/sourcecode]

And here the ini file of our gadget

[sourcecode language=”xml”]
[GadgetSettings]
amazonId = my_amazonId
alexaGadgetSecret = my_alexaGadgetSecret

[GadgetCapabilities]
Alexa.Gadget.StateListener = 1.0 – wakeword
Custom.gonzalo123 = 1.0
[/sourcecode]

Whit this ini file I’m saying that my gadget will trigger a function when wakeword has been said and with my custom event “Custom.gonzalo123″

That’s the skill:

[sourcecode language=”javascript”]
const Alexa = require(‘ask-sdk’)

const RequestInterceptor = require(‘./interceptors/RequestInterceptor’)
const ResponseInterceptor = require(‘./interceptors/ResponseInterceptor’)
const LocalizationInterceptor = require(‘./interceptors/LocalizationInterceptor’)
const GadgetInterceptor = require(‘./interceptors/GadgetInterceptor’)

const LaunchRequestHandler = require(‘./handlers/LaunchRequestHandler’)
const ColorHandler = require(‘./handlers/ColorHandler’)
const HelpIntentHandler = require(‘./handlers/HelpIntentHandler’)
const CancelAndStopIntentHandler = require(‘./handlers/CancelAndStopIntentHandler’)
const SessionEndedRequestHandler = require(‘./handlers/SessionEndedRequestHandler’)
const FallbackHandler = require(‘./handlers/FallbackHandler’)
const ErrorHandler = require(‘./handlers/ErrorHandler’)

let skill

module.exports.handler = async (event, context) => {
if (!skill) {
skill = Alexa.SkillBuilders.custom().
addRequestInterceptors(
RequestInterceptor,
ResponseInterceptor,
LocalizationInterceptor,
GadgetInterceptor
).
addRequestHandlers(
LaunchRequestHandler,
ColorHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
FallbackHandler).
addErrorHandlers(
ErrorHandler).
create()
}

return await skill.invoke(event, context)
}
[/sourcecode]

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.”.

[sourcecode language=”javascript”]
const utils = require(‘../lib/utils’)

const GadgetInterceptor = {
async process (handlerInput) {
const endpointId = await utils.getEndpointIdFromConnectedEndpoints(handlerInput)
if (endpointId) {
utils.appendToSession(handlerInput, ‘endpointId’, endpointId)
}
}
}

module.exports = GadgetInterceptor
[/sourcecode]

And finally the handlers to trigger the custom event:

[sourcecode language=”javascript”]
const log = require(‘../lib/log’)
const utils = require(‘../lib/utils’)

const buildLEDDirective = (endpointId, color) => {
return {
type: ‘CustomInterfaceController.SendDirective’,
header: {
name: ‘setColor’,
namespace: ‘Custom.gonzalo123’
},
endpoint: {
endpointId: endpointId
},
payload: {
color: color
}
}
}

const ColorHandler = {
canHandle (handlerInput) {
return handlerInput.requestEnvelope.request.type === ‘IntentRequest’
&& handlerInput.requestEnvelope.request.intent.name === ‘ColorIntent’
},
handle (handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes()
const cardTitle = requestAttributes.t(‘SKILL_NAME’)

const endpointId = utils.getEndpointIdFromSession(handlerInput)
if (!endpointId) {
log.error(‘endpoint’, error)
return handlerInput.responseBuilder.
speak(error).
getResponse()
}

const color = handlerInput.requestEnvelope.request.intent.slots[‘color’].value
log.info(‘color’, color)
log.info(‘endpointId’, endpointId)

return handlerInput.responseBuilder.
speak(`Ok. The selected color is ${color}`).
withSimpleCard(cardTitle, color).
addDirective(buildLEDDirective(endpointId, color)).
getResponse()
}
}

module.exports = ColorHandler
[/sourcecode]

Here one video with the working example:

Source code in my github

Alexa skill and account linking with serverless and Cognito

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.

That’s my skill:
[sourcecode language=”javascript”]
const Alexa = require(‘ask-sdk’)

const RequestInterceptor = require(‘./interceptors/RequestInterceptor’)
const ResponseInterceptor = require(‘./interceptors/ResponseInterceptor’)
const LocalizationInterceptor = require(‘./interceptors/LocalizationInterceptor’)
const GetLinkedInfoInterceptor = require(‘./interceptors/GetLinkedInfoInterceptor’)

const LaunchRequestHandler = require(‘./handlers/LaunchRequestHandler’)
const CheckAccountLinkedHandler = require(‘./handlers/CheckAccountLinkedHandler’)
const HelloWorldIntentHandler = require(‘./handlers/HelloWorldIntentHandler’)
const HelpIntentHandler = require(‘./handlers/HelpIntentHandler’)
const CancelAndStopIntentHandler = require(‘./handlers/CancelAndStopIntentHandler’)
const SessionEndedRequestHandler = require(‘./handlers/SessionEndedRequestHandler’)
const FallbackHandler = require(‘./handlers/FallbackHandler’)
const ErrorHandler = require(‘./handlers/ErrorHandler’)
const RequestInfoHandler = require(‘./handlers/RequestInfoHandler’)

let skill

module.exports.handler = async (event, context) => {
if (!skill) {
skill = Alexa.SkillBuilders.custom().
addRequestInterceptors(
RequestInterceptor,
ResponseInterceptor,
LocalizationInterceptor,
GetLinkedInfoInterceptor
).
addRequestHandlers(
LaunchRequestHandler,
CheckAccountLinkedHandler,
HelloWorldIntentHandler,
RequestInfoHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
FallbackHandler).
addErrorHandlers(
ErrorHandler).
create()
}

return await skill.invoke(event, context)
}
[/sourcecode]

The most important thing here is maybe GetLinkedInfoInterceptor.

[sourcecode language=”javascript”]
const log = require(‘../lib/log’)
const cognito = require(‘../lib/cognito’)
const utils = require(‘../lib/utils’)

const GetLinkedInfoInterceptor = {
async process (handlerInput) {
if (utils.isAccountLinked(handlerInput)) {
const userData = await cognito.getUserData(handlerInput.requestEnvelope.session.user.accessToken)
log.info(‘GetLinkedInfoInterceptor: getUserData: ‘, userData)
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes()
if (userData.Username !== undefined) {
sessionAttributes.auth = true
sessionAttributes.emailAddress = cognito.getAttribute(userData.UserAttributes, ’email’)
sessionAttributes.userName = userData.Username
handlerInput.attributesManager.setSessionAttributes(sessionAttributes)
} else {
sessionAttributes.auth = false
log.error(‘GetLinkedInfoInterceptor: No user data was found.’)
}
}
}
}

module.exports = GetLinkedInfoInterceptor
[/sourcecode]

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.

[sourcecode language=”javascript”]
const utils = require(‘../lib/utils’)

const CheckAccountLinkedHandler = {
canHandle (handlerInput) {
return !utils.isAccountLinked(handlerInput)
},
handle (handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes()
const speakOutput = requestAttributes.t(‘NEED_TO_LINK_MESSAGE’, ‘SKILL_NAME’)
return handlerInput.responseBuilder.
speak(speakOutput).
withLinkAccountCard().
getResponse()
}
}

module.exports = CheckAccountLinkedHandler
[/sourcecode]

Later we can create one intent to give the information to the user of maybe, in another case, perform an authorization workflow

[sourcecode language=”javascript”]
const RequestInfoHandler = {
canHandle (handlerInput) {
const request = handlerInput.requestEnvelope.request
return (request.type === ‘IntentRequest’
&& request.intent.name === ‘RequestInfoIntent’)
},
handle (handlerInput) {
const request = handlerInput.requestEnvelope.request
const requestAttributes = handlerInput.attributesManager.getRequestAttributes()
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes()
const repromptOutput = requestAttributes.t(‘FOLLOW_UP_MESSAGE’)
const cardTitle = requestAttributes.t(‘SKILL_NAME’)

let speakOutput = ”

let inquiryTypeId = getResolvedSlotIDValue(request, ‘infoTypeRequested’)
if (!inquiryTypeId) {
inquiryTypeId = ‘fullProfile’
speakOutput += requestAttributes.t(‘NOT_SURE_OF_TYPE_MESSAGE’)
} else {
if (inquiryTypeId === ’emailAddress’ || inquiryTypeId === ‘fullProfile’) {
speakOutput += requestAttributes.t(‘REPORT_EMAIL_ADDRESS’, sessionAttributes.emailAddress)
}

if (inquiryTypeId === ‘userName’ || inquiryTypeId === ‘fullProfile’) {
speakOutput += requestAttributes.t(‘REPORT_USERNAME’, sessionAttributes.userName)
}
}

speakOutput += repromptOutput

return handlerInput.responseBuilder.
speak(speakOutput).
reprompt(repromptOutput).
withSimpleCard(cardTitle, speakOutput).
getResponse()
}
}

module.exports = RequestInfoHandler
[/sourcecode]

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.

Here you can see the source code in my github.

Playing with threads and Python. Part 2

Today I want to keep on playing with python and threads (part1 here). The idea is create one simple script that prints one asterisk in the console each second. Simple, isn’t it?

[sourcecode language=”python”]
from time import sleep
while True:
print("*")
sleep(1)
[/sourcecode]

I want to keep this script running but I want to send one message externally, for example using RabbitMQ, and do something within the running script. In this demo, for example, stop the script.

In javascript we can do it with a single tread process using the setInterval function but, since the rabbit listener with pika is a blocking action, we need to use threads in Python (please tell me if I’m wrong). The idea is to create a circuit breaker condition in the main loop to check if I need to stop or not the main thread.

First I’ve created my Rabbit listener in a thread:

[sourcecode language=”python”]
from queue import Queue, Empty
import threading
import pika
import os

class Listener(threading.Thread):
def __init__(self, queue=Queue()):
super(Listener, self).__init__()
self.queue = queue
self.daemon = True

def run(self):
channel = self._get_channel()
channel.queue_declare(queue=’stop’)

channel.basic_consume(
queue=’stop’,
on_message_callback=lambda ch, method, properties, body: self.queue.put(item=True),
auto_ack=True)

channel.start_consuming()

def stop(self):
try:
return True if self.queue.get(timeout=0.05) is True else False
except Empty:
return False

def _get_channel(self):
credentials = pika.PlainCredentials(
username=os.getenv(‘RABBITMQ_USER’),
password=os.getenv(‘RABBITMQ_PASS’))

parameters = pika.ConnectionParameters(
host=os.getenv(‘RABBITMQ_HOST’),
credentials=credentials)

connection = pika.BlockingConnection(parameters=parameters)

return connection.channel()
[/sourcecode]

Now in the main process I start the Listener and I enter in one endless loop to print my asterisk each second but at the end of each loop I check if I need to stop the process or not

[sourcecode language=”python”]
from Listener import Listener
from dotenv import load_dotenv
import logging
from time import sleep
import os

logging.basicConfig(level=logging.INFO)

current_dir = os.path.dirname(os.path.abspath(__file__))
load_dotenv(dotenv_path="{}/.env".format(current_dir))

l = Listener()
l.start()

def main():
while True:
logging.info("*")
sleep(1)
if l.stop():
break

if __name__ == ‘__main__’:
main()
[/sourcecode]

As we can see int the stop function we’re using the queue.Queue package to communicate with our listener loop.

And that’s all. In the example I also provide a minimal RabbitMQ server in a docker container.

[sourcecode language=”xml”]
version: ‘3.4’

services:
rabbit:
image: rabbitmq:3-management
restart: always
environment:
RABBITMQ_ERLANG_COOKIE:
RABBITMQ_DEFAULT_VHOST: /
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS}
ports:
– "15672:15672"
– "5672:5672"

[/sourcecode]

Source code available in my github

Alexa skill example with Serverless framework

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.

[sourcecode language=”javascript”]
const Alexa = require(‘ask-sdk-core’)
const RequestInterceptor = require(‘./interceptors/RequestInterceptor’)
const ResponseInterceptor = require(‘./interceptors/ResponseInterceptor’)
const LaunchRequestHandler = require(‘./handlers/LaunchRequestHandler’)
const HelloWorldIntentHandler = require(‘./handlers/HelloWorldIntentHandler’)
const HelpIntentHandler = require(‘./handlers/HelpIntentHandler’)
const CancelAndStopIntentHandler = require(‘./handlers/CancelAndStopIntentHandler’)
const SessionEndedRequestHandler = require(‘./handlers/SessionEndedRequestHandler’)
const FallbackHandler = require(‘./handlers/FallbackHandler’)
const ErrorHandler = require(‘./handlers/ErrorHandler’)

let skill

module.exports.handler = async (event, context) => {
if (!skill) {
skill = Alexa.SkillBuilders.custom().
addRequestInterceptors(RequestInterceptor).
addResponseInterceptors(ResponseInterceptor).
addRequestHandlers(
LaunchRequestHandler,
HelloWorldIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
FallbackHandler).
addErrorHandlers(
ErrorHandler).
create()
}

return await skill.invoke(event, context)
}
[/sourcecode]

It has one Intent that answers to hello command.

[sourcecode language=”javascript”]
const HelloWorldIntentHandler = {
canHandle (handlerInput) {
return handlerInput.requestEnvelope.request.type === ‘IntentRequest’
&& handlerInput.requestEnvelope.request.intent.name === ‘HelloWorldIntent’
},
handle (handlerInput) {
const speechOutput = ‘Hello World’
const cardTitle = ‘Hello world’

return handlerInput.responseBuilder.
speak(speechOutput).
reprompt(speechOutput).
withSimpleCard(cardTitle, speechOutput).
getResponse()
}
}

module.exports = HelloWorldIntentHandler
[/sourcecode]

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.

[sourcecode language=”xml”]
service: hello-world

provider:
name: aws
runtime: nodejs8.10
region: ${opt:region, self:custom.defaultRegion}
stage: ${opt:stage, self:custom.defaultStage}

custom:
defaultRegion: eu-west-1
defaultStage: prod

functions:
info:
handler: src/index.handler
events:
– alexaSkill: amzn1.ask.skill.my_skill
[/sourcecode]

then I deploy the lambda function

npx serverless deploy –aws-s3-accelerate

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).

[sourcecode language=”javascript”]
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’)
})
})
[/sourcecode]

Full code in my github account.

Playing with threads and Python

Today I want to play a little bit with Python and threads. This kind of post are one kind of cheat sheet that I like to publish, basically to remember how do do things.

I don’t like to use threads. They are very powerful but, as uncle Ben said: Great power comes great responsibility. I prefer decouple process with queues instead using threads, but sometimes I need to use them. Let’s start

First I will build a simple script without threads. This script will append three numbers (1, 2 and 3) to a list and it will sum them. The function that append numbers to the list sleeps a number of seconds equals to the number that we’re appending.

[sourcecode language=”python”]
import time

start_time = time.time()

total = []

def sleeper(seconds):
time.sleep(seconds)
total.append(seconds)

for s in (1, 2, 3):
sleeper(s)

end_time = time.time()

total_time = end_time – start_time

print("Total time: {:.3} seconds. Sum: {}".format(total_time, sum(total)))
[/sourcecode]

The the outcome of the script is obviously 6 (1 + 2 + 3) and as we’re sleeping the script a number of seconds equals to the the number that we’re appending our script, it takes 6 seconds to finish.

Now we’re going to use a threading version of the script. We’re going to do the same, but we’re going to use one thread for each append (with the same sleep function)

[sourcecode language=”python”]
import time
from queue import Queue
import threading

start_time = time.time()

queue = Queue()

def sleeper(seconds):
time.sleep(seconds)
queue.put(seconds)

threads = []
for s in (1, 2, 3):
t = threading.Thread(target=sleeper, args=(s,))
threads.append(t)
t.start()

for one_thread in threads:
one_thread.join()

total = 0
while not queue.empty():
total = total + queue.get()

end_time = time.time()

total_time = end_time – start_time
print("Total time: {:.3} seconds. Sum: {}".format(total_time, total))
[/sourcecode]

The outcome of our script is 6 again, but now it takes 3 seconds (the highest sleep).

Source code in my github

Playing with TOTP (2FA) and mobile applications with ionic

Today I want to play with Two Factor Authentication. When we speak about 2FA, TOTP come to our mind. There’re a lot of TOTP clients, for example Google Authenticator.

My idea with this prototype is to build one Mobile application (with ionic) and validate one totp token in a server (in this case a Python/Flask application). The token will be generated with a standard TOTP client. Let’s start

The sever will be a simple Flask server to handle routes. One route (GET /) will generate one QR code to allow us to configure or TOTP client. I’m using the library pyotp to handle totp operations.

[sourcecode language=”python”]
from flask import Flask, jsonify, abort, render_template, request
import os
from dotenv import load_dotenv
from functools import wraps
import pyotp
from flask_qrcode import QRcode

current_dir = os.path.dirname(os.path.abspath(__file__))
load_dotenv(dotenv_path="{}/.env".format(current_dir))

totp = pyotp.TOTP(os.getenv(‘TOTP_BASE32_SECRET’))

app = Flask(__name__)
QRcode(app)

def verify(key):
return totp.verify(key)

def authorize(f):
@wraps(f)
def decorated_function(*args, **kws):
if not ‘Authorization’ in request.headers:
abort(401)

data = request.headers[‘Authorization’]
token = str.replace(str(data), ‘Bearer ‘, ”)

if token != os.getenv(‘BEARER’):
abort(401)

return f(*args, **kws)

return decorated_function

@app.route(‘/’)
def index():
return render_template(‘index.html’, totp=pyotp.totp.TOTP(os.getenv(‘TOTP_BASE32_SECRET’)).provisioning_uri("gonzalo123.com", issuer_name="TOTP Example"))

@app.route(‘/check/<key>’, methods=[‘GET’])
@authorize
def alert(key):
status = verify(key)
return jsonify({‘status’: status})

if __name__ == "__main__":
app.run(host=’0.0.0.0′)
[/sourcecode]

I’ll use an standard TOTP client to generate the tokens but with pyotp we can easily create a client also

[sourcecode language=”python”]
import pyotp
import time
import os
from dotenv import load_dotenv
import logging

logging.basicConfig(level=logging.INFO)

current_dir = os.path.dirname(os.path.abspath(__file__))
load_dotenv(dotenv_path="{}/.env".format(current_dir))

totp = pyotp.TOTP(os.getenv(‘TOTP_BASE32_SECRET’))

mem = None
while True:
now = totp.now()
if mem != now:
logging.info(now)
mem = now
time.sleep(1)
[/sourcecode]

And finally the mobile application. It’s a simple ionic application. That’s the view:

[sourcecode language=”html”]
<ion-header>
<ion-toolbar>
<ion-title>
TOTP Validation demo
</ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<div class="ion-padding">
<ion-item>
<ion-label position="stacked">totp</ion-label>
<ion-input placeholder="Enter value" [(ngModel)]="totp"></ion-input>
</ion-item>
<ion-button fill="solid" color="secondary" (click)="validate()" [disabled]="!totp">
Validate
<ion-icon slot="end" name="help-circle-outline"></ion-icon>
</ion-button>
</div>
</ion-content>
[/sourcecode]

The controller:

[sourcecode language=”js”]
import { Component } from ‘@angular/core’
import { ApiService } from ‘../sercices/api.service’
import { ToastController } from ‘@ionic/angular’

@Component({
selector: ‘app-home’,
templateUrl: ‘home.page.html’,
styleUrls: [‘home.page.scss’]
})
export class HomePage {
public totp

constructor (private api: ApiService, public toastController: ToastController) {}

validate () {
this.api.get(‘/check/’ + this.totp).then(data => this.alert(data.status))
}

async alert (status) {
const toast = await this.toastController.create({
message: status ? ‘OK’ : ‘Not valid code’,
duration: 2000,
color: status ? ‘primary’ : ‘danger’,
})
toast.present()
}
}
[/sourcecode]

I’ve also put a simple security system. In a real life application we’ll need something better, but here I’ve got a Auth Bearer harcoded and I send it en every http request. To do it I’ve created a simple api service

[sourcecode language=”js”]
import { Injectable } from ‘@angular/core’
import { isDevMode } from ‘@angular/core’
import { HttpClient, HttpHeaders, HttpParams } from ‘@angular/common/http’
import { CONF } from ‘./conf’

@Injectable({
providedIn: ‘root’
})
export class ApiService {

private isDev: boolean = isDevMode()
private apiUrl: string

constructor (private http: HttpClient) {
this.apiUrl = this.isDev ? CONF.API_DEV : CONF.API_PROD
}

public get (uri: string, params?: Object): Promise<any> {
return new Promise((resolve, reject) => {
this.http.get(this.apiUrl + uri, {
headers: ApiService.getHeaders(),
params: ApiService.getParams(params)
}).subscribe(
res => {this.handleHttpNext(res), resolve(res)},
err => {this.handleHttpError(err), reject(err)},
() => this.handleHttpComplete()
)
})
}

private static getHeaders (): HttpHeaders {

const headers = {
‘Content-Type’: ‘application/json’
}

headers[‘Authorization’] = ‘Bearer ‘ + CONF.bearer

return new HttpHeaders(headers)
}

private static getParams (params?: Object): HttpParams {
let Params = new HttpParams()
for (const key in params) {
if (params.hasOwnProperty(key)) {
Params = Params.set(key, params[key])
}
}

return Params
}

private handleHttpError (err) {
console.log(‘HTTP Error’, err)
}

private handleHttpNext (res) {
console.log(‘HTTP response’, res)
}

private handleHttpComplete () {
console.log(‘HTTP request completed.’)
}
}
[/sourcecode]

And that’s all. Here one video with a working example of the prototype:

Source code here

PHP/Lumen data source for Grafana

Today I need to integrate a third party service into Grafana. I cannot access directly to the service’s database, so I will integrate via JSON datasource. Grafana allows us to build custom data sources but in this case I don’t need to create a new one. I can use the simple JSON datasource

grafana-cli plugins install grafana-simple-json-datasource

Now I need to create one REST server to serve the data that our JSON datasource needs. According to the documentation we need three routes:

  • GET / should return 200 ok.
  • POST /search used by the find metric options on the query tab in panels.
  • POST /query should return metrics based on input.
  • POST /annotations should return annotations.

We’re going to create a PHP/Lumen server. Basically the routes of the application are those ones:
[sourcecode language=”php”]
<?php

use Laravel\Lumen\Routing\Router;
use App\Http\Middleware;
use Laravel\Lumen\Application;
use Dotenv\Dotenv;
use App\Http\Handlers;

require_once __DIR__ . ‘/../vendor/autoload.php’;

(Dotenv::create(__DIR__ . ‘/../env/local’))->load();

$app = new Application(dirname(__DIR__));
$app->middleware([
Middleware\CorsMiddleware::class,
]);

$app->router->group([‘middleware’ => Middleware\AuthMiddleware::class], function (Router $router) {
$router->get(‘/’, Handlers\HelloHandler::class);
$router->post(‘/search’, Handlers\SearchHandler::class);
$router->post(‘/query’, Handlers\QueryHandler::class);
$router->post(‘/annotations’, Handlers\AnnotationHandler::class);
});

return $app;
[/sourcecode]

We need to take care with CORS. I will use the Middleware that I normally use in those cases

[sourcecode language=”php”]
<?php

namespace App\Http\Middleware;

use Closure;

class CorsMiddleware
{
public function handle($request, Closure $next)
{
$headers = [
‘Access-Control-Allow-Origin’ => ‘http://localhost:3000&#8217;,
‘Access-Control-Allow-Methods’ => ‘POST, GET, OPTIONS, PUT, DELETE’,
‘Access-Control-Allow-Credentials’ => ‘true’,
‘Access-Control-Max-Age’ => ‘86400’,
‘Access-Control-Allow-Headers’ => ‘accept, content-type, Content-Type, Authorization, X-Requested-With’,
];

if ($request->isMethod(‘OPTIONS’)) {
return response()->json(‘{"method":"OPTIONS"}’, 200, $headers);
}

$response = $next($request);
foreach ($headers as $key => $value) {
$response->header($key, $value);
}

return $response;
}
}
[/sourcecode]

I’ll use also a basic authentication so we’ll use a simple Http Basic Authentication middleware
[sourcecode language=”php”]
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class AuthMiddleware
{
const NAME = ‘auth.web’;

public function handle(Request $request, Closure $next)
{
if ($request->getUser() != env(‘HTTP_USER’) || $request->getPassword() != env(‘HTTP_PASS’)) {
$headers = [‘WWW-Authenticate’ => ‘Basic’];

return response(‘Unauthorized’, 401, $headers);
}

return $next($request);
}
}
[/sourcecode]

HelloHandler is a dummy route that the datasource needs to check the connection. We only need to answer with a 200-OK

[sourcecode language=”php”]
<?php
namespace App\Http\Handlers;

class HelloHandler
{
public function __invoke()
{
return "Ok";
}
}
[/sourcecode]

SearchHandler will return the list of available metrics that we´ll use within our grafana panels. They aren’t strictly necessary. We can return an empty array and use later one metric that it isn’t defined here (it’s only to fill the combo that grafana shows us)

[sourcecode language=”php”]
<?php
namespace App\Http\Handlers;

class SearchHandler
{
public function __invoke()
{
return [25, 50, 100];
}
}
“`

QueryHandler is an important one. Here we’ll return the datapoints that we´ll show in grafana. For testing purposes I’ve created one handler that read the metric, and the date from and date to that grafana sends to the backend and return a random values for several metrics and fixed ones to the rest. It’s basically to see something in grafana. Later, in the real life project, I’ll query the database and return real data.

[sourcecode language="php"]
<?php

namespace App\Http\Handlers;

use Illuminate\Http\Request;

class QueryHandler
{
public function __invoke(Request $request)
{
$json = $request->json();
$range = $json->get(‘range’);
$target = $json->get(‘targets’)[0][‘target’];

$tz = new \DateTimeZone(‘Europe/Madrid’);
$from = \DateTimeImmutable::createFromFormat("Y-m-d\TH:i:s.uP", $range[‘from’], $tz);
$to = \DateTimeImmutable::createFromFormat("Y-m-d\TH:i:s.uP", $range[‘to’], $tz);

return [‘target’ => $target, ‘datapoints’ => $this->getDataPoints($from, $to, $target)];
}

private function getDataPoints($from, $to, $target)
{
$interval = new \DateInterval(‘PT1H’);
$period = new \DatePeriod($from, $interval, $to->add($interval));

$dataPoints = [];
foreach ($period as $date) {
$value = $target > 50 ? rand(0, 100) : $target;
$dataPoints[] = [$value, strtotime($date->format(‘Y-m-d H:i:sP’)) * 1000];
}

return $dataPoints;
}
}
[/sourcecode]

Also I’ll like to use annotations. It’s something similar. AnnotationHandler will handle this request. For this test I’ve created two types of annotations: One each hour and another one each 6 hours

[sourcecode language=”php”]
<?php

namespace App\Http\Handlers;

use Illuminate\Http\Request;

class AnnotationHandler
{
public function __invoke(Request $request)
{
$json = $request->json();
$annotation = $json->get(‘annotation’);
$range = $json->get(‘range’);

return $this->getAnnotations($annotation, $range);
}

private function getAnnotations($annotation, $range)
{
return $this->getValues($range, ‘PT’ . $annotation[‘query’] . ‘H’);
}

private function getValues($range, $int)
{
$tz = new \DateTimeZone(‘Europe/Madrid’);
$from = \DateTimeImmutable::createFromFormat("Y-m-d\TH:i:s.uP", $range[‘from’], $tz);
$to = \DateTimeImmutable::createFromFormat("Y-m-d\TH:i:s.uP", $range[‘to’], $tz);

$annotation = [
‘name’ => $int,
‘enabled’ => true,
‘datasource’ => "gonzalo datasource",
‘showLine’ => true,
];

$interval = new \DateInterval($int);
$period = new \DatePeriod($from, $interval, $to->add($interval));

$annotations = [];
foreach ($period as $date) {
$annotations[] = [‘annotation’ => $annotation, "title" => "H " . $date->format(‘H’), "time" => strtotime($date->format(‘Y-m-d H:i:sP’)) * 1000, ‘text’ => "teeext"];
}

return $annotations;
}
}
[/sourcecode]

And that’s all. I’ve also put the whole example in a docker-compose file to test it

[sourcecode language=”xml”]
version: ‘2’

services:
nginx:
image: gonzalo123.nginx
restart: always
ports:
– "80:80"
build:
context: ./src
dockerfile: .docker/Dockerfile-nginx
volumes:
– ./src/api:/code/src
api:
image: gonzalo123.api
restart: always
build:
context: ./src
dockerfile: .docker/Dockerfile-lumen-dev
environment:
XDEBUG_CONFIG: remote_host=${MY_IP}
volumes:
– ./src/api:/code/src
grafana:
image: gonzalo123.grafana
build:
context: ./src
dockerfile: .docker/Dockerfile-grafana
restart: always
environment:
– GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER}
– GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD}
– GF_USERS_DEFAULT_THEME=${GF_USERS_DEFAULT_THEME}
– GF_USERS_ALLOW_SIGN_UP=${GF_USERS_ALLOW_SIGN_UP}
– GF_USERS_ALLOW_ORG_CREATE=${GF_USERS_ALLOW_ORG_CREATE}
– GF_AUTH_ANONYMOUS_ENABLED=${GF_AUTH_ANONYMOUS_ENABLED}
– GF_INSTALL_PLUGINS=${GF_INSTALL_PLUGINS}
ports:
– "3000:3000"
volumes:
– grafana-db:/var/lib/grafana
– grafana-log:/var/log/grafana
– grafana-conf:/etc/grafana
volumes:
grafana-db:
driver: local
grafana-log:
driver: local
grafana-conf:
driver: local
[/sourcecode]

Here you can see the example in action:

Full code in my github

Playing with lambda, serverless and Python

Couple of weeks ago I attended to serverless course. I’ve played with lambdas from time to time (basically when AWS forced me to use them) but without knowing exactly what I was doing. After this course I know how to work with the serverless framework and I understand better lambda world. Today I want to hack a little bit and create a simple Python service to obtain random numbers. Let’s start

We don’t need Flask to create lambdas but as I’m very comfortable with it so we’ll use it here.
Basically I follow the steps that I’ve read here.

[sourcecode language=”python”]
from flask import Flask

app = Flask(__name__)

@app.route("/", methods=["GET"])
def hello():
return "Hello from lambda"

if __name__ == ‘__main__’:
app.run()
[/sourcecode]

And serverless yaml to configure the service

[sourcecode language=”xml”]
service: random

plugins:
– serverless-wsgi
– serverless-python-requirements
– serverless-pseudo-parameters

custom:
defaultRegion: eu-central-1
defaultStage: dev
wsgi:
app: app.app
packRequirements: false
pythonRequirements:
dockerizePip: non-linux

provider:
name: aws
runtime: python3.7
region: ${opt:region, self:custom.defaultRegion}
stage: ${opt:stage, self:custom.defaultStage}

functions:
home:
handler: wsgi_handler.handler
events:
– http: GET /
[/sourcecode]

We’re going to use serverless plugins. We need to install them:

[sourcecode]
npx serverless plugin install -n serverless-wsgi
npx serverless plugin install -n serverless-python-requirements
npx serverless plugin install -n serverless-pseudo-parameters
[/sourcecode]

And that’s all. Our “Hello world” lambda service with Python and Flask is up and running.
Now We’re going to create a “more complex” service. We’re going to return a random number with random.randint function.
randint requires two parameters: start, end. We’re going to pass the end parameter to our service. The start value will be parameterized. I’ll parameterize it only because I want to play with AWS’s Parameter Store (SSM). It’s just an excuse.

Let’s start with the service:

[sourcecode language=”python”]
from random import randint
from flask import Flask, jsonify
import boto3
from ssm_parameter_store import SSMParameterStore

import os
from dotenv import load_dotenv

current_dir = os.path.dirname(os.path.abspath(__file__))
load_dotenv(dotenv_path="{}/.env".format(current_dir))

app = Flask(__name__)

app.config.update(
STORE=SSMParameterStore(
prefix="{}/{}".format(os.environ.get(‘ssm_prefix’), os.environ.get(‘stage’)),
ssm_client=boto3.client(‘ssm’, region_name=os.environ.get(‘region’)),
ttl=int(os.environ.get(‘ssm_ttl’))
)
)

@app.route("/", methods=["GET"])
def hello():
return "Hello from lambda"

@app.route("/random/<int:to_int>", methods=["GET"])
def get_random_quote(to_int):
from_int = app.config[‘STORE’][‘from_int’]
return jsonify(randint(from_int, to_int))

if __name__ == ‘__main__’:
app.run()
[/sourcecode]

Now the serverless configuration. I can use only one function, handling all routes and letting Flask do the job.

[sourcecode language=”xml”]
functions:
app:
handler: wsgi_handler.handler
events:
– http: ANY /
– http: ‘ANY {proxy+}’
[/sourcecode]

But in this example I want to create two different functions. Only for fun (and to use different role statements and different logs in cloudwatch).

[sourcecode language=”xml”]
service: random

plugins:
– serverless-wsgi
– serverless-python-requirements
– serverless-pseudo-parameters
– serverless-iam-roles-per-function

custom:
defaultRegion: eu-central-1
defaultStage: dev
wsgi:
app: app.app
packRequirements: false
pythonRequirements:
dockerizePip: non-linux

provider:
name: aws
runtime: python3.7
region: ${opt:region, self:custom.defaultRegion}
stage: ${opt:stage, self:custom.defaultStage}
memorySize: 128
environment:
region: ${self:provider.region}
stage: ${self:provider.stage}

functions:
app:
handler: wsgi_handler.handler
events:
– http: ANY /
– http: ‘ANY {proxy+}’
iamRoleStatements:
– Effect: Allow
Action: ssm:DescribeParameters
Resource: arn:aws:ssm:${self:provider.region}:#{AWS::AccountId}:*
– Effect: Allow
Action: ssm:GetParameter
Resource: arn:aws:ssm:${self:provider.region}:#{AWS::AccountId}:parameter/random/*
home:
handler: wsgi_handler.handler
events:
– http: GET /
[/sourcecode]

And that’s all. “npx serverless deploy” and my random generator is running.