From 7c4248ca97653524c10950bc8861eb90f6d996f3 Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Tue, 25 Aug 2026 21:31:43 +0200 Subject: [PATCH] =?UTF-8?q?docs(spec):=20LM-200=20=E2=80=94=20une=20case?= =?UTF-8?q?=20vide=20de=20la=20matrice=20n'est=20pas=20toujours=20une=20la?= =?UTF-8?q?cune?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La PAC ne se pilote qu'en SgReady (contacts SECS de signalisation, 4 états normés) ou en COMMUNICANT via un plugin nymea. Ni Relay ni Variable : le premier couperait l'alimentation du compresseur et lui retirerait son autorité sur son propre cycle (dégivrage, retour d'huile, temporisation) ; le second suppose un cadran en watts qu'un compresseur n'est pas. LM-101 disait « le domaine ne détermine pas le mécanisme et réciproquement » — vrai du couplage de CONCEPTION, faux lu comme une liberté de combiner. Précisé. Piège nommé au passage : les contacts SG-Ready SONT des relais (K1/K2 au banc sont deux things power), mais ils signalent au lieu de porter la charge. Voir des relais dans une config ne dit rien du mécanisme qui convient. Le mécanisme communicant, encore appelé ModbusSetpoint ici, porte un nom trop étroit : ce qui le définit est qu'un thing nymea porte le modèle de la machine, pas le transport. §8 point 5 : faire du domaine un discriminant de validation est une décision de contrat, pas un correctif — trois options pesées, non tranchée. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01HajXLUczEyZd22JeewRfff --- specs/spec_loadmodel.md | 80 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/specs/spec_loadmodel.md b/specs/spec_loadmodel.md index 54176a8..af9cbb8 100644 --- a/specs/spec_loadmodel.md +++ b/specs/spec_loadmodel.md @@ -1,12 +1,18 @@ # SPEC — Modèle de charges (domaine × mécanisme) -Version : 0.1.2 +Version : 0.1.3 Dépôt : `etm-powersync-energy-plugin-etm` Statut : **intention de conception figée.** §3 (schéma persisté) est **en cours d'implémentation** depuis le 2026-08-09 : `relay-router`, `etmvariableload` et `sg-ready` sont trois charges utiles discriminées par `adapter`. Le reste du document reste une intention. +> **0.1.3** — LM-200 ajouté (§2) : toutes les cases de la matrice ne se valent pas. +> Certaines sont *non prévues*, d'autres sont **interdites par la physique** — le cas de la +> PAC, qui ne se pilote qu'en `SgReady` ou en communicant. Précise LM-101, qui était trop +> permissif lu seul. Aucune décision de conception renversée ; une contrainte qui existait +> dans le matériel et pas dans le document. + > **0.1.2** — LM-104 ajouté (§1) : « supposer conservateur pour annoncer, tenter > systématiquement pour agir ». Principe dégagé des tests d'ECS-411 et ECS-414, > inscrit ici parce qu'il vaut pour tout mécanisme et non pour l'ECS seul. @@ -37,7 +43,11 @@ document reste une intention. **Écrite une seule fois, partagée par tous les domaines.** **LM-101** — Une charge configurée choisit **un domaine et un mécanisme**. Le -domaine ne détermine pas le mécanisme et réciproquement. +domaine ne détermine pas le mécanisme et réciproquement — mais il en **exclut** +certains, cf. **LM-200**. L'indépendance des deux couches porte sur le *couplage +de conception*, pas sur la liberté de combiner : elle dit qu'aucune des deux +n'a besoin de connaître l'autre pour être écrite, jamais que toute paire est +réalisable. **LM-102** — Aucune des deux couches ne répartit de budget. La couche domaine se retire de l'allocation via `available` ; elle ne la négocie pas. Règles absolues @@ -83,7 +93,55 @@ SmartHome. | `Ev` | `Relay` (prise commandée : GreenUp, Witty) · `ModbusSetpoint` (borne) | | `SmartHome` | `Relay` — voir §7, sémantique différente | -Les cases vides ne sont pas interdites, elles ne sont simplement pas prévues. +**LM-200 — Une case vide se lit de deux façons, et il faut les distinguer.** La +plupart sont *non prévues* : rien ne les interdit, elles n'ont simplement pas +d'usage identifié. D'autres sont **impossibles**, parce que le mécanisme ne sait +pas commander cet organe-là. Les confondre revient à traiter une contrainte +physique comme une lacune de feuille de route. + +**Le cas dur, et le seul identifié à ce jour : la PAC.** Une pompe à chaleur se +pilote de **deux** façons, et de deux seulement : + +| Voie | Mécanisme | Ce qui circule | +|---|---|---| +| **SG-Ready** | `SgReady` | deux contacts **secs** vers l'entrée de signalisation — 4 états normés | +| **Communicante** | mécanisme *communicant*, via un plugin nymea | mode, consignes, limite de puissance — le modèle **propre** de la machine | + +**Ni `Relay`, ni `Variable`.** Ce ne sont pas des cases à remplir plus tard : + +- **`Relay` couperait l'ALIMENTATION du compresseur.** Un `RelayRouter` étage de la + puissance réelle ; appliqué à une PAC, il lui retire son autorité sur son propre + cycle — dégivrage, retour d'huile, temporisation de redémarrage — et transforme + chaque décision d'arbitrage en court-cycling. Le verrou `minOn/minOff` limite la + casse, il ne rend pas le geste légitime. +- **`Variable` suppose un cadran en watts.** Un triac module une résistance ; un + compresseur n'est pas gradable. Une PAC communicante peut certes accepter une + *limite* de puissance, mais elle l'interprète selon son propre modèle et ne + s'engage sur aucune valeur instantanée — ce n'est pas la sémantique d'enveloppe + que `Variable` promet à l'optimiseur, et cette promesse alimente le budget. + +**⚠️ Le piège de lecture : « ma PAC est pilotée par deux relais, donc `Relay` ».** +Non. Les contacts SG-Ready **sont** des relais, et au banc `.75` ce sont bien deux +things `power` (K1/K2). Mais ils **signalent**, ils ne **portent** pas la charge. +C'est exactement ce qui sépare les deux mécanismes : `Relay` commute de la +puissance, `SgReady` commute du sens. Voir des relais dans une configuration ne +dit donc rien du mécanisme qui convient. + +**Ce que ça implique pour le mécanisme communicant, qui n'existe pas encore.** Il +est appelé `ModbusSetpoint` dans ce document : **le nom est trop étroit.** Ce qui +le définit n'est pas Modbus — c'est qu'un **thing nymea** porte le modèle de la +machine et que le moteur lui parle dans ce modèle-là. Le transport (Modbus +RTU/TCP, IP propriétaire, passerelle cloud) est une affaire de plugin +d'intégration, invisible d'ici. À renommer quand il sera implémenté ; à ne pas +confondre avec `Variable` d'ici là, malgré la ressemblance de surface d'une +« consigne » à écrire. + +**Rien n'empêche aujourd'hui d'exprimer une case interdite.** `domain` est une +métadonnée d'intention, **optionnelle**, que l'arbitre ne lit pas : une charge +`domain: "hvac"` + `adapter: "relay-router"` est acceptée par `isValid()`, et +l'omission du domaine contournerait de toute façon tout contrôle qui s'y +appuierait. Faire du domaine un discriminant de validation est une **décision de +contrat**, pas un correctif — elle est portée en §8. --- @@ -321,6 +379,22 @@ distinct, pas comme une variante. 3. Sémantique d'action du SmartHome (LM-701) : à spécifier avant toute implémentation, elle ne se déduit pas du reste. 4. Rotation d'équité (LM-503) : politique à définir si le besoin se confirme. +5. **Le domaine doit-il devenir un discriminant de validation (LM-200) ?** Aujourd'hui + `domain` est une métadonnée optionnelle que l'arbitre ne lit pas ; rien ne refuse + `domain: "hvac"` + `adapter: "relay-router"`. Trois façons de traiter la case interdite, + par coût croissant : + - **la documenter seulement** — l'app guide le choix, le moteur ne juge pas. C'est + l'état actuel, désormais écrit ; + - **refuser dans `isValid()`** — cohérent avec « les états invalides doivent être + inexprimables » (LM-302), mais `domain` cesse d'être une métadonnée : ce serait un + changement de contrat, à annoncer à l'app. Et le contrôle reste **contournable par + omission**, puisque le domaine est facultatif — un refus qu'on esquive en effaçant + un champ protège mal ; + - **avertir sans refuser** — une ligne de journal à la construction de l'adaptateur. + Ne bloque pas une installation atypique, laisse une trace lisible au banc. + + Le troisième est le seul qui ne mente ni sur le statut de `domain`, ni sur la force + réelle du contrôle. **À trancher, pas par moi.** ---