etm-powersync-app/docs/CADRAGE_zones_clim.md
Patrick Schurig f33b8c243a feat(support): le rapport de bug pré-rempli, repoussé depuis le premier jour
/bug-report était un stub. Un champ libre seul produit « ça ne marche pas », et l'aller-retour
pour obtenir versions, charges et état d'arbitrage coûte des jours. Tout ce que l'app SAIT
est donc joint d'office : version du paquet, hôte et état de connexion, ligne de versions du
schéma, les charges déclarées avec adaptateur / rang / domaine, et le dernier cycle publié.

Deux choix qui ne sont pas cosmétiques :

- LES DEUX IDENTITÉS DU BUDGET sont dans le rapport, avec leur verdict. Elles valent souvent
  plus que la description du symptôme : portées fausses, elles désignent le moteur ; portées
  vraies, elles désignent l'écran. Sans elles, le premier échange consiste à les demander.

- Aucun secret n'y entre : ni PIN installateur même haché, ni jeton, ni mot de passe. Un
  rapport voyage — courriel, capture, fil de discussion — et ce qui y entre en sort. Les
  UUID de Things restent : ce sont eux qui permettent de recouper avec le journal de la box,
  et ils n'ont aucune valeur hors de l'installation.

Les absences gardent leur sens, comme partout ailleurs : « aucune télémétrie reçue » n'est
pas un arbitrage à zéro, « budget ABSENT » n'est pas un budget de zéro watt, et un
financement omis se dit omis.

Pas d'envoi automatique : il n'existe aucun service de collecte, et un bouton « Envoyer »
sans destinataire donnerait le sentiment que quelqu'un l'a reçu. Le rapport se copie, ce qui
est vérifiable.

package_info_plus est ajouté plutôt qu'une constante de version recopiée à la main : une
constante dérive de pubspec.yaml en silence, et un rapport qui annonce la mauvaise version
envoie chercher un défaut dans un code qui n'est pas celui-là.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
2026-08-27 14:54:27 +02:00

8.1 KiB
Raw Blame History

Cadrage — écran des zones de climatisation

Date : 2026-08-27 · Banc : .75, nymea 1.15.2+202606191336~trixie1 Objet : ce qu'il faut sur le banc pour rendre l'écran exerçable, avant d'écrire une ligne de code d'affichage.

Ce document ne propose pas de maquette. C'est un cadrage : il dit ce qui existe, ce qui manque, et quelles décisions doivent être prises avant de coder. La maquette se valide avec Patrick, comme tout nouvel écran.


1. Où en est l'écran aujourd'hui

lib/screens/ac_screen.dart, 1 105 lignes. Il appelle exactement une méthode du contrat : AirConditioning.GetZones, et seulement pour en compter le résultat (_zonesSurLaBox). Tout le reste est de la maquette :

const _Zone(name: 'Salon',   currentTemp: 19.5, targetTemp: 21.0, …),
const _Zone(name: 'Chambre', currentTemp: 18.0, targetTemp: 19.0, …),
const _Zone(name: 'Bureau',  currentTemp: 20.0, targetTemp: 22.0, …),
const _Zone(name: 'Cuisine', currentTemp: 21.5, targetTemp: 21.0, …),

Quatre pièces, huit températures, un état SG-Ready et deux consignes ECS — aucun de ces nombres ne vient de la box. C'est le même défaut que le SOC véhicule à 62 %, en plus grand : des grandeurs physiques crédibles, affichées comme des mesures.

.75 déclare zéro zone (GetZones → zones: []). L'écran affiche donc quatre pièces là où l'installation n'en connaît aucune.


2. Le contrat, tel que la box le publie

Huit méthodes, trois notifications (ZoneAdded · ZoneChanged · ZoneRemoved).

ZoneInfo est presque entièrement en lecture (r:) — température, humidité, PM2.5, COV, consigne courante, dérogation et sa fin, zoneStatus, et les six listes d'appareils. On n'écrit jamais un objet complet : on écrit par les six Set*.

Écriture Ce qu'elle pose
AddZone(name, o:thermostats, o:valves, o:indoorSensors, o:outdoorSensors, o:windowSensors, o:notifications) crée
RemoveZone(zoneId) supprime
SetZoneName(zoneId, name) renomme
SetZoneThings(zoneId, …les six listes…) rattache
SetZoneSetpointOverride(zoneId, mode, setpointOverride, o:minutes) dérogation manuelle
SetZoneStandbySetpoint(zoneId, …) consigne de réduit
SetZoneWeekSchedule(zoneId, …) planning 7 jours

SetpointOverrideMode : None · Timed · Unlimited · Eventual. ZoneStatusFlag : None · TimeScheduleActive · SetpointOverrideActive · WindowOpen · BadAir · HighHumidity — un drapeau, donc combinable.

Le planning est un TemperatureWeekSchedule = 7 × TemperatureDaySchedule = liste de {startTime, endTime, temperature}.


3. ⚠️ Le banc ne peut PAS héberger une zone — et ce n'est pas « il manque un appareil »

Vérifié le 2026-08-27 sur les 58 classes déclarées par .75 : aucune classe ne porte l'une des interfaces qu'une zone exige. Pas « aucun appareil créé » — aucune classe, donc aucun plugin installé ne sait en créer un.

Les huit plugins installés sont : AbbB2x, AbbTerra, GpioController, SunSpec, eastron, energySimulation, genericEnergy, mqttclient. Tous énergie ou entrées/ sorties : rien de thermique, rien de capteur d'ambiance.

Ce que chaque emplacement exige exactement

Lu dans airconditioningmanager.cpp:605 (verifyThingIds), qui refuse par AirConditioningErrorInvalidThingType :

Emplacement Interface exigée
thermostats thermostat
windowSensors closablesensor
indoorSensors + outdoorSensors temperaturesensor ou humiditysensor ou vocsensor ou pm25sensor
notifications notifications
valves aucune — non vérifié

valves est au schéma d'AddZone et de SetZoneThings, mais verifyThingIds ne le reçoit pas : la box accepte n'importe quel ThingId dans cet emplacement sans le contrôler. À ne pas exposer comme un choix guidé tant que le point n'est pas tranché — un champ que la box accepte sans vérifier produit une configuration qui « marche » et ne fait rien.


4. Ce qu'il faut sur le banc — la liste courte

Trois paquets, tous présents dans le dépôt apt du banc (apt-cache search le 2026-08-27) :

ssh etm@192.168.1.75
sudo apt install nymea-plugin-generic-heatingcooling \
                 nymea-plugin-generic-sensors
# optionnel, pour le drapeau WindowOpen :
#   les capteurs d'ouverture sont DÉJÀ dans generic-sensors (voir tableau)

Ce que ces deux paquets apportent, vérifié dans nymea-plugins-genericthings/*/integrationplugin*.json :

Classe à créer Interfaces Remplit
Generic thermostat thermostat, temperaturesensor thermostats — et sert aussi de sonde
Generic temperature sensor temperaturesensor indoorSensors / outdoorSensors
Generic humidity sensor humiditysensor indoorSensors
Generic door or window sensor closablesensor windowSensors → drapeau WindowOpen

Les paquets *-simulation (heating-simulation, sensors-simulation, closables-simulation) sont une alternative : ils animent leurs valeurs au lieu de les laisser fixes, ce qui est préférable pour voir un écran vivre. Les classes generic*, elles, sont pilotables à la main — préférable pour provoquer un état précis (fenêtre ouverte, humidité haute) sans attendre.

Recommandation : les deux. generic* pour fabriquer un cas à volonté, *-simulation pour regarder l'écran bouger tout seul.

Le banc minimal qui exerce tout le contrat

Deux zones, parce qu'une seule ne montre ni le tri ni le cas « la pièce d'à côté » :

Zone Contenu Ce qu'elle rend exerçable
Salon 1 thermostat + 1 capteur d'humidité + 1 capteur d'ouverture dérogation, planning, WindowOpen, HighHumidity
Chambre 1 thermostat seul le cas pauvre : aucune sonde d'ambiance, et l'écran doit le dire au lieu d'afficher des tirets

Plus un capteur de température déclaré outdoorSensors, pour que la distinction intérieur/extérieur ne reste pas théorique.


5. Ce qui doit être tranché avant de coder

  1. Le drapeau zoneStatus est un ensemble, pas un état. WindowOpen + BadAir peuvent être vrais ensemble. Un écran qui affiche « le » statut choisira lequel taire — c'est une décision d'interface, à prendre explicitement.
  2. vocsensor et pm25sensor sont acceptés comme sondes d'ambiance, et ZoneInfo publie voc et pm25. La qualité d'air entre donc dans cet écran par la porte du contrat. Est-ce qu'on l'affiche, ou est-ce qu'on la garde pour plus tard ?
  3. valves — exposé ou pas ? Voir la réserve du §3.
  4. Le rattachement d'appareils appartient-il à cet écran ou au mode installateur ? AddZone et SetZoneThings sont PermissionScopeAdmin ; les six autres sont PermissionScopeControlThings. La box sépare déjà les deux : l'écran devrait suivre.
  5. Le lien avec les charges pilotées reste à définir. La PAC du banc est une charge sg-ready arbitrée par le waterfall ; une zone est un objet du contrat AirConditioning. Les deux ne se connaissent pas. L'écran actuel les mélange (un bloc SG-Ready sous les pièces), et ce mélange n'a aucun support dans le contrat. À trancher avant de le reconduire.
  6. Tier Auto. Les zones sont derrière ce palier ; pro_lock_badge.dart existe déjà.

6. Ordre de marche proposé

  1. Installer les deux paquets sur .75 et créer le banc minimal du §4.
  2. Écrire la sonde tools/rpc/zones.dart — instantané + écoute des trois notifications, comme telemetry_watch.dart. Aucun écran avant elle : c'est elle qui dira ce que la box publie vraiment quand une fenêtre s'ouvre.
  3. Modéliser ZoneInfo en lecture seule, avec la règle maison — omis, jamais nul.
  4. Retirer les quatre pièces de maquette. Tant que le modèle n'est pas branché, un écran vide qui dit « aucune zone déclarée » est plus juste que quatre pièces inventées.
  5. Maquette, validation avec Patrick, puis l'écran.