agent bourse, revue glissante, niveau VW estime depuis l'autonomie, alertes

This commit is contained in:
2026-10-12 17:15:53 +02:00
parent 740c83e20e
commit 9d100b45c6
20 changed files with 535 additions and 20 deletions

View File

@@ -269,6 +269,9 @@ async function detect() {
let alerteEcs = '';
try { alerteEcs = await checkAlerteECS(); } catch { /* idem : jamais bloquant */ }
if (alerteEcs) alert = [alert, alerteEcs].filter(Boolean).join('\n\n');
let revue = '';
try { revue = await checkAlerteRevue(); } catch { /* idem : jamais bloquant */ }
if (revue) alert = [alert, revue].filter(Boolean).join('\n\n');
let piles = '';
try { piles = await checkPiles(); } catch { /* idem : jamais bloquant */ }
if (piles) alert = [alert, piles].filter(Boolean).join('\n\n');
@@ -359,8 +362,12 @@ async function answerReleves(text, releves) {
rattache = lignePrefixe.startsWith('✅');
}
const maintenant = Math.round(Date.now() / 1000);
// 12/10/2026 : lecture-tableau.py passe CONCIERGE_RELEVE_SOURCE=photo_autonomie:<km>/<réf> quand le niveau est
// déduit de l'autonomie (VW) ; il ne vaut que pour un relevé unique AVEC niveau.
const srcAut = /^photo_autonomie:\d+\/\d+$/.test(process.env.CONCIERGE_RELEVE_SOURCE || '') ? process.env.CONCIERGE_RELEVE_SOURCE : null;
const aEcrire = releves.map((r, i) => ({ vehicule: r.vehicule, km: r.km, niveau: r.niveau,
ts: i === 0 && rattache ? Math.round(ctx.on) : maintenant }));
ts: i === 0 && rattache ? Math.round(ctx.on) : maintenant,
...(srcAut && releves.length === 1 && r.niveau !== null ? { source: srcAut } : {}) }));
// Dedoublonnage : meme vehicule et meme km deja dans la file (message relance,
// ou compteur qui n'a pas bouge : un second releve identique n'apprend rien).
let file = [];
@@ -536,6 +543,26 @@ async function checkAlerteECS() {
return String(a.message).slice(0, 300);
}
// 10/10/2026, demande du propriétaire (via EVCS) : revue glissante. EVCS publie sensor.evcs_alerte_revue
// (id, code, texte, a) ; on relaie « ⚠️ texte » UNE fois par id, jour et nuit. Alerte de plus de 2 h (mise en
// service, retour après une panne) → notée sans envoi. Capteur absent ou illisible → rien.
const REVUE = 'sensor.evcs_alerte_revue';
const REVUE_STATE = path.join(__dirname, 'concierge-alerte-revue-state.json');
async function checkAlerteRevue() {
if (fs.existsSync(ALERT_KILL)) return '';
let s;
if (SIM) { if (!SIM.revue) return ''; s = { state: SIM.revue.id, attributes: SIM.revue }; }
else { try { s = await haState(REVUE); } catch { return ''; } }
const a = s?.attributes || {};
if (!a.id || !a.texte || s.state === 'unknown' || s.state === 'unavailable') return '';
const st = SIM ? (SIM.revueState || {}) : loadJSON(REVUE_STATE, {});
if (st.lastId === a.id) return '';
saveJSON(REVUE_STATE, { lastId: a.id });
const age = (SIM?.maintenant ?? Date.now()) - Date.parse(a.a || '');
if (!(age <= 2 * 3600 * 1000)) return ''; // trop vieille ou sans date : notée, pas envoyée
return `⚠️ ${String(a.texte).trim().slice(0, 400)}`;
}
// 10/10/2026, demande du propriétaire (via EVCS) : piles faibles et capteurs muets. EVCS calcule
// sensor.evcs_piles (alertes [{entite, statut, texte}]) ; on relaie le `texte` de chaque alerte QUI APPARAÎT
// (clé entite|statut), une fois, regroupées en un seul message. Une alerte disparue puis revenue repart.