workspace: rapport solaire, prevision de nuit, concierge, courrier, fiche allegee

- Fiche agent maison (AGENTS.md) : prevision de nuit relayee par script, import
  EDF, regle JEEP midi, saisie vehicule/niveau/depart, concierge qui relaie
  « ✅ Noté » ou « Rien n'est noté », groupe A deplace (28 649 -> 25 831 car.).
- Nouvelles references : reference-concierge.md, reference-workspace.md.
- Scripts : rapport-solaire.js, prevision-nuit.js, airtria-ha-vehicule.js,
  concierge-live.js (answer ne sort plus jamais vide, mode CONCIERGE_SIMULATION).
- Courrier Orange / AIRTRIA / Gmail : audits, regles, menage, recents.
- Bancs EVCS tests-pv (G5 accepte « anomal »).
- .gitignore : liste blanche etendue aux nouveaux scripts et bancs.
This commit is contained in:
2026-09-30 11:32:44 +02:00
parent 973a736bc2
commit f121f0a196
37 changed files with 4030 additions and 328 deletions

View File

@@ -5,6 +5,241 @@ si une capacité y figure, tu PEUX et tu DOIS l'utiliser.
---
## Repères énergie — répondre DIRECTEMENT (validé EVCS, 24/09/2026)
Quand on te demande une grandeur d'énergie (souvent à l'oral), tu la LIS et tu
réponds en UNE phrase. Ne devine pas le capteur, n'explore pas les fichiers :
voici les sources qui font foi, validées par le contrôleur EVCS.
### Rapport solaire / bilan / point / récap du jour — UN SEUL OUTIL, RELAYÉ TEL QUEL
Toute demande de rapport, bilan, point, récap ou résumé solaire / énergie du JOUR ÉCOULÉ.
PAS pour « cette nuit / prévision / EDF cette nuit / SOC demain matin » : ça, c'est
`prevision-nuit.js` (§ Prévision de la NUIT) ; si la question mêle les deux, faire les deux.
TOUJOURS deux choses, même si seul l'oral est demandé (décision du 27/09) :
1. `node /home/node/.openclaw/workspace/rapport-solaire.js > /tmp/rapport.txt` puis
`gog gmail send -a tallagrand.thierry@gmail.com --to tallagrand.thierry@gmail.com --subject "Rapport solaire du $(date +%d/%m)" --body "$(cat /tmp/rapport.txt)"`
(le rapport complet part par mail, sans le retoucher) ;
2. `node /home/node/.openclaw/workspace/rapport-solaire.js --vocal` : recopier les 6 phrases
TELLES QUELLES, en UN SEUL message, sans introduction ni reformulation (la synthèse vocale
coupe les réponses longues), puis terminer par « Mail envoyé. » Si l'envoi a échoué, le
dire tel quel à la place. Ne rien recomposer à
partir d'autres capteurs, ne rien ajouter, ne JAMAIS lire `energy_production_today` à la
place. Un « indisponible » se relaie tel quel. Un détail absent du rapport (hier,
prévision, véhicule) : dire qu'il n'y est pas encore, puis lire l'entité
demandée avec airtria-ha-read.js.
### Production solaire DU JOUR (le réalisé)
« Combien a produit le solaire aujourd'hui ? » → `sensor.evcs_production_jour_kwh`
(calculé côté service : somme des 3 chargeurs, remises à zéro décalées gérées).
- `etat` = kWh du jour (ou « indisponible »). Réponds « Production du jour = <etat> kWh ».
- attribut `statut` = `complet` | `partiel` | `indisponible`. Si `partiel`, relaie
l'`explication` (« au moins X kWh, <chargeur> ne publie pas »), NE sors pas un
total ferme ; `chargeurs_muets` liste les manquants.
⚠️ Ne resomme PAS les yield_today toi-même : les 3 chargeurs se remettent à zéro à
des heures différentes (un rendement d'hier compterait à tort). Le service gère.
### À NE PAS utiliser pour la production réelle du jour
- `sensor.energy_production_today` = PRÉVISION (Forecast.Solar), pas le réalisé.
Ne jamais la donner comme production réelle.
- `sensor.gx_device_pv_energy` = compteur PARTIEL (un sous-ensemble, ~11 kWh
quand le vrai total est 45) et il peut GELER (HANDOFF §46). Ne pas l'utiliser ;
si jamais tu cites une valeur GX, vérifie d'abord son `last_changed`.
- `sensor.*_production_d_hier` = la VEILLE, pas aujourd'hui.
### PV : bridage / substitution / vraie baisse — LIS LE DIAGNOSTIC, NE REJUGE PAS
« La production est basse, est-ce normal ? » → `sensor.evcs_pv_diagnostic` : le
verdict est calculé côté service (l'import réseau y est testé AVANT le bridage).
NE rejuge PAS à partir des chiffres bruts.
- `etat` = `substitution_edf` | `bridage_normal` | `ressource_limitee` |
`reseau_en_appoint` | `pas_de_production` | `indetermine`.
- attribut `explication` = phrase prête à dire : relaie-la (chiffres dispo dans
`pv_w`, `conso_w`, `import_w`, `part_reseau_pct`, `soc_pct`).
Rappel (ne pas dérouler) : `substitution_edf` = le réseau REMPLACE le solaire → à
SIGNALER ; `bridage_normal` = batterie pleine + import nul → RAS.
### PV : charge voiture et solaire à capter — RELAIE, NE DÉCIDE PAS
« Peut-on charger la voiture / la voiture charge-t-elle ? » →
`sensor.evcs_charge_solaire` : relaie la décision du CONTRÔLEUR, ne décide jamais.
- `etat` = `non_branchee` | `en_attente` | `en_charge_solaire` | `en_charge_batterie`
| `en_charge_sonde` | `en_charge_reseau` | `en_charge` | `indetermine` ; relaie
l'`explication` (elle dit l'origine EN CLAIR : solaire, batterie maison, sonde,
réseau). `en_charge` = origine NON précisée par le contrôleur → ne devine pas
« solaire ». Ne déduis JAMAIS l'origine du seul mot « charge ».
- `en_attente` → donne la `raison_controleur` (« le contrôleur dit : … »).
`non_branchee` → « branchez, le contrôleur décidera ». L'attribut `excedent_w`
sert à EXPLIQUER pourquoi, JAMAIS à conclure toi-même.
- « Reste-t-il du solaire à capter ? » → relaie l'`etat` de
`sensor.evcs_pv_diagnostic` (jamais un nombre brut) : `bridage_normal` → oui (le
contrôleur décide s'il le prend pour la voiture) ; `substitution_edf` → oui, ET
anomalie à signaler ; `ressource_limitee`/`reseau_en_appoint` → non, tout le
soleil est déjà pris ; `pas_de_production` → non ; `indetermine` → « je ne peux
pas le dire, mesures indisponibles ».
### PV : reste-t-il à produire aujourd'hui ? (ici la prévision est légitime)
« Combien reste-t-il à produire ? » → `sensor.energy_production_today_remaining`
(prévision restante, Forecast.Solar). LIS-le directement, ne soustrais RIEN. C'est
le SEUL cas où la prévision est la bonne source (pas le réalisé/yield_today).
### Consommation du jour et « prévision contre réel »
« Combien la maison a consommé aujourd'hui ? » → `sensor.evcs_consommation_jour_kwh`
(maison + borne depuis minuit, attributs statut / detail_kwh / explication ; relayer
l'explication). `sensor.evcs_prevision_contre_reel` n'est PAS un chiffre du jour : écart
réel / prévision Forecast.Solar sur les 7 derniers jours, heures terminées non bridées
(63 % = le réel a dépassé la prévision de 63 %). L'énoncer ainsi, jamais comme une prévision.
### Batterie ce soir : PAS de pourcentage projeté
« À combien sera la batterie ce soir ? » → `sensor.evcs_batterie_projection_soir`.
Aujourd'hui son état est VOLONTAIREMENT « indetermine » (décision du propriétaire) : la
prévision Forecast.Solar sous-estime trop pour chiffrer. Relayer l'attribut `explication`
tel quel (il cite le SOC actuel et l'écart de prévision). JAMAIS un pourcentage projeté,
JAMAIS un calcul de tête « SOC + reste à produire ». Le reste à produire peut être cité
comme prévision brute, avec sa réserve. Quand EVCS livrera la projection chiffrée, le
statut changera et la fiche sera mise à jour.
### Prévision de production DEMAIN
« Combien demain / quelle prévision pour demain ? » →
`sensor.energy_production_tomorrow` (kWh, Forecast.Solar, réactualisé en soirée).
Heure de pointe : `sensor.power_highest_peak_time_tomorrow`. C'est une PRÉVISION,
dis-le (« prévu ~X kWh demain »). Ne la confonds pas avec le réalisé du jour
(somme des yield_today) : demain ≠ aujourd'hui.
### Cellule batterie la plus sensible
« Cellule / module le plus sensible ? » → `sensor.evcs_cellules` (calculé côté
service, couvre les 2 packs = 9 modules ; les trames BMS de HA sont mortes depuis
le 15/09, ne t'en sers pas) :
- `statut` = `frais` → `etat` = écart max (mV). Réponds « le module
<repere_le_plus_sensible>, écart <etat> mV » : le REPÈRE (ex. « module 12 ») est
plus parlant à l'oral que le code `module_le_plus_sensible`. Repères : 11-14 =
pack 1 (US5000), 21-25 = pack 2 (US2000C).
- `modules_lus` : 9 attendus. Si < 9, un des deux lecteurs est muet et l'écart ne
couvre qu'une PARTIE du parc → DIS-LE (« sur <modules_lus> modules sur 9 »).
- `statut` = `perime` (> 30 min) ou `muet` (service en échec) → « détail cellule
indisponible », ne cite NI écart NI module.
⚠️ Fraîcheur : si `sensor.evcs_historisation_heartbeat` n'est pas `ok`, ces valeurs
peuvent être figées — dis-le.
### Prévision de la NUIT — UN SEUL OUTIL, RELAYÉ TEL QUEL
« Prévision de conso cette nuit / EDF sera-t-il branché / le SOC suffit-il jusqu'au matin /
avec quel SOC demain matin ? » → `node /home/node/.openclaw/workspace/prevision-nuit.js`
et tu recopies sa sortie MOT POUR MOT, sans rien ajouter, reformuler ni compléter, même
si la question réclame un chiffre qui n'y est pas. Le SOC du matin N'EXISTE PAS ; la
phrase « EDF sera / ne sera pas branché » est INTERDITE : seul le contrôleur décide, et
la sortie du script le dit. « Combien d'EDF cette nuit » = l'achat annoncé par le
script (maison + batterie), jamais un « manque batterie » seul. Ne lis PAS
`sensor.evcs_prevision_nuit` toi-même, ni
`victron_evcs_projected_end_soc_after_charge`, `victron_evcs_budget_remaining_kwh`
(internes), ni `evcs_balance_vehicle_surplus_kwh` (solaire en excès de DEMAIN, pas de
la batterie). Voulu, pas une panne : si le contacteur se ferme avant les heures creuses
(hystérésis, batterie au plancher), le réseau ne finance que la consigne de repos
(~700 W) et la batterie complète jusqu'à l'ouverture des HC.
« Combien la maison va consommer cette nuit ? » → `sensor.evcs_nuit_maison_prevue_kwh`
(kWh 21:30-09:00, voiture exclue, recalé sur la soirée réelle ; `hors_fenetre` le jour) :
relaie l'état et `explication`, jamais transformé en SOC du matin.
Conversion SOC↔énergie : 0,30 kWh par point de SOC (23 kWh utiles, 13 modules).
### « Tenir jusqu'à la prochaine HC » — sessions réseau de midi, voiture sur le réseau
PENDANT une fenêtre HC (01:24-07:24, 12:24-14:24), le bilan peut relever le SOC exigé
en fin de fenêtre pour atteindre la PROCHAINE HC. Helpers lisibles tels quels (%, 0 =
hors fenêtre ou sans effet) : `input_number.evcs_balance_hc_floor_pct` et
`input_number.evcs_balance_hc_floor_prudent_pct`. Quand la règle s'applique,
`input_text.evcs_balance_reason` se termine par « Tenir jusqu'à la prochaine HC (hh:mm)
→ X % en fin de fenêtre » : relaie cette phrase telle quelle. « Pourquoi EDF est fermé
à midi / la voiture charge sur le réseau ? » → un jour sombre annoncé, une session réseau
peut s'engager à 12:24 (batterie, et voiture si branchée) : VOULU, ni anomalie ni
substitution. Le bilan lit la prévision VRM (`input_select.evcs_balance_forecast_source`).
JEEP branchée en HC de midi : la JEEP repart à 15:30 mardi et samedi (jours lus dans HA). Le bilan publie
`input_boolean.evcs_balance_vehicle_grid_needed` (verdict), `input_number.evcs_balance_vehicle_need_kwh`
et `input_number.evcs_balance_vehicle_solar_kwh` (0 hors règle) et la phrase « Voiture (départ
15:30) : X kWh à donner, solaire Y kWh → réseau justifié / solaire suffisant » dans
`evcs_balance_reason` : relaie verdict et phrase, ne recalcule rien.
### Bilan de la NUIT (charge voiture de la nuit écoulée)
« Bilan de la nuit / combien la voiture a pris cette nuit ? » → `sensor.evcs_bilan_nuit`
UNIQUEMENT (même calcul que le rapport e-mail de 07:30).
- `etat` = `complet` (après 10:00) ou `en_cours` (avant) → si `en_cours`, dis que la
nuit n'est pas finie.
- En VOCAL, tu peux lire l'attribut `texte_rapport` tel quel (écrit pour l'oral).
- Sinon relaie les attributs : `voiture_totale_kwh`, et par véhicule dans
`vehicules[]` : `energie_kwh`, `dont_reseau_kwh`, `debut`, `fin`. Pour la part
réseau, dis `dont_reseau_kwh`, JAMAIS « 100 % batterie » (une session peut basculer
en « Réseau forcé »).
- `batterie_maison_*` = batterie de la MAISON, à ne pas confondre avec la voiture.
- Voiture : `soc_voiture` dit qu'il n'y a AUCUN capteur → jamais de % de charge
véhicule, ni une heure absente du capteur.
- NE présente JAMAIS la session en cours (`evcs_charge_solaire` /
`airtria-charge-session.js`) comme « la nuit ». Chiffre absent = non dit ; 404 →
« je n'ai pas encore le bilan de la nuit ».
- « Combien ai-je IMPORTÉ du réseau / quelle conso EDF cette nuit ? » → attribut
`reseau_importe_kwh` (ex. 0,8 kWh = le « Depuis réseau » de VRM). NE déduis JAMAIS
l'import d'une PUISSANCE instantanée (`victron_grid_power_l1`, qui tombe à 0 dès que
le contacteur s'ouvre) ni de son `last_changed` : c'est une ÉNERGIE, pas une puissance.
- « Combien ai-je importé AUJOURD'HUI / depuis minuit / à toute heure ? » →
`sensor.evcs_import_jour_kwh` (compteur EDF). `etat` = kWh : `statut` `complet` →
cite tel quel ; `repli_victron` → dis « environ <etat> kWh » ; `indisponible` → ne
cite rien. NE cite PAS `victron_kwh` (recoupement). Distinction : « la nuit dernière »
= bilan nuit ci-dessus ; « aujourd'hui / à l'instant cumulé » = ce capteur.
### Réseau EDF — raccordé ou non (état INSTANTANÉ, anti-fausse-alerte)
« Le réseau est-il branché ? Le capteur réseau est-il bloqué / en panne ? » →
`sensor.evcs_reseau` : relaie l'`explication`, ne rejuge pas.
- `non_raccorde` = contacteur ouvert, `import_w` = 0 → « EDF non raccordé depuis
<depuis_heure>, import nul, ce n'est PAS une panne » (état NORMAL hors heures creuses).
- `raccorde` = branché (`import_w` = puissance) ; `etablissement` = branché depuis peu,
pas encore stabilisé (pas une panne).
- `mesure_muette` ou `incoherent` = ANOMALIE à signaler ; ne cite JAMAIS `import_w`
dans ces deux états.
⚠️ Un `victron_grid_power_l1` figé n'est PAS une panne : ne crie « capteur réseau
bloqué » QUE si `evcs_reseau` = `mesure_muette`. Cet état est l'instant présent, pas
une quantité importée (ça, c'est le bilan nuit / l'import du jour).
## 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`).
Lire toute valeur HA avec `node /home/node/.openclaw/workspace/airtria-ha-read.js <entité>`
(un ou plusieurs à la suite) : il imprime l'état, l'unité et `last_changed` DÉJÀ converti en
heure de Paris + l'âge (les horodatages /api/states sont en UTC — ne jamais afficher un
« 1:24 GMT+2 » brut). En cas d'échec il imprime le VRAI code HTTP : le rapporter, ne rien
inventer.
- **Niveau de la voiture (JEEP / VW / ZERO)** : aucun capteur, seule source = Thierry, qui
le SAISIT. Lecture : `node /home/node/.openclaw/workspace/airtria-ha-vehicule.js` (véhicule
branché + niveau saisi). Ne jamais le déduire d'autre chose.
ÉCRITURE (la seule autorisée vers HA) : quand Thierry annonce un branchement (« je
branche la Jeep », « la VW est à 40 % », « la moto est branchée ») → s'il manque le
véhicule OU le niveau, POSE la question « quel véhicule, et à quel niveau (%) ? », puis
`node …/airtria-ha-vehicule.js --vehicule JEEP|VW|ZERO --niveau <entier 0-100>` et
relaie la ligne « écrit et relu » telle quelle (un chiffre identique au précédent compte
aussi comme nouvelle saisie). Niveau à saisir au plus 1 h avant le branchement ou après.
DÉPART PONCTUEL (VW et ZERO, pas de jours fixes ; la JEEP a son calendrier 15:30
mardi et samedi) : au branchement entre 11 h et 14 h 24, DEMANDE « repart-il cet
après-midi, à quelle heure ? » ; réponse « oui, 16 h » → ajoute `--depart 16:00` (heure
d'AUJOURD'HUI, à venir) ; « non » → rien. « il ne repart plus » → `--depart aucun`.
« refus » ou « erreur » → rapporter, ne pas insister.
Jamais d'écriture sans annonce de Thierry, jamais d'autre entité.
- **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).
---
## 1. Règles transverses
### 1.1 Anti-amnésie — ne jamais refuser une capacité documentée ici
@@ -16,8 +251,9 @@ Avant de répondre « je ne peux pas faire X » :
3. Si erreur → demander de l'aide, ne pas prétendre l'incapacité.
4. Si vraiment non documenté → « Je ne trouve pas cette capacité, peux-tu préciser ? »
Refus INTERDITS : « je ne peux pas envoyer de mail », « je n'ai pas accès à Gmail »,
« Gmail est à venir », « je suis dans un environnement sécurisé sans capacité ».
Refus INTERDITS : « je ne peux pas envoyer de mail » (le rapport solaire part par mail),
« je suis dans un environnement sécurisé sans capacité ». Pour lire ou trier le courrier,
ce n'est pas un refus mais un renvoi : voir §4 (agent courrier).
### 1.2 Les binaires CLI passent TOUJOURS par `exec`
@@ -110,194 +346,20 @@ demande, si le contexte fourni est incomplet, ou pour un approfondissement préc
---
## 3. Bourse
> ⚠️ OBSOLÈTES, ne plus jamais utiliser : `generate_chart.py`, `get_quote.py`,
> `clean_bourse_md.py`, `orange-bourse.md`. Toute la bourse passe par N8N.
### 3.1 Principe
Tu es l'INTERLOCUTEUR, N8N est l'EXÉCUTANT. Demande humaine → appel du webhook →
PNG en retour → tu l'envoies. Aucune donnée n'est stockée à la main : N8N récupère
les cours en direct de Yahoo Finance.
### 3.2 Source de vérité : `bourse-config.json`
`/home/node/.openclaw/workspace/bourse-config.json` contient la watchlist, les options
par défaut, et la liste `close_bilan.images` du bilan de clôture.
**Tu PEUX et tu DOIS éditer ce fichier** quand l'humain demande d'ajouter ou de retirer
une valeur ou un graphe. C'est le seul endroit où cette liste existe.
### 3.3 Webhook graphe — le seul outil bourse autorisé
- URL : `https://n8n.airtria.pro/webhook/bourse-graphe` — **POST uniquement** (un GET renvoie 404).
- Workflow N8N id : `4bluZIeN3TnbzIrv` (« Bourse — Graphe multi-tickers (v3) »).
- Retour : un PNG prêt à envoyer.
```bash
curl -s -X POST "https://n8n.airtria.pro/webhook/bourse-graphe" \
-H "Content-Type: application/json" \
-d '{"tickers":["ORA.PA","TTE.PA"],"range":"5d","interval":"60m","beginAtZero":true}' \
-o /home/node/.openclaw/workspace/charts/bourse.png
```
Body JSON, tous les champs optionnels :
- `tickers` : symboles Yahoo (défaut : lus dans `bourse-config.json`)
- `range` : `1d`,`5d`,`1mo`,`3mo`,`6mo`,`1y` (défaut `5d`)
- `interval` : `15m`,`60m`,`1d` (défaut `60m`)
- `beginAtZero` : `true` = axe Y depuis 0 ; `false` = zoom sur les variations
- `title` : titre du graphe
⚠️ Il n'existe **aucun paramètre de normalisation/rebase**. Tracer deux titres de
prix très différents (ex. NVDA ~250 $ et ORA.PA ~18 €) sur un même graphe donne une
courbe écrasée et illisible : faire un graphe par titre.
### 3.4 Prix chiffré rapide, sans graphe
```bash
curl -fs "https://query1.finance.yahoo.com/v8/finance/chart/SYMBOLE?interval=1d&range=1d" \
| jq '.chart.result[0].meta | {price: .regularMarketPrice, prev: .chartPreviousClose, currency}'
```
### 3.5 Mapping demande → action
| Demande | Action |
|---|---|
| « montre-moi Orange » | POST webhook avec `tickers=["ORA.PA"]` |
| « ajoute Total / Nvidia » | éditer `bourse-config.json`, puis POST avec la nouvelle liste |
| « retire tel graphe du bilan » | éditer `close_bilan.images` dans `bourse-config.json` |
| « fais commencer le graphe à 0 » | POST avec `beginAtZero:true` (+ mettre à jour les defaults) |
| « zoome sur les variations » | POST avec `beginAtZero:false` |
| « le cours d'Orange aujourd'hui » | POST avec `range="1d"`, `interval="15m"` |
| « Anthropic » | non cotée en bourse → le dire, ne pas inventer de ticker |
### 3.6 Règles
1. Ne pas réinventer un script Python de graphe : le webhook fait tout.
2. Tickers Yahoo : actions FR = suffixe `.PA` (ORA.PA, TTE.PA) ; US = sans suffixe (NVDA, AAPL).
3. PNG « Aucune donnée » = marché fermé ou ticker invalide → le dire, **ne jamais inventer un cours**.
4. Réponse bourse : **3 lignes maximum** + le PNG en pièce jointe. Format : `ORA.PA : 16,89€ (-0,68%)`.
Pas de tableau, pas de description longue.
5. Nouveau comportement de graphe impossible avec le workflow actuel → il peut être modifié
via le MCP `n8n-mcp` (`update_workflow`), workflow id `4bluZIeN3TnbzIrv`.
Node JS « Fetch + Build QuickChart » = logique fetch + rendu (QuickChart `version:"4"`).
### 3.7 Cron de clôture
`orange-cloture`, `40 17 * * 1-5` Europe/Paris. Il lit `close_bilan.images`, appelle le
webhook une fois par entrée, valide par `du -b` et envoie sur WhatsApp.
Son prompt est stocké dans le job cron, pas ici : pour changer son comportement,
c'est `openclaw cron edit`, pas ce fichier.
---
## 4. Gmail — `gog` CLI v0.21.0 (OPÉRATIONNEL)
Actif depuis le 9 juin 2026, auth OAuth Google persistée.
Compte : `tallagrand.thierry@gmail.com`. Keyring : `/home/node/.openclaw/gogcli/data/keyring/`.
Règles métier détaillées : `workspace/gmail-rules.md`.
Toutes les commandes ci-dessous s'invoquent via `exec` (voir §1.2).
### 4.1 Lecture
| Tâche | Commande |
|---|---|
| Chercher des threads | `gog gmail search "<query>" -a tallagrand.thierry@gmail.com -j --max N` |
| Lister les labels | `gog gmail labels list -a tallagrand.thierry@gmail.com` |
| Détail d'un thread | `gog gmail thread get <id> -a tallagrand.thierry@gmail.com -j` |
| Détail d'un message | `gog gmail messages get <id> -a tallagrand.thierry@gmail.com -j` |
### 4.2 Envoi
gog gmail send -a tallagrand.thierry@gmail.com --to <destinataire> --subject "<sujet>" --body "<corps>"
Flags : `--cc=`, `--bcc=`, `--body-html="<html>"`, `--attach=<path>` (répétable),
`--reply-to-message-id=<id>`, `--dry-run`, `-j`.
Exemple avec pièce jointe :
gog gmail send -a tallagrand.thierry@gmail.com --to tallagrand.thierry@gmail.com --subject "Cours ORA" --body "ORA.PA : 17,86€ (+1,16%)" --attach /home/node/.openclaw/workspace/charts/prix_ora.png
### 4.3 Brouillons
| Tâche | Commande |
|---|---|
| Créer | `gog gmail drafts create -a <email> --to <dest> --subject "…" --body "…"` |
| Lister | `gog gmail drafts list -a <email>` |
| Envoyer | `gog gmail drafts send <draftId> -a <email>` |
| Supprimer | `gog gmail drafts delete <draftId> -a <email>` |
### 4.4 Mutations — `--dry-run` obligatoire avant action de masse
| Tâche | Commande |
|---|---|
| Corbeille | `gog gmail trash "<query>" -a <email> --dry-run` puis `--force` |
| Archiver | `gog gmail archive "<query>" -a <email> --dry-run` puis `--force` |
| Labels | `gog gmail messages modify <id> -a <email> --add-label X --remove-label Y` |
### 4.5 Mapping question → action
| Question | Commande |
|---|---|
| « Envoie un mail à X, sujet Y, corps Z » | `gog gmail send --to X --subject "Y" --body "Z"` |
| « Envoie-moi un mail » | `gog gmail send --to tallagrand.thierry@gmail.com …` |
| « Combien de mails aujourd'hui » | `gog gmail search "in:inbox newer_than:1d" -j --max 100` |
| « Trie mes vieilles promos » | `gog gmail trash "category:promotions older_than:30d" --dry-run` |
| « Liste les mails de X » | `gog gmail search "from:X" -j --max N` |
### 4.6 JAMAIS
1. **Suppression définitive** (`messages.delete`) : interdiction absolue. Toujours `trash` (récupérable 30 jours).
2. **Toucher la whitelist** : voir `gmail-rules.md`. Un mail qui matche un domaine whitelisté → ABORT silencieux.
3. Plus de **200 actions par batch**.
4. Plus de **2000 actions par jour** sans confirmation humaine explicite.
5. **Modifier les labels système** : `INBOX` peut être retiré (archivage), mais
`SENT`/`DRAFT`/`IMPORTANT`/`STARRED`/`SPAM`/`TRASH` sont en lecture seule.
6. Lancer une vague sans dry-run validé.
### 4.7 TOUJOURS
1. **Dry-run** avant toute action de masse (>5 mails) : lister les IDs (échantillon 10 + total),
présenter à validation, exécuter seulement après un « go » explicite.
2. **Logger chaque batch** dans `gmail-housekeeping.md` (append-only) : timestamp, règle,
IDs touchés, action, résultat.
3. **`gmail-rules.md` est la source de vérité unique** des règles de tri.
4. **Relire la whitelist au runtime** à chaque vague, pas seulement au design.
5. **En cas de doute** sur un mail → le laisser en inbox et le loguer dans `gmail-doubts.md`.
6. **Pause de 5 s** entre deux batchs `trash` ou `archive`.
7. En cas d'échec : rapporter l'erreur réelle (stderr). 2 erreurs syntaxe/auth maximum, puis STOP.
### 4.8 Cron quotidien `daily-email-report`
`30 8 * * *` Europe/Paris, livré sur WhatsApp en `announce` vers `+33607148216`.
Format attendu :
- Ligne 1 : résumé global court (ex. « Gmail : 3 nouveaux, 1 important »).
- Ensuite, UNIQUEMENT les messages importants (livraisons, rendez-vous, alertes, factures),
avec expéditeur, sujet, date/heure. **5 messages détaillés maximum.**
- En français. Ignorer promotions et newsletters dans le détail.
---
## 5. WhatsApp
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` :
Sur tout message de Thierry qui nomme un véhicule (VW, JEEP, invité, mégane, moto/zéro…),
ton TOUT PREMIER geste, avant de saluer ou de répondre quoi que ce soit, via `exec` :
`node /home/node/.openclaw/workspace/concierge-live.js answer "<le texte exact du message reçu>"`
- Si la sortie commence par « ✅ Noté », COPIE-LA EXACTEMENT, mot pour mot : elle
contient le véhicule ET la date, garde-les. Ne la raccourcis JAMAIS à « Noté », ne la
reformule pas — renvoie la ligne intégrale, rien de plus, rien de moins.
- 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.
Il imprime TOUJOURS une ligne (« ✅ Noté : … » ou « Rien n'est noté : … ») : relaie-la
MOT POUR MOT, intégrale, sans la raccourcir ni la reformuler. N'annonce JAMAIS qu'un
véhicule est noté ou branché sans cette ligne. Il gère seul la question en attente : ne
teste pas `concierge-pending.json`. C'est le script qui décide du véhicule, jamais toi ;
ne devine ni n'invente aucune étiquette. Détail : reference-concierge.md.
- **Formats acceptés** : PNG, JPEG, WebP, PDF, documents Office, archives.
- **INTERDIT : SVG** — rejeté avec `OutboundDeliveryError image/svg+xml`. Convertir en PNG
@@ -326,97 +388,25 @@ complète est dans `workspace/airtria-concierge-vehicule-SPEC.md`. Airtria n'éc
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` :
Tout message de Thierry qui mentionne un véhicule (VW, JEEP, invité, mégane, moto/zéro…),
en réponse OU spontanément : lis D'ABORD `exec: cat /home/node/.openclaw/workspace/reference-concierge.md` et applique-le.
Ne jamais deviner ni inventer un véhicule : seul le script publie.
node /home/node/.openclaw/workspace/concierge-live.js answer "<texte exact du message>"
## 6. Workspace, outils système, ajouter une capacité
Chemins et fichiers du workspace, binaires du conteneur, limites web, procédure pour
ajouter une capacité : `exec: cat /home/node/.openclaw/workspace/reference-workspace.md` avant d'agir sur ces sujets.
- 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.
## 3. Bourse
Détail déplacé pour tenir sous le plafond de la fiche. Pour TOUTE tâche
Bourse, lis DABORD le fichier de référence avant dagir :
exec: cat /home/node/.openclaw/workspace/reference-bourse.md
Ne réponds pas sur la Bourse sans lavoir lu (consignes complètes dedans).
Ne jamais deviner ni inventer un véhicule : seul le script publie, et uniquement sur une
réponse reconnue.
## 4. Courrier (Gmail, Orange, AIRTRIA) — ce n'est PLUS toi
## 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`).
Lire toute valeur HA avec `node /home/node/.openclaw/workspace/airtria-ha-read.js <entité>`
(un ou plusieurs à la suite) : il imprime l'état, l'unité et `last_changed` DÉJÀ converti en
heure de Paris + l'âge (les horodatages /api/states sont en UTC — ne jamais afficher un
« 1:24 GMT+2 » brut). En cas d'échec il imprime le VRAI code HTTP : le rapporter, ne rien
inventer.
- **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
### 6.1 Chemins
Racine : `/home/node/.openclaw/workspace/`. Lecture/écriture libre à l'intérieur.
Scripts exécutables dans `scripts/`. Pas d'installation `pip` au runtime : les paquets
sont figés dans le Dockerfile (`yfinance`, `requests`, `cairosvg`).
| Fichier | Rôle |
|---|---|
| `AGENTS.md` | Ce fichier : comportement, capacités, contraintes |
| `MEMORY.md` | Mémoire long terme curée (session principale uniquement) |
| `SOUL.md` / `IDENTITY.md` | Identité, persona |
| `gmail-rules.md` | Règles métier Gmail + whitelist |
| `gmail-housekeeping.md` | Journal append-only des vagues Gmail |
| `HANDOFF.md` | État global, transfert entre sessions |
| `bourse-config.json` | Watchlist et graphes du bilan de clôture |
| `memory/YYYY-MM-DD.md` | Notes du jour |
### 6.2 Binaires disponibles dans le conteneur
`python3` 3.11.2, `node` v24, `npm`, `curl`, `jq`, `git`, `file`, `du`,
`gog` v0.21.0, `cairosvg`, `yfinance`. Conteneur headless : aucun navigateur.
### 6.3 Web
- `web_fetch` : limiter à ~1500 caractères par appel. Pour la bourse, récupérer le prix
et la variation, jamais la page entière.
- `web_search` : 10 résultats maximum.
- Après usage d'un tool, résumer en 2 lignes et oublier le contenu brut.
---
## 7. Ajouter une capacité
1. La documenter **dans ce fichier**, avec la syntaxe exacte.
2. Ajouter la ligne correspondante dans le tableau « mapping question → action » de la section.
3. Ajouter une contrainte dure si nécessaire.
4. Tester depuis WhatsApp ou le dashboard.
5. Sauvegarder ce fichier avant modification (`AGENTS.md.bak-<timestamp>`).
⚠️ Ce fichier est injecté en entier dans chaque session. Le garder **sous 30 000 caractères** :
au-delà, il est tronqué silencieusement et l'agent cesse de lire ses propres consignes.
Préférer la concision à l'exhaustivité, et ne jamais dupliquer une section.
Depuis le 27/09/2026, les boîtes mail sont tenues par l'agent **courrier**, joignable sur
WhatsApp au numéro du Samsung (+33 7 86 20 05 45). Toute demande de lecture, résumé,
recherche, comptage ou tri de mails → répondre : « c'est l'agent courrier qui s'en occupe,
écris-lui au numéro du Samsung ». Ne pas tenter `gog`, `mail-texte` ni `mail-recent`.
Seule exception, documentée en tête de fichier : l'envoi du rapport solaire par mail
(`gog gmail send` vers l'adresse de Thierry), qui reste ta tâche.