2020-03-11
md
Des appareils sans fil X10 refusent de mourir
<-Contrôleur sans fil X10 avec l'interface CM19A dans Domoticz

Comme un boomerang, du vieux matériel est revenu à la maison. Parmi les appareils récupérés, deux dispositifs demeurent utiles: une télécommande sans fil PalmPad X10 et un émetteur-récepteur radio X10 avec connexion USB, soit le CM19A. Ces appareils étaient au coeur du système domotique à ses débuts. On pourrait difficilement mieux les assortir qu'avec un Raspeberry Pi B de la première génération pour en faire un système de contrôle supplémentaire de luminaires semblable aux boutons Zigbee ajoutés dernièrement et au contrôle vocal via Alexa en place depuis quelques années. Malgré son âge et sa technologie dépassée, la télécommande PalmPad offre des avantages. Elle est utilisable par tous, u'importe le niveau de connaissance technique. En d'autres mots, des invités peuvent allumer ou éteindre plusieurs luminaires au rez-de-chaussée avec le PalmPad sans aucune aide. D'ailleurs, j'estime qu'il est plus pratique d'utiliser la télécommande pour contrôler l'intensité lumineuse du plafonnier dans l'entrée que de passer par Alexa.

Table des matières

  1. Le matériel
  2. Configuration du Raspberry Pi
  3. Installation de mochad
  4. Prise en charge de mochad par Domoticz
  5. Commutateurs virtuels pour Mochad
  6. Script Python pour se connecter à Mochad
  7. Fixer le niveau d'intensité lumineuse dans Domoticz
  8. Obtenir le niveau d'intensité lumineuse dans Domoticz
  9. Analyse syntaxique des messages de mochad
  10. Addiciel à la paserelle Mochad de Domoticz
  11. Jouer dans la cour des grands
  12. Version 0.5 de mochas

Le matériel toc

Raspberry Pi model B L'ordinateur monocarte est un Raspberry Pi modèle B (Rev 2.0) (c) 2011.12. Cette revision du tout premier modèle grand public faisait passer la mémoire à 512 Mo. Les autres caractéristiques, deux ports USB 2.0, un port Ethernet 10/100Mb, 1 port CSI, 1 port DSI et un connecteur d'entrée/sortie avec 28 broches ne changeaient pas. La puce système est un BCM2835 de Broadcom qui renferme un processeur ARM1176JZF-S (monocœur, 32 bits) cadencé à 700 Mhz. Ce Pi vieux de 12 ans n'est plus en vente, mais fonctionnellement il ressemble au Raspberry Pi Zero qui est toujours en production. Les deux sont basés sur la puce BCM2825, bien qu'elle soit cadencée à 1 000 MHz sur le modèle plus récent. Le Pi Zero n'a qu'un seul port USB, mais en revanche il possède le même connecteur d'entrée/sortie de 40 broches adopté dès la seconde génération des Pi. La version Pi Zero W ajoute une connexion Wi-Fi et serait donc facilement substituée au vieux modèle B.

CM19A Le CM19A de X10 (l'entreprise) est une passerelle entre un ordinateur, auquel il est relié avec une connexion USB, et des modules qui transmettent ou reçoivent des commandes radio X10 (le protocole). Les transmetteurs sont des télécommandes sans fil, des capteurs de mouvements, des capteurs de porte de sécurité et ainsi de suite. Seule la capacité du CM19A à retransmettre les paquets radio X10 reçus d'une télécommande vers une connexion sérielle est utile puisque je ne possède aucun module X10 récepteur contrôlé par radio. On peut utiliser l'interface CM15A ActiveHome Pro beaucoup plus gros et bien plus dispendieux que le CM19A. En plus de gérer les commandes sans fil comme le CM19A, le CM15A prend en charge la transmission et réception des signaux numériques superposés sur le courant secteur alternatif du système X10 initial.

PalmPad La télécommande X10 radio utilisée est le PalmPad (HR12A ou PHR03) avec huit couples de boutons ON | OFF et deux boutons pour intensifier ou diminuer l'intensité lumineuse du dernier dispositif allumé ayant cette capacité. On peut voir sur la photographie, l'étiquette avec les noms des luminaires contrôlés ce qui explique pourquoi j'estime que cette télécommande est si facile d'utilisation même pour les non initiés. X10 produisait d'autres télécommandes radio qui pourraient être utilisées aussi bien que le PalmPad. En voici que j'ai pu identifier: Keychain Remote Control (KR19A ou PHR04), CreditCard Thin Remote Control (KR22A), Slimline Switch Decorator White (SS13A ou PHW04D). La télécommande prend aussi en charge le capteur de mouvement sans fil ActiveEye (MS16A ou PMS03).

clé Wi-Fi Le petit ordinateur doit être connecté au réseau local ce qui pourrait être fait aisément avec un câble Ethernet. Ce n'est malheureusement pas pratique là où se trouve le Pi, alors j'ai rajouté une clé USB-Wifi pour une connexion sans fil. Auparavant, j'ai utilisé des clés contenant des puces Realtek dont les numéros d'identité étaient 0bda:8179, 0bda:0179 et 0bda:8176. Aucune ne fonctionne correctement maintenant. Je ne vais pas reprendre cette discussion ici. Heureusement, j'ai trouvé dans ma collection deux adaptateurs qui sont adéquats.

vpi@vieuxpi:~ $ lsusb ... Bus ... Device ...: ID 148f:7601 Ralink Technology, Corp. MT7601U Wireless Adapter ... Bus ... Device ...: ID 0bda:818b Realtek Semiconductor Corp. RTL8192EU 802.11b/g/n WLAN Adapter ...

Évidemment, ce problème n'existe pas chez les Raspberry Pi avec radio Wi-Fi intégré, soit les modèles 3 et plus récents et les modèles Zero W.

Carte SD Enfin il faut une carte mémoire de type SD (ou micro-SD avec adaptateur SD). Une carte de 8 Go peut facilement contenir le système d'exploitation et tous les programmes et données qu'on y rajoutera. Il est avantageux de choisir une carte de bonne qualité et préférablement rapide.

En principe, il ne faut pas plus de matériel, car l'installation peut se réaliser en mode étêté signifiant qu'on peut se passer d'un clavier et d'un moniteur reliés au Pi. Cependant, qu'on ajoute une clé Wi-Fi ou non, il est fortement recommandé de connecter le Raspberry Pi au réseau avec un câble Ethernet pour l'installation du système d'exploitation. Les téléchargements des mises à jour sont volumineux et demandent beaucoup de temps au petit modèle B. En plus, il est facile de faire une erreur lorsqu'on définit les paramètres du réseau Wi-Fi et alors la seule façon de se connecter à l'appareil étêté sera par session SSH via l'interface Ethernet ou directement par la console via un clavier et moniteur.

Configuration du Raspberry Pi toc

Une des raisons que tant d'enthousiastes demeurent fidèles envers l'écosystème du Raspberry Pi est le support indéfectible envers les premiers Pi vendus. Ainsi la plus récente version de Raspberry Pi OS fonctionne sur le modèle B vieux de douze ans est la plus récente version fonctionnant sur le modèle B. Parmi les nombreuses versions du système d'exploitation, j'ai choisi Raspberry Pi OS Lite (32-bit) Bookworm (2023-12-11-raspios-bookworm-armhf-lite.img).

Le téléversement du système d'exploitation sur la carte SD est très facile avec Raspberry Pi Imager qu'on obtient en cliquant sur le lien. Il y a tellement de tutoriels sur ce dernier qu'il est inutile de reprendre ce sujet ici. Notons toutefois que lors du téléversement avec Imager, j'ai activé le serveur SSH, modifié le nom d'utilisateur à vpi et changer le nom d'hôte du Raspberry Pi à vieuxpi. Il est aussi possible de saisir les coordonnées d'un réseau Wi-Fi, mais on peut également attendre à plus tard si c'est possible d'établir une connexion filaire au réseau local avec accès à l'Internet.

il a été possible d'ouvrir une session SSH avec le Raspberry Pi après son premier démarrage. Il faut être patient, car le Raspberry Pi doit faire des opérations, dont l'expansion du système de fichier, qui prennent beaucoup de temps pendant ce premier démarrage.

michel@hp:~$ avahi-resolve -n vieuxpi.local Échec de la résolution du nom d'hôte « oldpi.local »  : Temps d'attente écoulé ... michel@hp:~$ avahi-resolve -n vieuxpi.local vieuxpi.local 192.168.1.161 michel@hp:~$ ssh vpi@vieuxpi.local The authenticity of host 'vieuxpi.local (192.168.1.161)' can't be established. ED25519 key fingerprint is SHA256:Tts9tXi3UrKMNBYneptT2T4kx2W9PeimNeFkKpnfGv5. This host key is known by the following other names/addresses: ~/.ssh/known_hosts:30: [hashed name] Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'vieuxpi.local' (ED25519) to the list of known hosts. vpi@vieuxpi.local's password: Linux vieuxpi 6.1.0-rpi7-rpi-v6 #1 Raspbian 1:6.1.63-1+rpt1 (2023-11-24) armv6l The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law.

Identifions exactement le système d'exploitation.

vpi@vieuxpi:~ $ uname -a Linux vieuxpi 6.1.0-rpi7-rpi-v6 #1 Raspbian 1:6.1.63-1+rpt1 (2023-11-24) armv6l GNU/Linux vpi@vieuxpi:~ $ cat /etc/*release PRETTY_NAME="Raspbian GNU/Linux 12 (bookworm)" NAME="Raspbian GNU/Linux" VERSION_ID="12" VERSION="12 (bookworm)" VERSION_CODENAME=bookworm ID=raspbian ID_LIKE=debian HOME_URL="http://www.raspbian.org/" SUPPORT_URL="http://www.raspbian.org/RaspbianForums" BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs" vpi@vieuxpi:~ $ cat /etc/debian_version 12.1

il faut mettre à jour le système d'exploitation auquel de nombreuses modifications ont été apportées depuis sa sortie. C'est une opération qui peut être lente, surtout avec un Pi monocoeur. Après le lancement de la commande multiple ci-dessous, on aura le temps de se préparer un café ou un léger repas.

vpi@vieuxpi:~ $ date; sudo apt update && sudo apt upgrade -y; date Sun 4 Feb 15:16:40 AST 2024 ... 34 packages can be upgraded. Run 'apt list --upgradable' to see them. W: http://raspbian.raspberrypi.com/raspbian/dists/bookworm/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details. ... The following NEW packages will be installed: linux-headers-6.1.0-rpi8-common-rpi linux-headers-6.1.0-rpi8-rpi-v6 linux-headers-6.1.0-rpi8-rpi-v7 linux-headers-6.1.0-rpi8-rpi-v7l linux-image-6.1.0-rpi8-rpi-v6 linux-image-6.1.0-rpi8-rpi-v7 linux-image-6.1.0-rpi8-rpi-v7l The following packages have been kept back: linux-image-rpi-v8:arm64 The following packages will be upgraded: bluez firmware-atheros firmware-brcm80211 firmware-libertas firmware-misc-nonfree firmware-realtek kms++-utils libbluetooth3 libc-bin libc-dev-bin libc-devtools libc-l10n libc6 libc6-dbg libc6-dev libcamera-apps-lite libkms++0 linux-compiler-gcc-12-arm linux-headers-rpi-v6 linux-headers-rpi-v7 linux-headers-rpi-v7l linux-image-rpi-v6 linux-image-rpi-v7 linux-image-rpi-v7l linux-kbuild-6.1 linux-libc-dev locales raspberrypi-net-mods raspberrypi-sys-mods raspi-config raspi-firmware rpi-eeprom rpicam-apps-lite 33 upgraded, 7 newly installed, 0 to remove and 1 not upgraded. Need to get 186 MB of archives. After this operation, 158 MB of additional disk space will be used. ... update-initramfs: Generating /boot/initrd.img-6.1.0-rpi7-rpi-v7 update-initramfs: Generating /boot/initrd.img-6.1.0-rpi7-rpi-v6 W: Operation was interrupted before it could finish Sun 4 Feb 16:02:12 AST 2024 vpi@vieuxpi:~ $

Ce fut déconcertant d'aboutir à ce dernier avertissement après une mise à niveau de 46 minutes. J'ai décidé de répéter les étapes.

vpi@vieuxpi:~ $ sudo apt update Hit:1 http://raspbian.raspberrypi.com/raspbian bookworm InRelease Hit:2 http://archive.raspberrypi.com/debian bookworm InRelease Reading package lists... Done Building dependency tree... Done Reading state information... Done 1 package can be upgraded. Run 'apt list --upgradable' to see it. W: http://raspbian.raspberrypi.com/raspbian/dists/bookworm/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details. vpi@vieuxpi:~ $ apt list --upgradable Listing... Done linux-image-rpi-v8/stable 1:6.1.73-1+rpt1 arm64 [upgradable from: 1:6.1.63-1+rpt1] N: There is 1 additional version. Please use the '-a' switch to see it vpi@vieuxpi:~ $ sudo apt upgrade -y Reading package lists... Done Building dependency tree... Done Reading state information... Done Calculating upgrade... Done The following packages have been kept back: linux-image-rpi-v8:arm64 0 upgraded, 0 newly installed, 0 to remove and 1 not upgraded. vpi@vieuxpi:~ $ uname -a Linux vieuxpi 6.1.0-rpi8-rpi-v6 #1 Raspbian 1:6.1.73-1+rpt1 (2024-01-25) armv6l GNU/Linux

Apparamment, il n'y avait rien de plus à faire, et j'ai supposé que la mise à niveau de Raspian 6.1.63 à 6.1.73 avait fonctionné correctement. La vérification de l'état de systemd semble le confirmer.

vpi@vieuxpi:~ $ systemctl | grep failed vpi@vieuxpi:~ $ systemctl status ● vieuxpi State: running Units: 289 loaded (incl. loaded aliases) Jobs: 0 queued Failed: 0 units Since: Wed 1969-12-31 20:00:13 AST; 54 years 1 month ago systemd: 252.17-1~deb12u1+rpi1 CGroup: / ├─init.scope ...

Si l'on veut que le Raspberry Pi accède au réseau local avec le Wi-Fi et si l'on n'a pas défini les paramètres avec Imager avant le téléversement du système d'exploitation, il faut activé le Wi-Fi maintenant. Avant, du temps de Raspbian on utilisait l'utilitaire raspi-config pour configurer les paramètres réseau, mais maintenant dans Rasberry Pi OS c'est avec l'utilitaire nmtui (NetManager) qu'on procède.

Il faut doter le Raspberry Pi d'une adresse IP statique, car plusieurs logiciels ne prennent pas en charge les noms d'hôte du domaine .local. La meilleure façon de le faire est d'attribuer une adresse fixe au Pi d'après son adresse MAC dans le routeur. Comme ça, il n'y a rien à changer sur le Raspberry Pi et si le sous-réseau devait changer le Raspberry Pi pourrait obtenir une adresse valide. On peut aussi fixé l'adresse IP d'une interface réseau sur le PI avec NetManager. Il faut s'assurer que l'adresse choisie n'est pas dans la plage des adresses dynamique du routeur. Mon approche est de donner une adresse fixe à l'interface Wi-Fi et de continuer avec une adresse dynamique pour l'interface Ethernet qui n'est pas utilisée normalement. Si quelque chose change avec le réseau Wi-Fi, il sera possible de brancher le Pi sur le réseau câblé et le retrouver comme lors du premier démarrage.

Installation de mochad toc

Selon la documentation au sujet de mochad ce nom est un sigle provenant de Multiple Online Controllers for Home Automation Daemon. La description du programme est plus éloquente: mochad is a Linux TCP gateway daemon for the X10 CM15A RF (radio frequency) and PL (power line) controller and the CM19A RF controller. Comme traduction libre je propose: mochad est un service Linux agissant comme passerelle entre les contrôleurs CM15A ou CM19A de X10 et les connexions TCP. Ce service doit être en place avant d'intégrer le matériel X10 dans Domoticz.

J'ai suivi mes propres instructions pour installer mochad depuis la version qu'on retrouve dans mon référentiel sur GitHub. Les explications dans ce dépot sont en anglais, mais les instructions d'installation en français sont dans un ancien billet sur ce site. Je ne vais pas répéter tous ces détails ici.

Après l'installation de mochad, le service devrait démarrer lorsqu'un émetteur-récepteur CM15A ou CM19A est branché à un port USB du Raspberry Pi. On peut alors vérifier le fonctionnement en se connectant à mochad avec netcat.

vpi@vieuxpi:~ $ nc localhost 1099 02/01 18:57:11 Rx RF HouseUnit: J1 Func: On 02/01 18:57:15 Rx RF HouseUnit: J3 Func: On 02/01 18:57:17 Rx RF HouseUnit: J2 Func: Off 02/01 18:57:45 Rx RF House: J Func: Dim 02/01 18:57:47 Rx RF House: J Func: Bright

Ces messages provenaient de mochad alors que j'appuyais sur des touches du PalmPad dont la molette de réglage du code maison était sur « J ».

Il est très important de vérifier que l'on a accès au service depuis tout autre ordinateur sur le réseau local dont nécessairement celui sur lequel roule Domoticz. Ci-dessous je teste avec mon ordinateur bureau.

michel@hp:~$ nc 192.168.1.11 1099 02/02 11:23:20 Rx RF HouseUnit: J5 Func: On 02/02 11:23:21 Rx RF HouseUnit: J5 Func: Off 02/02 11:23:22 Rx RF HouseUnit: J4 Func: Off

Prise en charge de mochad par Domoticz toc

Domoticz communique avec le service mochad directement avec une passerelle nommée Mochad CM15Pro/CM19A bridge with LAN interface (Mochad pour faire court) qu'il faut activer.

Les étapes pour réaliser cette activation devraient être évidentes quand on examine la figure ci-dessus. Notons qu'il faut choisir le type de matériel (étape 3) avant de voir tous les champs à compléter. On peut donner le nom qu'on veut à l'étape 5 et il faut ajuster l'adresse distante qui est l'adresse IP du Raspberry Pi sur lequel mochad est installé. Malheureusement, l'adresse distante ne peut pas être un nom d'hôte local comme vieuxpi.local. C'est pour cela qu'il fallait donner une adresse IP statique à l'interface réseau du Raspberry Pi. Le port pour la connexion TCP est par défaut 1099. La valeur serait différente seulement si SERVER_PORT dans mochad.c avait été modifié avant la compilation de la source.

Il ne faut pas oublier d'activer la passerelle (étape 4) et, surtout, de cliquer sur Ajouter (dernière étape 7) quand les champs sont correctement définis. Si l'on fait une erreur ou si quelque chose change plus tard, on peut toujours modifier les paramètres de la passerelle en la sélectionnant (en cliquant sur celle-ci) puis en cliquant sur Modifier.

Après l'activation et l'ajout de la passerelle, on peut vérifier que la passerelle communique avec le service mochad sur le Raspberry Pi en examinant le journal de Domoticz (Configuration/Log).

journal de Domoticz

On devrait y voir que Mochad a réussi à se connecter à mochad; c'est l'enregistrement Mochad: connected to: xxx.xxx.xxx.xxx. On peut alors essayer de vérifier que la télécommande PalmPad fonctionne. La seule façon de le faire ici, c'est d'activer un des boutons   ce qui entraîne l'affichage d'un message d'erreur de décodage de la part de Mochad qu'on peut voir ci-dessus. Rien n'apparaît dans le journal lorsqu'un quelconque bouton blanc de la télécommande est activé. Clairement, Mochad comprend des messages de type 02/19 20:39:17 Rx RF HouseUnit: J6 Func: Off provenant de mochad, mais pas ceux qui ne contiennent pas d'adresse d'unité comme 02/20 15:53:30 Rx RF House: J Func: Dim. Au moins cela constitue une confirmation que mochad est correctement installé sur le vieux Raspberry Pi et que la passerelle Mochad reçoit bien les messages. Cela explique aussi pourquoi il nous faudra prendre en charge les messages Bright et Dim directement.

Commutateurs virtuels pour Mochad toc

puisqu'il y a 8 paires de boutons marche/arrêt sur le PalmPad et que celui-ci sera placé dans la salle de séjour, j'ai dressé une liste de sept luminaires au rez-de-chaussée qu'on pourra contrôler avec l'appareil. Il y a un groupe, au début de cette liste, activer ensemble trois appareils de la salle de séjour (Torchère, Bibliothèque et Lampe) .

dispositifIdxtype
1Salon1groupe
2Torchère1lampe
3Bibliothèque3lampe
4Lampe4lampe
5Balcons140lampe
6Entrée113lampe (gradateur)
7Lustre89lampe (gradateur)
8Cuisine90lampe (gradateur)

La colonne Idx contient le numéro d'identité unique affecté à chaque dispositif virtuel dans Domoticz qui contrôle déjà le luminaire. Aucun changement ne sera apporté à ce fonctionnement. L'astuce, déjà expliqué en 2017 dans la section 4 de Contrôleur sans fil X10 avec l'interface CM19A dans Domoticz, est l'ajout d'un second commutateur virtuel sur la passerelle Mochad pour transmettre les commandes du PalmPad au commutateur contrôlant déjà le luminaire. Si l'on décide de se défaire du matériel X10, on pourra éliminer ces commutateurs secondaires sans conséquences pour le bon fonctionnement des commutateurs principaux dans Domoticz.

À titre d'exemple, examinons l'ajout d'un commutateur virtuel sur Mochad pour contrôler indirectement le plafonnier de l'entrée avec le PalmPad. En principe, il y a plus d'une façon d'ajouter un nouveau dispositif virtuel dans Domoticz. Voici celle qui me semble la plus facile à mettre en oeuvre avec le matériel X10 utilisé.

Dans l'onglet Interrupteurs, cliquer sur le bouton Détection auto. Le message suivant est affiché pendant quelques secondes.

Pendant l'affichage de ce message, on active un bouton de la télécommande. Dans mon cas j'ai appuyé sur le sixième bouton On et la boîte de dialogue suivante apparaît.

Il faut saisir le nom du dispositif virtuel qu'on veut créer, puis cliquer sur le Add Device. Pour m'y retrouver plus tard, j'annexe « _mochad » au nom du nouveau dispositif virtuel qui sera contrôlé par la télécommande. Comme tout nouveau dispositif, il sera affiché après les autres dans l'onglet Interrupteurs.

Cliquer sur son bouton Modifier. Les caractéristiques du dispositif sont alors affichées dans l'onglet.

Les requêtes HTML suivantes sont saisies dans les champs Action On et Off.

Action On:
http://192.168.1.22:8080/json.htm?type=command&param=switchlight&idx=113&switchcmd=On

Action Off:
http://192.168.1.22:8080/json.htm?type=command&param=switchlight&idx=113&switchcmd=Off

Évidemment, il faudra ajusté l'URL de Domoticz et le numéro d'identité du dispositif virtuel (l'idx) qui contrôle le luminaire.

Maintenant, il faut utiliser la télécommande pour vérifier qu'elle contrôle effectivement le plafonnier de l'entrée. Si tout fonctionne bien, on peut éliminer ce nouveau dispositif de l'onglet en changeant son nom à $entree_mochad. Encore une fois, il y a plus d'une façon de renommer un dispositif virtuel, mais ici le plus simple est de cliquer sur son bouton Modifier de nouveau. On pourra toujours retrouver ce dispositif « invisible » dans la liste des dispositifs: Configuration/Dispositifs.

J'ai refait toutes ces démarches pour les six autres luminaires en ajustant le nom de chaque nouvel interrupteur virtuel et le numéro d'identité du dispositif contrôlant le luminaire visé dans ses requêtes HTTP.

Le premier couple de boutons marche/arrêt de la télécommande ne contrôle pas un luminaire, mais plutôt un groupe et, conséquemment, il faut modifier la requête HTTP. Plus précisément, il faut ajuster la valeur associée à la clé param.

Action On:
http://192.168.1.22:8080/json.htm?type=command&param=switchscene&idx=1&switchcmd=On

Action Off:
http://192.168.1.22:8080/json.htm?type=command&param=switchscene&idx=1&switchcmd=Off

Si les seules actions possibles pour tous les appareils contrôlés avec la télécommande PalmPad sont la mise en marche et l'arrêt, alors il n'y a plus rien à ajouter et ce billet n'est qu'une mise à jour de celui de 2017. En revanche si certains des appareils sont des lampes avec gradateur alors aussi bien trouver une façon de tenir compte des messages des boutons   de la télécommande.

Le wiki de Domoticz contient une longue page consacré à l'API du service domoticz: Domoticz API/JSON URL's. La section Turn a light/switch on/off contient l'explication suivante pour les requêtes faites ci-dessus:

Turn a light/switch on/off
/json.htm?type=command¶m=switchlight&idx=99&switchcmd=

{ "status": "OK", "title": "SwitchLight" }

When error:

{ "message" : "Error sending switch command, check device/hardware (idx=nnn) !", "status" : "ERROR", "title" : "SwitchLight" }

On ne peut pas dire qu'il s'agit d'une documentation prolixe, mais pour une action aussi simple c'est suffisant. En revanche on verra ci-dessous que l'information peut être insuffisante quand il s'agit de l'intensité lumineuse et que j'ai dû expérimenter pour arriver à mes fins.

Script Python pour se connecter à mochad toc

Voici un petit script en Python 3 qui établie une connexion avec mochad et imprime les messages de ce service dans une boucle sans fin.

#!/usr/bin/python3 import socket BUFFER_SIZE = 1024 def ncrx(host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((host, int(port))) while True: data = s.recv(BUFFER_SIZE) if not data: print('socket recv error') break print(repr(data)) #HOST = "localhost" HOST = "192.168.1.11" PORT = 1099 ncrx(HOST, PORT)

J'ai pensé qu'il serait préférable de prendre en charge certaines exceptions pour obtenir des messages un peu plus descriptifs.

#!/usr/bin/python3 import socket import errno BUFFER_SIZE = 1024 def ncrx(host, port): global count with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((host, int(port))) while True: data = s.recv(BUFFER_SIZE) if not data: raise OSError(errno.ECONNRESET, "Connection reset") print(repr(data)) #----------------------------------------------------------------------- #HOST = "localhost" HOST = "192.168.0.12" PORT = 1099 try: ncrx(HOST, PORT) except ConnectionRefusedError: print("Host {}:{} refused connection".format(HOST, PORT)) exit(1) except KeyboardInterrupt: print("User interrupt") ## exit code 130 exit(2) except OSError as E: if E.errno == errno.EHOSTUNREACH: print("No route to {}".format(HOST)) exit(3) elif E.errno == errno.ECONNRESET: print("{}:{} reset the connection".format(HOST, PORT)) exit(4) else: raise except Exception as E: print(repr(E)) exit(5)

J'ai compris plus tard que cette prise en charge des exceptions n'était pas au point. Il est approprié de répéter ici que je n'ai qu'une connaissance élémentaire de Python et que le code que je crée est fait avec l'aide d'un outil de recherche sur le web. Peut-être obtiendrai-je l'aide d'une intelligence artificielle à l'avenir, mais en attendant je serai reconnaissant pour toutes les suggestions susceptibles d'améliorer le code.

Jusqu'ici nous n'avons créé qu'une pâle imitation d'une petite partie de ce que netcat peut faire.

Fixer le niveau d'intensité lumineuse dans Domoticz toc

L'interface de programmation d'application (ou API pour application programming interface) de Domoticz ne contient pas de méthode pour accroître ou diminuer l'intensité lumineuse d'un gradateur virtuel. Tout ce que l'on trouve sur le sujet est une méthode pour fixer l'intensité à un niveau quelconque : Set a dimmable light/selector to a certain level. Voici la syntaxe de la requête HTTP à formuler.

http://{domoticz}/json.htm?type=command&param=switchlight&idx={idx}&switchcmd=Set%20Level&level={value}"

Il faut, bien sur, remplacer {domoticz} par l'adresse IP et le port du serveur Domoticz, {idx} par l'index du dispositif virtuel et {value} par l'intensité lumineuse désirée. Dans le cas des gradateurs installés dans la maison, cette intensité est une valeur entre 0 et 100, mais il existe des appareils avec des échelles d'intensité différentes. Le petit script Python suivant permet de tester l'API de domoticz.

#!/usr/bin/python3 import requests import time domoticz = "192.168.1.22:8080" verbose = 1 idx = [113, 3, 505, -9] # 133 is a dimmable light # 3 is an on/off light # 505 there is no device with that idx # -9 is not an impossible idx def set_level(idx, value): with requests.get(f"http://{domoticz}/json.htm?type=command&param=switchlight&idx={idx}&switchcmd=Set%20Level&level={value}", timeout=2) as rq: if verbose > 1: print(rq, rq.status_code) if rq.ok: if verbose: print(f"level of {idx} set to {value}") else: print(f"level of {idx} could not be set, response code = {rq.status_code}") level = -40 n = 0 while (level < 150): print("level {}:".format(level), end=' ') set_level(idx[level % 4], level) time.sleep(1) level = level + 10 n = n + 1

Le script ne fonctionne que si l'hôte de Domoticz et l'ordinateur depuis lequel ce script est exécuté sont membres du même réseau de confiance paramétré dans la section Sécurité de Domoticz. Il me faudrait revenir sur la sécurité des connexions plus tard, mais ce ne sera qu'à la fin de ce billet.

Reseau de confiance

Le script est instructif. L'API de Domoticz est très stricte quant à la syntaxe des requêtes tout en tolérant des valeurs peu sensées.

michel@hp:~/Documents/Python/$ python3 test_set_level.py level -40: level of 113 set to -40 level -30: level of 3 set to -30 level -20: level of 505 set to -20 level -10: level of -9 set to -10 level 0: level of 113 set to 0 level 10: level of 3 set to 10 level 20: level of 505 set to 20 level 30: level of -9 set to 30 level 40: level of 113 set to 40 level 50: level of 3 set to 50 level 60: level of 505 set to 60 level 70: level of -9 set to 70 level 80: level of 113 set to 80 level 90: level of 3 set to 90 level 100: level of 505 set to 100 level 110: level of -9 set to 110 level 120: level of 113 set to 120 level 130: level of 3 set to 130 level 140: level of 505 set to 140

Aucune exception n'a été engendrée et le code HTTP en réponse à chaque requête est toujours 200 (rq.ok est l'équivalent de rq.status_code == 200). Le journal de Domoticz ne signale rien d'anormal sauf pour les indices de dispositif non valides.

2024-02-28 12:34:58.447 Virtual: Light/Switch (Entrée) 2024-02-28 12:34:58.430 Status: User: domo (IP: 192.168.1.139) initiated a switch command (113/Entrée/Set Level) 2024-02-28 12:34:59.470 Virtual: Light/Switch (Lampes) 2024-02-28 12:34:59.461 Status: User: domo (IP: 192.168.1.139) initiated a switch command (3/Lampes/Set Level) 12:35:00.483 Error: User: domo, switch not found (idx=505)! 2024-02-28 12:35:01.489 Error: User: domo, switch not found (idx=-9)! 2024-02-28 12:35:02.509 Virtual: Light/Switch (Entrée) 2024-02-28 12:35:02.495 Status: User: domo (IP: 192.168.1.139) initiated a switch command (113/Entrée/Set Level) 2024-02-28 12:35:03.543 Virtual: Light/Switch (Lampes) 2024-02-28 12:35:03.522 Status: User: domo (IP: 192.168.1.139) initiated a switch command (3/Lampes/Set Level) 2024-02-28 12:35:04.555 Error: User: domo, switch not found (idx=505)! 2024-02-28 12:35:05.563 Error: User: domo, switch not found (idx=-9)! 2024-02-28 12:35:06.583 Virtual: Light/Switch (Entrée) 2024-02-28 12:35:06.571 Status: User: domo (IP: 192.168.1.139) initiated a switch command (113/Entrée/Set Level) 2024-02-28 12:35:07.612 Virtual: Light/Switch (Lampes) 2024-02-28 12:35:07.598 Status: User: domo (IP: 192.168.1.139) initiated a switch command (3/Lampes/Set Level) 2024-02-28 12:35:08.627 Error: User: domo, switch not found (idx=505)! 2024-02-28 12:35:09.633 Error: User: domo, switch not found (idx=-9)! 2024-02-28 12:35:10.657 Virtual: Light/Switch (Entrée) 2024-02-28 12:35:10.640 Status: User: domo (IP: 192.168.1.139) initiated a switch command (113/Entrée/Set Level) 2024-02-28 12:35:11.682 Virtual: Light/Switch (Lampes) 2024-02-28 12:35:11.668 Status: User: domo (IP: 192.168.1.139) initiated a switch command (3/Lampes/Set Level) 2024-02-28 12:35:12.693 Error: User: domo, switch not found (idx=505)! 2024-02-28 12:35:13.699 Error: User: domo, switch not found (idx=-9)! 2024-02-28 12:35:14.716 Virtual: Light/Switch (Entrée) 2024-02-28 12:35:14.705 Status: User: domo (IP: 192.168.1.139) initiated a switch command (113/Entrée/Set Level) 2024-02-28 12:35:15.749 Virtual: Light/Switch (Lampes) 2024-02-28 12:35:15.731 Status: User: domo (IP: 192.168.1.139) initiated a switch command (3/Lampes/Set Level) 2024-02-28 12:35:16.761 Error: User: domo, switch not found (idx=505)! 2024-02-28 12:34:58.447 Virtual: Light/Switch (Entrée)

L'interface Web de Domoticz n'affiche pas les valeurs négatives. En fait une intensité inférieure ou égale à zéro est interprétée comme une directive d'éteindre le luminaire, qu'importe si le dispositif virtuel est un commutateur ou un gradateur. Quand un gradateur est éteint, l'intensité lumineuse n'est pas modifiée. Ainsi quand le gradateur est réallumé, il reviendra à son niveau de luminosité antérieur. De même, fixer l'intensité lumineuse à une valeur positive allume la lampe contrôlée, qu'importe qu'elle soit à intensité variable ou non. Cela me semble tout à fait acceptable, mais en revanche le traitement des intensités supérieures à 100 % est surprenant. L'image ci-dessous est éloquente à ce sujet.

Intensité supérieure à 100%

Toute intensité supérieure à 100 % est affichée, qu'importe le type de dispositif virtuel, mais on ne peut dépasser 100 % d'intensité lumineuse et Domoticz fixe le niveau de luminosité à 100 dans ces situations de dépassement.

Si l'adresse IP de Domoticz est incorrect alors une avalanche d'exception est déclenchée.

michel@hp:~/Documents/Python/$ python3 test_set_level.py level -40: Traceback (most recent call last): File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 169, in _new_conn conn = connection.create_connection( File "/usr/lib/python3/dist-packages/urllib3/util/connection.py", line 96, in create_connection raise err File "/usr/lib/python3/dist-packages/urllib3/util/connection.py", line 86, in create_connection sock.connect(sa) TimeoutError: timed out During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 700, in urlopen httplib_response = self._make_request( File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 395, in _make_request conn.request(method, url, **httplib_request_kw) File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 234, in request super(HTTPConnection, self).request(method, url, body=body, headers=headers) File "/usr/lib/python3.10/http/client.py", line 1283, in request self._send_request(method, url, body, headers, encode_chunked) File "/usr/lib/python3.10/http/client.py", line 1329, in _send_request self.endheaders(body, encode_chunked=encode_chunked) File "/usr/lib/python3.10/http/client.py", line 1278, in endheaders self._send_output(message_body, encode_chunked=encode_chunked) File "/usr/lib/python3.10/http/client.py", line 1038, in _send_output self.send(msg) File "/usr/lib/python3.10/http/client.py", line 976, in send self.connect() File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 200, in connect conn = self._new_conn() File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 174, in _new_conn raise ConnectTimeoutError( urllib3.exceptions.ConnectTimeoutError: (&lt;urllib3.connection.HTTPConnection object at 0x7f49845ce050&gt;, 'Connection to 192.168.1.245 timed out. (connect timeout=2)') During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3/dist-packages/requests/adapters.py", line 439, in send resp = conn.urlopen( File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 756, in urlopen retries = retries.increment( File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 574, in increment raise MaxRetryError(_pool, url, error or ResponseError(cause)) urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='192.168.1.245', port=8080): Max retries exceeded with url: /json.htm?type=command&param=switchlight&idx=113&switchcmd=Set%20Level&level=-40 (Caused by ConnectTimeoutError(&lt;urllib3.connection.HTTPConnection object at 0x7f49845ce050&gt;, 'Connection to 192.168.1.245 timed out. (connect timeout=2)')) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/michel/Documents/Python/mochas/extras/test_set_level.py", line 31, in &lt;module&gt; set_level(idx[n % 4], level) File "/home/michel/Documents/Python/mochas/extras/test_set_level.py", line 17, in set_level with requests.get(f"http://{domoticz}/json.htm?type=command&param=switchlight&idx={idx}&switchcmd=Set%20Level&level={value}", timeout=2) as rq: File "/usr/lib/python3/dist-packages/requests/api.py", line 76, in get return request('get', url, params=params, **kwargs) File "/usr/lib/python3/dist-packages/requests/api.py", line 61, in request return session.request(method=method, url=url, **kwargs) File "/usr/lib/python3/dist-packages/requests/sessions.py", line 544, in request resp = self.send(prep, **send_kwargs) File "/usr/lib/python3/dist-packages/requests/sessions.py", line 657, in send r = adapter.send(request, **kwargs) File "/usr/lib/python3/dist-packages/requests/adapters.py", line 504, in send raise ConnectTimeout(e, request=request) requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='192.168.1.245', port=8080): Max retries exceeded with url: /json.htm?type=command&param=switchlight&idx=113&switchcmd=Set%20Level&level=-40 (Caused by ConnectTimeoutError(&lt;urllib3.connection.HTTPConnection object at 0x7f49845ce050&gt;, 'Connection to 192.168.1.245 timed out. (connect timeout=2)'))

Si une erreur HTTP se produit, en utilisant /json.html plutôt que /json.htm par exemple, rien ne fonctionne, mais aucune exception n'est soulevée et rien n'est affiché dans le journal de Domoticz.

michel@hp:~/Documents/Python$ python3 test_set_level.py level -40: level of 113 could not be set, response code = 404 level -30: level of 3 could not be set, response code = 404 level -20: level of 505 could not be set, response code = 404 level -10: level of -9 could not be set, response code = 404 ...

On peut faire mieux dans la gestion des exceptions et erreurs HTTP. Voici une nouvelle version de la fonction set_level.

def set_level(idx, value): try: rq = requests.get(f"http://{domoticz}/json.htm?type=command&param=switchlight&idx={idx}&switchcmd=Set%20Level&level={value}", timeout=2) rq.raise_for_status() # HTTP errors (i.e. 404) will become an HTTPError exception except requests.exceptions.RequestException as e: raise SystemExit(e) # pass on only the first exception with rq: if verbose > 1: print(rq, rq.status_code) if rq.ok: if verbose: print(f"level of {idx} set to {value}") else: print(f"level of {idx} could not be set, response code = {rq.status_code}")

Encore une fois, la gestion des exceptions sera améliorée dans le script final. Malheureusement, si la requête est mal formée alors rien n'est modifié au niveau des dispositifs virtuels et réels, le journal contiendra un message d'erreur ou d'avertissement, mais la fonction set_level agira comme si rien n'était.

Le message suivant est affiché dans le journal si la requête envoyée contient une coquille comme type=comand :

2024-02-28 14:38:46.179 Status: [WebServer] Deprecated RType (comand) for API request. Call ignored, please use correct API Command! (192.168.1.139)

Le niveau du message (status = 2) n'est pas une indication qu'il s'agit d'une erreur, même si la requête est ignorée. De même, si on se trompe avec switchlight, la commande est ignorée et le journal contient le massage suivant au niveau 1 comme si c'était normal.

2024-02-28 15:17:37.682 Invalid API command received! (sitchlight)

Pire du point de vue d'identification des erreurs, quand le commandement final est St%20Level&level= plutôt que Set%20Level&level= il n'y a aucune exception et rien n'apparaît dans le journal.

Avant de faire ces derniers tests, j'espérais qu'il serait possible d'examiner le journal de Domoticz après la requête pour voir si une erreur s'était produite. J'étais dubitatif, car l'API pour récupérer des messages du journal, n'est pas simple comme la capture d'écran ci-dessous le montre.

Retrieve Domoticz log

Il faut spécifier l'heure locale depuis laquelle on veut les messages et le niveau des messages à afficher. L'heure doit être le nombre de secondes écoulée depuis le début de l'époche Linux. Vu l'incohérence des niveaux des message, il faut récupérer tous les messages depuis l'envoi de la requête pour fixer l'intensité lumineuse. Donc il faudra éplucher la réponse au format JSON pour retrouver les messages pertinents et soulever une exception le cas échéant. Ce serait beaucoup d'efforts pour atteindre une solution incomplète comme le dernier test à démontré.

La leçon est claire: il faut examiner les luminaires pour s'assurer que le script fonctionne bien. Si ce n'est pas le cas, un examen des dispositifs virtuels et du journal pendant l'exécution du script et les exceptions soulevées par ce dernier pourrait révéler l'erreur, mais rien n'est garanti à ce niveau.

Obtenir le niveau d'intensité lumineuse dans Domoticz toc

Puisqu'il y a une requête pour fixer le niveau d'intensité lumineuse, on penserait qu'il y aurait une pour obtenir l'intensité. Or la symétrie get/set qu'on retrouve dans les définitions d'objets n'est pas respectée dans Domoticz. Pour lire une propriété d'un dispositif virtuel, il faut obtenir l'état entier du dispositif et rétrouver la propriété désirée dans cette information assez touffue.

01: { 02: "ActTime" : 1709152015, 03: "AstrTwilightEnd" : "19:42", 04: "AstrTwilightStart" : "05:20", 05: "CivTwilightEnd" : "18:32", 06: "CivTwilightStart" : "06:30", 07: "DayLength" : "11:03", 08: "NautTwilightEnd" : "19:07", 09: "NautTwilightStart" : "05:55", 10: "ServerTime" : "2024-02-28 16:26:55", 11: "SunAtSouth" : "12:31", 12: "Sunrise" : "07:00", 13: "Sunset" : "18:02", 14: "app_version" : "2024.4", 15: "result" : 16: [ 17: { 18: "AddjMulti" : 1.0, 19: "AddjMulti2" : 1.0, 20: "AddjValue" : 0.0, 21: "AddjValue2" : 0.0, 22: "BatteryLevel" : 100, 23: "CustomImage" : 0, 24: "Data" : "On", 25: "Description" : "", 26: "DimmerType" : "abs", 27: "Favorite" : 0, 28: "HardwareDisabled" : false, 29: "HardwareID" : 2, 30: "HardwareName" : "Virtual", 31: "HardwareType" : "Dummy (Does nothing, use for virtual switches only)", 32: "HardwareTypeVal" : 15, 33: "HaveDimmer" : true, 34: "HaveGroupCmd" : true, 35: "HaveTimeout" : false, 36: "ID" : "000140C1", 37: "Image" : "Light", 38: "IsSubDevice" : false, 39: "LastUpdate" : "2024-02-28 16:03:43", 40: "Level" : 100, 41: "LevelInt" : 100, 42: "MaxDimLevel" : 100, 43: "Name" : "Entr\u00e9e", 44: "Notifications" : "true", 45: "PlanID" : "3", 46: "PlanIDs" : 47: [ 48: 3 49: ], 50: "Protected" : false, 51: "ShowNotifications" : true, 52: "SignalLevel" : 8, 53: "Status" : "On", 54: "StrParam1" : "", 55: "StrParam2" : "", 56: "SubType" : "Switch", 57: "SwitchType" : "Dimmer", 58: "SwitchTypeVal" : 7, 59: "Timers" : "true", 60: "Type" : "Light/Switch", 61: "TypeImg" : "dimmer", 62: "Unit" : 1, 63: "Used" : 1, 64: "UsedByCamera" : false, 65: "XOffset" : "314", 66: "YOffset" : "399", 67: "idx" : "113" 68: } 69: ], 70: "status" : "OK", 71: "title" : "Devices" 72: }

Toute cette information est obtenue en responsa à une requête API Retrieve status of Domoticz instance. Pour autant que je sache, c'est toujours la même structure qu'importe la nature du dispositif et conséquemment il y a des enregistrements qui ne correspondent à rien. Il y a six clés rattachées à la variabilité de la luminosité, dont trois avec valeur numérique et trois avec une chaîne comme valeur décrivant la nature du dispositif.

Clé Valeur
Gradateur Commutateur
"DimmerType" "abs" "none"
"HaveDimmer" true true
"Level" 49 90
"IntLevel" 49 90
"MaxDimLevel"100 100
"SwitchType" "dimmer""On/Off"

Une vérification de "SwitchType" distingue les gradateurs des commutateurs ci-dessous alors que la valeur de "HaveDimmer" ne fonctionnerait pas. Quelle est la différence entre "Level" et "IntLevel" ? J'ai choisi d'utiliser le dernier en supposant que la valeur du premier pourrait être une chaîne selon le type de gradateur. Les clés "DimmerType" et "MaxDimLevel" ne sont pas utilisées ici parce que l'intensité est fixée de façon absolue sur une échelle de 0 à 100 sur tous les gradateurs de la maison. Voici une fonction qui peut extraire la valeur de "LevelInt" si elle existe.

#!/usr/bin/python3 import requests import time domoticz = "192.168.1.22:8080" verbose = 0 idx = [113, 3, 505, -9] # 133 is a dimmable light # 3 is an on/off light # 505 there is no device with that idx # -9 is an impossible idx def get_level(idx): try: rq = requests.get(f"http://{domoticz}/json.htm?type=command&param=getdevices&rid={idx}", timeout=2) rq.raise_for_status() # HTTP errors (i.e. 404) will become an HTTPError exception except requests.exceptions.RequestException as e: raise SystemExit(e) # pass on only the first exception with rq: if verbose > 1: print(f"\nidx = {idx}, rq.text = {rq.text}") print("Status_code", rq.status_code) if rq.ok: try: data = rq.json()["result"][0] # read respone as JSON string if not data["SwitchType"] == "Dimmer": if verbose: print(f"device {idx} is not a dimmer") return -1 return data["LevelInt"] except KeyError as e: if verbose: print(f"No key named {e}") pass except Exception as e: if verbose: print(f"Error reading JSON response: {e}") pass else: print(f"could not get level of {idx}, response code = {rq.status_code}") return -1 for n in idx: print(f"level of {n} is {get_level(n)}") time.sleep(1)

La requête HTTP auprès de Domoticz est faite de la même façon qu'auparavant dans set_level. La réponse du serveur domotique au format JSON est convertie en dictionnaire et d'où l'on obtient l'information désirée. Si le numéro idx ne correspond pas à un dispositif, même quand il est négatif, Domoticz retourne un code 200 et une réponse au format JSON qui contient le préambule, soit les lignes 1 à 14 seulement. Dans ce cas, une exception de type KeyError est créée quand on demande la valeur associée à la clé "result". Cette exception sera soulevée si les clés "SwitchType et "LevelInt" ne sont pas trouvées.

Analyse syntaxique des messages de mochad toc

Le service mochad peut transmettre plusieurs types de messages, mais seulement deux sont pertinents. Comparons ces deux types de messages qui sont composés de huit éléments.

0 1 2 3 4 5 6 7
mois/jour heure Dir Tech. Type Adr. Cmd Action
02/20 18:53:40 Rx RF HouseUnit: J3 Func: (On|Off)
02/20 19:02:08 Rx RF House: J Func: (Bright|Dim)

On sait que la passerelle Mochad de Domoticz ne peut pas décoder le second type de message. Parce que ce type de message contient une adresse incomplète, il faut décoder le premier type de message aussi pour conserver la dernière adresse X10 complète et pour l'utiliser pour modifier l'intensité du bon appareil.

#!/usr/bin/python3 VERBOSE = 2 HOUSE = "J" DELTA = 15 Unit = "" # where the current unit number is saved def update_level(Unit, delta): print(f"Modify level of unit {Unit} by {delta}") return 27 def parseX10(data): global Unit # where the current unit number is saved if VERBOSE > 1: print(f"parse10({data}, current Unit={Unit}") parts = data.split(' ') #for e in parts: print(e) # Check the structure of the packet if len(parts) < 8: if VERBOSE: print(f"{data} is an incomplete packet") return if (parts[2] != "Rx") or (parts[3] != "RF") or (parts[6] != "Func:"): if VERBOSE: print("not valid RF Rx packet") # it could be a valid packet, just not one for us return # we do not vet the date, but we check the house address if HOUSE != parts[5][0]: if VERBOSE: print(f'House "{parts[5][0]}" is not monitored') return #remove trailing LF action = parts[7].rstrip() # switch on address type of packet if parts[4] == "HouseUnit:": if (action == "On" or action == "Off"): try: unit = int(parts[5][1:]) if (unit < 1) or (unit > 16): raise ValueError Unit = str(unit) except: if VERBOSE: print(f"'{parts[5][1:]}' is an invalid device number") pass else: if VERBOSE: print(f"'{action}' is an invalid action") return if parts[4] == "House:": if action == "Dim": delta = -DELTA elif action == "Bright": delta = DELTA else: if VERBOSE: print(f"'{action}' is an invalid action") return clev = update_level(Unit, delta) if VERBOSE: if clev < -1: print(f"could not get current level of unit {Unit}") elif clev == -1: print("no matching device") else: print(f"level of unit {Unit} set to {clev}") return if VERBOSE: print(f"'{parts[4]}' is an invalid address type") tests = [ "02/20 19:13:08 Rx RF House: J Func: Bright", "02/20 19:13:07 Rx RF HouseUnit: J12 Func: Off", "02/20 19:13:14 Rx RF House: J Func: Dim", "02/20 19:13:07 Rx RF HouseUnit: J9 Func: On", "02/20 19:13:14 Rx RF House: J Func: Bright", "02/20 19:13:14 Rx RF House: J Func: dim", "02/20 18:53:40 Rx RF HouseUnit: J3 Func: oops", "02/20 19:13:14 Rx RF House: F Fun: Dim", "02/20 18:53:40 Rx PL HouseUnit: J3 Func: OFF", "02/20 18:53:40 Tx RF HouseUnit: J3 Func: OFF", "02/20 18:53:40 Rx RF HouseUnit: A3 Func: On", "19:13:14 Rx RF House: J", "02/20 19:13:07 Rx RF HouseUnit: J Func: On", "02/20 19:13:07 Rx RF HouseUnit: J-3 Func: On" ] for test in tests: print() parseX10(test)

À la première étape de l'analyse, le message obtenu de mochad est divisé en éléments avec l'espace comme séparateur. On vérifie qu'il y a huit éléments, car il arrive souvent que mochad transmet des messages incomplets. Après on s'assure que les éléments fixes (Rx, RF et Func:) sont présents. On coupe court à l'analyse si le message est à propos d'une unité avec un code maison autre que celui spécifié dans la constante HOUSE. Ce code correspond à la lettre choisie avec la roulette au bas du PalmPad. Rendu ici, il suffit d'examiner les éléments 4 et 7 pour sauvegarder le nom de la dernière unité allumée ou éteinte dans la variable Unit ou pour ajuster l'intensité de cette dernière unité. La constante DELTA spécifie de combien il faut augmenter ou diminuer l'illumination. Évidemment, la fonction update_level n'est qu'une ébauche ci-dessus.

Addiciel à la paserelle Mochad de Domoticz toc

En combinant les fonctions présentées ci-dessus, on aboutit à une première ébauche de l'addiciel à la passerelle Mochad.

#!/usr/bin/python3 import socket import errno import requests VERBOSE = 1 HOST = "192.168.0.12" # coud be "localhost" if running on same machine as mochad PORT = 1099 HOUSE = "K" DELTA = 15 DOMOTICZ = "192.168.0.45:9071" DEVICES = {"6":113, "7":89, "8":90} Unit = "" # this is the variable used to save the current unit def getIdx(unit): try: return DEVICES[unit] except: return -1 def get_level(idx): if idx < 0: return -1 try: rq = requests.get(f"http://{DOMOTICZ}/json.htm?type=command&param=getdevices&rid={idx}", timeout=2) rq.raise_for_status() # HTTP errors (i.e. 404) will become an HTTPError exception except requests.exceptions.RequestException as e: raise SystemExit(e) # pass on only the first exception with rq: if VERBOSE > 1: print(f"\nidx = {idx}") print(f"rq.text(start) = {rq.text[:48]}") print(f"rq.text(end) = {rq.text[-48:]}") print("Status_code", rq.status_code) if rq.ok: try: data = rq.json()["result"][0] # read respone as JSON string and keep first "result" entry if not data["SwitchType"] == "Dimmer": if VERBOSE: print(f"device {idx} is not a dimmer") return -1 if not data["DimmerType"] == "abs": if VERBOSE: print(f"dimmer {idx} is not set absolutely") return -1 if not data["MaxDimLevel"] == 100: if VERBOSE: print(f"the level of dimmer {idx} is not a percentage") return -1 return data["LevelInt"] except KeyError as e: if VERBOSE: print(f"No key named {e}") pass except Exception as e: if VERBOSE: print(f"Error reading JSON response: {e}") pass else: print(f"could not get the level of {idx}, response code = {rq.status_code}") return -1 def set_level(idx, value): try: rq = requests.get(f"http://{DOMOTICZ}/json.htm?type=command&param=switchlight&idx={idx}&switchcmd=Set%20Level&level={value}", timeout=2) rq.raise_for_status() # HTTP errors (i.e. 404) will become an HTTPError exception except requests.exceptions.RequestException as e: raise SystemExit(e) # pass on only the first exception with rq: if VERBOSE > 1: print(rq, rq.status_code) if rq.ok: if VERBOSE: print(f"level of {idx} set to {value}") else: print(f"level of {idx} could not be set, response code = {rq.status_code}") def update_level(unit, delta): currentIdx = getIdx(unit) if currentIdx < 0: return -1 newLevel = get_level(currentIdx) if newLevel < 0: return -2 if newLevel == 1: newLevel = 0 newLevel = newLevel + delta # clamp the value if newLevel > 100: newLevel = 100 elif newLevel < 10: newLevel = 1 # can't set level to 0 set_level(currentIdx, newLevel) if newLevel <= 1: rq = requests.get(f'http://{DOMOTICZ}/json.htm?type=command&param=switchlight&idx={currentIdx}&switchcmd=Off') newLevel = 0 return newLevel def parseX10(data): global Unit # where the current unit number is saved if VERBOSE > 1: print(f"parse10({data}, current Unit={Unit}") parts = data.split(' ') #for e in parts: print(e) # Check the structure of the packet if len(parts) < 8: if VERBOSE: print(f"{data} is an incomplete packet") return if (parts[2] != "Rx") or (parts[3] != "RF") or (parts[6] != "Func:"): if VERBOSE: print("not a valid RF Rx packet") # it could be a valid packet, just not one for us return # we do not vet the date, but we check the house address if HOUSE != parts[5][0]: if VERBOSE: print(f'House "{parts[5][0]}" is not monitored') return #remove trailing LF action = parts[7].rstrip() # switch on address type of packet if parts[4] == "HouseUnit:": if (action == "On" or action == "Off"): try: unit = int(parts[5][1:]) if (unit < 1) or (unit > 16): raise ValueError Unit = str(unit) if VERBOSE: print(f"Unit set to '{Unit}'") except: if VERBOSE: print(f"'{parts[5][1:]}' is an invalid device number") pass else: if VERBOSE: print(f"'{action}' is an invalid action") return if parts[4] == "House:": if not Unit: if VERBOSE: print("No Unit defined") return if action == "Dim": delta = -DELTA elif action == "Bright": delta = DELTA else: if VERBOSE: print(f"'{action}' is an invalid action") return clev = update_level(Unit, delta) if VERBOSE: if clev < -1: print(f"could not get current level of unit {Unit}") elif clev == -1: print("no matching device") else: print(f"level of unit {Unit} set to {clev}") return if VERBOSE: print(f"'{parts[4]}' is an invalid address type") BUFFER_SIZE = 1024 def ncrx(host, port): global count with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((host, int(port))) while True: data = s.recv(BUFFER_SIZE) if not data: raise OSError(errno.ECONNRESET, "Connection reset") parseX10(data.decode()) #----------------------------------------------------------------------- try: ncrx(HOST, PORT) except ConnectionRefusedError: print(f"Host {HOST}:{PORT} refused connection") exit(1) except KeyboardInterrupt: print("User interrupt") ## exit code 130 exit(2) except OSError as E: if E.errno == errno.EHOSTUNREACH: print(f"No route to {HOST}") exit(3) elif E.errno == errno.ECONNRESET: print(f"{HOST}:{PORT} reset the connection") exit(4) else: raise except Exception as E: print(repr(E)) exit(5)

La seule nouveauté c'est la fonction update_level(unit, delta). En premier elle obtient le numéro d'identité du dispositif virtuel contrôlé avec l'unité X10 unit. S'il y a erreur et unit n'est pas une clé dans DEVICES, la fonction ne fait rien. Autrement, la fonction obtient le niveau de luminosité actuel selon Domoticz. Si ce n'est pas possible, alors l'exécution de la fonction est terminée. Si tout est bien, la fonction ajoute delta pour obtenir la nouvelle intensité. S'assurant que cette valeur est entre 0 et 100, elle fixe l'intensité en utilisant la fonction set_level. La démarche est en fait un plus complexe, car Domoticz refuse de fixer le niveau à 0. Il éteint plutôt le luminaire et l'on a déjà vu qu'il ne met pas l'intensité à 0 dans ce cas. Donc, quand le niveau est ramené à 0 avec le bouton Dim, je triche un peu en disant à Domoticz de régler l'intensité sur 1, puis en lui disant d'éteindre la lumière. Selon Domoticz l'intensité lumineuse est de 1 et le luminaire est éteint. Quand Domoticz retourne un niveau de luminosité de 1, ce chiffre est ramené à 0 avant d'ajouter delta. Donc les luminosités au bas de l'échelle sont 0 et 15 (=DELTA), mais on peut aboutir à 10 diminuant l'intensité depuis 100 avec le PalmPad.

J'ai testé avec succès ce script sur mon ordinateur bureau et il a fonctionné continuellement sur le Raspberry Pi depuis plus de deux semaines. Cependant, il n'est pas pratique de lancer manuellement ce programme, alors je l'ai sauvegardé sous le nom mochadad.py dans le dossier /home/pi/.local/bin. Puis j'ai vérifié que Python3 est là où la première ligne du script indique qu'il se trouve. Ensuite, j'ai marqué le script comme un fichier exécutable.

pi@tarte:~ $ which python3 /usr/bin/python3 pi@tarte:~ $ chmod +x .local/bin/mochadad.py pi@tarte:~ $ ls -l .local/bin/mochadad.py -rwxr-xr-x 1 vpi vpi 6366 Feb 24 02:27 .local/bin/mochadad.py

Il restait une problème un peu épineux que j'ai bâclé avec une tâche cron qui s'exécute 5 minutes après chaque démarrage de tarte. Voici la tâche.

@reboot sleep 300; nohup /home/pi/.local/bin/mochadad.py &

On ajoute cette ligne dans un fichier système avec la commande suivante

pi@tarte:~ $ crontab -e

Le & terminal indique que mochadad.py doit fonctionner en arrière-plan alors que nohup est pour supprimer les messages du programme vers la console. Pourquoi 5 minutes d'attente avant de lancer mochadad.py ? C'est pour s'assurer que mochad est activé, car autrement, une exception arrête l'exécution de mochadad.py. Je pourrais changer le script et mettre la dernière partie après la ligne #----- dans une boucle perpétuelle qui s'exécute toutes les 60 secondes. Comme ça mochad ou Domoticz arrêtent de fonctionner pendant un certain temps puis reviennent en ligne, mochadad.py pourra se reconnecter. J'ai procédé autrement.

Jouer dans la cour des grands toc

Comme complément de formation pour autodidacte, j'ai décidé d'essayer de transformer ce script en service géré par systemd. Je ne vais pas présenter en détail le coude source du script sensiblement modifié. On le trouvera dans le référentiel mochas sur GitHub avec deux autres fichiers qu'on verra ci-dessous. Pour faire comme les autres services ajoutés au système d'exploitation, j'ai décidé d'installer le script dans le même répertoire que mochad. Du coup, j'ai changé son nom pour la symétrie qu'on peut voir ci-dessous.

vpi@vieuxpi:~ $ ls -l /usr/local/bin/ total 132 -rwx------ 1 root root 122100 Feb 4 18:24 mochad -rwx------ 1 root root 8964 Feb 18 12:37 mochas

Comme pour mochad seul root a les permissions pour écrire, lire et exécuter le script nommé mochas.

vpi@vieuxpi:~ $ sudo chmod 700 /usr/local/bin/mochas

Il fallait aussi modifier le script pour expédié ses messages vers le journal du système puisque tout se fait en arrière plan. J'ai aussi voulu rendre l'utilisation du script plus facile pour les deux ou trois autres personnes qui pourraient se servir de mochas pour intégré un PalmPad dans Domoticz. Pour cela j'ai rajouté un fichier de configuration, mochas.json stocké dans le dossier /etc/mochas.

{ "LOGLEVEL": "error", "HOST": "192.168.168.168", "PORT": 1099, "HOUSE": "J", "DELTA": 15, "DOMOTICZ": "192.168.168.168:8080", "DEVICES": { "6": 66, "7": 177, "8": 288 } }

Je pense qu'il n'est pas nécessaire d'expliquer la structure et le contenu de ce fichier. Le seul ajout par rapport au paramètres constants dans mochadad.py est LOGLEVEL qui peut prendre une de trois valeurs: error, info ou debug avec error comme choix typique en temps normal et info si l'on rencontre des problème ou debug pour obtenir encore plus d'information.

Enfin, il fallait créer un fichier de service qui est enregistré dans le dossier /etc/systemd/system.

[Unit] Description=A Domoticz Mochad bridge assistant Requires=mochad.service After=multi-user.target mochad.service [Service] Type=exec RestartSec=5 Restart=always ExecStart=/usr/local/bin/mochas & StandardError=null [Install] WantedBy=multi-user.target

Le type de service est fixé sur exec puisque c'est ce qui est recommandé dans la documentation de systemd. J'ai rajouté l'enregistrement StandardError=null dans la section [Service]. C'est quelque chose qu'on voit rarement dans les exemples présentés dans les tutoriels. La raison est que les messages envoyés au journal apparaissaient deux fois. Je pense qu'il serait possible de stopper ce comportement avec le module logger plutôt que syslog, mais ce sera quelque chose à voir éventuellement.

L'enregistrement ExecStart=/usr/local/bin/mochas & est essentiel, mais on comprend facilement ce qu'il fait. Dans une session j'ai lancé l'affichage du journal en temps réel, puis dans une autre session j'ai démarré le service mochas.

vpi@vieuxpi:~ $ sudo systemctl start mochas.service

On peut donc suivre le démarrage de mochas et le voir à l'oeuvre alors que j'ajustais l'intensité d'un gradateur.

vpi@vieuxpi:~ $ journalctl -f ... Mar 02 12:03:29 vieuxpi sudo[3544]: pam_unix(sudo:session): session closed for user root Mar 02 12:04:19 vieuxpi sudo[3580]: vpi : TTY=pts/0 ; PWD=/home/vpi ; USER=root ; COMMAND=/usr/bin/systemctl start mochas.service Mar 02 12:04:19 vieuxpi sudo[3580]: pam_unix(sudo:session): session opened for user root(uid=0) by vpi(uid=1000) Mar 02 12:04:19 vieuxpi systemd[1]: Starting mochas.service - A Domoticz Mochad bridge assistant... Mar 02 12:04:19 vieuxpi systemd[1]: Started mochas.service - A Domoticz Mochad bridge assistant. Mar 02 12:04:19 vieuxpi sudo[3580]: pam_unix(sudo:session): session closed for user root Mar 02 12:04:23 vieuxpi mochas[3584]: Starting mochas Mar 02 12:04:23 vieuxpi mochas[3584]: Connected to mochad Mar 02 12:07:25 vieuxpi mochas[3584]: Level of current_unit 7 (idx=89) set to 15 Mar 02 12:07:27 vieuxpi mochas[3584]: Level of current_unit 7 (idx=89) set to 1 Mar 02 12:07:27 vieuxpi mochas[3584]: Unit 7 (idx=89) turned off and level set to 0 Mar 02 12:07:32 vieuxpi mochas[3584]: Level of current_unit 7 (idx=89) set to 15 Mar 02 12:07:33 vieuxpi mochas[3584]: Level of current_unit 7 (idx=89) set to 30

Notons que mochas n'a rencontré aucun problème puisque mochad était déjà en marche. Quand le Raspberry Pi redémarre, ce n'est pas aussi simple. Voici les enregistrements du journal du système au sujet de mochad et mochas lors d'un démarrage du Raspberry Pi.

vpi@vieuxpi:~ $ journalctl | grep -E 'mochad|mochas' ... Mar 02 12:09:51 vieuxpi systemd[1]: Starting mochad.service - Mochad a TCP gateway service for X10-RF (CM15A/CM15Pro/CM19A)... Mar 02 12:09:51 vieuxpi mochad[371]: starting Mar 02 12:09:52 vieuxpi mochad[374]: Found CM19A Mar 02 12:09:52 vieuxpi mochad[374]: In endpoint 0x83, Out endpoint 0x04 Mar 02 12:09:53 vieuxpi systemd[1]: Started mochad.service - Mochad a TCP gateway service for X10-RF (CM15A/CM15Pro/CM19A). Mar 02 12:10:10 vieuxpi systemd[1]: Starting mochas.service - A Domoticz Mochad bridge assistant... Mar 02 12:10:10 vieuxpi systemd[1]: Started mochas.service - A Domoticz Mochad bridge assistant. Mar 02 12:10:19 vieuxpi mochas[553]: Starting mochas Mar 02 12:10:19 vieuxpi systemd[1]: mochas.service: Main process exited, code=exited, status=1/FAILURE Mar 02 12:10:20 vieuxpi systemd[1]: mochas.service: Failed with result 'exit-code'. Mar 02 12:10:20 vieuxpi systemd[1]: mochas.service: Consumed 4.057s CPU time. Mar 02 12:10:25 vieuxpi systemd[1]: mochas.service: Scheduled restart job, restart counter is at 1. Mar 02 12:10:25 vieuxpi systemd[1]: Stopped mochas.service - A Domoticz Mochad bridge assistant. Mar 02 12:10:25 vieuxpi systemd[1]: mochas.service: Consumed 4.057s CPU time. Mar 02 12:10:25 vieuxpi systemd[1]: Starting mochas.service - A Domoticz Mochad bridge assistant... Mar 02 12:10:25 vieuxpi systemd[1]: Started mochas.service - A Domoticz Mochad bridge assistant. Mar 02 12:10:32 vieuxpi mochas[621]: Starting mochas Mar 02 12:10:33 vieuxpi systemd[1]: mochas.service: Main process exited, code=exited, status=1/FAILURE Mar 02 12:10:33 vieuxpi systemd[1]: mochas.service: Failed with result 'exit-code'. Mar 02 12:10:33 vieuxpi systemd[1]: mochas.service: Consumed 3.885s CPU time. Mar 02 12:10:38 vieuxpi systemd[1]: mochas.service: Scheduled restart job, restart counter is at 2. Mar 02 12:10:38 vieuxpi systemd[1]: Stopped mochas.service - A Domoticz Mochad bridge assistant. Mar 02 12:10:38 vieuxpi systemd[1]: mochas.service: Consumed 3.885s CPU time. Mar 02 12:10:38 vieuxpi systemd[1]: Starting mochas.service - A Domoticz Mochad bridge assistant... Mar 02 12:10:38 vieuxpi systemd[1]: Started mochas.service - A Domoticz Mochad bridge assistant. Mar 02 12:10:45 vieuxpi mochas[677]: Starting mochas Mar 02 12:10:45 vieuxpi mochas[677]: Connected to mochad

Le logiciel d'initialisation systemd essaye de démarrer tous les services en parallèle pour accéléré le lancement du système d'exploitation. En même temps, on peut ordonner le démarrage des services avec des options dans le fichier de service. C'est le but des enregistrements WantedBy=multi-user.target, Requires=mochad et After=multi-user.target mochad.service. Effectivement, mochas n'est démarré qu'après l'activation du service mochad. Toutefois, mochas est arrêté à 12:10:25 et 12:10:38. Je suppose que la raison est que mochad n'est pas encore connecté au CM19A et qu'il est conséquemment impossible de se connecter à son port TCP. Or parmi les paramètres du fichier de service de mochas on retrouve les enregistrements RestartSec=5 et Restart=always. Les conséquences sont que le service sera automatiquement redémarré toutes les 5 secondes s'il est arrêté. Après deux tentatives, mochas est enfin connecté à mochad. Voilà pourquoi il n'a pas été nécessaire de créer la boucle pour repartir le service dans le script que j'avais mentionnée auparavant.

Vérification faite, il semble que mochad et mochas sont en mesure de fonctionner correctement après la déconnexion du CM19A puis son rebranchement sur le Raspberry Pi. En d'autres mots, cette configuration des services est robuste. Ceci étant dit, je suis certain que ce premier exercice à écrire un fichier de service systemd pour un script Python3 laisse à désiré. Toutes suggestions pour améliorer le code sont bienvenues.

Version 0.5 de mochas toc

Par défaut, mochas suppose que les paramètres de sécurité du serveur domotique sont fixés au niveau le plus faible possible. Aucun nom d'utilisateur ni mot de passe n'est fourni lors de l'utilisation de l'API de Domoticz et il n'y aucun chiffrement des requêtes HTTP qui sont donc en texte brut. Nous avons déjà vu que pour que cela fonctionne, Domoticz et mochas doivent être sur le même sous-réseau qui, à son tour, doit être inclus dans les réseaux de confiance dans les paramètres de sécurité (Configuration / Paramètres / Sécurité) de Domoticz.

Avec la version 0.5 de mochas (disponible sur le référentiel), l'addiciel n'a plus besoin d'être sur un réseau de confiance. Dans ce cas, les requêtes HTTP ou HTTPS envoyées à Domoticz doivent contenir le nom et le mot de passe d'un utilisateur de Domoticz disposant des droits d'administrateur. Les informations d'identification doivent être combiné en une seule chaine: "nom_d'utilisateur:mot_de_passe". Cette chaîne (sans les guillemets) doit être codée en base64 et ajouté dans l'entête de la requête. Si cette chaîne codée est spécifiée dans le fichier de configuration de mochas, elle sera automatiquement utilisée. Cependant, les requêtes HTTP non sécurisées ne provenant pas d'un réseau de confiance seront ignorées par Domoticz par défaut. Pour modifier ce comportement il faut cocher

     [ ] autoriser l'authentification Basic-Auth sur du pur HTTP (API seulement)

dans la section Protection API: des paramètres de sécurité de Domoticz directement au dessus des réseaux de confiance dans Configuration / Paramètres / Sécurité).

En aucun cas, l’encodage base64 ne doit être considéré comme sécurisé. On vérifie facilement qu'un chaîne codée comme cG9uZG1pc3Q6c2VhYnJlYXpl correspond à la chaîne pondmist:seabreaze. Par conséquent, si cette option est utilisée avec des requêtes HTTP, cela revient à envoyer les informations d'identification sous forme de texte brut dans un en-tête HTTP.

Heureusement, à partir de la version 0.5, les requêtes HTTP sécurisées peuvent être activées dans mochas. Il suffit de définir la valeur d'un nouveau paramètre dans le fichier de configuration mochas.json. Toutefois, il y a une complication. Le certificat numérique fourni avec Domoticz étant autosigné, la vérification échouera à moins que le certificat autosigné soit remplacé par un certificat d'une autorité reconnue ou que la vérification de l'autorité de certification soit désactivée. Encore une fois, un nouveau paramètre dans le fichier de configuration contrôle ce comportement.

Voici le modèle du fichier de configuration pour la version 0.5 de mochas qui devra être adapté à la situation locale.

{ "LOGLEVEL": "error", "HOST": "192.168.168.168", "PORT": 1099, "HOUSE": "J", "DELTA": 15, "CREDS64": "", "TLS": false, "VERIFY": true, "DOMOTICZ": "192.168.168.168:8080", "DEVICES": { "6": 66, "7": 177, "8": 288 } }

Comme on peut voir il contient trois nouveaux paramètres qui sont toutefois facultatifs.

TypeCléValeur
oLOGLEVELniveau des messages affichés dans le journal, l'un des suivants : "erreur", "info" ou "debug".
oHOSTl'adresse IP de mochad.
oPORTle port TCP de mochad. Cette valeur est codée en dur dans la source mochad donc peu susceptible de changer.
oHOUSECode maison X10 surveillé, une lettre de « A » à « P ».
oDELTAValeur absolue du changement du niveau de lumière à appliquer lors de la réception d'un paquet Dim ou Bright. Le niveau de lumière est un entier compris entre 0 et 100, la valeur delta doit donc être considérablement inférieure à 50 et bien sûr supérieure à 0.
fCREDS64Informations d'identification "utilisateur: mot de passe" encodées en base64 d'un utilisateur Domoticz avec des droits d'administrateur. Valeur par défaut : "" (aucun).
fTLSSi true alors le protocole sécurisé HTTPS avec données chiffrées est utilisé, si false le protocole HTTP avec données en texte brut est utilisé. Le port TCP spécifié dans la valeur de DOMOTICZ doit être correct. Valeur par défaut: false.
fVERIFYLorsque cela est vrai, le certificat numérique renvoyé par Domoticz sera vérifié. Cette valeur devra être explicitement définie sur false si le certificat autosigné fourni avec Domoticz n'a pas été remplacé par un certificat d'une autorité de certification reconnue. Cette valeur est ignorée si TLS est false. Valeur par défaut: true.
oDOMOTICZAdresse IP et port TCP de Domoticz. Il n'y a pas de valeur par défaut pour le port et il doit correspondre au protocole HTTP non sécurisé ou au protocole HTTPS sécurisé selon la valeur du paramètre TLS.
oDEVICESCarte des paires clé:valeur où la clé est une chaîne représentant un numéro d'unité X10 (de "1" à "16") et sa valeur est le numéro d'identité (idx) du dispositif Domoticz contrôlant un gradateur.

La colonne Type indique si une entrée est obligatoire (o) ou facultative (f). La valeur par défaut sera utilisée si la valeur facultative n'est pas présente. La présence des clés obligatoires est vérifiée au chargement du fichier de configuration. Aucun contrôle de validité des valeurs est faite à ce moment-là.

Pour résumer,

Sans aucun doute, il y a eu une grande amélioration de la sécurité dans Domoticz depuis la version 2023.1. Inévitablement, cette plus grande sécurité s'achète au prix de complications dont je n'ai pas encore saisi l'étendue complète.