Bonjour,
je vous propose un tuto sur l’envoi de données d’un aspirateur robot Roborock S51 sous Valetudo à Gladys via MQTT.
Si votre robot n’est pas compatible avec Valetudo, le code pourra être adapté à vos besoins, cela vous fera une base.
TUTO n°4 : Gestion de son robot aspirateur sous Valetudo dans Gladys.
EDIT 28/06/2023 : Refont complète du code du fichier RobotAspiToGladys.sh qui simplifie son utilisation
EDIT 25/06/2023 : Modification de code (JsonArrayToString en autre), d’infos sur Valetudo RE ainsi que sur la carte
Côté Aspirateur Robot :
Il faut installer Valetudo RE dessus.
Valetudo (pour la partie installation) : Getting Started | Valetudo
Valetudo RE : GitHub - rand256/valetudo: Valetudo RE - experimental vacuum software, cloud free
Commandes MQTT Valetudo RE : MQTT API · rand256/valetudo Wiki · GitHub
Il m’a fallut configurer la connexion MQTT dans Valetudo RE.
URL : mqtt://gladys:XYZ@192.168.XXX.XXX:1883
Je n’ai pas touché à la config de base :
Identifiant : rockrobo
Topic Prefix : valetudo
Autoconfiguration Prefix : homeassistant
Côté BASH :
- Fichier partagé : /opt/mosquitto/MQTTConfig.sh
Je vais utiliser le fichier partagé proposé avec mes autres tuto : [TUTO] Partage d'informations entre BASH et Gladys
Il permet de tester la présence de commandes, d’avoir un partage des variables de connexion à Gladys et d’apporter de la couleur dans les messages.
Il faut y rajouter le code suivant dans le bloc « Variables à personnaliser » :
# Raccourcis des topics Gladys
GladysSet="gladys/master/device"
GladysGet="gladys/device"
# Raccourcis à des appareils MQTT
AspiRobot="mqtt:salle:aspirobot/feature/mqtt:aspirobot"
En adaptant AspiRobot avec votre topic.
Il semble aussi qu’il faille modifier la variable ORANGE par :
ORANGE="\e[1m\e[38;5;202m"
- Script BASH : /opt/mosquitto/RobotAspiToGladys.sh
L’idée est de faire une boucle qui va surveiller l’arrivée des informations afin de les afficher dans Gladys.
Le script va envoyer toutes les informations qu’il trouve à Gladys qui ne mettra à jour que ceux affichés.
jq va remplir un array associatif avec toutes les clés et valeurs qu’il trouve pour chaque topic.
Cette notion de topic est importante car elle sera utilisée dans Gladys.
Il est possible de personnaliser les valeurs (format date par ex) et les types (text ou state).
Il faut regarder un peu le code qui est normalement assez détaillé.
Il faudra automatiser son lancement au démarrage du pc/raspberry.
Nécessite les commandes mosquitto_pub et mosquitto_sub (paquet debian mosquitto-clients) ainsi que de jq (paquet debian jq).
#!/bin/bash
#########################################################
## Renvoi des informations de l'aspirateur vers Gladys ##
#########################################################
# Nécessite les paquets : mosquitto-clients jq
# À exécuter au démarrage, sa boucle infinie surveillera en permanence ce qu'il se passe
# Chargement du fichier commun
source /opt/mosquitto/MQTTConfig.sh
# Vérification de la présence des dépendances
! CommandCheck "mosquitto_pub:mosquitto-clients" "mosquitto_sub:mosquitto-clients" "jq:jq" && exit 1
### Variables à personnaliser
# Variable raccourci définie dans Valétudo
ValeRock="valetudo/rockrobo"
# Variables pour la concaténation en texte lors de la récupération de valeurs au format array json
ValueSeparator=", "
LineSeparator="@"
### Variables à personnaliser
# Calculs pour la création du tableau
TerminalWidth=$(($(tput cols) - 2 - 54))
printf -v ColumnValueDash '─%.0s' $(seq 1 ${TerminalWidth})
printf -v ColumnValueSpace ' %.0s' $(seq 1 $(( ${TerminalWidth} - 8 )))
# Affichage des en-têtes du tableau
echo "Base du topic : ${GladysSet}/${AspiRobot}:"
echo "┌─────────────────────────────────────────────┬───────┬${ColumnValueDash}┐"
echo -e "│ ${ORANGE}Topic ${RAZ} │ ${FUCHSIA}Type${RAZ} │ ${BLEUFONCE}Valeur${RAZ} ${ColumnValueSpace}│"
echo "├─────────────────────────────────────────────┼───────┼${ColumnValueDash}┤"
# Fonction descendante dans les objets {} et qui ajoute les clés et sous-clés dans un array avec sa valeur
function AutoJsonReader()
{
# $1 : Topic / Objet mère
# $2 : Code JSON
# AllValues : Nom de la variable globale qui sera remplie
# Définition des variables locales
local Key Value Line
# Utilisation de NULL comme séparateurs, le -j permet de ne pas avoir de saut de ligne entre les entrées
mapfile -td '' Lines < <(jq -rj 'to_entries[] | "\(.key)@@@\(.value)\u0000"' <<< "${2}")
# Boucle sur le code JSON et renvoie clé@@@valeur
for Line in "${Lines[@]}"
do
# Valeur de la clé avec ajout de la clé mère si présente
Key="${1}${Line%%@@@*}"
# Valeur de la clé
Value="${Line##*@@@}"
# Si la valeur est un objet {}
if [[ "${Value:0:1}${Value: -1:1}" == '{}' ]]
then
# Relance la fonction avec ce code pour remplir les sous-clés
AutoJsonReader "${Key}." "${Value}"
else
# Ajout de la (sous-)clé et sa valeur dans l'array
AllValues["${Key}"]="${Value}"
fi
done
}
# Fonction convertissant un array json en texte
# L'array peut en contenir d'autres mais pas de {}
function JsonArrayToString()
{
# Variables locales
local NewString Line
# Si l'array contient un sous array
if [[ "${1:0:2}" == '[[' ]]
then
# Boucle traitant les array un à un
while read Line
do
# Appel de la fonction avec un sous-array et récupération de sa valeur texte
NewString+="$(JsonArrayToString "${Line}")${LineSeparator:-@}"
done < <(jq -c '.[]' <<< "${1}")
# Renvoi du texte final avec suppression du dernier LineSeparator
echo "${NewString/%${LineSeparator:-@}}"
elif [[ "${1:0:1}" == "[" ]]
then
# Conversion de l'array en texte
NewString=$(jq -r --arg Separator "${ValueSeparator:-:}" 'join($Separator)' <<< "${1}")
# Renvoi du texte soit final soit d'un sous-array
echo "${NewString}"
fi
}
# Boucle sur les informations envoyées par l'aspirateur
# ${ValeRock}/map_data non utilisée, remplacé la "caméra"
while read Topic JSONCode
do
# Ne conserve que le dernier niveau du topic pour le case
# ex : valetudo/rockrobo/state => state
Topic="${Topic##*/}"
# Remise au propre de l'array
unset AllValues
declare -A AllValues
# Remplissage de l'array
AutoJsonReader "${Topic}:" "${JSONCode}"
# Dans le cas du champ erreur, il n'est retourné que si c'est le cas, je lui donne donc une valeur par défaut
if [[ ${Topic} == "state" ]]
then
[[ -z ${AllValues["state:error"]} ]] && AllValues["state:error"]="Aucune erreur"
fi
# Boucle sur les noms des champs en récupérant.
for Field in "${!AllValues[@]}"
do
# Récupération de la valeur correspondante.
Value="${AllValues[${Field}]}"
# Retraitement de la valeur de certaines informations, il suffit d'utiliser leur Topic comme clé
case "${Field}" in
# Format de type date
"attributes:last_bin_out"|"attributes:last_bin_full"|"command_status:updated"|"attributes:last_run_stats.endTime"|"attributes:last_run_stats.startTime")
Value=$((Value / 1000 ))
Value=$(date +'%d/%m/%Y' -d "@$Value") ;;
"attributes:last_run_stats.duration") Value=$((Value / 60 )) ;;
esac
# Type text par défaut
ValueType="text"
TypeSpace=" "
# Si on veut forcer un type text, il suffit d'ajouter des clés séparées par | dans le @()
if [[ "${Field}" != @() ]]
then
# Si le type est un numérique, on le passe en state
[[ "${Value}" == ?(+|-)+([0-9])?(.|,)*([0-9]) ]] && { ValueType="state"; unset TypeSpace; }
fi
# Ne traite pas les valeurs null (non trouvées)
if [[ ${Value} != "null" ]]
then
# Conversion des array json en texte si la valeur commence par un [ et finit par un ]
[[ "${Value:0:1}${Value: -1:1}" == '[]' ]] && Value="$(JsonArrayToString "${Value}")"
# Calculs pour le tableau
printf -v FieldSpace ' %.0s' $(seq 1 $(( 43 - ${#Field} )))
printf -v ValueSpace ' %.0s' $(seq 1 $(( ${#ColumnValueSpace} - ${#Value} + 7)))
# Affichage des informations dans le terminal dans un tableau
echo -e "│ ${ORANGE}${Field}${RAZ}${FieldSpace} │ ${FUCHSIA}${ValueType}${RAZ}${TypeSpace} │ ${BLEUFONCE}${Value}${RAZ}${ValueSpace}│"
# Envoi de la valeur du bon topic à Gladys
# Utilisation des raccourcis pour définir le topic entier
# Il est important de bien configurer les appareils MQTT dans Gladys pour avoir le bon format de Topic
mosquitto_pub -u "${User}" -P "${Pass}" -t "${GladysSet}/${AspiRobot}:${Field}/${ValueType}" -m "${Value}"
# Récupération de la valeur retour de la commande mosquitto_pub
MosquittoReturns=${?}
# Affiche un message en cas d'erreur de mosquitto
(( ${MosquittoReturns} )) && echo -e "[${ROUGE}Erreur${RAZ}] La commande mosquitto_sub a renvoyé le code ${MosquittoReturns}." 1>&2
fi
done
# Séparateur entre les topics
echo "├─────────────────────────────────────────────┼───────┼${ColumnValueDash}┤"
done < <(mosquitto_sub -v -u "${User}" -P "${Pass}" -t ${ValeRock}/state \
-t ${ValeRock}/attributes \
-t ${ValeRock}/command_status \
-t homeassistant/vacuum/valetudo_rockrobo/config)
Si on le lance, il affichera par ex :
bash RobotAspiToGladys.sh
Les topics sont importants, il faudra les indiquer à Gladys dans la suite.
- Script BASH : /opt/mosquitto/GladysToRobotAspi.sh
Il est possible d’envoyer des commandes mqtt à Valetudo.
Dans le code suivant, je propose 3 actions qui seront lancées lorsque j’appuie sur un bouton Xiaomi.
1 clic (code 1) : start
2 clics (code 2) : stop
clic long (code 5) : retour à la base
Pour changer les actions, il suffit de modifier la variable Actions.
Il faudra automatiser son lancement au démarrage du pc/raspberry.
Nécessite les commandes mosquitto_pub et mosquitto_sub (paquet debian mosquitto-clients).
#!/bin/bash
#####################################################################################################
## Surveillance du bouton spécial aspirateur et déclenchement des actions adéquate de l'aspirateur ##
#####################################################################################################
# Nécessite le paquet : mosquitto-clients
# Chargement du fichier commun
source /opt/mosquitto/MQTTConfig.sh
# Vérification de la présence des commandes
! CommandCheck "mosquitto_pub:mosquitto-clients" "mosquitto_sub:mosquitto-clients" && exit 1
### Variables à personnaliser
# Liste des actions, correspondance des codes MQTT et des commandes
Actions=(null start stop pause locate return_to_base clean_spot)
### Variables à personnaliser
# Boucle infinie de récupération de la valeur du bouton MQTT
while read Valeur
do
# Blocage si le code envoyé est inconnu
if [[ ${Valeur} != @([1-6]) ]]
then
echo -e "[${ORANGE}Attention${RAZ}] Valeur ${Valeur} inconnue..." 1>&2
continue
fi
# Récupération de la commande associée au code du bouton
Action="${Actions[${Valeur}]}"
# Envoi de la commande à l'aspirateur via le brocker
echo -e "Exécution de l'action '${BLEUFONCE}${Action}${RAZ}'."
mosquitto_pub -u "${User}" -P "${Pass}" -t valetudo/rockrobo/command -m ${Action}
# Récupération de la valeur retour de la commande mosquitto_pub
MosquittoReturns=${?}
# Affiche un message en cas d'erreur de mosquitto
(( ${MosquittoReturns} )) && echo -e "[${ROUGE}Erreur${RAZ}] La commande mosquitto_sub a renvoyé le code ${MosquittoReturns}." 1>&2
done < <(mosquitto_sub -u "${User}" -P "${Pass}" -t "${GladysGet}/${AspiRobot}:action/state")
Si on l’exécute ça donne par ex :
bash GladysToRobotAspi.sh
Côté Gladys :
- Création de l’aspirateur au niveau MQTT :
Nom : Robot Aspirateur
Id externe : mqtt:salle:aspirobot (mqtt:Pièce:Élément)
Pièce : Salle
Dans mon cas ça donne ça :
Il faut adapter à ce que vous avez mis dans la variable AspiRobot du fichier bash MQTTConfig.sh
- Création des fonctionnalités voulues avec le bon type :
Cette étape va être longue puisqu’il faut créer toutes les fonctionnalités à gérer avec Gladys…
Je me suis créé des blocs pour rassembler les informations.
Il faut faire attention à utiliser les bons types de fonctionnalités afin que les données reçues soient compatibles.
Batterie, texte, surface ou durée…
Exemples de formatage de l’id externe des fonctionnalités :
L’important est de reprendre les topics indiqués par le script bash RobotAspiToGladys.sh et de les donner à Gladys.
Dans mon cas ça donne ça :
- Création de la camera affichant la carte :
Il est possible d’utiliser http://IpAspirateur/api/simple_map pour récupérer la carte, ex:
wget http://AspiRobotIP/api/simple_map -O "/tmp/AspiRobotMap.png"
Mais celle-ci redemande toutes les minutes une image, ce qui semble lourd pour l’aspirateur.
Il est possible d’installer GitHub - rand256/valetudo-mapper: Valetudo companion service qui générera une carte depuis les données MQTT.
On peut alors donner l’adresse http://ipServeur:3000/api/map/image (ou port 3002 ?) à l’appareil de type caméra.
Dans mon cas ça donne ça :
- Ajout des différentes fonctionnalités sur le tableau de bord :
Rien de spécifique ici, il faut procéder comme d’habitude.
Perso, voilà ce que j’ai fait :
- Gestion du bouton d’action :
Il suffit d’avoir une fonctionnalité spécifique dans l’appareil mqtt (visible dans mon screenshot)
- Type : Inconnu
- Nom : Action
- Id : mqtt:aspirobot:action
- Valeurs : de 1 à 10
- Est-ce un capteur ? Non
D’avoir un déclencheur comme un bouton (boutton AspiRobot dans mon cas, avec une belle faute )
De créer une scène au bouton qui enverra le code de l’action à la fonctionnalité ci-dessus :
Le code sera reçu par le fichier /opt/mosquitto/GladysToRobotAspi.sh.
En espérant que cela puisse servir, au moins de base ou d’idée de départ pour d’autres aspirateurs.
Valetudo :
Si vous utiliser cette version, il semble y avoir pas mal d’infos : MQTT | Valetudo
Merci à : Faire communiquer votre aspirateur robot Xiaomi Roborock en MQTT
Autres tuto :
TUTO n°1 : Afficher la température du Raspberry dans Gladys.
TUTO n°2 : Lancer une commande BASH via à une action Gladys.
TUTO n°3 : Indiquer la présence d’un utilisateur via le WI-FI de son téléphone.