Récupérer sous Jeedom la température d’un thermomètre BLE Xiaomi LYWSD03MMC

Je fais partie des nombreuses personnes qui ont été attirées par le capteur de température et d’humidité Xiaomi LYWSD03MMC en raison de son prix très attractif. En lot de quelques exemplaires, on le trouve à environ 3€ pièce, pile incluse, chez les revendeurs chinois.

YUD Xiaomi Mijia LYWSD03MMC Bluetooth 4.2 Thermomètre Hygromètre Deuxième  Génération - Cdiscount Jardin

Il dispose d’une connectivité Bluetooth Low Energy et remonte les informations de deux manières :

  • En mode connecté, elles sont alors accessibles en clair.
  • Dans ses trames d’advertising, elles sont alors chiffrées.

Il n’est pas envisageable d’utiliser le mode connecté dans un système domotique : la consommation d’énergie serait bien trop importante pour sa pauvre pile CR2032. Seule une écoute passive des trames d’advertising qu’il émet est une solution viable.

Comme elles sont malheureusement chiffrées, tout se complique. J’ai eu pas mal de difficultés pour arriver à les récupérer, alors je décris ici comment j’ai réussi.

Moyens de récupérer les données

Si vous arrivez ici par un moteur de recherche, vous avez déjà dû lire pas mal d’autres choses au sujet de ce capteur, aussi je vais essayer d’être synthétique :

  • On peut garder le firmware d’origine et utiliser diverses méthodes pour récupérer les clés de chiffrement (Mi Token / Mi Bind Key) :
  • L’autre possibilité est de flasher un firmware alternatif développé par ces mêmes personnes, qui transmet les données en clair (en plus d’apporter de nombreuses autres fonctionnalités que je ne détaillerai pas ici).

Intégration sous Jeedom via le plugin BLEA (Bluetooth Advertisement)

Pour information, j’ai installé Jeedom grâce à la manière officielle sur un Raspberry Pi 3B+. Je n’ai pas utilisé l’image toute faite par l’équipe Jeedom mais l’ai installé manuellement sur un système Raspbian Lite (version du 7 mai 2021 : 2021-05-07-raspios-buster-armhf-lite.img). J’ai ainsi obtenu Jeedom en version 4.1.27 et BLEA en version 2021-04-06 16:26:29.

Comme tout le monde, je cherchais la solution la plus élégante et demandant à faire le moins de bricolage possible. J’ai donc eu la démarche ci-dessous.

Essayer avec tout d’origine

Sait-on jamais, peut-être que des avancées ont été faites dans les dernières versions de Jeedom et que maintenant, tout fonctionne nativement.

Évidemment, ce n’est pas le cas. Le thermomètre est bien reconnu suite à un scan dans le plugin, mais la température, l’humidité et le niveau de batterie sont tous à 0.

Garder le firmware d’origine sur le thermomètre et donner à Jeedom le “bind key”

En fouillant dans le répertoire /var/www/html/plugins/blea/resources/blead/devices/ qui contient les fichiers Python chargés d’interfacer avec Jeedom les différents périphériques supportés, on tombe sur les fichiers suivants qui concernent notre thermomètre :

  • lywsd03.py
  • lywsd03.py_beta
  • lywsd03.py_with_bindkeys

Le premier est donc le fichier par défaut, mais deux autres sont présents (mais non exploités par le logiciel). Probablement des versions en cours de développement.

J’ai donc renommé lywsd03.py en lywsd03.py.bak pour garder une sauvegarde du fichier original et mis à la place lywsd03.py_with_bindkeys. A l’intérieur de fichier, j’ai adapté la table de correspondance adresses physiques / clés de chiffrement avant de redémarrer le Raspberry Pi.

Résultat : BLEA ne trouve plus le périphérique suite à un scan. Je n’ai pas poursuivi les investigations et suis passé à autre chose.

Je n’ai pas essayé le fichier lywsd03.py_beta.

Utiliser le firmware alternatif avec le script Jeedom d’origine

Je ne sais plus exactement ce qu’il se passe, mais cela ne fonctionne pas non plus. Si j’ai un peu de temps, il faudrait que je réessaie

Utiliser le firmware alternatif avec un script Jeedom modifié

C’est enfin la bonne, mais aussi celle qui demande le plus de modifications. Rien de bien compliqué toutefois.

Première étape, flasher le thermomètre

Il faut pour cela se rendre à l’URL https://pvvx.github.io/ATC_MiThermometer/TelinkMiFlasher.html avec un navigateur Google Chrome, Microsoft Edge ou Opera récent. J’ai rencontré des problèmes avec Microsoft Edge et tout a mieux fonctionné avec Chrome.

Cliquer sur “Connect”. Une fenêtre devrait s’ouvrir et la sonde LYWSD03MMC devrait apparaître. La sélectionner et valider. Attention, la recherche peut être longue et il ne faut pas cliquer ailleurs sinon elle va être annulée :

Cliquer sur “Do activation”. Les champs en-dessous Mi Token et Mi Bind Key doivent alors se remplir. C’est accessoirement de cette manière-là que l’on peut récupérer les clés de chiffrement si nécessaire (inutile dans notre cas).

Pour finir cliquer, sur “Custom Firmware ver X.Y” puis “Start Flashing”. Le bouton juste à côté “Original_OTA_…..” permet de remettre le firmware d’origine. Le flash du firmware alternatif dure environ une minute chez moi, mais il faut près de deux heures pour revenir à celui d’origine.

Ensuite, il faut se reconnecter à la sonde. Cette fois, elle devrait apparaître sous le nom “ATC_XXXXXX”. De nombreuses options de configuration apparaissent dessous. J’ai décidé de faire les modifications suivantes :

  • Décocher la case “comfort” et choisir un smiley fixe à afficher à la place du smiley qui dépend du niveau de confort ressenti.
  • Doubler la durée de l’advertising pour passer à 5s pour économiser la batterie.
  • Et surtout, changer le type d’advertising en “Atc1441” pour envoyer les données en clair.

Cliquer sur “Send Config” pour valider.

Les thermomètres sont flashés, il ne reste plus que le script Jeedom à modifier !

Pour information, sur mes 4 sondes, l’une d’elle ne renvoyait pas de version HW et par conséquent, l’outil ne proposait pas de firmware à flasher. J’ai dû forcer le flash en téléchargeant sur Github le fichier binaire le plus récent.

Seconde étape, modifier le script de Jeedom

Ce n’est pas une solution très élégante, mais il faut passer par là. J’espère que l’équipe intégrera ces modifications dans une version future.

J’ai tout pompé ici : https://community.jeedom.com/t/valeurs-non-remontees/44590/10 et là : http://jeedom.sigalou-domotique.fr/flasher-le-capteur-xiaomi-mijia-bluetooth-lywsd03-pour-jeedom.

Il faut donc remplacer le fichier lywsd03.py d’origine par le fichier suivant :

# coding: utf-8
from bluepy import btle
import time
import logging
import globals
import struct
from multiconnect import Connector
from notification import Notification

class Lywsd03():
        def __init__(self):
                self.name = 'lywsd03'
                self.ignoreRepeat = False

        def isvalid(self,name,manuf='',data='',mac=''):
                logging.debug('LYWSD03------isvalid data=%s, mac=%s, name=%s, manuf=%s' % (data, mac, name, manuf))
                if name.lower() in [self.name]:
                        return True
                if data.lower().startswith("1a18") and (mac.lower().startswith("a4:c1:38")):
                        #broadcasted advertising data
                        return True

        def parse(self,data,mac,name,manuf):
                logging.info('LYWSD03------adv data=%s, mac=%s, name=%s, manuf=%s' % (data, mac, name, manuf))
                action={}
                action['present'] = 1

                bd = bytes.fromhex(data)

                (temp,) = struct.unpack('>H', bd[8:10])
                
                
                if temp > 60000 :
                	temp = 0 - ( 65530 - temp ) 
                
                temp = temp / 10
                humi =  bd[10]
                batt = bd[11]

                action["temperature"] = temp
                action["moisture"] = humi
                action["battery"] = batt

                logging.info('LYWSD03------mac=%s, temp=%s, humi=%s, batt=%s' % (mac, temp,humi,batt))

                return action

        def read(self,mac):
                result={}
                try:
                        conn = Connector(mac)
                        conn.connect()
                        if not conn.isconnected:
                                conn.connect()
                                if not conn.isconnected:
                                        return
                        batt = bytearray(conn.readCharacteristic('0x3a'))
                        battery = batt[0]
                        notification = Notification(conn,Lywsd03)
                        notification.subscribe(10)
                        result['battery'] = battery
                        result['id'] = mac
                        logging.debug('LYWSD03------'+str(result))
                        return result
                except Exception as e:
                        logging.error(str(e))
                return result

        def handlenotification(self,conn,handle,data,action={}):
                result={}
                if hex(handle) == '0x36':
                        received = bytearray(data)
                        temperature = float(received[1] * 256 + received[0]) / 100
                        moisture = received[2]
                        result['moisture'] = moisture
                        result['temperature'] = temperature
                        result['id'] = conn.mac
                        result['source'] = globals.daemonname
                        globals.JEEDOM_COM.add_changes('devices::'+conn.mac,result)

globals.COMPATIBILITY.append(Lywsd03)

Pour cela, le plus simple pour moi est de se connecter en SSH et d’exécuter les commandes suivantes :

  • cd /var/www/html/plugins/blea/resources/blead/devices/
  • sudo mv lywsd03.py lywsd03.py.bak
  • sudo nano lywsd03.py
  • Coller avec un clic droit le contenu du fichier dans nano
  • Sauvegarder avec Ctrl X puis O
  • Redémarrer le Raspberry Pi avec sudo reboot

ATTENTION : il faudra refaire cette opération après une mise à jour du plugin, puisqu’elle aura écrasé notre modification !

Dernière étape, ajouter le thermomètre dans Jeedom

Dans le menu Outils/Protocoles domotiques/Bluetooth Advertisement, lancer un nouveau scan.

Les sondes doivent être automatiquement reconnues.

Pour chacune d’elles, il faut ensuite leur donner un nom et un objet parent.

Dans l’onglet “paramètres”, il faut décocher l’onglet “refresh forcé” : cela signifie que Jeedom attendra seulement des trames d’advertising en provenance de la sonde et n’essayera pas d’aller lui-même chercher les données en se connectant.

C’est enfin fini !

3 commentaires

  1. Merci pour ce travail.
    Je viens de tester et c’est ok, je suis juste reparti d’un autre script car ça ne fonctionnait pas.

      1. Je ne sais pas si Yoann a été notifié de ta réponse à son commentaire ou pas, dans le doute je vais lui envoyer un mail.
        A quel endroit est-ce que cela bloque ? De mon côté je n’ai pas eu de souci, mais c’était il y a un an et demi, les choses ont peut-être changé depuis.

Répondre à Cyril Annuler la réponse

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *