airtria: garde-fous de lecture (anti-substitution, last_changed) + ref. entites energie

Suite a une reponse WhatsApp erronee du 16/09 (question SOC VW : grandeur absente, puis
grandeurs voisines servies comme reponse - 5 faux sur 6, dont un compteur fige depuis 8 h
cite comme "cette nuit", et une batterie dite qui "monte" alors qu'elle descendait).
Corrections de domaine fournies par EVCS.

- AGENTS.md 1.7 : ne JAMAIS substituer une grandeur absente (le dire et s'arreter) ;
  verifier last_changed avant de citer une valeur comme actuelle ; publier une grandeur
  directionnelle avec le SIGNE de sa puissance.
- AGENTS.md 5ter : reference "ou lire quoi" (SOC maison + sens via battery_power,
  source_mode, cible de nuit vivante, compteurs gx_device_* empoisonnes a ne pas lire
  comme des deltas ; SOC voiture = n'existe pas).
- airtria-charge-session.js : lecteur SQL lecture seule (role airtria_ro, reutilise
  makeClient) de la session de charge en cours, pour l'energie reellement prise par la
  voiture (dont reseau) sans lire un compteur HA fige.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZsbhyGmRwZogdoBwT6C6y
This commit is contained in:
2026-09-16 03:40:26 +02:00
parent 6da250914d
commit f09e2efd6e
3 changed files with 79 additions and 0 deletions

View File

@@ -60,6 +60,22 @@ Pour vérifier qu'un PNG est correct, utiliser **`du -b`** (taille en octets) vi
Ne JAMAIS utiliser `view_image` ni `read` sur un `.png` : c'est lent, coûteux en contexte,
et inutile. Un PNG de graphe valide fait > 5000 octets.
### 1.7 Mesures : ne JAMAIS substituer une grandeur absente, vérifier `last_changed`
Pour toute question chiffrée (énergie, SOC, puissance, prix). Ces règles ont manqué le
16/09 (« SOC de la VW ? » : la grandeur n'existe pas ; Airtria l'a dit, puis a servi les
grandeurs voisines comme si elles répondaient → 5 affirmations fausses sur 6).
- **Ne jamais SUBSTITUER.** Grandeur demandée absente ou illisible → le dire et S'ARRÊTER.
Une grandeur voisine ne se propose qu'EXPLICITEMENT étiquetée comme autre chose
(« je n'ai pas X ; j'ai Y, qui est différent »), jamais comme la réponse. Le remplacement
silencieux est pire qu'un « je ne sais pas » : il change une surveillance en fausse
réassurance.
- **Vérifier `last_changed`** (donné par `/api/states/<entité>`) avant de citer une valeur
comme ACTUELLE : figée depuis des heures ≠ « maintenant » / « cette nuit ».
- **Grandeur directionnelle** (une batterie monte/descend) : ne jamais publier la valeur
sans le SIGNE de la puissance associée, sinon on affirme « ça monte » quand ça descend.
---
## 2. Démarrage de session et mémoire
@@ -308,6 +324,34 @@ en attente), lance via `exec` :
Ne jamais deviner ni inventer un véhicule : seul le script publie, et uniquement sur une
réponse reconnue.
## 5ter. Questions énergie / batterie / véhicule — OÙ LIRE QUOI
Référence EVCS (propriétaire du système EVCS), 16/09. Utiliser CES entités et appliquer
§1.7 (ne pas substituer, vérifier `last_changed`).
- **SOC de la voiture (VW/JEEP)** : N'EXISTE PAS (aucun capteur véhicule). Le dire et
s'arrêter — seule source = le propriétaire (objet de la question « km restants » au
branchement). Ne jamais le déduire d'autre chose.
- **SOC batterie maison, AVEC SON SENS** : `sensor.victron_battery_soc` (valeur) ET
`sensor.victron_battery_power` (signe : négatif = elle DONNE/décharge, positif =
PREND/charge). Ne jamais publier l'un sans l'autre — l'oubli a produit le faux « et ça
monte » alors qu'elle descendait.
- **Ce que fait le système MAINTENANT** : `input_text.victron_evcs_source_mode` (ex.
« Assistance batterie nuit » = voiture alimentée par le solaire stocké la veille, EDF
ouvert). Ne pas confondre un PROFIL / une limite (ce qui est PERMIS : `night_slow`,
DVCC 30 A) avec l'ACTIVITÉ (ce qui se passe).
- **Cible SOC de la nuit** : `input_number.victron_evcs_night_target_soc` (valeur VIVANTE
qui DÉCROÎT au fil de la nuit ; jamais « 100 % », jamais un nombre en dur).
- **Énergie prise par la voiture, session en cours** : via `exec`,
`node /home/node/.openclaw/workspace/airtria-charge-session.js` (SQL lecture seule). Il
imprime véhicule, kWh de la session, dont réseau, heure de début, mode. `dont_reseau = 0`
= la voiture ne coûte rien au réseau. S'il échoue : le dire, ne pas substituer.
- **COMPTEURS EMPOISONNÉS — ne JAMAIS lire comme un delta** : `sensor.gx_device_pv_energy`,
`…_dc_battery_charge_energy`, `…_dc_battery_discharge_energy` (`state_class: total` sans
`last_reset` : ils se figent et sautent de ±centaines de kWh — le 16/09 l'un affichait
12,11 kWh, figé depuis 8 h). Cumul sain seulement : les `*_total_yield` des chargeurs
solaires et `sensor.evcs_hq2239dnt7p_total_energy` (borne, monotone).
---
## 6. Workspace et outils système

View File

@@ -0,0 +1,33 @@
// airtria-charge-session.js — lecture seule : la session de charge EN COURS.
// -----------------------------------------------------------------------------
// Réutilise makeClient de concierge-detection.js (rôle airtria_ro, résolution du mot de
// passe, PG_BASE) : aucun autre droit, un seul SELECT. Airtria l'appelle via `exec` pour
// répondre « combien la voiture a pris cette nuit » sans SUBSTITUER un compteur HA figé
// (cf. AGENTS.md §1.7 et §5ter). En cas d'échec : le dire, ne rien inventer.
// SQL fourni par EVCS (16/09) ; `dont_reseau = 0` = la voiture ne coûte rien au réseau.
// -----------------------------------------------------------------------------
'use strict';
const { makeClient } = require('./concierge-detection.js');
const SQL = `
SELECT vehicule,
round(energie_kwh::numeric, 2) AS kwh_session,
round(energie_reseau_kwh::numeric, 2) AS dont_reseau,
to_char(debut, 'HH24:MI') AS depuis,
source_mode
FROM session_charge
WHERE NOT terminee
ORDER BY debut DESC
LIMIT 1`;
(async () => {
const c = makeClient();
await c.connect();
try {
const r = await c.query(SQL);
if (!r.rows.length) { console.log('Aucune session de charge en cours.'); return; }
const s = r.rows[0];
console.log(`${s.vehicule} : ${s.kwh_session} kWh pris cette session ` +
`(dont ${s.dont_reseau} kWh du réseau), depuis ${s.depuis}. Mode : ${s.source_mode}.`);
} finally { await c.end(); }
})().catch(e => { console.error('[charge-session]', e.message); process.exit(1); });