Automatiser son aspirateur Xiaomi avec Python sur Raspberry Pi

ou « comment passer l'aspirateur quand personne n'est à la maison ? »
Un confort de vie et un appartement propre
Je possède depuis maintenant près d'un an un aspirateur robot Xiaomi Mi Vacuum, et au delà du gain de temps, ce petit robot améliore nettement la propreté dans l'appartement. En effet, la fonction de programmation permet d'automatiser le nettoyage, ce qui n'est pas possible avec un aspirateur traditionnel : le travail, les sorties, et la vie de famille ne permettent pas toujours une régularité exemplaire dans ce genre de tâches ménagères quotidiennes.
Les limites de l'application Mi Home
Mais l'application Xiaomi Mi Home se retrouve vite limitée. Il n'est possible que de programmer des heures de passage et l'aspirateur est donc susceptible de déclencher lorsqu'on est à la maison. La domotique n'a d'interêt que si elle sait se faire oublier, et le robot n'est pas vraiment discret. Malheureusement l'application est incapable de savoir si l'appartement est vide ou non.

Pour ne pas entendre le robot et ne pas se le prendre dans les pattes, il est possible de coder un bête script à l'aide de la librairie Python-Miio et d'un Raspberry Pi (ou d'un NAS). Ce script déclencherait à une heure donnée le passage de l'aspirateur si nos smartphones ne sont pas connectés en WiFi à la box internet. J'ai utilisé quelques mois une instance de Home Assistant dans cet unique but, une solution fonctionnelle mais quelque peu démesurée.
Pour information, ma configuration actuelle est composée d'une Livebox v4, d'un Xiaomi Mi Vacuum (v1), de deux smartphones Google Pixel 2 (celui de la Miss et le mien) et d'une box ARM. Le fonctionnement sera identique avec un autre modèle de routeur internet, de smartphone, d'aspirateur robot Xiaomi et/ou avec un Raspberry Pi (tous modèles confondus, le script étant vraiment léger).

Dans mon cas, je souhaite que l'aspirateur déclenche à 11h30, mais si quelqu'un est là il tente de nouveau à 15h00, et je veux qu'il ne démarre qu'une seule fois par jour.
Obtenir le Token de l'aspirateur
Le token est un code, une clé qui permettra à votre script de donner des ordres à votre aspirateur. La page d'aide de Home Assistant propose différentes solutions pour récupérer le token en fonction de votre appareil ou de votre configuration.
Fixer l'IP du Robot et des téléphones
Il est préférable de fixer l'adresse IP de vos appareils pour éviter des mauvaises surprises.

Dans les réglages réseau de votre routeur vous pourrez donner une IP statique à vos appareils. Notez les adresses IP et gardez les pour la suite.
Installer Python-Miio et ses dépendances
Commençons par installer pip3, un outil indispensable pour télécharger des librairies Python :
sudo apt-get install python3-pip
Nous pouvons dès à présent installer Python-Miio :
sudo pip3 install python-miio
Sous les systèmes basés sur Debian, assurez-vous d'avoir libffi et openssl d'installés :
sudo apt-get install libffi-dev libssl-dev
Tester l'installation
Avant de commencer à coder un script, on peut déjà tester la bonne configuration de l'installation. On ouvre python :
python3
On peut exécuter :
from miio import Vacuum
vac = Vacuum("<ipAspirateur>", "<tokenAspirateur>")
vac.start()
Si l'aspirateur démarre, c'est bon signe, on peut passer à la suite !
Coller le script
En SSH, dans votre dossier /home/pi on créé un fichier "script.py" à l'aide de :
nano script.py
On insère :
from miio import Vacuum
import os
import requests
import time
import datetime
telephone1 = "<ipAppareil1>" #Mon téléphone
telephone2 = "<ipAppareil2>" #Un autre téléphone
isVacuumedToday = 0 #L'aspirateur est-il passé aujourd'hui ?
vac = Vacuum("<ipAspirateur>", "<tokenAspirateur>")
def isConnected():
telephone1IsConnected = os.system("ping -c 1 " + telephone1)
telephone2IsConnected = os.system("ping -c 1 " + telephone2)
if telephone1IsConnected == 0 or telephone2IsConnected == 0:
return 0
else:
return 1
def vacuumStart():
if isConnected() == 1 :
print("Trying to start the vacuum.")
print(vac.start())
else:
return 0
while 1:
current_time = datetime.datetime.now()
if current_time.hour == <heure> and current_time.minute == <minutes> and isVacuumedToday == 0: #1er essai à une heure donnée
vacuumStart()
isVacuumedToday = 1
elif current_time.hour == <heure> and current_time.minute == <minutes> and isVacuumedToday == 0: #2eme essai si l'aspirateur n'a pas démarré au premier horaire
vacuumStart()
isVacuumedToday = 1
elif current_time.hour == 0 and current_time.minute == 0:
isVacuumedToday = 0 #On réinitialise à minuit
else:
print("Wait 30 seconds")
print(isVacuumedToday)
time.sleep(30)
N'oubliez pas d'adapter le code selon vos besoins et les variables entre crochet par les données correspondantes :
<ipAppareil1>
<ipAppareil2>
<ipAspirateur>
<tokenAspirateur>
<heure>
<minutes>
On quitte nano en enregistrant avec Ctrl+X. Maintenant on peut dire au système d'exécuter le code au démarrage du Raspberry à l'aide de rc.local :
sudo nano /etc/rc.local
Insérez :
python3 /home/pi/script.py &

On enregistre et on reboot, et c'est parti, plus qu'à attendre l'horaire donné.
Cette solution n'est pas la meilleure, mais c'est le plus simple que j'ai trouvé, et la consommation de ressources est vraiment négligeable (quelques 20Mo de RAM utilisée). La mise en place ne prend pas beaucoup de temps et est assez simple à comprendre. Cela marche depuis plusieurs jours chez moi sans problème

Bonus : Notifications via Bot Telegram

Si vous possédez un bot Telegram, vous pouvez simplement mettre en place des notifications en rajoutant la commande :
r = requests.get("https://api.telegram.org/<tokenTelegram>/sendMessage?chat_id=<chatID>&text=L'aspirateur+démarre")
<tokenTelegram> est le token de votre bot (contactez le bot @botFather pour créer et obtenir le token de votre bot)
<chat_id> est l'identifiant de votre compte Télégram (contactez le bot @get_id_bot pour obtenir votre chat_id)
Appliqué dans le script, cela donne :
from miio import Vacuum
import os
import requests
import time
import datetime
telephone1 = "<ipAppareil1>"
telephone2 = "<ipAppareil2>"
isVacuumedToday = 0
vac = Vacuum("<ipAspirateur>", "<tokenAspirateur>")
r = requests.get("https://api.telegram.org/<tokenTelegram>/sendMessage?chat_id=<chatID>&text=Le+script+démarre") #optionnel
def isConnected():
telephone1IsConnected = os.system("ping -c 1 " + telephone1)
telephone2IsConnected = os.system("ping -c 1 " + telephone2)
if telephone1IsConnected == 0 or telephone2IsConnected == 0:
return 0
else:
return 1
def vacuumStart():
if isConnected() == 1 :
print("Trying to start the vacuum.")
print(vac.start())
r = requests.get("https://api.telegram.org/<tokenTelegram>/sendMessage?chat_id=<chatID>&text=L'aspirateur+démarre") #optionnel
else:
return 0
while 1:
current_time = datetime.datetime.now()
if current_time.hour == <heure> and current_time.minute == <minutes> and isVacuumedToday == 0:
vacuumStart()
isVacuumedToday = 1
elif current_time.hour == <heure> and current_time.minute == <minutes> and isVacuumedToday == 0:
vacuumStart()
isVacuumedToday = 1
elif current_time.hour == 0 and current_time.minute == 0:
isVacuumedToday = 0
else:
print("Wait 30 seconds")
print(isVacuumedToday)
time.sleep(30)
Merci à Bryan pour la relecture