#!/usr/bin/env node // prevision-nuit.js — imprime la prévision de la nuit calculée par EVCS (sensor.evcs_prevision_nuit), // en phrases prêtes à relayer TELLES QUELLES. Aucun calcul ici : le contrôleur décide et refait // le calcul à l'ouverture des heures creuses. Lecture seule. 'use strict'; const fs = require('fs'); const path = require('path'); const HA_BASE = process.env.HA_BASE || 'http://10.10.10.17:8123'; const token = () => fs.readFileSync(process.env.HA_TOKEN_FILE || path.join(__dirname, '.ha-token'), 'utf8').trim(); (async () => { const ac = new AbortController(); const t = setTimeout(() => ac.abort(), 10000); let s; try { const r = await fetch(`${HA_BASE}/api/states/sensor.evcs_prevision_nuit`, { headers: { Authorization: `Bearer ${token()}` }, signal: ac.signal }); if (!r.ok) { console.log(`Prévision de la nuit indisponible (HA ${r.status}).`); return; } s = await r.json(); } finally { clearTimeout(t); } const a = s.attributes || {}; if (s.state === 'indisponible' || a.statut === 'indisponible' || !a.texte_rapport) { // statut : frais | jour | indisponible const why = String(a.explication || a.texte_rapport || "le bilan énergétique du contrôleur est en état dégradé ou trop ancien").replace(/\.\s*$/, ""); console.log(`Prévision de la nuit indisponible pour l'instant : ${why}. Le contrôleur la republie dès que son bilan est de nouveau fiable ; en attendant, pas de chiffre.`); return; } const calc = a.calcule_a ? new Date(a.calcule_a).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Paris' }) : ''; console.log(`Prévision de la nuit (bilan EVCS de ${calc}) : ${a.texte_rapport}`); if (a.reserve_prevision) console.log(`Réserve : ${a.reserve_prevision}.`); console.log(`Le SOC de demain matin n'est pas projeté : le contrôleur refait le calcul à l'ouverture des heures creuses${a.debut_heures_creuses ? ` (${a.debut_heures_creuses})` : ''} et décide seul du réseau.`); })().catch((e) => console.log(`Prévision de la nuit indisponible (${e.message}).`));