Pour jouer avec en Bluetooth, le premier problème est d'obtenir son adresse MAC. J'avais déjà eu des problèmes avec le pèse-personne SilverGear à 10 euros la semaine précédente. Je dois être entouré de voisins fans de bidules connectés et c'est difficile de retrouver mon bidule dans toutes ces adresses. Le pèse-personne s'annonçait comme 'TY' (pour tuya) et il y en avait déjà 3 dans l'environnement... Dans un environnement moins fréquenté, j'ai découvert que mon pèse-personne avait une adresse DC:23:50:XX:XX:XX. La balance de cuisine s'annonce comme '4454' (cela apparaît également avec l'application du smartphone lors du 'pairing'). Ce n'est pas très explicite mais l'information figure sur l'étiquette à l'arrière. Son adresse MAC est BC:A5:46:XX:XX:XX.
linux$ bluetoothctl scan le # could have been 'sudo hcitool lescan ? ... [bluetooth]# scan on ... [NEW] Device BC:A5:46:XX:XX:XX 4454 ... ^D
Une fois son adresse MAC connue, on peut utiliser gatttool(1) pour aller plus loin et découvrir ce que le bidule propose. Un truc à essayer, par exemple, c'est '--primary':
linux$ gatttool -t public -b BC:A5:46:XX:XX:XX --primary # Cuisine attr handle = 0x0001, end grp handle = 0x000f uuid: 0000180a-0000-1000-8000-00805f9b34fb attr handle = 0x0010, end grp handle = 0xffff uuid: 0000ffb0-0000-1000-8000-00805f9b34fbVoilà qui est bien mais ne nous avance pas beaucoup. Si le '180a' se trouve bien dans les Bluetooth Assigned Numbers (Device Information Service), 'ffb0' n'y est pas... Essayons de nous connecter en mode interactif pour pouvoir l'interroger...
linux$ gatttool -t public -b BC:A5:46:17:F5:07 --interactive # Cuisine [BC:A5:46:XX:XX:XX7][LE]> connect Attempting to connect to BC:A5:46:XX:XX:XX Connection successful Notification handle = 0x0014 value: ac 40 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 a6 a7 Notification handle = 0x0014 value: ac 40 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 a6 a7 ... Notification handle = 0x0014 value: ac 40 00 00 01 0d 4c 00 00 00 00 00 00 00 00 00 00 00 a6 00 [BC:A5:46:XX:XX:XX][LE]> ^CEt là, bonne surprise! La balance envoie des 'notifications' dont la valeur change lorsque l'on dépose un poids dessus, sans aucune autre intervention que la connexion. Là, ce n'est pas encore trop pratique mais c'est encourageant. Le plus simple, maintenant, cela doit être d'écrire un petit script Python utilisant le module pygatt pour interagir avec le Bluetooth LE Generic Attribute Profile (GATT). Et, justement, dans la doc, il y a un exemple de captation des notifications. Le problème, c'est qu'il faut associer le 'callback' à un UUID et que nous n'avons que le 'handle' 0x0014... C'est là que la commande '--char-desc' de gatttool intervient. On trouve l'UUID correspondant au handle 0x0014, '0000ffb2-0000-1000-8000-00805f9b34fb' que l'on peut utiliser dans le script.
linux$ gatttool -t public -b BC:A5:46:XX:XX:XX --char-desc # Cuisine handle = 0x0001, uuid = 00002800-0000-1000-8000-00805f9b34fb handle = 0x0002, uuid = 00002803-0000-1000-8000-00805f9b34fb handle = 0x0003, uuid = 00002a23-0000-1000-8000-00805f9b34fb handle = 0x0004, uuid = 00002803-0000-1000-8000-00805f9b34fb handle = 0x0005, uuid = 00002a24-0000-1000-8000-00805f9b34fb handle = 0x0006, uuid = 00002803-0000-1000-8000-00805f9b34fb handle = 0x0007, uuid = 00002a25-0000-1000-8000-00805f9b34fb handle = 0x0008, uuid = 00002803-0000-1000-8000-00805f9b34fb handle = 0x0009, uuid = 00002a26-0000-1000-8000-00805f9b34fb handle = 0x000a, uuid = 00002803-0000-1000-8000-00805f9b34fb handle = 0x000b, uuid = 00002a27-0000-1000-8000-00805f9b34fb handle = 0x000c, uuid = 00002803-0000-1000-8000-00805f9b34fb handle = 0x000d, uuid = 00002a28-0000-1000-8000-00805f9b34fb handle = 0x000e, uuid = 00002803-0000-1000-8000-00805f9b34fb handle = 0x000f, uuid = 00002a29-0000-1000-8000-00805f9b34fb handle = 0x0010, uuid = 00002800-0000-1000-8000-00805f9b34fb handle = 0x0011, uuid = 00002803-0000-1000-8000-00805f9b34fb handle = 0x0012, uuid = 0000ffb1-0000-1000-8000-00805f9b34fb handle = 0x0013, uuid = 00002803-0000-1000-8000-00805f9b34fb handle = 0x0014, uuid = 0000ffb2-0000-1000-8000-00805f9b34fb handle = 0x0015, uuid = 00002902-0000-1000-8000-00805f9b34fbEt donc, le script devient :
#!/usr/bin/python3 import pygatt, time adapter = pygatt.GATTToolBackend() old_weight = 0 def handle_data(handle, value): """ handle -- integer, characteristic read handle the data was received on value -- bytearray, the data returned in the notification """ global old_weight weight = ((value[3]*256+value[4])*256+value[5])*256+value[6] if weight != old_weight : print(weight, flush=True) old_weight = weight try: adapter.start() device = adapter.connect('BC:A5:46:XX:XX:XX') # balance cuisine device.subscribe("0000ffb2-0000-1000-8000-00805f9b34fb", callback=handle_data) while True: time.sleep(10) finally: adapter.stop()Il y a visiblement encore des choses à explorer mais en utilisant les octets de 3 à 6, on obtient un nombre de milligrammes qui correspond à la valeur affichée sur la balance en grammes. La bidouille avec 'old_weight', c'est pour éviter les mesures redondantes plusieurs fois (combien?) par seconde. Pas sûr que cela soit stable au niveau du milligramme. Il faudrait peut-être faire quelque chose de plus sophistiqué...
linux$ ./kichen_notif.py 72110Et la balance affiche '72' grammes.
L'intérêt d'avoir accès aux mesures indépendamment de l'application, c'est que l'on peut maintenant faire ce que l'on veut. Je peux compter des boulons, multiplier le poids par le prix des bananes,...
Il faudrait aussi faire des expériences pour savoir dans quelle mesure le résultat est reproductible, correct, linéaire,... À quel point. Et, par exemple, si 3 grammes à partir de X grammes est plus précis qu'à partir de 0,...