Configuration d'un programme de tonte

Mon problème

J’ai intégré mon robot tondeuse Worx à home assistant, via l’intégration sous HACS et l’ajout d’une carte lovelace (que je ne trouve pas forcément utile…).
Il est possible de définir la programmation du robot manuellement ou de laisser le cloud Worx faire le boulot.
J’aimerais évaluer une autre solution : home assistant.
Mais je ne sais pas par où commencer, quelles briques utiliser, etc.
Je me dis que j’aimerai :

  1. définir des plages horaires pendant lesquelles, HA enverrait les ordres de départ au robot. Potentiellement plusieurs par jours. Sur une semaine.
  2. Éventuellement plusieurs tontes par jour au sein d’un même créneau (si le robot est rentré à la base se recharger entre temps)
  3. Pourquoi pas s’adapter à la météo et repousser des tontes si jamais il a plut.
  4. Gérer une mode party, pour annuler toute programmation si jamais on est en barbecue pendant les vacances :smiley:

Salut.

Concernant les briques il y a 2 grandes tendances :

  • Tout en automisation Ha
  • Tout en nodered

Ensuite, il faudra y décliner l’interface (mode party) et peut être aussi ton calendrier.
Et récupérer les informations de la météo

Pour ma part, je vais et j’ai prévu de passer par nodered.
Le robot ira tondre tous les jours, et sera rentré pendant les créneaux d’arrosages (déjà sous nodered) ou en cas de pluie (pluviométre).
Je laisse au robot la main sur la gestion des retours / charge / poursuite du programme.
Un bouton panique servira à forcer le retour à la base et mettre en pause la tonte (ton mode party) jusqu’à nouvel ordre.
J’ai pas spécialement prévu de jouer (entendre faire des modifications fines depuis une interface ha) sur le calendrier, parce de toute façon ça change pas beaucoup et que pour ma part, le parcours est aléatoire. Donc pour maximiser le résultat il faut aussi maximiser les passages.

En effet, je ne souhaite pas forcément avoir une belle ihm/lovelace card pour la partie calendrier : comme tu le dis, c’est pas qqch qui change souvent. Donc ça peut rester dans le back.

Pour ma part, le robot rentre déjà tout seul quand il pleut.
En fait, a y réfléchir, ce que je veux absolument c’est pouvoir définir un mode party, l’empêchant de se lancer. Une premiere option serait d’annuler la tonte quand HA reçoit une notification de départ cycle alors qu’un switch ‹ party › serait à true. Mais le problème c’est que le robot partirait pour revenir à la base. Dans ce cas, il ferrait tout le tour du fil périphérique…
Je préfère l’empêcher de démarrer !
Je n’ai pas trouvé comment faire. Ou du moins pas en le laissant maître de sa programmation. Il faudrait alors que j’annule toute programmation sur le robot et que je le fasse via HA.
Quand à la reprise de planning en cas de pluie, voir doubler les passes pour rattraper le temps perdu, je pense que c’est gadget finalement.

J’aimerai bien qu’il puisse redémarrer automatiquement en cas de batterie rechargé, mais il ne le fait que si il a sa programmation manuelle

C’est exactement ce que je ferai chez moi.
Pas de programmation dans le robot, HA (via nodered) donne le départ d’un cycle. Pour ça le node queue est assez sympa (je l’exploite pour les volets)

Mode party on => tu bloque la file+ envoie retour base. Mode partie off => tu la réactive (et le dernier message part).
Par ailleurs, tu n’es pas à la minutes près non plus (ou alors tu prépares tes BBQ à l’arrache), du coup c’est probablement acceptable qu’il faille laisser un peu de temps au robot de faire le trajet jusqu’à la base

Pour toi c’est quoi « manuelle » ? Si c’est hors robot, ça tombe bien, c’est HA qui pilote.
Dans la pire des cas : batterie 100% => départ (bloqué ou pas dans la file)

Quand je parlais de la gestion de la batterie en manuel, je parlais de la programmation manuelle du robot. Donc interne. Il a 2 modes : prog manuelle où je spécifie les heures de départ et de fin du dernier départ puis lui gère sa batterie et repart une fois à 100%. Ou sinon il y a l’automatique via le cloud et là, c’est Worx qui décide en fonction de… la géo-localisation, des stations météo locale, du type de pelouse, de la superficie, etc. Mais je ne suis pas ultra convaincu par cette IA.

Je n’avais jamais réellement pensé à node réd : je voulais au maximum éviter d’installer un n-ieme truc sur HA… Mais si il n’y a pas d’autre moyen simple de gérer une file d’action en fonction du temps, de la batterie, etc…
C’est lourd node red ?

Ok pour moi manuel c’était plus : rien n’est décidé par autre chose que toi …
Concernant NR, il doit y avoir moyen de s’en passer (et faire via HA), ça me parait complexe (du moins plus complexe que NR) mais je suis pas un utilisateur avancé des automatisations natives.
Si tu as un HA supervised, l’addon est ultra rapide à installer, ça te donnera une idée de la puissance nécessaire

Salut à tous !
Ca y est, je suis arrivé à ce que je voulais !
Au final j’ai réutilisé des composants natifs de l’intégration landroid.
Celle ci permet de changer la configuration du ratio de temps. Ce ratio permet d’augmenter ou diminuer le temps de tonte sans toucher à la programmation. Par exemple, si on met +100%, vous allez doubler votre temps de tonte programmer quotidiennement.
J’ai donc ajouté un template sensor « mode barbecue ». Quand il est activé, il appelle le service de changement de config pour changer le ratio à -100%. Et quand il est désactivé, il le bascule à 0%.
Il faudrait que je sauvegarde l’ancien ratio pour rétablir le ratio précédant le mode bbq… Mais bon, c’est déjà bien.
Voici ma carte :

1 « J'aime »

bonjour
j’essaie moi aussi d’intégrer mon landroid sous Ha, mais l’intégration de HACS ne semble pas fonctionner :frowning: Ca fonctionne tjs chez toi ?
Ensuite j’ai essayé celle ci qui est plus récente : https://github.com/Flodu31/HomeAssistant-LandroidWorx
chez moi, ca a fonctionné 2 s avec toutes les infos sur la carte proposée par le github, mais après ces 2s, plus rien, les sensors sont tous indisponibles, avec 2 messages d’erreur incompréhensible pour moi :

Logger: homeassistant.helpers.template_entity
Source: helpers/template_entity.py:364
First occurred: 18:03:11 (7 occurrences)
Last logged: 18:03:11

TemplateError('ValueError: Template error: as_timestamp got invalid input '' when rendering template '{{ as_timestamp( state_attr('sensor.landroid_rest', 'last_status')['timestamp']) | timestamp_custom('%d.%m.%Y %H:%M:%S') }}' but no default was specified') while processing template 'Template<template=({{ as_timestamp( state_attr('sensor.landroid_rest', 'last_status')['timestamp']) | timestamp_custom('%d.%m.%Y %H:%M:%S') }}) renders=4>' for attribute '_attr_native_value' in entity 'sensor.landroid_last_status_update'
TemplateError('UndefinedError: 'None' has no attribute 'payload'') while processing template 'Template<template=({{ (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['rsi']) }}) renders=4>' for attribute '_attr_native_value' in entity 'sensor.landroid_rsi'
TemplateError('UndefinedError: 'None' has no attribute 'payload'') while processing template 'Template<template=({% if (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['rain']['s'] == 1) %} {{ state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['rain']['cnt'] }} min {% else %} N/A {% endif %}) renders=4>' for attribute '_attr_native_value' in entity 'sensor.landroid_rain_delay'
TemplateError('UndefinedError: 'None' has no attribute 'payload'') while processing template 'Template<template=({% if (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == -1) %} UNKNOWN {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 0) %} IDLE {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 1) %} Home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 2) %} Start sequence {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 3) %} Leaving home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 4) %} Follow wire {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 5) %} Searching home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 6) %} Searching wire {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 7) %} Mowing {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 8) %} Lifted {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 9) %} Trapped {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 10) %} Blade blocked {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 11) %} Debug {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 12) %} Remote control {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 30) %} Going home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 31) %} Zone training {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 32) %} Border cut {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 33) %} Border cut {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 34) %} Pause {% endif %}) renders=4>' for attribute '_attr_native_value' in entity 'sensor.landroid_status'
TemplateError('UndefinedError: 'None' has no attribute 'payload'') while processing template 'Template<template=({% if (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == -1) %} UNKNOWN {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 0) %} No error! {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 1) %} Trapped {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 2) %} Lifted {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 3) %} Wire missing {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 4) %} Wire missing {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 5) %} Raining {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 6) %} Close door to mow {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 7) %} Close door to go home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 8) %} Blade motor blocked {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 9) %} Wheel motor blocked {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 10) %} Trapped timeout {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 11) %} Upside down {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 12) %} Battery low {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 13) %} Reverse wire {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 14) %} Charge error {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 15) %} Timeout finding home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 16) %} Mower locked {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 16) %} Battery over temperature {% endif %}) renders=4>' for attribute '_attr_native_value' in entity 'sensor.landroid_error'

et

Logger: homeassistant.helpers.event
Source: helpers/template.py:541
First occurred: 18:03:11 (7 occurrences)
Last logged: 18:03:11

Error while processing template: Template<template=({{ as_timestamp( state_attr('sensor.landroid_rest', 'last_status')['timestamp']) | timestamp_custom('%d.%m.%Y %H:%M:%S') }}) renders=2>
Error while processing template: Template<template=({{ (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['rsi']) }}) renders=2>
Error while processing template: Template<template=({% if (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['rain']['s'] == 1) %} {{ state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['rain']['cnt'] }} min {% else %} N/A {% endif %}) renders=2>
Error while processing template: Template<template=({% if (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == -1) %} UNKNOWN {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 0) %} IDLE {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 1) %} Home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 2) %} Start sequence {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 3) %} Leaving home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 4) %} Follow wire {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 5) %} Searching home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 6) %} Searching wire {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 7) %} Mowing {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 8) %} Lifted {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 9) %} Trapped {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 10) %} Blade blocked {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 11) %} Debug {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 12) %} Remote control {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 30) %} Going home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 31) %} Zone training {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 32) %} Border cut {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 33) %} Border cut {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 34) %} Pause {% endif %}) renders=2>
Error while processing template: Template<template=({% if (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == -1) %} UNKNOWN {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 0) %} No error! {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 1) %} Trapped {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 2) %} Lifted {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 3) %} Wire missing {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 4) %} Wire missing {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 5) %} Raining {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 6) %} Close door to mow {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 7) %} Close door to go home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 8) %} Blade motor blocked {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 9) %} Wheel motor blocked {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 10) %} Trapped timeout {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 11) %} Upside down {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 12) %} Battery low {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 13) %} Reverse wire {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 14) %} Charge error {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 15) %} Timeout finding home {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 16) %} Mower locked {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 16) %} Battery over temperature {% endif %}) renders=2>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 1888, in forgiving_float_filter
    return float(value)
TypeError: float() argument must be a string or a real number, not 'NoneType'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 539, in async_render
    render_result = _render_with_context(self.template, compiled, **kwargs)
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 2130, in _render_with_context
    return template.render(**kwargs)
  File "/usr/local/lib/python3.10/site-packages/jinja2/environment.py", line 1301, in render
    self.environment.handle_exception()
  File "/usr/local/lib/python3.10/site-packages/jinja2/environment.py", line 936, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "<template>", line 1, in top-level template code
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 1891, in forgiving_float_filter
    raise_no_default("float", value)
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 1589, in raise_no_default
    raise ValueError(
ValueError: Template error: float got invalid input 'None' when rendering template '{{ (state_attr('sensor.landroid_rest', 'distance_covered') | float / 1000) | round }}' but no default was specified

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 660, in async_render_to_info
    render_info._result = self.async_render(variables, strict=strict, **kwargs)
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 541, in async_render
    raise TemplateError(err) from err
homeassistant.exceptions.TemplateError: ValueError: Template error: float got invalid input 'None' when rendering template '{{ (state_attr('sensor.landroid_rest', 'distance_covered') | float / 1000) | round }}' but no default was specified

1 « J'aime »

Salut,

Tes 2 messages d’erreur disent juste que les valeurs renvoyées par l’appel à l’API ne rentre pas dans les cases attendues. Ce n’est pas la raison de ton souci.
Mais si tu regarde comment c’est fait, il y a 2 appels à l’API, le premier pour récupérer le token dans une fichier local, il est rafraichi toutes les heures.
Le second, utilise le token de sécurité et récupère les valeurs, appelé toutes les minutes.

Alors les première de choses à voir c’est dans outils de développement qu’y a t’il dans sensor.landroid_rest et ensuite si tu as bien le token dans le fichier « /config/.landroidToken ».
Si tu as le token, un peu plus avancé, c’est d’utiliser Rester ou Postman pour faire l’appel à l’API avec le token pour voir ce que ça donne.

bonjour
merci de ton aide,
{{ states(‹ sensor.landroid_rest ›)}} me renvoi 1
et dans le fichier .landroidToken j’ai : null
c’est pas bon signe…
edit : j’ai modifié mon longon et ca semble fonctionner ! (token non vide, et les infos remontent bien. Je ne sais pas ce que j’ai fait hier, j’ai sans doute changer 2 fois mon logon ou alors j’avais 2 fenêtres avec studio code server d’ouvert avec des logon différents…
merci bcp.
maintenant il faut que je vois comment piloter la tondeuse via HA en m’inspirant de l’intégration qui ne fonctionne plus…

Bonjour, alors j’ai réussi a intégrer ma tondeuse sous HA avec la méthode ci-dessous :

  1. un shell dans /config/3rdparty/worx/worx_update.sh
    qui contient
##########################################################################
### Script de mise a jour des statuts Tondeuse Landroid  ###
### 2020 05 25                                                         ###
##########################################################################
TOKEN=$(curl -v -X POST -d '{"client_id": "150da4d2-bb44-433b-9429-3773adc70a2a", "grant_type": "password", "scope": "*", "client_secret": "nCH3A0WvMYn66vGorjSrnGZ2YtjQWDiCvjg7jNxK", "username": "votreadressemail", "password":"votremotdepasse"}' -H 'Content-Type: application/json' https://id.eu.worx.com/oauth/token | jq -r '.access_token') 
curl -X GET -H 'Accept: application/json' -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" "https://api.worxlandroid.com/api/v2/product-items" > /config/www/worx/worx_curl1.json
curl -X GET -H 'Accept: application/json' -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" "https://api.worxlandroid.com/api/v2/product-items/votrenumerodeserie?status=1&mac_address=votremacadresse" > /config/www/worx/worx_curl2.json
  1. ce shell est appelé toutes les 20 minutes, et génére deux fichiers dans /config/web/worx

trés facile de voir le contenu en tapant :
http://IPDEVOTREHA:8123/local/worx/worx_curl2.json

dans configuration.yaml

shell_command:
  worx_update: /config/3rdparty/worx/worx_update.sh      

et en mettant en place une automatisation qui appelle toutes les 20 minutes le service shell_command.worx_update

  1. on créer les appels REST dans configuration.yaml
rest:
  - scan_interval: 60
    resource: http://192.168.0.10:8123/local/worx/worx_curl2.json         
    sensor:
      - name: "Tondeuse Worx Infos"
        value_template: "{{ value_json.last_status.timestamp}}"
        json_attributes:
          - "distance_covered"
          - "mower_work_time"
          - "blade_work_time"
          - "battery_charge_cycles"
          
rest:
  - scan_interval: 60
    resource: http://192.168.0.10:8123/local/worx/worx_curl2.json
    sensor:
      - name: "Tondeuse Worx Batterie"
        json_attributes_path: "$.last_status.payload.dat.bt"
        value_template: "OK"
        json_attributes:
          - "p"
          - "v"
          - "t"
  1. les sensors (sensors.yaml)
  - platform: template
    sensors:
      worx_batt_p:
        friendly_name: "Worx batterie pourcentage"
        value_template: "{{ state_attr('sensor.Tondeuse_worx_batterie', 'p') }}" 
        unit_of_measurement: "%"

      worx_batt_v:
        friendly_name: "Worx batterie volts"
        value_template: "{{ state_attr('sensor.Tondeuse_worx_batterie', 'v') }}"
        unit_of_measurement: "V"        

      worx_batt_t:
        friendly_name: "Worx batterie Temp"
        value_template: "{{ state_attr('sensor.Tondeuse_worx_batterie', 't') }}"  
        unit_of_measurement: "°C"        

      worx_batt_cycle:
        friendly_name: "Worx batterie cycles"
        value_template: "{{ state_attr('sensor.Tondeuse_worx_infos', 'battery_charge_cycles') }}" 
        
      worx_infos_dist:
        friendly_name: "Worx infos distance"
        value_template: "{{ state_attr('sensor.Tondeuse_worx_infos', 'distance_covered')/1000| round(1) }}" 

      worx_infos_work:
        friendly_name: "Worx infos work"
        value_template: "{{ (state_attr('sensor.Tondeuse_worx_infos', 'mower_work_time')/60)| round(1) }}"     

      worx_infos_blades:
        friendly_name: "Worx infos lames"
        value_template: "{{ (state_attr('sensor.Tondeuse_worx_infos', 'blade_work_time')/60)| round(1) }}"  
 

Donc :

  1. vous pouvez vérifier la génération du json en allant à http://IPDEVOTREHA:8123/local/worx/worx_curl2.json

  2. le fichier shell me permet de créer le token, et d’appeler ensuite le webservice (je ne sais pas le faire depuis HA :slight_smile: )

  3. dans le fichier worx_update.sh remplacez :
    a) votrenumerodeserie = le numéro de serie de votre tondeuse
    b) votreadresseemail = votre email
    c) votremotdepasse = votre mot de passe
    d) votremacadresse = adresse mac de votre tondeuse

Worx pour récuperer votre num serie
et votre routeur vous donnera sa MAC adresse.
(c’est mon premier article, pardonnez la mise en forme)

1 « J'aime »

Bonjour,
Juste au dessus, il y a ce lien : GitHub - Flodu31/HomeAssistant-LandroidWorx: Integration for Home Assistant and Landroid Worx mower ou c’est fait dans HA directement avec command_line

Bonjour,
J’ai besoin d’un peu d’aide,
j’ai fait l’acquisition d’une tondeuse landroid worx et j’aimerais l’intégrer dans mon HA.
J’ai suivi le github de flodu31 en changeant le username, le password et le n° de série, mais je n’ai rien qui remonte sur mon HA:

mon command_line.yaml:

# Landroid Worx
  - sensor:
    name: rest_token
    scan_interval: 3600 # every 1 hour
    command: 'json=$(curl --location --request POST ''https://id.eu.worx.com/oauth/token'' --header ''Content-Type: application/x-www-form-urlencoded'' --data-urlencode ''client_id=150da4d2-bb44-433b-9429-3773adc70a2a'' --data-urlencode ''scope=*'' --data-urlencode ''client_secret=nCH3A0WvMYn66vGorjSrnGZ2YtjQWDiCvjg7jNxK'' --data-urlencode ''grant_type=password'' --data-urlencode ''username=YourUserName'' --data-urlencode ''password=YourPassword'' | jq -r ''.access_token'') && echo $json > /config/.landroidToken'
    
  - sensor:
    name: landroid_rest
    scan_interval: 60 # every 1 minute
    command: 'curl -X GET -H "Accept: application/json" -H "Content-Type: application/json" -H "Authorization: Bearer $(cat /config/.landroidToken)" https://api.worxlandroid.com/api/v2/product-items/YourSerialNumber?status=1'
    json_attributes:
        - blade_work_time
        - mower_work_time
        - firmware_version
        - distance_covered
        - battery_charge_cycles
        - last_status
        - dat
        - name

puis mon sensor:


# Landroid Worx
  - platform: template
    sensors:
      landroid_blade_work_time:
        friendly_name: Temps de travail de lame
        icon_template: mdi:robot-mower-outline
        value_template: >-
          {% set t = state_attr("sensor.landroid_rest", "blade_work_time") | int(0) %}
          {% if t != 0 %}
            {{ "%0d J %0.02d H %0.02d M" | format(t // 1440, ((t % 1440) // 60), t % 60) }}
          {%- else -%}
            {{ '0 min' }}
          {% endif %}
      landroid_mower_work_time:
        friendly_name: Temps total de la tondeuse
        icon_template: mdi:robot-mower-outline
        value_template: >-
          {% set t = state_attr("sensor.landroid_rest", "mower_work_time") | int(0) %}
          {% if t != 0 %}
            {{ "%0d J %0.02d H %0.02d M" | format(t // 1440, ((t % 1440) // 60), t % 60) }}
          {%- else -%}
            {{ '0 min' }}
          {% endif %}  
      landroid_firmware_version:
        friendly_name: Version du firmware
        value_template: "{{ (state_attr('sensor.landroid_rest', 'firmware_version')) }}"
      landroid_distance_covered:
        friendly_name: Distance parcourue
        icon_template: mdi:map-marker-distance
        unit_of_measurement: 'km'
        value_template: "{{ (state_attr('sensor.landroid_rest', 'distance_covered') | float / 1000) | round }}"
      landroid_battery:
        friendly_name: Batterie
        unit_of_measurement: '%'
        icon_template: mdi:battery
        value_template: "{{ (state_attr('sensor.landroid_rest', 'last_status')['en charge']['dat']['bt']['p']) | float(0) }}"
      landroid_battery_charge_cycles:
        friendly_name: Cycle de charge de la batterie
        icon_template: mdi:battery-charging-30
        value_template: "{{ (state_attr('sensor.landroid_rest', 'battery_charge_cycles')) }}"
      landroid_last_status_update:
        friendly_name: Dernière mise à jour
        icon_template: mdi:update
        value_template: "{{ as_timestamp( state_attr('sensor.landroid_rest', 'last_status')['horodatage']) | timestamp_custom('%d.%m.%Y %H:%M:%S') }}" 
      landroid_rsi:
        friendly_name: Signal Wifi
        unit_of_measurement: 'dBm'
        icon_template: mdi:wifi
        value_template: "{{ (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['rsi']) }}"
      landroid_rain_delay:
        friendly_name: Retard dû à la pluie
        icon_template: mdi:weather-rainy
        value_template: >-
            {% if (state_attr('sensor.landroid_rest', 'last_status')['en charge']['dat']['pluie']['s'] == 1) %}
              {{ state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['rain']['cnt'] }} min
            {% else %}
              N/A
            {% endif %}
      landroid_status:
        # https://github.com/nibi79/worxlandroid/blob/master/src/main/java/org/openhab/binding/worxlandroid/internal/codes/WorxLandroidStatusCodes.java
        friendly_name: Status
        icon_template: mdi:information-outline
        value_template: >-
            {% if (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == -1) %}
              INCONNU
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 0) %}
              IDLE
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 1) %}
              à ma maison
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 2) %}
              Start sequence
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 3) %}
              Quitter ma maison
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 4) %}
              Suivre le fil
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 5) %}
              Recherhce ma maison
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 6) %}
              Recherche fil
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 7) %}
              Fauchage
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 8) %}
              Levé
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 9) %}
              Attrapé
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 10) %}
              Lame bloquée
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 11) %}
              Debug
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 12) %}
              Télécommande
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 30) %}
              Retour à ma maison
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 31) %}
              Formation de zone
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 32) %}
              Coupe de bordure
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 33) %}
              Coupe de bordure
            {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['ls'] == 34) %}
              Pause
            {% endif %}
      landroid_error:
        # https://github.com/nibi79/worxlandroid/blob/master/src/main/java/org/openhab/binding/worxlandroid/internal/codes/WorxLandroidErrorCodes.java
        friendly_name: Erreur
        icon_template: mdi:information-outline
        value_template: >-
          {% if (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == -1) %}
            INCONNU
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 0) %}
            Pas d’erreur !
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 1) %}
            Attrapé
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 2) %}
            Levé
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 3) %}
            Fil manquant
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 4) %}
            Fil manquant
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 5) %}
            il pleut !
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 6) %}
            Fermer la porte pour tondre
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 7) %}
            Fermer la porte pour rentrer à la maison
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 8) %}
            Moteur à lame bloqu
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 9) %}
            Moteur de roue bloqué
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 10) %}
            Pris au piège
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 11) %}
            Sens dessus dessous
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 12) %}
            Batterie faible
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 13) %}
            Fil inversé
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 14) %}
            Erreur de charge
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 15) %}
            Je ne trouve pas ma maison
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 16) %}
            Tondeuse verrouillé
          {% elif (state_attr('sensor.landroid_rest', 'last_status')['payload']['dat']['le'] == 16) %}
            Batterie en chauffe
          {% endif %}

puis ma carte:

title: NONO le petit robot
header:
  type: picture
  image: local/img/worx.png
  tap_action:
    action: none
  hold_action:
    action: none
show_header_toggle: false
state_color: false
type: entities
entities:
  - entity: sensor.landroid_status
  - entity: sensor.landroid_error
  - entity: sensor.landroid_rain_delay
  - entity: sensor.landroid_battery
  - entity: sensor.landroid_distance_covered
  - entity: sensor.landroid_mower_work_time
  - entity: sensor.landroid_blade_work_time
  - entity: sensor.landroid_rsi
  - entity: sensor.landroid_battery_charge_cycles
  - entity: sensor.landroid_firmware_version
  - entity: sensor.landroid_last_status_update

1 « J'aime »

Bonjour j’ai el emel code pour mes 2 landroid et ça marche! Tu es sur des tes mots de passe login et nul de série?

Bonjour,
Je viens également de faire l’acquisition de robot Worx Landroid M500+ Wr165e et j’aimerais l’ajouter à HA.
J’ai déjà téléchargé dans HACS « Landroid Cloud » mais je ne sais pas si c’est nécessaire dans le Github de Flo31.

Ca marche chez vous ? tout fonctionne ? des points particulier a communiquer avant que je me lance ? :wink:

Merci

Bonjour, non avec le github pas besoin d’installer quoi que ce soit juste dans le configuration.yaml et enventiellement sensors.yaml !. Intégration via hacf elle a l’air de bugger

Bonjour,
merci du retour,
c’est bien mon user et mon mdp que j’utilise pour me connecter au cloud de landroid, et c’est bien mon SN récupéré sur le site.
Bon je verrais ce wk, encore merci et bonne journée.

Pour information, pour ma part l’intégration LANDROID Cloud sous HACS fonctionne très bien, et j’ai vu aujourd’hui qu’il y avait tout un tas d’attributs disponible. à voir comment les récupérer.

Bonjour Joris_Voirin,
tu n’as plus qu’à faire ce qu’il y a noté sur le github, sinon tu peux récupérer plus haut mon message avec tout ce qu’il faut, tu n’as plus qu’à changer le « user », « mdp » et le « SN » pour mettre les tiens.

1 « J'aime »

Merci de ton retour, si je comprends bien, j’ai juste à copier/coller aux différents endroits que tu as décrit le code en remplaçant uniquement le User, Mdp & SN ?