diff --git a/tests/diagnostics-agent/run.mjs b/tests/diagnostics-agent/run.mjs index cef5c01..8ae3919 100644 --- a/tests/diagnostics-agent/run.mjs +++ b/tests/diagnostics-agent/run.mjs @@ -2,7 +2,7 @@ // Les cas PV reprennent les gammes du banc OpenClaw (HANDOFF §77) : G3, G4, G5, G7. // node tests/diagnostics-agent/run.mjs import fs from 'node:fs'; -import { diagnosticPv, etatCellules, productionJour, chargeSolaire, bilanNuit, etatReseau, importJour, COMPTEURS_IMPORT, COMPTEUR_EDF, MPPT_MODES, ORIGINE_PAR_SOURCE } from '../../tools/diagnostics-agent.mjs'; +import { diagnosticPv, etatCellules, productionJour, chargeSolaire, bilanNuit, etatReseau, importJour, COMPTEURS_IMPORT, COMPTEUR_EDF, MPPT_MODES, ORIGINE_PAR_SOURCE, consoJour, COMPTEURS_CONSO } from '../../tools/diagnostics-agent.mjs'; const LIM = 'voltage_current_limited', ACT = 'mppt_active'; function st({ pv, conso, reseau, soc = 80, courant = 0, edf = 'off', modes }) { @@ -249,6 +249,26 @@ for (const [nom, r, test] of imp) { console.log(`${ok ? 'OK ' : 'KO '} ${nom}`); } -const n = cas.length + cel.length + prod.length + charge.length + 1 + bn.length + res.length + imp.length; +// --- Consommation du jour : releve reel du 26/09 a 17:11 (VRM ~25 kWh) ------ +{ + const [R, O] = COMPTEURS_CONSO.map(([, id]) => id); + const mi = { [R]: 9, [O]: 292.4 }; + const cj = [ + ['conso reelle 26/09 17:11 : 0,6 + 24,6 = 25,2 kWh, complet', consoJour({ [R]: 9.6, [O]: 317 }, mi), + (r) => r.etat === 25.2 && r.attributs.statut === 'complet' && r.attributs.detail_kwh.depuis_reseau === 0.6], + ['compteur remis a zero -> indisponible, jamais negatif', consoJour({ [R]: 9.6, [O]: 3.1 }, mi), + (r) => r.etat === 'indisponible'], + ['un compteur muet -> partiel « au moins »', consoJour({ [R]: null, [O]: 317 }, mi), + (r) => r.etat === 24.6 && r.attributs.statut === 'partiel' && /au moins/.test(r.attributs.explication)], + ['minuit inconnu -> indisponible', consoJour({ [R]: 9.6, [O]: 317 }, {}), (r) => r.etat === 'indisponible'], + ]; + for (const [nom, r, test] of cj) { + const ok = test(r); if (!ok) { ko++; console.log(JSON.stringify(r)); } + console.log(`${ok ? 'OK ' : 'KO '} ${nom}`); + } + globalThis.__nConso = cj.length; +} + +const n = (globalThis.__nConso || 0) + cas.length + cel.length + prod.length + charge.length + 1 + bn.length + res.length + imp.length; console.log(`\n${n - ko}/${n}`); process.exit(ko ? 1 : 0); diff --git a/tools/diagnostics-agent.mjs b/tools/diagnostics-agent.mjs index 56afd44..51d51e5 100644 --- a/tools/diagnostics-agent.mjs +++ b/tools/diagnostics-agent.mjs @@ -473,3 +473,49 @@ export function importJour(courant, minuit) { return { etat: 'indisponible', attributs: { statut: 'indisponible', ...commun, explication: 'Compteurs d\'energie muets ou remis a zero : import du jour inconnu.' } }; } + +// --------------------------------------------------------------------- +// Consommation du jour (« Consommation Total » de VRM), 26/09/2026 +// --------------------------------------------------------------------- +// Ce que la sortie AC du MultiPlus a delivre depuis minuit : maison ET borne +// (la borne est cablee sur la sortie AC, comme dans VRM). Deux sources : +// depuis le reseau (ac_in_1 -> ac_out) et depuis l'onduleur (inverter -> +// ac_out, c'est-a-dire solaire + batterie). Verifie le 26/09 a 17:11 : +// 0,6 + 24,6 = 25,2 kWh pour ~25 affiches par VRM a 17 h. Compteurs VE.Bus a +// 0,1 kWh pres, sans last_reset : une valeur INFERIEURE a celle de minuit est +// une remise a zero (redemarrage du MultiPlus), jamais une conso negative. +export const COMPTEURS_CONSO = [ + ['depuis_reseau', 'sensor.airtria_mpii_v1_energy_from_ac_in_1_to_ac_out'], + ['depuis_onduleur', 'sensor.airtria_mpii_v1_energy_from_inverter_to_ac_out'], +]; + +export function consoJour(courant, minuit) { + const detail = {}; + const muets = []; + const remis = []; + let total = 0; + for (const [nom, id] of COMPTEURS_CONSO) { + const d = delta(courant[id], minuit[id]); + if (d.remis) { remis.push(nom); detail[nom] = null; continue; } + if (d.muet) { muets.push(nom); detail[nom] = null; continue; } + detail[nom] = Math.round(d.v * 10) / 10; + total += d.v; + } + total = Math.round(total * 10) / 10; + const commun = { detail_kwh: detail }; + if (remis.length) { + return { etat: 'indisponible', attributs: { statut: 'indisponible', ...commun, + explication: 'Compteur du MultiPlus remis a zero depuis minuit : consommation du jour inconnue.' } }; + } + if (muets.length === COMPTEURS_CONSO.length) { + return { etat: 'indisponible', attributs: { statut: 'indisponible', ...commun, + explication: 'Compteurs de consommation muets : consommation du jour inconnue.' } }; + } + if (muets.length) { + return { etat: total, attributs: { statut: 'partiel', compteurs_muets: muets, ...commun, + explication: `Consommation partielle : au moins ${total} kWh depuis minuit (compteur ${muets.join(', ')} muet).` } }; + } + return { etat: total, attributs: { statut: 'complet', ...commun, + explication: `${total} kWh consommes depuis minuit (maison et borne), dont ` + + `${detail.depuis_reseau} depuis le reseau.` } }; +} diff --git a/tools/historise-evcs.mjs b/tools/historise-evcs.mjs index 71c02c8..56798b0 100644 --- a/tools/historise-evcs.mjs +++ b/tools/historise-evcs.mjs @@ -30,7 +30,7 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import pg from 'pg'; import { texteRapport, envoyer, donneesNuit, texteDepuis } from './rapport-nuit.mjs'; -import { diagnosticPv, etatCellules, productionJour, chargeSolaire, bilanNuit, etatReseau, importJour, minuitLocal, COMPTEURS_IMPORT, COMPTEUR_EDF } from './diagnostics-agent.mjs'; +import { diagnosticPv, etatCellules, productionJour, chargeSolaire, bilanNuit, etatReseau, importJour, minuitLocal, COMPTEURS_IMPORT, COMPTEUR_EDF, consoJour, COMPTEURS_CONSO } from './diagnostics-agent.mjs'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const ONCE = process.argv.includes('--once'); @@ -1875,6 +1875,9 @@ async function publierDiagnosticPv(st) { // renvoie en tete l'etat en vigueur au debut de la fenetre, donc un compteur // immobile (contacteur ouvert toute la soiree) a bien une valeur. const IDS_IMPORT = [...COMPTEURS_IMPORT, COMPTEUR_EDF]; +// La consommation du jour (26/09) lit ses compteurs de sortie AC au meme minuit. +const IDS_CONSO = COMPTEURS_CONSO.map(([, id]) => id); +const IDS_MINUIT = [...new Set([...IDS_IMPORT, ...IDS_CONSO])]; let minuitCache = { jour: null, valeurs: null }; async function valeursAMinuit() { const minuit = minuitLocal(new Date()); @@ -1883,7 +1886,7 @@ async function valeursAMinuit() { const debut = new Date(minuit.getTime() - 6 * 3600e3).toISOString(); const r = await fetch( `${HA_URL}/api/history/period/${debut}?end_time=${encodeURIComponent(jour)}` + - `&filter_entity_id=${IDS_IMPORT.join(',')}&minimal_response&no_attributes`, + `&filter_entity_id=${IDS_MINUIT.join(',')}&minimal_response&no_attributes`, { headers: { Authorization: `Bearer ${HA_TOKEN}` }, signal: AbortSignal.timeout(15000) }, ); if (!r.ok) throw new Error(`historique HA ${r.status}`); @@ -1895,7 +1898,7 @@ async function valeursAMinuit() { if (id) valeurs[id] = nums.length ? nums[nums.length - 1] : null; } // On ne met en cache qu'une lecture complete : sinon on reessaie au cycle suivant. - if (IDS_IMPORT.every((id) => Number.isFinite(valeurs[id]))) minuitCache = { jour, valeurs }; + if (IDS_MINUIT.every((id) => Number.isFinite(valeurs[id]))) minuitCache = { jour, valeurs }; return valeurs; } @@ -1912,6 +1915,15 @@ async function publierImportJour(st) { unit_of_measurement: typeof ij.etat === 'number' ? 'kWh' : undefined, ...ij.attributs, calcule_a: new Date().toISOString(), }); + // Consommation du jour (« Consommation Total » de VRM) : sortie AC du MultiPlus. + const cc = {}; + for (const id of IDS_CONSO) { const v = Number(st.get(id)?.state); cc[id] = Number.isFinite(v) ? v : null; } + const cj = consoJour(cc, minuit); + await publierCapteur('sensor.evcs_consommation_jour_kwh', cj.etat, { + friendly_name: 'EVCS — consommation du jour (maison et borne)', icon: 'mdi:home-lightning-bolt', + unit_of_measurement: typeof cj.etat === 'number' ? 'kWh' : undefined, ...cj.attributs, + calcule_a: new Date().toISOString(), + }); return 1; }