Cette activité va permettre d’aborder les notions suivantes :
Notre objet connecté sera architecturé autour d’un ESP32.
L’ESP32 est le successeur de l’ESP8266 auquel on a ajouté le Bluetooth. Il existe différents modules.
Liens :
Autres activités :
AWS (Amazon Web Services) est un ensemble de services de cloud computing fourni par Amazon.com et essentiellement destiné aux entreprises.
Depuis 2017, AWS propose plus de 90 services, comprenant le calcul, le stockage, le réseau, la base de données, l’analyse de données, des services applicatifs, du déploiement, de la gestion de système, de la gestion d’applications mobiles, des outils pour les développeurs et pour l’internet des objets. [Source : https://fr.wikipedia.org/wiki/Amazon_Web_Services]
AWS fournit notamment des services IoT pour des solutions industrielles, grand public et commerciales :
Remarque : D’après une enquête de la Fondation Eclipse, AWS serait la plateforme cloud privilégiée par 34 % des développeurs de l’IoT devant Azure (23%) et Google Cloud Platform (20%) .
Il faudra créer un compte sur AWS. Puis, rechercher et sélectionner AWS IoT.
Il existe plusieurs moyens pour gérer les service dans AWS :
aws-cli
qui est une interface en ligne de commandesOn s’intéressera ici à AWS IoT Core qui est un service basé sur le cloud permettant aux appareils connectés d’interagir avec d’autres appareils et applications cloud. L’AWS IoT Core prend en charge les protocoles HTTP, WebSockets et MQTT. AWS IoT Core assure l’authentification et le chiffrement de bout en bout.
Vous pouvez utiliser l’une des deux méthodes suivantes pour gérer les objets connectés dans AWS IoT :
aws-cli
qui est une interface en ligne de commandes (rapide et efficace)Quelque soit la méthode utilisée, le principe est le suivant :
Lien : Enregistrement d’un appareil
À partir de l’AWS Console, qui est l’interface graphique proposée par Amazon, on va créer notre objet connecté architecturé ici autour d’un ESP32 :
Remarque : quelque soit votre objet connecté, ce sera toujours la même procédure que celle décrite ci-dessous. Donc, idem pour une Raspberry Pi.
On va ensuite créer un certificat X.509 pour notre objet :
Attention, c’est la phase essentielle car il faut :
xxxxx.cert.perm
(on peut le télécharger plus tard)xxxxx.private.key
(on ne pourra pas le télécharger plus tard)$ curl https://www.amazontrust.com/repository/AmazonRootCA1.pem > root-CA.crt
Remarque : Le lien Télécharger “CA racine pour AWS IoT” permet seulement d’accéder à la page Certificats X.509 et AWS IoT sur laquelle il faudra choisir un certificat CA.
Maintenant, il faut créer et associer une stratégie à notre objet via le certificat :
Les stratégies permettent de gérer les accès dans AWS. On les attachent ensuite à des identités IAM (utilisateurs, groupes d’utilisateurs ou rôles) ou des ressources AWS. Une stratégie est un objet dans AWS qui, lorsqu’il est associé à une identité ou à une ressource, définit les autorisations de ces dernières. AWS évalue ces stratégies lorsqu’une entité mandataire (utilisateur ou rôle) envoie une demande.
Les autorisations dans les stratégies déterminent si la demande sera acceptée ou refusée. La plupart des stratégies sont stockées dans AWS sous forme de documents JSON. Dans notre cas, on donnera les droits d’effectuer des subscribe et publish MQTT sur cet objet avec ce certiciat et on aura :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:*",
"Resource": "*"
}
]
}
Remarque : Évidemment ces paramètres seraient trop permissifs pour un environnement de production. Il faudra alors les resteindre.
Lien : Stratégies et autorisations AWS
Pour rappel, l’objectif était le suivant :
On va vérifier notre objet : (j’ai recréé un certificat)
Il y a un certificat pour cet objet :
Il y a une stratégie attachée à ce certificat :
Il y a un objet associé à cette stratégie :
Dans tous les cas, les informations pour se connecter à notre objet se trouvent ici :
Installation d’aws-cli
:
$ pip3 install awscli --upgrade --user
Puis, vérifiez que aws-cli
a été installé correctement :
$ aws --version
aws-cli/1.16.144 Python/3.6.7 Linux/4.15.0-47-generic botocore/1.12.134
Allez sur la console IAM pour récupérer l’AWS Access Key ID
et l’AWS Secret Access Key
.
Pour une utilisation générale, le moyen le plus rapide pour configurer l’installation d’aws-cli
est :
$ aws configure
AWS Access Key ID [None]: ****************2WNF
AWS Secret Access Key [None]: ****************z6O3
Default region name [None]: eu-central-1
Default output format [None]: json
Puis, vérifiez que aws-cli
a été configuré correctement :
$ aws configure list
Name Value Type Location
---- ----- ---- --------
profile <not set> None None
access_key ****************2WNF shared-credentials-file
secret_key ****************z6O3 shared-credentials-file
region eu-central-1 config-file ~/.aws/config
Liens :
Il est possible maintenant de lister nos objets connectés :
$ aws iot list-things
{
"things": [
{
"thingName": "monesp32",
"thingArn": "arn:aws:iot:eu-central-1:494915339550:thing/monesp32",
"attributes": {},
"version": 1
}
]
}
Remarque : l’objet monesp32
apparaîtra si vous l’avez créé avec AWS Console dans l’étape précédente.
Il est aussi possible de créer un nouvel objet :
$ aws iot create-thing --thing-name "UnNouvelObjet"
{
"thingName": "UnNouvelObjet",
"thingArn": "arn:aws:iot:eu-central-1:494915339550:thing/UnNouvelObjet",
"thingId": "2d12ea83-1b3d-4d76-99c5-d0a3b22a9986"
}
$ aws iot describe-thing --thing-name "UnNouvelObjet"
{
"defaultClientId": "UnNouvelObjet",
"thingName": "UnNouvelObjet",
"thingId": "2d12ea83-1b3d-4d76-99c5-d0a3b22a9986",
"thingArn": "arn:aws:iot:eu-central-1:494915339550:thing/UnNouvelObjet",
"attributes": {},
"version": 1
}
Ce qui donnera :
Il faut ensuite créer une paire de clés RSA 2048 bits et un certificat X.509 :
$ aws iot create-keys-and-certificate --set-as-active --certificate-pem-outfile certificate.pem.crt --public-key-outfile public.pem.key --private-key-outfile private.pem.key
{
"certificateArn": "arn:aws:iot:eu-central-1:494915339550:cert/2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc",
"certificateId": "2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc",
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDWTCCAkGgAwIBAgIUYzUnmu/76OeLZH7qoWXg9lBa62gwDQYJKoZIhvcNAQEL\n
BQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g\n
...
/FIcV\nt4+6r64vKqprpdCbSaa85JgJAHr080PYhA0QQ+BotiaeUwPKaqYfjiyI9NsG\n-----END CERTIFICATE-----\n",
"keyPair": {
"PublicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz4eOHLJQvzD1yD22DY2/\nzgebI/17IxRBhlDycJOcLtRT4DivWVBMmIozvZYWepWO6GXmyACSjFWT7Dt6V2iB\nVXxzj3/LvGMKXlssUmYAjnAbyp2EUdv3QyobBAmvjv5RA5z8VVlamd9osuzni01Y\n
...
JCsU1U5+yUlKdwwvXwb4mvMCFiIt40cfhtwMZk26AKaZw7H1yaEXQaU=\n-----END RSA PRIVATE KEY-----\n"
}
}
$ ls -l
-rw------- 1 tv tv 1,2K avril 22 09:01 certificate.pem.crt
-rw------- 1 tv tv 1,7K avril 22 09:01 private.pem.key
-rw------- 1 tv tv 451 avril 22 09:01 public.pem.key
$ curl https://www.amazontrust.com/repository/AmazonRootCA1.pem > root-CA.crt
Comme on l’a vu précedemment, un objet doit posséder un certificat X.509 pour pouvoir communiquer avec AWS IoT. Pour l’attacher à un certificat, il faut utiliser la commande :
$ aws iot attach-thing-principal --thing-name "UnNouvelObjet" --principal "arn:aws:iot:eu-central-1:494915339550:cert/2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc"
Ce qui donnera :
Il reste à créer une stratégie et à l’associer à l’objet.
$ aws iot create-policy --policy-name "MaNouvelleStrategie" --policy-document "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"iot:*\",\"Resource\":\"*\"}]}"
{
"policyName": "MaNouvelleStrategie",
"policyArn": "arn:aws:iot:eu-central-1:494915339550:policy/MaNouvelleStrategie",
"policyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"iot:*\",\"Resource\":\"*\"}]}",
"policyVersionId": "1"
}
Ce qui donnera :
Pour finir, on va attacher cette nouvelle stratégie au certificat associé au nouvel objet :
$ aws iot attach-policy --policy-name "MaNouvelleStrategie" --target "arn:aws:iot:eu-central-1:494915339550:cert/2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc"
Et on obtient :
Pour rappel, l’objectif était le suivant :
aws-cli
fournit les commandes pour lister l’ensemble de nos ressources IoT :
$ aws iot list-things
{
"things": [
{
"thingName": "UnNouvelObjet",
"thingArn": "arn:aws:iot:eu-central-1:494915339550:thing/UnNouvelObjet",
"attributes": {},
"version": 1
},
...
]
}
$ aws iot list-policies
{
"policies": [
{
"policyName": "MaNouvelleStrategie",
"policyArn": "arn:aws:iot:eu-central-1:494915339550:policy/MaNouvelleStrategie"
},
...
]
}
$ aws iot list-certificates
{
"certificates": [
{
"certificateArn": "arn:aws:iot:eu-central-1:494915339550:cert/2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc",
"certificateId": "2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc",
"status": "ACTIVE",
"creationDate": 1555916468.752
},
...
]
}
$ aws iot list-attached-policies --target "arn:aws:iot:eu-central-1:494915339550:cert/2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc"
{
"policies": [
{
"policyName": "MaNouvelleStrategie",
"policyArn": "arn:aws:iot:eu-central-1:494915339550:policy/MaNouvelleStrategie"
}
]
}
$ aws iot list-principal-things --principal "arn:aws:iot:eu-central-1:494915339550:cert/2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc"
{
"things": [
"UnNouvelObjet"
]
}
$ aws iot list-targets-for-policy --policy-name "MaNouvelleStrategie"
{
"targets": [
"arn:aws:iot:eu-central-1:494915339550:cert/2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc"
]
}
$ aws iot list-thing-principals --thing-name "UnNouvelObjet"
{
"principals": [
"arn:aws:iot:eu-central-1:494915339550:cert/2946f548241cebdbb770d0a1b83d32218f09292b4736f9752702a6ebe46faecc"
]
}
Dans tous les cas, les informations pour se connecter à notre objet se trouvent ici :
$ aws iot describe-endpoint
{
"endpointAddress": "XXXXXXXXXXXXXX.iot.eu-central-1.amazonaws.com"
}
Bilan : En 5 commandes avec aws-cli
, on a réalisé la même chose qu’AWS Console !
Il est possible de tester notre objet directement dans AWS Console. Ici j’ai choisi un kit de connexion en Python sous Linux :
Vous pouvez accéder aussi à un client MQTTdans AWS :
Et surveiller votre objet connecté :
On va maintenant tester avec le client MQTT Mosquitto :
$ sudo apt-get install mosquitto-clients
Pour faire un publish :
$ mosquitto_pub --cert f183df69c8-certificate.pem.crt --key f183df69c8-private.pem.key --cafile root-CA.crt -h XXXXXXXXXXXXXX-ats.iot.eu-central-1.amazonaws.com -p 8883 -t 'test/thing' -m "Hello from Mosquitto"
On obtient :
Maintenant on va faire un publish à partir de la console AWS et un subscribe avec le client Mosquitto :
$ mosquitto_sub --cert f183df69c8-certificate.pem.crt --key f183df69c8-private.pem.key --cafile root-CA.crt -h XXXXXXXXXXXXXX-ats.iot.eu-central-1.amazonaws.com -p 8883 -t 'test/+'
{
"message": "Hello from AWS IoT console"
}
Autre client MQTT : MQTT.fx
Pour finir, on va aussi tester avec HTTP :
$ curl --tlsv1.2 --cacert root-CA.crt --cert f183df69c8-certificate.pem.crt --key f183df69c8-private.pem.key -X POST -d "{ \"message\": \"Hello from HTTP\" }" "https://XXXXXXXXXXXXXX-ats.iot.eu-central-1.amazonaws.com:8443/topics/test/thing"
{"message":"OK","traceId":"ccf5f94d-06c9-18df-60ff-737b0afc9ee8"}
On obtient :
On va créer un projet sous PlatformIO pour une carte LOLIN32 avec le framework Arduino :
Pour notre objet connecté ESP32, on va utiliser directement une bibliothèque fournie pour cela : AWS_IOT.
Remarque : pour une Raspberry Pi, on suivra le guide du kit SDK pour Embedded C AWS IoT.
On sélectionne donc la bibliothèque AWS_IOT pour ESP32 :
Le fichier de projet platformio.ini
sera le suivant :
[env:lolin32]
platform = espressif32
board = lolin32
framework = arduino
lib_deps =
AWS_IOT
upload_port = /dev/ttyUSB0
upload_speed = 115200
Il faut maintenant importer les certificats pour assurer une connexion. Pour cela, il faut éditer le fichier .piolibdeps/AWS_IOT_ID2071/src/aws_iot_certficates.c
et faire un copier/coller du contenu des différents fichiers téléchargés précedemment de la manière suivante :
const char aws_root_ca_pem[] = {"-----BEGIN CERTIFICATE-----\n"
"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n"
...
"rqXRfboQnoZsG4q5WTP468SQvvG5\n"
"-----END CERTIFICATE-----\n"};
const char certificate_pem_crt[] = {"-----BEGIN CERTIFICATE-----\n"
"MIIDWTCCAkGgAwIBAgIUAYNodFlLN9FU57uRs79VIUYL414wDQYJKoZIhvcNAQEL\n"
...
"RBC05RL60muyhwXTxmGzeo9bWP1hty8D4tYQHnqi7pf29PX/xqX9Mkd6hiJS\n"
"-----END CERTIFICATE-----\n"};
const char private_pem_key[] = {"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEogIBAAKCAQEAyD3RMwUkFVkXbtsA+C+9ycaI+aEWqzNhx/1CLxSqtbvte6M/\n"
...
"eHV2rMmVnevhvOfytSucGhuTNOf7SiA1XRAOUqegOiQSlxsUPhk=\n"
"-----END RSA PRIVATE KEY-----\n"};
La bibliothèque fournit plusieurs exemples à exploiter : Examples AWS_IOT.
Le principe est le suivant :
AWS_IOT
:#include <Arduino.h>
#include <AWS_IOT.h>
AWS_IOT aws;
char HOST_ADDRESS[] = "XXXXXXXXXXXXXX-ats.iot.eu-central-1.amazonaws.com";
char CLIENT_ID[] = "monesp32-aws-iot"; // un id différent pour chaque client
char TOPIC_NAME_PUBLISH[] = "$aws/things/monesp32/shadow/update";
char TOPIC_NAME_SUBSCRIBE[] = "$aws/things/monesp32/shadow/#";
void setup()
{
Serial.begin(115200);
while (!Serial);
// initialiser le WiFi ...
if(aws.connect(HOST_ADDRESS, CLIENT_ID)== 0)
{
Serial.println(F("AWS connexion ok"));
delay(1000);
if(aws.subscribe(TOPIC_NAME_SUBSCRIBE, mySubCallBackHandler) == 0)
{
Serial.println(F("AWS subscribe ok"));
}
else
{
Serial.println(F("AWS subscribe erreur !"));
}
}
else
{
Serial.println(F("AWS connexion erreur !"));
}
}
int msgCount = 0;
char payload[64];
void loop()
{
sprintf(payload, "Hello from ESP32 : %d", msgCount++);
if(aws.publish(TOPIC_NAME_PUBLISH, payload) == 0)
{
Serial.print(F("AWS publish : "));
Serial.println(payload);
}
else
{
Serial.println(F("AWS publish erreur !"));
}
// etc ...
}
void mySubCallBackHandler (char *topicName, int payloadLen, char *payLoad)
{
Serial.print(F("AWS received topic : "));
Serial.print(topicName);
Serial.print(F(" -> "));
Serial.println(payLoad);
}
Une Device Shadow est une représentation persistante des objets et de leur état dans le cloud AWS.
Il est possible d’utiliser le service Device Shadow pour obtenir et/ou définir l’état d’un appareil via MQTT (ou HTTP), que l’appareil soit connecté ou non à Internet. Chaque shadow d’appareil sera identifié de façon unique par le nom de l’objet correspondant :
$aws/things/monesp32/shadow/
AWS IoT fournit trois méthodes à utiliser avec un shadow d’appareil : UPDATE
(crée un shadow d’appareil s’il n’existe pas, ou met à jour le contenu avec les données fournies dans la demande), GET
(récupère le dernier état stocké dans le shadow d’appareil) et DELETE
(supprime un shadow d’appareil, avec tout son contenu).
Le service Device Shadow utilise ensuite des rubriques MQTT pour faciliter la communication entre les applications et l’objet connecté. On utilisera le topic pour mettre à jour l’état de l’appareil :
$aws/things/monesp32/shadow/update
AWS IoT répondra en publiant dans les topics $aws/things/monesp32/shadow/update/accepted
ou $aws/things/monesp32/shadow/update/rejected
.
Lien : Liste des rubriques shadow MQTT
Pour simplifier, on pourra s’abonner avec le filtre de rubrique suivant : $aws/things/monesp32/shadow/#
Un shadow d’appareil est en fait un document JSON utilisé pour stocker et extraire les informations sur l’état en cours d’un objet connecté :
{
"state" : {
"desired" : {
"couleur" : "rouge",
"etat" : "on"
}
}
}
{
"state" : {
"reported" : {
"couleur" : "rouge",
"etat" : "on"
}
}
}
Pour les tests, j’ai utilisé une carte ESP32-Weather qui, autour d’un ESP32, intégre un capteur d’éclairement lumineux TSL 2591, un capteur DHT22 de température et d’humidité et une Led Bicolore. Les mesures sont affichées périodiquement sur l’écran OLED de la carte. (Le brochage des différents composants est fourni dans le code source).
Pour l’exemple, on va publier les données JSON de la manière suivante :
char payload[512];
const char payloadFormat[] = "{\"state\":{\"reported\":{\"temperature\":%.1f,\"ressentie\":%.1f,\"luminosite\":%u,\"humidite\":%u}}}";
sprintf(payload,payloadFormat,getTemperature(),getRessentie(),getLuminosite(),getHumidite());
if(aws.publish(TOPIC_NAME_PUBLISH, payload) == 0)
{
Serial.print(F("AWS publish :"));
Serial.println(donneesSonde);
}
else
{
Serial.println(F("AWS publish erreur !"));
}
On obtient :
AWS connexion ok
AWS subscribe ok
AWS publish : {"state":{"reported":{"temperature":24.5,"ressentie":24.1,"luminosite":8252,"humidite":42}}}
AWS received topic : $aws/things/monesp32/shadow/update -> {"state":{"reported":{"temperature":24.5,"ressentie":24.1,"luminosite":8252,"humidite":42}}}
AWS received topic : $aws/things/monesp32/shadow/update/accepted -> {"state":{"reported":{"temperature":24.5,"ressentie":24.1,"luminosite":8252,"humidite":42}},"metadata":{"reported":{"temperature":{"timestamp":1555841961},"ressentie":{"timestamp":1555841961},"luminosite":{"timestamp":1555841961},"humidite":{"timestamp":1555841961}}},"version":1,"timestamp":1555841961}
Avec aws-cli
, les commandes iot-data
permettent une communication bidirectionnelle sécurisée entre les objets connectés à Internet et le cloud AWS :
Lien : Les commandes iot-data
$ aws iot-data get-thing-shadow --thing-name "monesp32" "output.txt" && cat "output.txt"
{"state":{"reported":{"temperature":24.3,"ressentie":24.0,"luminosite":0,"humidite":45}},"metadata":{"reported":{"temperature":{"timestamp":1555874715},"ressentie":{"timestamp":1555874715},"luminosite":{"timestamp":1555874715},"humidite":{"timestamp":1555874715}}},"version":556,"timestamp":1555921093}
Alexa est un assistant personnel intelligent développé par Amazon.com et rendu populaire par l’appareil Echo. Il est capable d’interaction vocale, de lire de la musique, faire des listes de tâches, régler des alarmes, lire des podcasts et des livres audio, et donner la météo, le trafic et d’autres informations en temps réel.
Amazon Echo est une enceinte connectée, conçue par Amazon, ayant la capacité d’obéir à la voix humaine, de parler, et, dans une certaine mesure, d’interagir avec un humain. L’appareil peut être connecté à des objets domotiques qui peuvent ainsi être contrôlés par la voix humaine.
Alexa fournit un ensemble de fonctionnalités intégrées, appelées skills (compétences). Un(e) Skill Alexa peut être considéré(e) comme une application (ou un service).
Avec ASK (Alexa Skills Kit), il est possible de développer ses propres skills et donc d’ajouter des fonctionnalités à Alexa.
Il vous faut un compte (gratuit) sur https://developer.amazon.com/alexa. Un compte amazon.fr sera reconnu. Ce compte permet d’accéder à la console de développement Alexa et à ask
, la ligne de commande Alexa (CLI).
Il vous faut aussi un compte AWS (aws.amazon.com). Vous aurez besoin de la clé d’accès et la clé secrète d’un utilisateur IAM de votre compte AWS.
Remarque : ce sont des comptes différents.
Un appareil Amazon Echo s’active quand il reconnait le mot “Alexa” par défaut. C’est le wake-word qui permet déclencher le traitement vocal. Il en existe d’autres : Amazon, Echo, Ordinateur.
Les sons capturés sont ensuite envoyés vers un serveur d’Amazon (cloud).
Étape n°1 : Reconnaissance Automatique de la Parole (Automatic Speech Recognition - ASR). L’ASR transforme les sons en texte. Le résultat d’ASR est une chaîne de caractères.
Étape n°2 : Compréhension du Langage Naturel (Natural Language Understanding - NLU). NLU transforme le texte en intentions et entités. Le résultat du NLU est un document JSON.
Étape n°3 : Traitement de la requête par un service qui sera capable de répondre à la question ou la commande. S’il s’agit d’une commande destinée à une skill, la requête JSON sera envoyée à un web service du développeur de la skill (endpoint). Le web service doit traiter la requête et produire une répondre sous la forme d’un document JSON qui contiendra le texte qu’Alexa “lira” à l’utilisateur.
Remarque : L’IA qui gère ASR et NLU est de type d’apprentissage supervisé. Elle a besoin de données pour pouvoir extrapoler le résultat d’une analyse.
Exemple : “Alexa, joue Ac-Dc sur Dezzer”
Le développeur de la skill devra fournir un Modèle de Conversation (Language Model). C’est la liste des intentions comprises par la skill et les entités associées aux intentions (types et valeurs).
Exemple : “demande à mon esp quelle est la température”
Remarque : Alexa ne veut pas du point d’interrogation dans les questions ! Dans la phrase ci-dessus, “demande à” est le déclencheur. C’est un mot de liaison qui précède le nom d’invocation de la skill. Il existe plusieurs mots déclencheurs qui permettent d’utiliser Alexa de façon naturelle : “Dis à …”, “Lance …”, “Va sur …”, …
Le endpoint peut être hébergé soit sur AWS Lambda ou sur un serveur HTTPS.
Remarque : AWS Lambda est un service informatique qui vous permet d’exécuter du code sans nécessiter la mise en service ni la gestion de serveurs.
En résumé, pour créer une skill Alexa, il faudra :
Installer NodeJS :
$ sudo apt-get install nodejs
Si besoin (problème de EACCES permission denied) :
$ mkdir ~/.npm-global
$ npm config set prefix '~/.npm-global'
$ vim ~/.profile
export PATH=~/.npm-global/bin:$PATH
$ source ~/.profile
Puis :
$ npm install -g ask-cli
Il faut ensuite initialiser l’environnement avec le compte AWS et Développeur Amazon :
$ ask init
This command will initialize the ASK CLI with a profile associated with your Amazon developer credentials.
------------------------- Step 1 of 2 : ASK CLI Initialization -------------------------
Switch to "Login with Amazon" page and sign-in with your Amazon developer credentials.
If your browser did not open the page, run the initialization process again with command "ask init --no-browser".
ASK Profile "default" was successfully created. The details are recorded in ask-cli config ($HOME/.ask/cli_config).
Vendor ID set as YYYYYYYYYYYY.
------------------------- Step 2 of 2 : Associate an AWS Profile with ASK CLI -------------------------
? If you want to host your skill's backend code in AWS Lambda (recommended), you must associate an AWS profile with the ASK CLI. Do you want to associate an AWS profile? Yes
Please follow the instruction from https://developer.amazon.com/docs/smapi/set-up-credentials-for-an-amazon-web-services-account.html
Fill in the AWS Access Key ID and AWS Secret Access Key below. CLI will generate/modify the AWS credential file for you.
? AWS Access Key ID: XXXXXXXXXXXXXXXXXXXX
? AWS Secret Access Key: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
AWS profile "ask_cli_default" was successfully created. The details are recorded in aws credentials file ($HOME/.aws/credentials).
AWS profile "ask_cli_default" was successfully associated with your ASK profile "default".
------------------------- Initialization Complete -------------------------
Here is the summary for the profile setup:
ASK Profile: default
AWS Profile: ask_cli_default
Vendor ID: YYYYYYYYYYYY
On utilise la commande ask new
pour créer une skill. Il faut choisir l’environnement d’exécution de la skill (NodeJS ou Python) puis un exemple GitHub depuis une liste (le squelette de l’exemple Hello World suffira ici) :
$ ask new
? Please select the runtime Node.js V8
? List of templates you can choose Hello World
? Please type in your skill name: skill-esp32
Skill "skill-esp32" has been created based on the chosen template
$ cd skill-esp32/
$ ls -l
drwxr-xr-x 2 tv tv 4096 avril 12 07:11 hooks
drwxr-xr-x 2 tv tv 4096 avril 12 07:11 instructions
drwxr-xr-x 3 tv tv 4096 avril 12 07:11 lambda
-rw-r--r-- 1 tv tv 11358 avril 12 07:11 LICENSE.txt
drwxr-xr-x 2 tv tv 4096 avril 12 07:11 models
-rw-r--r-- 1 tv tv 2033 avril 12 07:11 README.md
-rw-r--r-- 1 tv tv 784 avril 12 07:11 skill.json
On trouve notamment :
.ask
: répertoire caché qui contient le fichier de configuration de ASK CLIhooks
: répertoire qui contient les scripts post_new_hook
et pre_deploy_hook
à exécuter si besoin avant et après un déploiement.lambda
: répertoire qui contient le code source de la fonction.models
: répertoire qui contient les modèles de conversation de la skill par langue. Il existe par défaut celui pour l’anglais en-US.json
.skill.json
: le fichier qui contient les métadonnées de la skill, nécessaire pour publication sur le store.On rappelle que pour créer une skill, il faudra :
On va donc modifier 3 fichiers :
fr-FR.json
avec le nom d’invocation, les phrases et les valeurs des entités en français ;skill.json
avec le nom et sa la description ;index.js
avec les réponses qui seront lues avec la voix d’Alexa.Extrait de ces fichiers :
$ cp models/en-US.json models/fr-FR.json
$ vim models/fr-FR.json
{
"interactionModel": {
"languageModel": {
"invocationName": "mon esp",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "GetTemperature",
"samples": [
"quelle est la température actuelle",
"quelle est la température",
"quelle température",
"dis moi la température actuelle",
"dis moi la température",
"dis moi température",
"dis la température actuelle",
"dis la température",
"dis température",
"donne moi la température actuelle",
"donne moi la température",
"donne moi température",
"donne la température actuelle",
"donne la température",
"donne température"
]
},
...
]
}
}
}
Remarque : pour cette première skill, j’ai décidé de ne pas utiliser de slot (température, humidité, …). Et ce n’est vraiment pas une bonne idée ! Voir : Slot Type Reference
$ vim skill.json
{
"manifest": {
"publishingInformation": {
"locales": {
"fr-FR": {
"summary": "Description courte",
"examplePhrases": [
"Alexa ouvre mon esp",
"mon esp",
"aide"
],
"name": "skill-esp32",
"description": "Description longue"
}
},
"isAvailableWorldwide": true,
"testingInstructions": "Sample Testing Instructions.",
"category": "KNOWLEDGE_AND_TRIVIA",
"distributionCountries": []
},
"apis": {
"custom": {
"endpoint": {
"sourceDir": "lambda/custom",
"uri": "ask-custom-skill-esp32-default"
}
}
},
"manifestVersion": "1.0"
}
}
Le code de la skill se trouve dans le fichier lambda/custom/index.js
:
$ vim lambda/custom/index.js
/* eslint-disable func-names */
/* eslint-disable no-console */
const Alexa = require('ask-sdk-core');
// Configuration AWS IoT
var config = {};
config.IOT_BROKER_ENDPOINT = "<your-broker-endpoint>".toLowerCase();
config.IOT_BROKER_REGION = '<broker-region>';
config.IOT_THING_NAME = '<the-thing-name>';
config.IOT_ACCESS_KEY_ID = '<your-access-key-id>';
config.IOT_SECRET_ACCESS_KEY = '<your-access-key-secret>';
config.params = { thingName: config.IOT_THING_NAME };
// Bibliothèque AWS SDK
var AWS = require('aws-sdk');
// Initialisation AWS
AWS.config.region = config.IOT_BROKER_REGION;
AWS.config.update({accessKeyId: config.IOT_ACCESS_KEY_ID, secretAccessKey: config.IOT_SECRET_ACCESS_KEY});
var iotData = new AWS.IotData({endpoint: config.IOT_BROKER_ENDPOINT});
...
const GetTemperatureHandler = {
canHandle(handlerInput) {
console.log("GetTemperatureHandler [canHandle] request.type -> " + handlerInput.requestEnvelope.request.type);
if(handlerInput.requestEnvelope.request.type === 'IntentRequest')
console.log("GetTemperatureHandler : [canHandle] request.intent.name -> " + handlerInput.requestEnvelope.request.intent.name);
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'GetTemperature';
},
async handle(handlerInput) {
const responseBuilder = handlerInput.responseBuilder;
const reponse = await getTemperature();
console.log("GetTemperatureHandler : reponse -> " + reponse);
return responseBuilder
.speak(reponse)
.withSimpleCard('mon esp', reponse)
.getResponse();
},
};
...
const skillBuilder = Alexa.SkillBuilders.custom();
exports.handler = skillBuilder
.addRequestHandlers(
LaunchRequestHandler,
GetTemperatureHandler,
GetTemperatureRessentieHandler,
GetHumiditeHandler,
GetLuminositeHandler,
AllumerLedHandler,
EteindreLedHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler
)
.addErrorHandlers(ErrorHandler)
.lambda();
function getTemperature() {
return new Promise(((resolve, reject) => {
iotData.getThingShadow(config.params, function (err, data) {
let speechText = '';
if (err)
{
console.log(err, err.stack);
speechText = "Impossible d\'obtenir la température";
reject(speechText);
}
else
{
console.log(data);
var payload = JSON.parse(data.payload);
var temperature = payload.state.reported.temperature;
console.log("temperature : " + temperature);
speechText = "La température est de " + temperature + " degrés celcius";
resolve(speechText);
}
});
}));
}
...
function allumerLed() {
var payloadObj = { "state":
{ "desired":
{"led":1}
}
};
var paramsUpdate = {
"thingName" : config.IOT_THING_NAME,
"payload" : JSON.stringify(payloadObj)
};
// Topic : $aws/things/monesp32/shadow/update/delta
iotData.updateThingShadow(paramsUpdate, function(err, data) {
if (err){
console.log(err, err.stack);
}
});
}
function eteindreLed() {
var payloadObj = { "state":
{ "desired":
{"led":0}
}
};
var paramsUpdate = {
"thingName" : config.IOT_THING_NAME,
"payload" : JSON.stringify(payloadObj)
};
// Topic : $aws/things/monesp32/shadow/update/delta
iotData.updateThingShadow(paramsUpdate, function(err, data) {
if (err){
console.log(err, err.stack);
}
});
}
$ cd lambda/custom/
$ npm install
$ cd ../..
Déploiement :
$ ask deploy
Profile for the deployment: [default]
-------------------- Update Skill Project --------------------
Skill Id: amzn1.ask.skill.46fca081-3306-485e-9e89-76e7e87cd374
Skill deployment finished.
Model deployment finished.
Lambda deployment finished.
Lambda function(s) updated:
[Lambda ARN] arn:aws:lambda:us-east-1:494915339550:function:ask-custom-skill-esp32-default
[Info]: No in-skill product to be deployed.
Your skill is now deployed and enabled in the development stage. Try simulating your Alexa skill using "ask dialog" command.
Sur la console ASK, on peut voir que la skill a été créee :
Et son modèle de conversation aussi :
On va aussi vérifier que notre fonction lambda a bien été créée à partir de la console AWS :
On va s’assurer que la fonction lambda est bien liée avec la skill d’Alexa et le service AWS IoT :
Il faudra évidemment que l’utilisateur IAM de votre compte AWS possèdent les autorisations nécessaires (ici en version développement) :
Test de la skill :
$ ask dialog --locale fr-FR
User > demande à mon esp quelle est la luminosité
Alexa > La luminosité est de 0 lux
User > demande à mon esp quelle est la température ressentie
Alexa > La température ressentie est de 24 degrés celcius
User > demande à mon esp quelle humidité
Alexa > L'humidité est de 45 pourcent
User > demande à mon esp allume led
Alexa > d'accord
User > demande à mon esp eteindre led
Alexa > d'accord
User > demande à mon esp quelle luminosité
Alexa > La luminosité est de 8134 lux
User > demande à mon esp allume led
Alexa > d'accord
...
User > ouvre mon esp
Alexa > Bienvenue sur mon esp, vous pouvez me demander la température, l'humidité ou la luminosité.
User > température
Alexa > La température est de 25.2 degrés celcius
Sur l’ESP32 :
...
AWS réception [delta] : {"version":668,"timestamp":1556033949,"state":{"led":1},"metadata":{"led":{"timestamp":1556029994}}}
AWS réception [delta] : {"version":669,"timestamp":1556033995,"state":{"led":0},"metadata":{"led":{"timestamp":1556033995}}}
On peut accéder aux logs à partir de la console AWS CloudWatch :
Et les notions :