Développement d'une intégration custom MiGo (Netatmo) pour thermostats Saunier Duval - Appel aux testeurs

Bonjour à tous,

Je me suis lancé tout recemment dans le développeemnt d’une intégration Home Assistant pour les thermostats Saunier Duval pilotés via l’application MiGo (basée sur l’API Netatmo).

La genèse du projet

Comme plusieurs d’entre vous peut-être, j’ai d’abord essayé d’utiliser l’intégration myPyllant pour connecter mon thermostat Saunier Duval à Home Assistant. Résultat : échec d’authentification systématique malgré des identifiants valides.

Après investigation (voir l’issue #336), j’ai découvert que l’app MiGo utilise une API complètement différente de myVAILLANT. L’authentification MiGo passe par l’API Netatmo (app.netatmo.net), alors que myPyllant utilise l’infrastructure Vaillant Identity (identity.vaillant-group.com). Ce sont deux systèmes totalement distincts et incompatibles.

Ce problème touche de nombreux utilisateurs en France, mais aussi en Italie et en Espagne. J’ai donc décidé de créer une intégration dédiée en reverse-engineering l’API Netatmo utilisée par l’application iOS MiGo.

Fonctionnalités actuelles

L’intégration propose déjà un bon nombre de fonctionnalités :

Entité Climate :

  • Affichage de la température actuelle

  • Réglage de la température de consigne

  • Modes : Auto (programme), Heat (manuel), Off (hors-gel)

  • Presets : Absent, Eau chaude seule, Hors-gel

Capteurs :

  • Température et humidité par pièce

  • Température extérieure

  • Niveau de batterie du thermostat

  • Force du signal WiFi/RF

  • Versions firmware

Contrôles :

  • Activation/désactivation de l’eau chaude sanitaire (ECS)

  • Réglage de la courbe de chauffe

  • Température ECS

  • Offset de température par pièce

Diagnostics :

  • État de la chaudière (en marche/arrêt)

  • Erreurs chaudière et eBus

  • État de connexion des appareils

Qui peut tester ?

Je recherche des testeurs possédant :

  • Un thermostat Saunier Duval connecté via l’app MiGo (icône )
  • :warning: Attention : l’app MiGO Link (icône blanche & rouge) n’est PAS compatible

  • Une installation Home Assistant fonctionnelle

  • HACS installé (recommandé)

Pour le moment, j’ai pu tester uniquement sur ma chaudière Saunier Duval Isotwin Condens 25-A. J’aurais besoin de retours sur d’autres modèles pour valider la compatibilité.

Pour les testeurs

  • Accès au code via GitHub et installation simple via HACS

  • Support direct pour la mise en place et le débogage

  • Vos retours influenceront directement les améliorations futures

Comment participer ?

Si vous êtes intéressé, merci d’indiquer :

  1. Le modèle de votre chaudière Saunier Duval

  2. Le modèle de votre thermostat

  3. Confirmation que vous utilisez bien l’app MiGo (pas MiGo Link)

Lien GitHub : https://github.com/tomahoax/ha-migo-netatmo

Merci d’avance pour votre aide !

Excellente nouvelle.
J’ai tous les prérequis :

  • Ma chaudière est une SAUNIER DUVAL themaplus F35 condens
  • J’ai bien un MiGo
  • HA en mode core (docker) donc sans add-on. Je gère à la main.
  • HACS

Il existe néanmoins une intégration qui marche avec ma config:

La Vaillant vSmart.

Elle marche correctement, mais, il manque des éléments de configuration.

Etant, comme beaucoup d’autres ici, un ex-Jeedom, je continue donc d’utiliser le plugin https://market.jeedom.com/index.php?v=d&p=market_display&id=3447

Le prix est désormais assez délirant. Je l’ai depuis X années. C’était 4 euros :slight_smile:

Fonctionnellement, elle correspond, pour moi, à tous les besoins. Ca peut être une source d’idées.

Notamment, dans ta liste, il manque, pour mon usage, le changement de programme de chauffe.

Merci pour ton feeedback @golfvert !

Je ne connaissais pas Vaillant vSmart je vais regarder et voir ça peut fonctioner avec mon installation, concernant Jeedom j’en suis pas équipé d’où l’approche HA en première intention.

Quand tu dis qu’il manque des élements de configuration, tu peux stp lister ce que tu y verrais idéalement pour que je regarde si je peux enrichir l’intégration que je suis en train de faire évoluer.

Concernant le changement de programme c’est bien déjà disponible :

Les deux choses que j’utilise le plus dans l’application MiGo de Jeedom ce sont :

  • le changement de programme de chauffe. Les jours où je suis en télétravail, je passe le programme en “travail” et quelque soit le jour, ça defini le planning de la journée. Ou alors, je peux programmer une absence de N jours et planifier le changement de programme avant le retour via le calendrier.
  • la baisse temporaire du chauffage. Je peux choisir facilement de mettre à X° pendant Y minutes. Par défaut, sur MiGo, c’est Z heures. Là, je peux choisir très simplement une durée.

Bonjour,

Merci pour cette intégration ! Après plusieurs échecs avec les differentes myVaillant, j’ai testé celle-ci et cela fonctionné du premier coup :slight_smile:

Bonjour,

J’ai une chaudière ThemaPlus Condens 25-A avec MiGo. Vraiment content de découvrir cette intégration que je vais m’empresser de tester. Je vous dis prochainement si cela a fonctionné.

Bonjour,

J’ai donc fait le test, l’installation s’est bien passée, j’ai obtenu les mesures de températures intérieur et extérieur ainsi que la consigne. Malheureusement, dès que j’ai voulu changer de mode de fonctionnement, des messages d’exception sont apparus en blanc sur fond noir en bas de page de HA IOS. Grosse frayeur, à force de manips, je suis resté bloqué sur le mode absent hors-gel, y compris dans l’application MiGo IOS (gros bouton arrêt affiché et figé). Une déconnexion/reconnexion de MiGo IOS ainsi que la mise hors tension/sous tension de la chaudière (ou reset) n’ont pas permis de rétablir le fonctionnement normal. Étonnamment, MiGo android fonctionnait parfaitement sur un autre smartphone. Je m’en suis sorti en désinstallant l’app IOS et en la réinstallant à nouveau.

J’ajoute que mon thermostat est bien un modèle mural MiGo sans référence particulière.

Dans l’app IOS est indiqué ceci :

Logiciel interne : 14

Version logicielle : 1030

J’ai un numéro de série si nécessaire ?

Bonjour @tomahoax ,

J’ai une chaudiere Saunier Duval Themaplus avec un thermostat MiGo/Vaillant et comme toi j’ai Jeedom de demarré seulement pour gere la chaudiere en lien avec HAOS.

Je suis tres interressé par ton Intergration, que je vais testé de ce pas ……

:grinning_face:

Et bien, tu peux ajouter à la liste des application non compatible eRELAX/vsmart android, mon login et mot de passe ne sont pas reconnu.

:smiling_face_with_tear:

bonjour,

Saunier duval DUOMAX CONDENS F30 90

migo (appli sur android)

nickel ton integration est au top, merci pour tout

tu pense qu'une version hors cloud est jouable (je vais scanner les ports à mon retour de vacance si tu veux)

Salut,

J'aurais aimé avoir cette intégration y'a 10 ans quand j'ai installé ma chaudière :smiley:

Merci, ça a fonctionné immédiatement avec ma Saunier Duval IsoTwin Condens !

Par contre il me manque une chose par rapport à Vaillant vSMART. Je me suis fait un bouton qui affiche l'état de l'ECS (pas du boost). Comme ça, quand je rentre tard le soir et que la chaudière est déjà en mode nuit, j'appuie dessus avant de prendre une douche pour que ça active le "boost eau chaude" afin d'avoir de l'eau chaude. Ce bouton repose sur la plage horaire du planning en cours qui remonte dans l'intégration "concurrente". C'est un tableau renseigné à la main qui indique pour tous les couples planning/plage possible si l'ECS est activée ou pas. Et plutôt que de changer le planning ou la plage horaire, si l'ESC est à OFF l'appuie sur le bouton active le mode boost (et le bouton affiche ECS ON, un nouvelle appuie coupe le boost et le repasse à ECS OFF).

Je ne sais pas si je suis clair, mais si je pouvais avoir à minima avoir la plage horaire activée en cours ça serait génial. L'idéal serait cependant d'avoir l'info sur l'état de l'ECS :stuck_out_tongue:

Sauf erreur de ma part, ça ne remonte pas non plus le mode "absent".

J'ai fouiné dans /api/homesdata et j'ai trouvé deux familles de plannings. Chez moi, 12 au total : 6 de type therm et 6 de type event, avec les mêmes noms, les mêmes identifiants de zone et des timetables identiques. Les deux sont marqués selected en même temps.

  • les zones therm portent les températures, et leur tableau modules est toujours vide
  • les zones event portent modules: [{"id": ..., "dhw_enabled": true|false}], soit exactement l'interrupteur « Production d'eau chaude » qu'on voit dans l'appli sur chaque jeu de température

Extrait d'un planning event sélectionné (~vacances à la maison consigne basse) :

{
  "name": "Vacances int Light",
  "type": "event",
  "selected": true,
  "timetable": [
    {"zone_id": 1, "m_offset": 0},
    {"zone_id": 7, "m_offset": 465},
    {"zone_id": 0, "m_offset": 555}
  ],
  "zones": [
    {"id": 1, "modules": [{"dhw_enabled": false}]},
    {"id": 7, "name": "Matin", "modules": [{"dhw_enabled": false}]},
    {"id": 0, "modules": [{"dhw_enabled": true}]}
  ]
}

Les m_offset sont des minutes depuis lundi 00:00 en heure locale, et ça colle au créneau près avec ce que montre l'appli (465 = lundi 07:45, 555 = lundi 09:15).

J'ai patché mon intégration locale en prenant le planning event sélectionné, dans lequel je cherche la dernière entrée du timetable dont l'offset n'est pas dans le futur, et je lis dhw_enabled sur la zone correspondante.

J'ai implémenté ça en local sous forme d'un binary_sensor sur la gateway, avec des attributs schedule_name / zone_name / zone_id pour le debug, et un état unavailable plutôt qu'une valeur inventée si le flag n'est pas trouvé. Ça tourne chez moi depuis cet aprèm et ça suit le planning tout seul, sans plus rien de codé en dur.

J'ai la flème de pousser une PR mais je peux fournir les 3 fichiers modifiés si besoin :slight_smile:

Je regarde pour le mode absent dès que j'ai un instant, qu'il faudra croiser avec dhw_enabled qui visiblement reste à true quand le mode absent est activé (qui, j'espère, doit couper l'ECS).

EDIT :

J'ai poussé un peu plus les tests et je pense être tombé sur un bug d'affichage du mode. Si les capteurs, configurations et diagnostics ont l'air bons, aussi bien pour la gateway que le thermostat, l'entité climate n'indique pas le bon état, dans mon cas tout du moins, et je crois avoir trouvé pourquoi.

MiGo empile trois notions distinctes

  1. le mode chaudière (Mode normal / Eau chaude seulement / En veille [hors-gel]), accessible via les Actions rapides de l'appli
  2. le mode maison (Absent, avec son bouton « Je suis de retour »)
  3. la consigne de la pièce

Tout ça sans compter le boost eau chaude. De toute façon, d'après les tests effectués, l'ECS est toujours activée par le planning, quel que soit le mode chaudière (même hors-gel), j'ai testé au robinet dans les différents cas.

Toutefois, l'entité climate ne regarde que therm_setpoint_mode (la pièce), donc les deux premiers n'apparaissent nulle part dans HA, et surtout, le mode chaudière écrase la consigne de la pièce, ce qui produit un affichage faux.

Ce que remonte l'API MiGo

J'ai dumpé /api/homesdata et /api/homestatus dans 5 configurations (la 6ème est impossible, les Actions rapides sont grisées quand la veille est active) :

# Mode chaudière Absent therm_mode therm_setpoint_mode Consigne
1 Normal non schedule home 21 °C
2 Normal oui away home 18,5 °C
3 Eau chaude seulement non schedule hg 7 °C
4 Eau chaude seulement oui schedule hg 7 °C
5 Veille (hors-gel) non hg home 7 °C
6 Veille + Absent non testable

Trois choses ressortent de ce tableau.

therm_mode mélange deux notions indépendantes, il vaut away pour le mode maison et hg pour la veille, alors que ce sont deux réglages distincts dans l'appli.

Le hg de la pièce ne signifie pas hors-gel mais « chauffage coupé par le mode ECS seule », alors que la vraie veille remonte home. C'est inversé par rapport à l'intuition, et c'est ça qui produit l'affichage faux, puisque home est mappé sur HVACMode.HEAT.

Enfin, les dumps 3 et 4 sont rigoureusement identiques, octet pour octet. En mode « Eau chaude seulement », activer Absent ne change strictement rien dans la réponse de l'API.

Côté HA

Situation réelle Consigne affichée Mode HA Préréglage HA
Eau chaude seulement + Absent 7 °C Éteint Hors-gel
Veille (hors-gel) 7 °C Chauffage (aucun)
Absent seul 18,5 °C Chauffage (aucun)

En veille (hors-gel), le thermostat de l'intégration m'affiche :

Pourtant, dans les capteurs on peut voir que la chaudière est arrêtée :

Quand je désactive le mode hors-gel :

(la chaudière est toujours affichée "arrêtée" puisqu'il fait plus de 21° dans la pièce).

Quand j'active le mode "eau chaude seulement" :

Aucun changement quand j'active le mode absent (par dessus eau chaude seulement), ce qui est logique vu que l'API ne remonte rien de différent.

Par contre, mode absent "seul" :

Ca affiche bien la température définie pour la plage horaire "absent", mais rien dans préréglage :confused: Normal finalement, vu que Absent ne remonte jamais dans therm_setpoint_mode, il se traduit seulement par une consigne abaissée à away_temp.

L'info existe, mais sur un autre endpoint

À côté de MiGo j'utilise aussi l'intégration vaillant-vsmart, et elle affiche le mode absent correctement, y compris quand « Eau chaude seulement » est actif. J'ai donc dumpé son API pour comparer.

Elle n'utilise pas les mêmes endpoints : /api/getthermostatsdata sur api.netatmo.com, là où MiGo utilise homesdata et homestatus sur app.netatmo.net. Et son modèle de données colle beaucoup mieux à l'appli :

Notion dans l'appli getthermostatsdata homesdata
Mode chaudière system_mode (winter / summer / frostguard) déduit de therm_mode + consigne pièce
Mode absent setpoint_away.setpoint_activate (booléen) therm_mode = away, invisible en mode ECS seule
Boost eau chaude setpoint_hwb dhw_enabled sur la gateway
Consigne ECS dhw, dhw_min, dhw_max absent

Les trois modes des Actions rapides correspondent exactement à winter / summer / frostguard, sous forme d'un champ unique.

Et surtout, le cas où MiGo perd l'information. En « Eau chaude seulement + Absent », getthermostatsdata renvoie :

system_mode   : 'summer'
setpoint_away : activate=True
est_setpoint_temp : 18.5

Donc la donnée existe bien côté Netatmo, elle est juste absente de homesdata et homestatus.

Une réserve importante

Cet endpoint est à réserver aux modes. Il porte aussi un flag ECS par zone, hw, directement dans les programmes de chauffage, mais ses valeurs ne sont plus synchronisées avec l'appli. Sur mon planning actif, 3 zones sur 6 divergent :

Zone Appli MiGo hw (getthermostatsdata) dhw_enabled (homesdata)
Nuit OFF True false
À la maison ON False true
Week End ? False true
Soirée ON True true
Fin de soirée ON True true
Matin OFF False false

C'est l'appli qui a raison, donc le planning ECS doit rester sur homesdata. Probablement un vestige que Netatmo ne met plus à jour depuis l'introduction des plannings de type event.

Pistes

Si l'ajout d'un second endpoint te semble trop lourd, exposer therm_mode en entité séparée (un select ou un sensor) couvrirait déjà une partie du besoin. Il est déclaré dans models.py (HomeConfig.therm_mode) mais un grep ne le trouve nulle part ailleurs que dans l'appel d'écriture set_therm_mode, il n'est jamais lu.

Sinon, interroger getthermostatsdata en complément donnerait system_mode et setpoint_away proprement, avec les mêmes identifiants Saunier Duval.

Je peux fournir les dumps complets des deux API si ça peut aider, dis-moi.

Dernière info au passage, ni le mode Absent, ni la veille (hors-gel), ni Eau chaude seulement ne coupent la production d'eau chaude. Vérifié au robinet, 5 minutes d'eau chaude en continu avec la veille active. Seul le planning ECS semble décider.

En attendant j'utilise les 2 intégrations modifiées un peu à l'arrache à la main en complément, au moins j'ai les infos dont j'ai besoin et qui collent au terrain :smiley:

EDIT 2 : en fait le mode absent coupe bien l'eau (cf tranche horaire associée), jme suis fait avoir par les ballons qui étaient déjà chauds :wink:

EDIT 3 (je ne peux pas faire plus de 3 réponses sur le topic :x) :

Suite et fin de mes aventures, avec une bonne nouvelle : tout tient dans MiGo, pas besoin de garder vSMART à côté.

Le token MiGo passe sur l'endpoint legacy

Je me demandais s'il fallait un second jeu d'identifiants pour interroger getthermostatsdata. Réponse : non. Le token obtenu avec les identifiants MiGo (na_client_ios_sdbg) est accepté tel quel sur api.netatmo.com/api/getthermostatsdata. Une intégration unique suffit donc, sans rien demander de plus à la configuration.

J'ai ajouté l'appel dans le coordinateur, isolé dans un try : s'il échoue, le reste de l'intégration continue de fonctionner et les entités concernées passent en unavailable plutôt que de mentir.

Ce que ça donne comme entités

  • binary_sensor Mode absent, lu depuis setpoint_away
  • sensor Mode chaudière, exposant system_mode (Chauffage / Eau chaude seulement / Veille)
  • switch Mode absent, écrit via setthermmode (celui qui existait déjà dans l'API MiGo), donc sans effet de bord sur le mode chaudière

Ce dernier point corrige au passage un bug de vSMART : son async_set_preset_mode envoie systématiquement TemperatureControlMode.HEATING en même temps que le mode absent, ce qui désactive le mode été sans prévenir. Faudrait que je leur signale...

Correction sur ce que j'écrivais hier

Dans mon tableau, le cas 4 (Eau chaude seulement + Absent) montrait therm_mode à schedule, d'où ma conclusion que l'info était perdue. En relisant les logs de debug, quand l'absence est activée via setthermmode, therm_mode passe bien à away même avec system_mode: summer. La perte que j'avais constatée venait probablement du fait que j'avais activé l'absence depuis l'appli. Je n'ai pas retesté proprement les deux chemins, donc à prendre avec des pincettes.

Du coup mon capteur combine les deux sources : therm_mode en priorité (immédiat), setpoint_away en repli. C'est nécessaire parce que le flag legacy reflète l'état du thermostat lui-même, synchronisé par radio avec plusieurs minutes de retard : juste après avoir activé l'absence, therm_mode dit déjà away alors que setpoint_away répond encore False.

Et le mode absent coupe bien l'ECS

Confirmé proprement cette fois, même planning, même zone (« À la maison », ECS active), seule l'absence change :

  • sans absence : capteur on, overridden_by_away: false
  • avec absence : capteur off, overridden_by_away: true

Logique, puisque « Absent » est un jeu de température comme les autres, avec « Production d'eau chaude » décochée, et qu'il remplace le créneau en cours. Mon capteur ECS renvoie donc off dès que l'absence est active, avec l'attribut pour distinguer la cause.

Détail amusant : l'appli grise le boost eau chaude quand le mode absent est actif, mais l'API l'accepte sans broncher, et le bandeau « Boost eau chaude sanitaire » apparaît ensuite dans l'appli. L'intégration fait donc mieux que l'outil officiel sur ce coup, ce qui est pratique quand on rentre plus tôt que prévu.

Deux correctifs sur du code existant

En testant, je suis tombé sur un souci de latence d'affichage qui ne vient pas de mon ajout :

  • le switch boost ECS n'utilise aucun cache optimiste, il lit dhw_enabled depuis homestatus, donc l'état ne bouge qu'au rafraîchissement suivant
  • le switch anticipation remplit son cache après _call_api_and_refresh, sans écriture d'état ensuite : le rafraîchissement recalcule l'état avant que le cache ne soit posé, donc l'interface reste sur l'ancienne valeur

J'ai factorisé un _call_api_optimistically dans MigoApiControlMixin qui remplit le cache et écrit l'état avant l'appel, et restaure la valeur précédente si l'appel échoue. Les trois switches l'utilisent. Les entités number étaient déjà dans le bon ordre, je n'y ai pas touché.

Bilan

15 fichiers touchés, 84 tests qui passent (24 nouveaux couvrant la résolution des créneaux, le bouclage dimanche→lundi, l'appariement linked_schedules, la priorité entre les deux sources d'absence et les cas inconnus), ruff propre à part deux PLR0917 préexistants dans api.py. Traductions ajoutées dans les 5 langues du dépôt.

J'ai aussi découvert linked_schedules dans homesdata, qui appaire explicitement les plannings therm et event sous forme de paires d'identifiants. C'est plus robuste que de se fier au double selected, mon code s'en sert avec repli sur l'ancienne méthode.