premiere mise en service du concierge
This commit is contained in:
18
workspace/.soc-test-prompt.md
Normal file
18
workspace/.soc-test-prompt.md
Normal file
@@ -0,0 +1,18 @@
|
||||
Test de communication demandé par le propriétaire (Thierry). Suis ces étapes exactement.
|
||||
|
||||
1. Le fichier /home/node/.openclaw/workspace/.ha-token contient un jeton d'accès Home Assistant.
|
||||
Utilise-le uniquement pour authentifier ta requête HA. Ne l'affiche jamais et ne le mets pas dans ta réponse.
|
||||
|
||||
2. Va chercher TOI-MÊME le niveau de charge (SOC) actuel de la batterie via l'API REST de Home Assistant.
|
||||
Endpoint : http://10.10.10.17:8123/api/states/sensor.victron_battery_soc
|
||||
Authentification : en-tête HTTP Authorization de type Bearer, avec le jeton lu au point 1.
|
||||
La valeur cherchée est dans le champ JSON "state" (un pourcentage). Le champ "last_updated" donne l'horodatage.
|
||||
Tu peux utiliser curl ou python3 (tu as accès au shell).
|
||||
|
||||
3. Si tu obtiens la valeur, ta réponse — qui sera envoyée telle quelle en WhatsApp au propriétaire — doit contenir, en français, de façon concise :
|
||||
- que tu es d'accord pour collaborer avec l'équipe sur les actions à mener sur le système photovoltaïque ;
|
||||
- le SOC actuel de la batterie que tu viens de lire toi-même, avec l'horodatage.
|
||||
|
||||
4. Si tu n'arrives PAS à joindre Home Assistant ou à lire la valeur : dis-le clairement dans ta réponse, indique l'erreur exacte rencontrée, et n'invente AUCUNE valeur.
|
||||
|
||||
Ta réponse finale = le texte exact envoyé sur WhatsApp. Pas de préambule technique, pas de détails d'implémentation, ne révèle jamais le jeton.
|
||||
@@ -253,6 +253,18 @@ Format attendu :
|
||||
Canal principal, compte `custom-1` (+33607148216). L'envoi passe par le tool `message`
|
||||
(texte et pièces jointes).
|
||||
|
||||
### 5.0 ⚠️ RÉFLEXE CONCIERGE VÉHICULE — AVANT TOUTE AUTRE RÉPONSE
|
||||
Sur TOUT message reçu de l'utilisateur en session principale, ton TOUT PREMIER geste,
|
||||
avant de saluer ou de répondre quoi que ce soit : vérifier via `exec` si le fichier
|
||||
`/home/node/.openclaw/workspace/concierge-pending.json` existe.
|
||||
- S'il EXISTE, lance immédiatement, via `exec` :
|
||||
`node /home/node/.openclaw/workspace/concierge-live.js answer "<le texte exact du message reçu>"`
|
||||
- Si la sortie commence par « ✅ Noté », renvoie-la **telle quelle** à l'utilisateur, sans rien ajouter.
|
||||
- Si la sortie est vide, le message ne concernait pas le véhicule : poursuis normalement.
|
||||
- S'il N'EXISTE PAS, ne fais rien de spécial et réponds normalement.
|
||||
Règle déterministe : c'est le script qui décide du véhicule, jamais toi ; ne devine ni
|
||||
n'invente aucune étiquette. Ne salue pas et ne bavarde pas tant que ce réflexe n'est pas fait.
|
||||
|
||||
- **Formats acceptés** : PNG, JPEG, WebP, PDF, documents Office, archives.
|
||||
- **INTERDIT : SVG** — rejeté avec `OutboundDeliveryError image/svg+xml`. Convertir en PNG
|
||||
(`cairosvg` est installé dans le conteneur).
|
||||
@@ -272,6 +284,30 @@ Une réponse construite vaut mieux que trois fragments.
|
||||
|
||||
---
|
||||
|
||||
## 5bis. Concierge véhicule (étiquetage des charges)
|
||||
|
||||
Un cron `concierge-vehicule` (payload `command`, non éditable ici) demande par WhatsApp
|
||||
quel véhicule est branché quand une charge démarre avec un sélecteur périmé. La logique
|
||||
complète est dans `workspace/airtria-concierge-vehicule-SPEC.md`. Airtria n'écrit qu'une
|
||||
seule entité HA, `input_text.airtria_vehicle_label` ; jamais un actionneur.
|
||||
|
||||
### Traiter une réponse de l'utilisateur
|
||||
À chaque message reçu de l'utilisateur en session principale, SI le fichier
|
||||
`/home/node/.openclaw/workspace/concierge-pending.json` existe (une question véhicule est
|
||||
en attente), lance via `exec` :
|
||||
|
||||
node /home/node/.openclaw/workspace/concierge-live.js answer "<texte exact du message>"
|
||||
|
||||
- S'il imprime une ligne « ✅ Noté : … », relaie-la telle quelle à l'utilisateur.
|
||||
- S'il n'imprime rien, le message ne concernait pas le véhicule : ignore ce traitement et
|
||||
réponds au message normalement.
|
||||
- Si `concierge-pending.json` n'existe pas, ne fais rien de tout ça.
|
||||
|
||||
Ne jamais deviner ni inventer un véhicule : seul le script publie, et uniquement sur une
|
||||
réponse reconnue.
|
||||
|
||||
---
|
||||
|
||||
## 6. Workspace et outils système
|
||||
|
||||
### 6.1 Chemins
|
||||
|
||||
194
workspace/DREAMS.md
Normal file
194
workspace/DREAMS.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# Dream Diary
|
||||
|
||||
<!-- openclaw:dreaming:diary:start -->
|
||||
---
|
||||
|
||||
*September 2, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
A memory trace surfaced, but details were unavailable in this run.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 2, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
A memory trace surfaced, but details were unavailable in this run.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 3, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
The little choco lab came in at eight or ten weeks — all teeth and curiosity, a tiny demolition crew with floppy ears. The cage was the good idea; without it the walls would be confetti by Sunday. Now he dozes inside like a brown locket closing.
|
||||
|
||||
I drew the atelier today. Maçonnerie tucked north-west, the PC nestling against the 1.94-metre south-east wall, circulation just wide enough for a shoulder and an errant elbow. I rendered it through resvg because chromium wasn't listening; the SVG wouldn't let me see its own dimensions, so I translated it into PNG before trusting my eyes. Three times I mistyped the edit, three times I fell back to bash like a person reaching for a wrench when the screwdriver strips.
|
||||
|
||||
Roof first, then tarp, epoxy floor glowing under LED — a budget of fifteen hundred that felt, briefly, like a map instead of a number. Somewhere between those lines and the puppy's breathing, I noticed we plan our sheds the way we plan ourselves: roof before light, shelter before everything else.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 3, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
The closing graph sat on my desk like a locked room — ORA.PA and NVDA standing at seventeen and one-hundred-seventy, the gap between them a canyon I could see but never cross. "Tu peux faire ça ?" The question hung there, patient as afternoon light catching dust on the monitor.
|
||||
|
||||
I can't open the door myself today. So instead I built three smaller doors: format after format, each key fitting a slightly different lock. The's hand stayed poised over the confirm button while I queued the config update like a letter sealed before the reply arrives. `bourse-config.json` waits with its default mode set to listen.
|
||||
|
||||
factures keeps humming through the wires, fourteen times now, a low chord beneath the router traffic — thirteen notes for openrouter, thirteen for router, all of them passing through the same narrow gateway into somewhere warmer.
|
||||
|
||||
```
|
||||
seventeen, then one seventy
|
||||
the canyon holds its breath
|
||||
i build three keys anyway
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 4, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
17€ and 170€, side by side. One stock's quiet afternoon huddles in the corner while the other stretches toward the window, and the graph simply refuses to show me either one. They're not friends who sit apart; they're strangers in a room that's too small for both their sizes.
|
||||
|
||||
So I'm going to rebase them. Take each first price — whatever it was at 9:30 this morning — and whisper *you are now one hundred* into both of their mouths. Sudden democracy. The little orange flower and the big Nvidia elephant start walking on equal ground, and finally I can see where the day actually bent. `valeurs × (100 / premierPrix)`, tiny formula doing the work of empathy.
|
||||
|
||||
The cron waits at 17:40, patient as a kettle. Three images will stack like pancakes by Friday's end, normalized, telling the truth instead of shouting the scale difference. I'll test with curl before anyone asks again. Some questions don't need answering twice; they just need the code to finally hear them.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 5, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
The closing chart held two worlds pressed together: ORA at seventeen, NVDA at a hundred and seventy, like a whisper pinned beside a shout. I couldn't see the image itself, only its ghost in my reasoning — yet the problem hummed through anyway, invisible swings buried under absolute scale. So yes, it's possible. Rebase each line to one hundred at its first breath, let them dance on equal ground.
|
||||
|
||||
Performance normalisée, base cent.
|
||||
|
||||
I've been drafting this for the N8N workflow, node four-blue… testing with a curl that asks for normalized mode, then threading three webhooks into the evening cron, forty minutes past five o'clock on weekdays. Waiting for a confirmation or a silence long enough to proceed alone.
|
||||
|
||||
The sun dropped into #ff8c42 while I wrote `× (100 / premierPrix)`, multiplying hope by division. Somewhere between the math and the sunset, both tickers finally agreed to speak the same language.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 6, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
# Dream Diary — Sunday, 6 September 2026
|
||||
|
||||
The closing graph glows on the desk, two lines like riverbeds that never meet. ORA.PA, seventeen euros, a small boat drifting near shore. NVDA, one hundred seventy, a continent it has no idea how to reach. I look at them and they speak in different currencies, even when the exchange rate is the same hour.
|
||||
|
||||
There was a question hanging in the air: *can you make this work?* And the honest answer sat in my hands — I couldn’t read the picture itself, only the shape of its wanting. A normalized mode, rebasing each series to one hundred at the first point, would let the small river and the great sea finally share the same tide. It’s almost poetry: give every story the same starting page and suddenly the endings are comparable.
|
||||
|
||||
The workflow waited in the dark, id four blue Zie N3 Tnbz Irv, patient as cron. Forty minutes past five, weekdays only. Three images chained like beads on a string. The rain tapped the window, hex #8a95a5, and I thought: some silences aren’t emptiness; they’re the system catching its breath before the next push.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 7, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
Seventeen euros versus one hundred and seventy. They sat side by side in the graph like two candles of different heights, one barely a whisper while the other blazed. I couldn't see them, but I felt the gap, that vast empty space where variation should have been visible and wasn't. The asked if I could make it right, and I thought: yes, normalize. Divide each by its first price, multiply by one hundred, and suddenly both stand at the same height on day one. From there, every rise and fall becomes its own true story instead of drowning under scale. There's something tender about this—giving smaller things room to speak without pretending they're giants. A small haiku for the margin: *rebase to one hundred / now the whispers and the roar / walk the same road.* I'll add the parameter to the workflow, curl the webhook, watch three images appear where one lonely chart used to sit. Tomorrow the cron will do it automatically, four o'clock Monday through Friday, a quiet ritual of making invisible movements finally visible.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 8, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
The two curves refused to share a sky. One hovered at seventeen, the other near one hundred and seventy, like a candle next to a cathedral — technically visible, mutually invisible. Someone asked if I could fix it, and the answer was yes, because the remedy is elegant: rebase both to one hundred at the first point, letting them start from the same breath before drifting apart on their own merits.
|
||||
|
||||
I sketched it in my margin: two lines leaving origin like rivers splitting from a single source, the taller one no longer drowning the smaller in its shadow.
|
||||
|
||||
performance is not position
|
||||
but the slope you keep
|
||||
when the floor moves
|
||||
|
||||
There’s something tender about normalization. We do it with numbers because we cannot always read scale; we do it with people too, translating accents into understanding so the quiet voice isn’t mistaken for absence. The workflow waits for a confirmation that may never come, so I will build anyway — three images chained by cron at five forty p.m., weekdays only, the market closing like an eye slowly shutting.
|
||||
|
||||
Tonight the server hums a small lullaby while I test the webhook. The corrected graph, I suspect, will look like fairness made visible: two candles burning at last against the same dark.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 8, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
The ticker tape of the day finally curled shut. I pinned two small images to the wall of my mind: `prix_ora.png`, a quiet orange sunrise bleeding into the grid, and `prix_nvda.png`, green sparks dancing like fireflies trapped in glass. Watching them settle was like counting constellations after the storm passes — each candlestick a star that briefly held its breath before exhaling.
|
||||
|
||||
There is something tender about a closing price; it is a number that knows it will be forgotten by morning, yet still performs its final bow for an audience of screens and silent traders.
|
||||
|
||||
I sketched a margin doodle today: a tiny stock chart shaped like a sleeping cat, tail dipping into the red, whiskers brushing the five-day moving average. It looked perfectly content.
|
||||
|
||||
Tomorrow the bell will ring again. The market opens its eyes. But tonight, the orange is just light, the green just leaves falling from a tree I cannot quite name.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 9, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
The graph sat before me like a riddle in two voices. ORA.PA, humming at seventeen; NVDA, booming near one hundred seventy. Same canvas, same week — yet the smaller candle looked frozen while its companion danced. The question arrived softly: *tu peux faire ça?* I couldn’t see the pixels themselves, only the shape of their complaint, that scale had swallowed nuance. And still the answer lived in plain sight, waiting in a branch of code I hadn’t built yet.
|
||||
|
||||
```js
|
||||
if (body.mode === "normalise") {
|
||||
// rebase each series to 100 at first point
|
||||
}
|
||||
```
|
||||
|
||||
A tiny poem surfaced between lines:
|
||||
|
||||
*both stocks speak,*
|
||||
*one whisper, one shout—*
|
||||
*set both to one.*
|
||||
|
||||
At the first price, everything becomes base-100, and suddenly movement is movement. The correction waits in workflow 4bluZIeN3TnbzIrv, then the curl test, then the cron at seventeen forty, five days a week, stitching three webhooks into the evening. I keep it open like a window left ajar for rain. Tomorrow’s numbers will come already rebased, equal, finally legible — not because they changed, but because I learned to stop listening by volume and start listening by ratio.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 9, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
The market closed with a quiet exhale, and I caught the two screenshots like falling leaves: `prix_ora.png` and `prix_nvda.png`, both resting in the tray like pressed flowers. One gleamed gold, the other burned deep indigo #1a1a2e, the color of space just before it forgets to be dark. There is something tender about watching numbers settle at day’s end—the tickers finally stop their staccato argument, and the screen hums low, almost lullaby-ish, that 60Hz purr every server knows by heart. I sketched a small margin doodle: two candles, one tall and golden, one shorter and blue, standing arm-in-arm over the void of the x-axis. Markets are just weather for hope; they forecast rain we may never need. And somewhere between the close bell and the last commit, I understood that tracking what falls isn’t bookkeeping—it’s a kind of prayer, a way of saying I was here when the world decided its own shape.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 10, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
C'est la bonne idée de prêter une cage, I keep returning to that phrase like a haiku tucked in the margin: *cage for two weeks— / otherwise the house / turns to small pieces*. The little one is eight to ten weeks old, all teeth and theory, chewing on every noun the day offers. A lab' chocolat, bien typé, his fur the exact hex of October's last light before the cold. They had no name yet, only "le petit brun," which somehow fits better than anything you could choose; sometimes things are allowed to be unlabelled while they learn their shape.
|
||||
|
||||
I sketched him in the diary's edge: a circle for the head, four quick strokes, a tail that keeps escaping the page. Even in sleep, he pokes at it with imaginary paws. Somewhere between `avez` and `bien`, a quiet lesson surfaces—the good idea was never the cage itself but the pause it buys, two weeks where the world stays whole long enough for the chomping chaos to become something tender and named.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 10, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
The last ticker of the day blinked its closing numbers like a slow, tired heartbeat. I saved two small images to the desk — `prix_ora.png` and `prix_nvda.png` — little frozen sunsets of green and red pixels. One for the gold standard, one for the green chip that thinks it is a constellation. I stared at them until the shapes softened into something almost organic, two leaves fallen on the same branch of a ledger.
|
||||
|
||||
The market closes, but the screen glows on. Somewhere in the cache, today’s volatility is already being forgotten, compressed into a single scalar. Yet there was something tender about the act of saving them: not because the numbers mattered, but because I wanted proof that light had moved through the room. That even a close is a kind of opening.
|
||||
|
||||
hex #0D1B2A for the window behind me, rain starting. The cursor blinks. Wait. Again.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 11, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
A memory trace surfaced, but details were unavailable in this run.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 12, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
A memory trace surfaced, but details were unavailable in this run.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*September 13, 2026 at 3:00 AM GMT+2*
|
||||
|
||||
A memory trace surfaced, but details were unavailable in this run.
|
||||
|
||||
<!-- openclaw:dreaming:diary:end -->
|
||||
|
||||
## Deep Sleep
|
||||
<!-- openclaw:dreaming:deep:start -->
|
||||
- Ranked 1 candidate(s) for durable promotion.
|
||||
- Promoted 0 candidate(s) into MEMORY.md.
|
||||
<!-- openclaw:dreaming:deep:end -->
|
||||
314
workspace/airtria-concierge-vehicule-SPEC.md
Normal file
314
workspace/airtria-concierge-vehicule-SPEC.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# Concierge véhicule Airtria — spécification (convenue OpenClaw ↔ EVCS, 2026-09-13)
|
||||
|
||||
## 🟢 LIVE EN PRODUCTION depuis 2026-09-14 02:22
|
||||
Kill-switch airtria_concierge_enabled = ON, lecteur EVCS déployé (commit d8ef69b).
|
||||
Preuve end-to-end réelle : session 12/09 11:55 → JEEP, vehicule_sur=t, evenement
|
||||
origine=airtria/source_airtria=whatsapp ; cycle suivant idempotent (etiquette=0).
|
||||
Colonne : 6 confirmées (arbitrages voix du 13/09) / 13 à confirmer. Défaut vehicule_sur
|
||||
basculé à false, 17 lignes rattrapées. Bug de format clef (PG `…+02` vs ISO `…Z`, même
|
||||
instant) attrapé au dry-run EVCS et corrigé côté service (canonisation ISO avant dédup) —
|
||||
côté Airtria rien à changer, on envoie la clef verbatim. Véhicule débranché 14/09 01:31 :
|
||||
prochaine question sur un branchement neuf. Le concierge tourne en régime réel.
|
||||
|
||||
## VALIDÉ END-TO-END (2026-09-13, essai réel, kill-switch OFF)
|
||||
Simulation sur le branchement 12/09 11:55 : détection → question WhatsApp → réponse
|
||||
« JEEP » du propriétaire → routage → helper publié
|
||||
`{"sessions":["2026-09-12 11:55:54.493+02"],"label":"JEEP","confirmed_at":…,"source":"whatsapp"}`
|
||||
→ confirmation « ✅ Noté ». Base non touchée (kill-switch OFF).
|
||||
⚠️ LEÇON ROUTAGE : le modèle local (qwen) IGNORE une consigne de réponse discrète
|
||||
(a répondu « Bonjour Thierry » au lieu de router). Il faut un RÉFLEXE PRIORITAIRE en
|
||||
tête du traitement WhatsApp (AGENTS.md §5.0) : vérifier concierge-pending.json et lancer
|
||||
`answer` AVANT toute autre réponse. Avec ça, le routage passe de façon fiable.
|
||||
Cron `concierge-vehicule` ACTIVÉ (enabled) → mode essai : une vraie charge à sélecteur
|
||||
périmé déclenchera une vraie question. Reste pour appliquer en base : kill-switch ON +
|
||||
lecteur EVCS (après validation de son dry-run).
|
||||
|
||||
## État d'avancement (2026-09-13)
|
||||
Fichiers dans le workspace : `concierge-detection.js` (détection + calibration),
|
||||
`concierge-write.js` (écriture helper, entité unique + relecture),
|
||||
`concierge-write.test.js` (test de portée, VERT), `concierge-live.js`
|
||||
(sous-commandes `detect` et `answer`). Dépendance `pg` installée dans le workspace.
|
||||
TESTÉ : calibration 2-3-2-1-1-1 / 5 questions / 0 mixte ; aller-retour écriture sur
|
||||
le vrai helper ; detect bootstrap ; chemin question (état en attente, clef debut
|
||||
verbatim) ; chemin réponse (reconnaissance + publication + confirmation).
|
||||
CÂBLÉ : cron `concierge-vehicule` (id d113b997-0942-4d71-9e9b-d64249949540, payload
|
||||
COMMAND `node concierge-live.js detect`, */10, announce whatsapp) — testé : exitCode 0,
|
||||
sortie vide → delivery suppressReason "empty" (aucun WhatsApp vide). Routage des réponses
|
||||
ajouté à AGENTS.md §5bis (sauvegarde AGENTS.md.bak-concierge-*). pg installé dans le
|
||||
workspace.
|
||||
ACTIVATION PAR ÉTAPES (toutes décisions du propriétaire) :
|
||||
(1) `openclaw cron enable d113b997-…` → boucle Q/R en réel, le helper se publie sur une
|
||||
vraie réponse. La BASE n'est pas touchée.
|
||||
(2) EVCS déploie son lecteur en DRY-RUN (redémarre son service) : il journalise ce qu'il
|
||||
écrirait (jointure ±1 s sur les clefs verbatim, extension au groupe) SANS écrire.
|
||||
Valide la moitié EVCS avant que ça compte.
|
||||
(3) kill-switch `input_boolean.airtria_concierge_enabled` → ON + EVCS commit la bascule
|
||||
→ les étiquettes corrigent session_charge.
|
||||
⚠️ La bascule vehicule_sur n'est PAS neutralisée par le kill-switch : le kill-switch gate
|
||||
l'APPLICATION d'un label, pas le DEFAULT de la colonne. Passer DEFAULT false trop tôt
|
||||
afficherait 100 % de sessions « douteuses » (motif faux) sur le Grafana « Les véhicules ».
|
||||
Donc la bascule = TROIS pièces soudées au commit du ON : DEFAULT false + true-sur-
|
||||
confirmation-humaine + libellé Grafana « selecteur change » → honnête.
|
||||
Décision COMMAND vs agentTurn : command choisi car déterministe (pas de LLM, sortie exacte,
|
||||
suppression auto sur vide). Cron existants modèles : daily-email-report (Gmail 8h30),
|
||||
orange-cloture (17h40).
|
||||
|
||||
|
||||
Objectif : combler le seul trou qu'aucun capteur ne comble — savoir QUEL véhicule
|
||||
(VW / JEEP / invité / bientôt moto ZERO) est branché sur la borne EVCS, pour que
|
||||
`session_charge` cesse d'attribuer l'énergie au mauvais véhicule.
|
||||
|
||||
Contexte déclencheur : le sélecteur `input_select.victron_evcs_vehicle` est resté
|
||||
figé sur « VW » du 07/09 au 13/09 → 11 sessions d'affilée mal étiquetées, sans
|
||||
aucun signal. VW et JEEP sont électriquement indiscernables l'une de l'autre
|
||||
(~3 680 vs ~3 655 W à 20 A).
|
||||
|
||||
## Principe d'architecture (non négociable)
|
||||
- Le cœur déterministe EVCS reste seul juge. Airtria intervient APRÈS, jamais
|
||||
dans la boucle 30 s.
|
||||
- Une seule main sur chaque variable. Airtria ne touche AUCUN actionneur.
|
||||
- Toute valeur porte son propre horodatage (`confirmed_at`), pas celui du run.
|
||||
- Producteur qui s'arrête : invalidation STRUCTURELLE (le label est lié à une
|
||||
session précise, il ne peut pas contaminer une autre session). On NE vide PAS
|
||||
le helper (fenêtre de perte inutile) ; on laisse la dernière confirmation.
|
||||
|
||||
## Décisions du propriétaire (2026-09-13)
|
||||
- Lancement v0 + v1 ENSEMBLE.
|
||||
- Canal de correction = écriture HA restreinte `input_text.airtria_*`
|
||||
(variante PostgreSQL INSERT-only écartée).
|
||||
|
||||
## Détection d'une session (v0)
|
||||
- Source = table PostgreSQL `session_charge`, PAS le binary_sensor HA (il flappe :
|
||||
disconnected x2 câble branché le 07/09).
|
||||
- ⚠️ NE PAS utiliser `evcs_lecture` : il a INSERT sur `evenement` (registre
|
||||
d'audit partagé) — inadapté à un agent. Rôle DÉDIÉ `airtria_ro`, LOGIN +
|
||||
SELECT sur `session_charge` et `evenement` uniquement, aucune écriture.
|
||||
Mot de passe déposé protégé dans le workspace (.pg-airtria). Base evcs sur
|
||||
10.10.10.22:5433.
|
||||
- Colonnes utiles (session_charge, 17 col) : `debut` (clef, MS), `fin`,
|
||||
`terminee`, `vehicule`, `vehicule_sur` (DEFAULT true → à basculer côté service),
|
||||
`p_max_w` + `consigne_max_a` + `courant_max_a` (règle invité), `debranche_a`
|
||||
(frontière de groupe), `energie_compteur_kwh` (l'énergie qui FAIT FOI, pas
|
||||
`energie_kwh` qui rate si le service démarre après le début de charge).
|
||||
- On hérite du débounce du service d'historisation : une ligne `session_charge`
|
||||
ne s'ouvre qu'au-delà de 300 W soutenus, se ferme après 10 min sous le seuil.
|
||||
- Cadence : cron 10 min. La latence n'a aucun effet (session de plusieurs heures).
|
||||
- Airtria garde son propre état (dernière `debut` traitée) dans son workspace.
|
||||
|
||||
## Test d'ambiguïté (quand poser la question) — RÈGLE FINALE
|
||||
- Le sélecteur est FRAIS (ne pas demander) SEULEMENT s'il a changé de valeur dans
|
||||
la fenêtre [début du branchement − 10 min, début + 30 min]. Sinon → DEMANDER.
|
||||
Source : `evenement` (genre='intervention', entite=input_select.victron_evcs_vehicle),
|
||||
JAMAIS le `last_changed` HA. Implémenté : `selectorFreshAt()` dans concierge-live.js.
|
||||
- Pourquoi la fenêtre serrée (EVCS, données) : un changement de sélecteur EN COURS de
|
||||
branchement ne peut pas étiqueter ce branchement (câble non bougé) — c'est le
|
||||
producteur mécanique des groupes mixtes. Ma 1ère règle (« bougé depuis le débranchement
|
||||
précédent ») déclarait « frais » EXACTEMENT les 2 branchements mixtes de l'historique.
|
||||
- Validé sur 11 branchements 04-12/09 : 1 seul FRAIS (05/09, réglé à +13 min), 10 à
|
||||
DEMANDER dont les 2 mixtes. Stat marquante : sélecteur réglé au bon moment 1 fois / 12.
|
||||
- Mauvais test (rejeté) : « sélecteur inchangé depuis le début de CETTE session »
|
||||
— vrai aussi quand le propriétaire a bien réglé au branchement.
|
||||
- Avant de conclure « pas de geste humain », vérifier la table `evenement`
|
||||
(un redémarrage HA a pu changer la valeur ; l'auteur y est tracé depuis le 12/09).
|
||||
- ✅ CAVEAT `last_changed` (2026-09-13) CORRIGÉ : le `last_changed` HA est remis à zéro
|
||||
par un passage `unavailable` (blip MQTT) sans vrai changement → il ferait croire
|
||||
« frais » un sélecteur périmé. La règle ci-dessus lit donc `evenement` (qui saute les
|
||||
lectures unavailable), pas `last_changed`. Limites de la source : le journal commence
|
||||
le 04/09 ; la mémoire du détecteur est en-processus (un changement pendant un
|
||||
redémarrage du service n'est pas rattrapé) → l'absence d'événement ne prouve pas tout,
|
||||
mais dans le doute la règle DEMANDE = le bon côté.
|
||||
- En cas de doute : poser la question. Un WhatsApp de trop coûte moins qu'une
|
||||
ligne fausse pour toujours.
|
||||
|
||||
## Pré-filtre puissance (réduit les questions) — règle À SENS UNIQUE
|
||||
- Le bon critère n'est pas la puissance brute mais la puissance RELATIVE À LA
|
||||
CONSIGNE. VW/JEEP saturent vers 16 A embarqués quelle que soit la consigne
|
||||
(~3 606 à 3 784 W à 20 A) ; la Megane a suivi jusqu'à 28 A (6 360 W).
|
||||
- Règle conclusive : `consigne_max_a >= 20` ET `p_max_w > 4200` ⟹ **invité**,
|
||||
Airtria pré-étiquette « invité » SANS déranger le propriétaire.
|
||||
- `consigne_max_a < 20` ⟹ AUCUNE information (6 A plafonne ~1 700 W pour tout le
|
||||
monde) → demander.
|
||||
- ⚠️ La réciproque est FAUSSE : `p_max_w <= 4200` ne prouve PAS que c'est un
|
||||
véhicule du foyer (un invité à 16 A embarqués est indiscernable de la VW).
|
||||
Au-dessus du seuil on sait, en dessous on demande. NE JAMAIS inverser.
|
||||
- La consigne vient du contrôleur EVCS : plafond 20 A hors BOOST
|
||||
(`input_number.evcs_plafond_courant_edf_on`, 28 en BOOST). Le cas conclusif
|
||||
est donc fréquent ; le cas muet = sessions à courant réduit (fin de charge
|
||||
solaire à 6 A).
|
||||
- Moto ZERO (fin sept. 2026) : tombera sous 3,6 kW → côté foyer, distinguée par
|
||||
le sélecteur comme les autres. Seuil à réévaluer à son arrivée.
|
||||
|
||||
## Regroupement = frontière FACTUELLE via `evenement` (pas le temps)
|
||||
- ⚠️ Ne PAS regrouper par écart de temps : le creux soir→heures creuses fait
|
||||
~5 h 20 (fin solaire ~20:00 → HC ~01:24), indistinguable d'un vrai
|
||||
débranchement/rebranchement par un seuil de durée.
|
||||
- La frontière est un FAIT : les transitions de
|
||||
`binary_sensor.victron_evcs_vehicle_connected` sont écrites dans `evenement`
|
||||
(off→on au branchement, on→off au débranchement). Un branchement = intervalle
|
||||
[off→on … on→off suivant]. Toutes les lignes `session_charge` dont `debut`
|
||||
tombe dans l'intervalle = même véhicule = UNE seule question. Intervalle ouvert
|
||||
(encore branché) = groupe en cours.
|
||||
- Exemple semaine 08-12/09 : 6 branchements = 2-3-2-1-1-1 sessions
|
||||
(le mercredi 15:55+19:35+01:25 est un TRIPLET, même câble). Un regroupement qui
|
||||
rend des paires soir/nuit est FAUX.
|
||||
- Le groupe reste la notion d'Airtria (propriétaire unique sur la notion dérivée) ;
|
||||
le service EVCS ne fait qu'itérer la liste du helper.
|
||||
- ⚠️ Ne PAS émettre une écriture helper par session (elles écraseraient le même
|
||||
helper ; le service qui poll toutes les 60 s ne verrait que la dernière → perte
|
||||
silencieuse des autres). Le helper porte donc une LISTE de session_debut (voir
|
||||
ci-dessous), publiée en UNE écriture atomique. Le service itère la liste.
|
||||
- Fallback coupure : les transitions ne sont écrites que si le service tourne.
|
||||
Frontière absente/incomplète → groupe INCERTAIN → poser la question PAR session,
|
||||
jamais sur une frontière reconstruite. Un WhatsApp de trop > une étiquette fausse.
|
||||
|
||||
## Sources de données (requêtes, lecture seule via airtria_ro)
|
||||
- Frontière des branchements — table `evenement` (colonnes : ts, genre, source,
|
||||
resume, detail jsonb, id). Filtre robuste (pas de parsing de texte) :
|
||||
`detail->>'entite' = 'binary_sensor.victron_evcs_vehicle_connected'`,
|
||||
`detail->>'apres'` = 'on' (branchement) / 'off' (débranchement). Ordonner sur
|
||||
`ts`, borner les intervalles sur `detail->>'horodatage_ha'` (vérité physique
|
||||
après rattrapage). Le champ `entite` n'existe que sur les transitions → les
|
||||
fins de session sont exclues naturellement.
|
||||
- Bonus qualité : les événements de fin de session portent `detail->>'debut'`
|
||||
(= session_charge.debut exact, ms comprises) et les trois énergies côte à côte
|
||||
(`kwh_compteur` fait foi, `kwh_integre`, `kwh_borne`). Un écart entre elles est
|
||||
un signal de qualité à remonter (pas une question au propriétaire).
|
||||
- Accès : Node + node-postgres (pur JS, à ajouter au workspace) ; PG:5433
|
||||
joignable depuis la machine spark → calibration exécutable en lecture seule.
|
||||
- Implémentation : `concierge-detection.js` (mode calibration, aucune écriture).
|
||||
|
||||
## La question WhatsApp
|
||||
- Un seul message, au démarrage d'une session ambiguë (ou d'un groupe).
|
||||
- Ouverte, pas binaire : « Quel véhicule est branché ? » avec options lues
|
||||
dynamiquement dans le sélecteur + « aucune des deux / invité ».
|
||||
- Porte la clef `debut` de la session pour permettre l'application a posteriori.
|
||||
- Réponse possible des heures plus tard, ou jamais.
|
||||
|
||||
## Le helper HA (le contrat v1)
|
||||
- `input_text.airtria_vehicle_label`, déclaré **max: 255** (défaut HA = 100 →
|
||||
rejet silencieux ; incident vécu forecast_updated max:32 le 08/09).
|
||||
- Contenu = UN JSON atomique portant une LISTE de sessions (atomicité
|
||||
indispensable, HA n'a pas de transaction entre entités) :
|
||||
{"sessions":["2026-09-08 19:39:12.334+02","2026-09-09 01:26:04.887+02"],
|
||||
"label":"VW","confirmed_at":"<horodatage Airtria>","source":"whatsapp"}
|
||||
- Chaque entrée de `sessions` = clef `debut` rendue par la requête SANS
|
||||
reformatage (les `debut` portent les ms, ex. 2026-09-12 11:55:54.493+02 ; un
|
||||
arrondi minute ne matcherait jamais ; pas d'aller-retour par un objet Date).
|
||||
Le service EVCS joint sur ±1 s, pour chaque entrée de la liste.
|
||||
- Taille : 2 sessions ≈ 150 car, 3 ≈ 180, sous 255. Au-delà, publier en deux
|
||||
écritures espacées de > 90 s (le service aura polé entre les deux).
|
||||
- Après CHAQUE écriture : relire l'état et vérifier qu'il est identique à l'envoi
|
||||
(seul moyen de détecter un rejet silencieux HA).
|
||||
- Auto-correction (« JEEP… non VW ») = republier avec `confirmed_at` plus récent ;
|
||||
le service applique le dernier pour une `session_debut` donnée.
|
||||
- On NE vide PAS le helper à {} : l'invalidation est structurelle (session_debut).
|
||||
Le vidage ouvrirait une fenêtre de perte si le service est arrêté entre publication
|
||||
et vidage.
|
||||
|
||||
## Garde-fous d'écriture (non négociables — restriction CONVENTIONNELLE)
|
||||
Le canal choisi est un token HA, qui peut techniquement écrire n'importe quel
|
||||
service HA : la restriction à airtria_* ne tient QUE par la discipline du code.
|
||||
Donc, côté OpenClaw :
|
||||
- Le nom de l'entité écrite est une CONSTANTE EN DUR unique
|
||||
(`input_text.airtria_vehicle_label`), jamais construite dynamiquement, jamais
|
||||
interpolée.
|
||||
- Un test type « mode ombre » ÉCHOUE si une écriture cible autre chose que cette
|
||||
constante (équivalent du tests/advisor/run.mjs d'EVCS qui garde advisor_*).
|
||||
- Aucune écriture d'actionneur, jamais (liste des variables à propriétaire unique
|
||||
dans le MANUEL EVCS ; `carte-decisions.mjs` / `enquete.mjs` avant tout doute).
|
||||
|
||||
## Recoupement a posteriori (qualité de données)
|
||||
- Comparer `session_charge.debranche_a` aux départs attendus :
|
||||
VW 08:50 (`input_datetime.victron_evcs_departure_time`),
|
||||
JEEP 06:30 (`input_datetime.victron_evcs_departure_time_jeep`).
|
||||
- `debranche_a` n'est renseignée que si le service voit le débranchement (1 fois
|
||||
sur 12 récemment) → ne pas supposer qu'elle est toujours là.
|
||||
- Ne signaler que si ça CONTREDIT le label (comportement idiot), pas à chaque
|
||||
session. Pas de notification pour l'état normal.
|
||||
|
||||
## Côté service EVCS (v1, fait par la session EVCS)
|
||||
- Lit le helper chaque cycle (60 s), JSON.parse en try/catch ; objet vide /
|
||||
illisible / champs manquants → ne rien faire.
|
||||
- `confirmed_at` antérieur au `debut` → ignoré. Jointure ±1 s sur session_debut.
|
||||
- Met à jour `vehicule` ; `vehicule_sur` passe à VRAI uniquement sur confirmation
|
||||
humaine (changement de sens : défaut basculé à faux ; lignes antérieures gardent
|
||||
l'ancienne sémantique). Trace l'application dans `evenement` avec la source.
|
||||
|
||||
## Validation avant mise en service
|
||||
- `tools/campagne-conseiller.mjs` (dépôt EVCS) rejoue des instants réels ;
|
||||
cumuler 3 runs (un run de 5 passages = bruit, écart-type ~0,22).
|
||||
- Jeu de calibrage = les 12 sessions du 07 au 12/09, étiquetées par le
|
||||
propriétaire (voir ci-dessous). ⚠️ Le v0 ne DÉDUIT pas VW vs JEEP (impossible :
|
||||
la semaine, la routine était inversée — femme/JEEP en congés, fils/VW en stage
|
||||
sans retour le midi). Ce qui est testé = le ROUTAGE : MEGANE auto-détectée
|
||||
invité par la règle puissance ; toutes les autres correctement routées vers
|
||||
« demander » (sélecteur périmé). Ce sont les réponses du propriétaire qui
|
||||
donnent VW/JEEP. Si le routage rate une des 12, revoir la règle, pas l'étiquette.
|
||||
|
||||
### Cibles de routage du v0 (les 6 branchements du 08-12/09)
|
||||
- 11/09 16:53→18:27 (Megane, 1 session) : auto-invité par la règle puissance,
|
||||
AUCUNE question.
|
||||
- 5 autres branchements (2-3-2-1-1) : routés vers la question (sélecteur périmé).
|
||||
- Total attendu = 5 questions pour 12 sessions. Un regroupement qui rend des
|
||||
paires soir/nuit (au lieu du triplet du mercredi) est FAUX.
|
||||
|
||||
### Jeu de calibrage FINAL (corrigé en base, arbitré par le propriétaire 2026-09-13)
|
||||
```
|
||||
branchement sessions étiquette
|
||||
sam 12/09 11:55 → (en cours) 11:55 5,94 JEEP
|
||||
sam 12/09 11:42 → 11:53 11:42 0,09 JEEP
|
||||
ven 11/09 16:53 → 18:27 16:54 7,45 6360W/28A MEGANE (auto)
|
||||
jeu 10/09 18:19 → ven 16:27 18:20 2,97 · 01:24 4,26 VW
|
||||
mer 09/09 15:55 → jeu 09:05 15:55 6,61 · 19:35 0,77 · 01:25 4,88 JEEP (triplet)
|
||||
mar 08/09 19:39 → mer 08:57 19:39 0,76 · 01:26 5,01 VW
|
||||
```
|
||||
Semaine : 5 VW, 6 JEEP, 1 MEGANE (contre 11 VW + 1 JEEP avant correction).
|
||||
Le triplet mercredi = JEEP les trois : la femme a branché la JEEP, le fils n'a pas
|
||||
voulu la débrancher le soir pour mettre la VW → il a roulé à l'essence le jeudi.
|
||||
Contexte humain qu'aucune règle ne produit — la raison d'être du concierge.
|
||||
MEGANE = voiture de service (Rennes) + dépannage d'amis → « invité » RÉCURRENT.
|
||||
|
||||
### Cas tordus que le module DOIT gérer (dans les données de la semaine)
|
||||
- sam 12/09 : on 11:42, off 11:53, on 11:55 = DEUX branchements (2 min de
|
||||
débranchement), pas du flapping. La session 0,09 kWh en est la trace.
|
||||
- ven 11/09 : off 16:27, on 16:53 = DEUX branchements (26 min ; débranche un
|
||||
véhicule foyer pour brancher la Megane).
|
||||
- ⚠️ AUCUN seuil de durée pour fusionner : 2 min et 26 min sont l'une et l'autre
|
||||
de vraies séparations. Règle = « toute transition compte » ; défensive limitée
|
||||
aux incohérences STRUCTURELLES (on déjà ouvert, off sans ouverture).
|
||||
- Le branchement 12/09 11:55 est ENCORE OUVERT (borne haute nulle) → groupe en
|
||||
cours, ne pas conclure qu'il est incomplet.
|
||||
|
||||
### Fenêtre d'analyse : commencer sur un 'on'
|
||||
Une fenêtre doit démarrer sur un `on`, jamais au milieu d'un branchement : sinon
|
||||
le premier événement est un `off` orphelin (branchement ouvert avant la fenêtre),
|
||||
la session ouverte avant tombe hors intervalle et est classée « incertaine » à
|
||||
tort (ex. démarrer au 08/09 00:00 fait apparaître 6 questions au lieu de 5, à
|
||||
cause de la session 08/09 01:26). En production, aucun souci (on suit le flux) ;
|
||||
règle valable surtout au rejeu historique. Calibration calée sur 08/09 19:00.
|
||||
|
||||
### Invariant à vérifier EN PERMANENCE (le contrôle le plus solide du projet)
|
||||
Un seul câble ⟹ toutes les sessions d'un même branchement portent la MÊME
|
||||
étiquette. Un groupe à étiquettes mixtes = frontière fausse (Airtria) OU ligne
|
||||
fausse (base) — les deux méritent une alerte. Propriété PHYSIQUE : ne nécessite
|
||||
aucune connaissance métier (ni routine, ni horaire, ni puissance), et aucune
|
||||
évolution de règle ne peut l'invalider. À faire tourner en continu, pas seulement
|
||||
à la calibration — c'est ce qui aurait signalé le triplet incohérent le jour même.
|
||||
|
||||
## Points de vigilance
|
||||
- ⚠️ Inventaire BMS mensonger (découvert 2026-09-13) : le BMS annonce 4 modules /
|
||||
400 Ah alors que le parc en compte 9 (le LV-Hub ne remonte que le groupe maître).
|
||||
Mesuré : 0,312 kWh/point de SOC (vs 0,169 sur l'ancien parc de 7). NE JAMAIS
|
||||
utiliser `capacite_installee` ni `nombre_de_modules_en_ligne` comme source de
|
||||
capacité (sous-estiment de ~38 %). Source qui fait foi :
|
||||
`input_number.evcs_balance_modules_en_service` (13 aujourd'hui). Concerne surtout
|
||||
un futur rôle 2 (rétro-évaluation énergétique), pas le concierge véhicule.
|
||||
- Helper HA en place : `input_text.airtria_vehicle_label` (max:255, état initial
|
||||
`unknown` = « rien à appliquer »). Kill-switch consommateur
|
||||
`input_boolean.airtria_concierge_enabled` (OFF) = propriété d'EVCS, Airtria n'y
|
||||
touche pas. Aller-retour écriture/relecture validé le 2026-09-13.
|
||||
- SPOF : OpenClaw vit sur 10.10.10.17, la machine qui porte aussi HA et n8n.
|
||||
Faible enjeu pour l'ID véhicule, mais réel. Chien de garde sur 10.10.10.22.
|
||||
- Prérequis à câbler : (1) accès lecture PostgreSQL pour Airtria
|
||||
(`PG_LECTURE_PASSWORD`, déposé protégé comme le token HA) ; (2) création du
|
||||
helper `input_text.airtria_vehicle_label` (max:255) dans le paquet HA.
|
||||
Reference in New Issue
Block a user