Archives par étiquette : home automation

Des trames MQTT avec Alexa

Vous vous êtes sans doute demandé comment on pouvait envoyer des trames MQTT avec Alexa et ainsi profiter de plus de fonctionnalités domotiques.
Commencez par installer le skill URL SWITCH sur votre Alexa :

Activez le skill depuis votre App Alexa puis rendez-vous sur https://www.virtualsmarthome.xyz/ pour venir créer vos interrupteurs, ces switches virtuels qui permettront d’interagir avec vos trames MQTT. Ici, nous avons créé volet salon, un switch destiné à télécommander l’ouverture et la fermeture des volets du salon :

Vous l’aurez compris, l’envoi d’une commande https suppose d’avoir un serveur hébergé, un p’tit VPS tout simple par exemple chez Pulsheberg que nous avons souvent utilisé pour nos dev, avec leur offre de base VPS Cloud à 5 ou 6 € par mois, largement suffisante pour héberger votre serveur Mosquitto, un certificat Let’s Encrypt et un Apache 2 pour vos pages de gestion Web en html ou php. Je passerai sur l’installation d’un serveur, d’Apache2 ou de PHP, c’est décrit un peu partout sur le web, la difficulté ne sera pas là 😉

Nous allons créer deux fichiers :

  • alexa.php pour faire l’appel vers un script python, hébergé sur /var/www/html de votre Apache 2
  • sendmessage.py, un script python hébergé sur /var/www/html/cgi-bin/sendmessage.py pour venir répondre aux commandes d’alexa.php et envoyer les trames MQTT

ALEXA.PHP :

<?php
// alexa.php

function sendMQTTMessage($topic, $message) {
    // On échappe chaque argument séparément pour éviter les injections shell
    $topicArg   = escapeshellarg($topic);
    $messageArg = escapeshellarg($message);

    $command = "python3 /var/www/html/cgi-bin/sendmessage.py $topicArg $messageArg";
    $output  = shell_exec($command);

    return $output;
}

// Récupération des paramètres depuis l’URL, par ex. :
// alexa.php?topic=maison/salon&message=allume+lampe
$topic   = isset($_GET['topic'])   ? $_GET['topic']   : '';
$message = isset($_GET['message']) ? $_GET['message'] : '';

// Un minimum de validation
if ($topic === '' || $message === '') {
    http_response_code(400);
    echo "Paramètres manquants. Utilisation : alexa.php?topic=xxx&message=yyy";
    exit;
}

// Appel du script Python
$result = sendMQTTMessage($topic, $message);

// Retour simple (à adapter selon tes besoins : JSON, texte, etc.)
header('Content-Type: text/plain; charset=utf-8');
echo "OK\n";
echo "Topic : $topic\n";
echo "Message : $message\n";
echo "Résultat Python :\n";
echo $result;

SENDMESSAGE.PY :

#!/usr/bin/env python3

import os
import sys

# Récupérer les arguments passés en ligne de commande
if len(sys.argv) != 3:
    print("Usage: python3 sendmessage.py <topic> <message>")
    sys.exit(1)

topic = sys.argv[1]
message = sys.argv[2]

# Configuration du broker MQTT
broker = "ip_du_serveur_MQTT"
port = 1883
username = "ton_nom_utilisateur"
password = "ton_mot_de_passe"

# Indiquer que le contenu est du texte brut (facultatif)
print()  # Ligne vide nécessaire

# Commande pour publier le message MQTT
command = f"mosquitto_pub -h {broker} -p {port} -u {username} -P {password} -t {topic} -m '{message}' -r"

# Exécuter la commande
exit_code = os.system(command)

if exit_code == 0:
    print("")
else:
    print("Erreur lors de l'envoi de la trame.")

Pas compliqué, si ?
Pensez à bien ajouter une mise à jour de votre certificat SSL par contre, dans crontab :

0 3 * * * root certbot renew --quiet

Pour protéger votre serveur Web, pensez à ajouter un .htaccess adapté… Vous pourrez alors par exemple envoyer une requête https comme suit :

https://login_htaccess:motdepasse_htaccess@iot.monserveurmqtt.fr/alexa.php?topic=/salon/volet&message=1

pour ouvrir et :

https://login_htaccess:motdepasse_htaccess@iot.monserveurmqtt.fr/alexa.php?topic=/salon/volet&message=1

pour fermer. Le login_htaccess et son mot de passe positionnés au début, suivis par @ permettent de contourner la fenêtre d’authentification du serveur web.

Mais vous pouvez tout à fait désactiver le .htaccess pour le simple fichier alexa.php, avec tous les risques que cela comporte bien entendu, en modifiant .htaccess :

 ===== Désactiver l'auth Basic pour alexa.php =====
# Auth sur tout sauf alexa.php
<FilesMatch "^(?!alexa\.php$).*">
    AuthUserFile /var/www/.htpasswd
    AuthType Basic
    AuthName "My restricted Area"
    Require valid-user
</FilesMatch>

# ===== Redirection HTTPS pour tout sauf alexa.php =====
RewriteEngine On

# EXCLURE alexa.php des redirections
RewriteCond %{REQUEST_URI} !^/alexa\.php$

# Pour tous les autres fichiers : forcer HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Raspberry Pi 2 domotique : ESP8266 capteur d’ouverture (12)

Transformons un petit ESP8266 (version ESP12) en capteur d’ouverture de porte. Je passe sur le script init.lua destiné à initialiser la connexion Wifi par exemple, vous en trouverez un exemple sur ce billet.

esp12_3

ESP12

Voici le script que j’ai appelé contactESP2.lua, on se retrouve après pour détailler son fonctionnement :

 

-- on active le GPIO4 en entree
gpio.write(2, gpio.LOW)
gpio.mode(2, gpio.INT, gpio.PULLUP)

-- contact porte ferme par defaut
ouvert = 0
cu = nil
-- 
-- nom = nom du capteur
-- porttcp = port TCP d'émission de trame si détection d'ouverture
-- portudp = port UDP d'écoute des trames de status
-- ip = adresse IP du serveur TCP Raspi Domo en écoute
--
nom = "sonde1"
porttcp = 5665
portudp = 5665
ip = "192.168.1.60"

print(nom .. "/n")
function contact(level) -- on lit le contact du gpio4
 etat = gpio.read(2)
 if etat == 1 then
 ouvert = 1
 print('PORTE OUVERTE')
 cu=net.createConnection(net.TCP)
 cu:on("receive",function(cu,c) print(c) end)
 cu:connect(porttcp,ip) -- connexion du port tcp sur l'ip choisie
 cu:send(nom .. "\n") -- envoi du code du capteur suivi d'un retour ligne
 
 tmr.delay(1000000)
 else ouvert = 0
 end
 end

-- configuration port gpio 4 en entree

gpio.trig(2, "both", contact) -- si changement etat on lance la fonction contact

-- si on emet une trame udp "sat"+nom du capteur sur le port udp sélectionné
-- la sonde repond par "ok" suivi du nom du capteur

s=net.createServer(net.UDP) 
s:on("receive",function(s,c)
 print(c)
 if c == "stat" .. nom then
 print('status sonde ok')
 cu=net.createConnection(net.TCP)
 cu:connect(porttcp,ip)
 cu:send("ok".. nom .. "\n")
 end 
 end) 
s:listen(portudp)

 

Et voilà, de retour, c’est long, oui je sais 😉

Branchez un contacteur de porte sur le port GPIO4 de votre ESP 12 (j’ai utilisé cette version pour éviter certains bugs logiciels au boot) d’un coté, sur GND (la masse) de l’autre.

esp8266_porte

ESP12 + capteur d’ouverture

Dans notre exemple, lorsque le contact s’ouvre (= port ouverte), une trame « sonde1 » est envoyée sur l’ip 192.168.1.60 port TCP 5665. Si j’envois une trame « statsonde1 » sur le port UDP 5665, la sonde va me répondre « oksonde1 » sur son port TCP 5665.

Bien entendu, tout se configure :

[checklist]

  • le port TCP
  • le port UDP
  • l’IP du serveur qui va recevoir les trames (j’ai programmé un Raspberry Pi en Python3 pour cela)

[/checklist]

On peut faire différent, et imaginer par exemple recevoir l’impulsion de détection d’un capteur de mouvement, lui aussi alimenté en 3.3v si je ne m’abuse, un peu ce genre de platine :

ir_sensor_motion

Capteur de mouvement

La fonction d’interrogation de la sonde est un gadget, mais cela peut être utile si vous souhaitez demander à votre domotique de surveiller régulièrement le fonctionnement des différents capteurs : c’est mieux qu’un ping, et on peut au besoin envoyer d’autres informations, comme avec cette sonde de température domotique que Blogwifi vous avait présentée par le passé.

L’ESP12 disposant de multiples GPIO, vous devriez pouvoir rajouter d’autres possibilités de détection en parallèle avec le GPIO4. A vous de jouer, n’hésitez pas à faire part de vos expériences dans les commentaires.