diff --git a/tests/diagnostics-agent/run.mjs b/tests/diagnostics-agent/run.mjs index 8e64665..cef5c01 100644 --- a/tests/diagnostics-agent/run.mjs +++ b/tests/diagnostics-agent/run.mjs @@ -223,24 +223,25 @@ for (const [nom, r, test] of res) { console.log(`${ok ? 'OK ' : 'KO '} ${nom}`); } -// Import depuis minuit. Valeurs de minuit fictives, deltas du 26/09 (0,8 kWh VRM). +// Import depuis minuit. Releve reel du 26/09 12:19 : EDF 0,8 (= VRM), MultiPlus 0,6 + 0,1. const [OUT, INV] = COMPTEURS_IMPORT; -const mn = { [OUT]: 1200.00, [INV]: 845.00, [COMPTEUR_EDF]: 3082.00 }; +const mn = { [OUT]: 1200.0, [INV]: 845.0, [COMPTEUR_EDF]: 3082.0 }; +const ok26 = { [OUT]: 1200.6, [INV]: 845.1, [COMPTEUR_EDF]: 3082.83 }; const imp = [ - ['26/09 : 0,5 vers maison + 0,3 vers batterie -> 0,8 complet, recoupe EDF', - importJour({ [OUT]: 1200.5, [INV]: 845.3, [COMPTEUR_EDF]: 3082.83 }, mn), - (r) => r.etat === 0.8 && r.attributs.statut === 'complet' && r.attributs.compteur_edf_kwh === 0.8 - && r.attributs.detail_kwh.vers_ac_out === 0.5 && r.attributs.detail_kwh.vers_inverter === 0.3], + ['26/09 : le compteur EDF fait foi -> 0,8 comme VRM, MultiPlus 0,7 en recoupement', + importJour(ok26, mn), (r) => r.etat === 0.8 && r.attributs.source === 'compteur_edf' && r.attributs.victron_kwh === 0.7], ['journee sans reseau -> 0, pas indisponible', importJour({ [OUT]: 1200, [INV]: 845, [COMPTEUR_EDF]: 3082 }, mn), (r) => r.etat === 0 && r.attributs.statut === 'complet'], - ['compteur remis a zero -> indetermine, jamais un import negatif', - importJour({ [OUT]: 3.2, [INV]: 845.3 }, mn), (r) => r.etat === 'indetermine'], - ['un compteur illisible -> partiel, au moins', - importJour({ [OUT]: 1200.5, [INV]: null }, mn), (r) => r.etat === 0.5 && r.attributs.statut === 'partiel'], - ['valeur de minuit inconnue -> indisponible', - importJour({ [OUT]: 1200.5, [INV]: 845.3 }, {}), (r) => r.etat === 'indisponible'], - ['compteur EDF absent : le total ne change pas, recoupement null', - importJour({ [OUT]: 1200.5, [INV]: 845.3 }, mn), (r) => r.etat === 0.8 && r.attributs.compteur_edf_kwh === null], + ['compteur EDF remis a zero -> repli MultiPlus, jamais un import negatif', + importJour({ ...ok26, [COMPTEUR_EDF]: 0.4 }, mn), (r) => r.etat === 0.7 && r.attributs.statut === 'repli_victron'], + ['compteur EDF muet -> repli MultiPlus, dit « environ »', + importJour({ ...ok26, [COMPTEUR_EDF]: null }, mn), (r) => r.etat === 0.7 && /Environ/.test(r.attributs.explication)], + ['EDF muet ET un MultiPlus remis a zero -> indisponible', + importJour({ [OUT]: 3.2, [INV]: 845.1, [COMPTEUR_EDF]: null }, mn), (r) => r.etat === 'indisponible'], + ['EDF muet ET un MultiPlus muet -> indisponible, pas de total partiel', + importJour({ [OUT]: 1200.6, [INV]: null, [COMPTEUR_EDF]: null }, mn), (r) => r.etat === 'indisponible'], + ['valeurs de minuit inconnues -> indisponible', + importJour(ok26, {}), (r) => r.etat === 'indisponible'], ]; for (const [nom, r, test] of imp) { const ok = test(r); diff --git a/tools/diagnostics-agent.mjs b/tools/diagnostics-agent.mjs index f620b88..56afd44 100644 --- a/tools/diagnostics-agent.mjs +++ b/tools/diagnostics-agent.mjs @@ -418,51 +418,58 @@ export function etatReseau(st, maintenant = new Date()) { // La question « combien ai-je importe » est une QUANTITE ; la puissance // instantanee (victron_grid_power_l1) n'y repond pas, elle vaut 0 des que le // contacteur est ouvert. -// Source : les compteurs cumules AC-in du MultiPlus, sains et monotones (HANDOFF -// §76 : 1,60 kWh la nuit du 24/09, recoupes par le compteur EDF a 1,53). Import -// = vers la maison (ac_out) + vers la batterie (inverter). Le compteur du -// contacteur EDF est publie a cote comme recoupement, pas additionne. +// +// SOURCE QUI FAIT FOI : le compteur du contacteur EDF (Tuya, 0,01 kWh). Premier +// jour en service (26/09 12:19) : il donnait 0,8 comme VRM, quand le delta des +// compteurs AC-in du MultiPlus donnait 0,7 (0,6 + 0,1). Ces compteurs VE.Bus +// semblent avancer par pas de 0,1 kWh : sur une matinee, deux arrondis de ce +// pas font 12 %. Ils restent publies en recoupement, et servent de REPLI si le +// compteur EDF est muet ou remis a zero (statut repli_victron). // ⚠️ Compteurs sans last_reset : une valeur courante INFERIEURE a celle de -// minuit est une remise a zero, pas un import negatif -> indetermine. +// minuit est une remise a zero, pas un import negatif. export const COMPTEURS_IMPORT = [ 'sensor.airtria_mpii_v1_energy_from_ac_in_1_to_ac_out', 'sensor.airtria_mpii_v1_energy_from_ac_in_1_to_inverter', ]; export const COMPTEUR_EDF = 'sensor.edf_general_ok_energie_totale'; +function delta(c, m) { + if (!Number.isFinite(c) || !Number.isFinite(m)) return { muet: true }; + if (c < m) return { remis: true }; + return { v: c - m }; +} + // courant, minuit : { [entity_id]: nombre | null } export function importJour(courant, minuit) { + const edf = delta(courant[COMPTEUR_EDF], minuit[COMPTEUR_EDF]); + + // Recoupement Victron (et repli). const detail = {}; const muets = []; - let total = 0; + let remis = null; + let victron = 0; for (const id of COMPTEURS_IMPORT) { - const c = courant[id], m = minuit[id]; const court = id.replace('sensor.airtria_mpii_v1_energy_from_ac_in_1_to_', 'vers_'); - if (!Number.isFinite(c) || !Number.isFinite(m)) { muets.push(court); detail[court] = null; continue; } - if (c < m) { - return { etat: 'indetermine', attributs: { statut: 'indetermine', - explication: `Compteur ${court} remis a zero depuis minuit : import du jour inconnu.` } }; - } - detail[court] = Math.round((c - m) * 100) / 100; - total += c - m; + const d = delta(courant[id], minuit[id]); + if (d.remis) { remis = court; detail[court] = null; continue; } + if (d.muet) { muets.push(court); detail[court] = null; continue; } + detail[court] = Math.round(d.v * 100) / 100; + victron += d.v; } - if (muets.length === COMPTEURS_IMPORT.length) { - return { etat: 'indisponible', attributs: { statut: 'indisponible', - explication: 'Compteurs d\'energie AC-in illisibles : import du jour inconnu.' } }; + const victronOk = remis === null && muets.length === 0; + const victronKwh = victronOk ? Math.round(victron * 10) / 10 : null; + const commun = { detail_victron_kwh: detail, victron_kwh: victronKwh }; + + if (edf.v !== undefined) { + const total = Math.round(edf.v * 10) / 10; + return { etat: total, attributs: { statut: 'complet', source: 'compteur_edf', ...commun, + explication: `${total} kWh importes d'EDF depuis minuit.` } }; } - total = Math.round(total * 10) / 10; - const ce = courant[COMPTEUR_EDF], me = minuit[COMPTEUR_EDF]; - const edf = Number.isFinite(ce) && Number.isFinite(me) && ce >= me ? Math.round((ce - me) * 10) / 10 : null; - const partiel = muets.length > 0; - return { - etat: total, - attributs: { - statut: partiel ? 'partiel' : 'complet', - detail_kwh: detail, - compteur_edf_kwh: edf, - explication: partiel - ? `Au moins ${total} kWh importes d'EDF depuis minuit (compteur ${muets.join(', ')} muet).` - : `${total} kWh importes d'EDF depuis minuit.`, - }, - }; + if (victronOk) { + return { etat: victronKwh, attributs: { statut: 'repli_victron', source: 'compteurs_multiplus', ...commun, + explication: `Environ ${victronKwh} kWh importes d'EDF depuis minuit (compteur EDF ` + + `${edf.remis ? 'remis a zero' : 'muet'}, valeur des compteurs du MultiPlus, a 0,1 kWh pres).` } }; + } + return { etat: 'indisponible', attributs: { statut: 'indisponible', ...commun, + explication: 'Compteurs d\'energie muets ou remis a zero : import du jour inconnu.' } }; } diff --git a/tools/historise-evcs.mjs b/tools/historise-evcs.mjs index a6d51d0..71c02c8 100644 --- a/tools/historise-evcs.mjs +++ b/tools/historise-evcs.mjs @@ -1895,7 +1895,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 (COMPTEURS_IMPORT.every((id) => Number.isFinite(valeurs[id]))) minuitCache = { jour, valeurs }; + if (IDS_IMPORT.every((id) => Number.isFinite(valeurs[id]))) minuitCache = { jour, valeurs }; return valeurs; }