diff --git a/HANDOFF.md b/HANDOFF.md index f92a634..6421108 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -1265,10 +1265,17 @@ autoriser une charge complete avec EDF.** Decisions (30/09) : profil **prudent + bande de SOC (-30 A en 40-60 %, -15 A en 25-40 %, valeurs fixes) : a SOC moyen la voiture ne tire qu'environ 1 kW, et un budget peut ne pas etre entierement delivre en 6 h. - P9 (reseau avant depart) est OFF chez le proprietaire : non touche. -- **A faire** : annoncer le budget et le mode dans `sensor.evcs_prevision_nuit` (lu par - l'agent) ; commande vocale « charge complete cette nuit » (SPARK, ecriture du seul - `input_select.victron_evcs_night_charge_mode`) ; carte sur EVCS Master ; verifier la - premiere nuit reelle (budget fige, arret, raison). +- **Fait le 30/09 apres-midi** (accord du proprietaire) : `sensor.evcs_prevision_nuit` + annonce le budget et le reglage (« La voiture peut prendre environ 6,3 kWh sur la batterie + sans EDF, puis elle s'arrete et attend le soleil (reglage « sans EDF ») » ; en session, le + budget FIGE et « Deja x kWh donnes ») ; attributs `mode_charge_nuit`, `budget_voiture_kwh`, + `budget_voiture_fige`, `budget_deja_donne_kwh`. Diagnostics 95/95, mutation qui mord. + Cartes : **Bilan energetique, onglet Production (4e)** « Voiture cette nuit — sans EDF ou + charge complete » (reglage, budget prevu, budget fige + deja donne) ; **EVCS Master**, + Parametres utilisateur (reglage + budget). Sauvegardes `*.bak-20260930-budget`. + Commande vocale demandee a SPARK (ecriture du seul `input_select.victron_evcs_night_charge_mode`). +- **A verifier** : la premiere nuit reelle avec voiture branchee (budget fige a 01:24, + arret au compteur, raison publiee). --- *Document mis a jour le : 30/09/2026 (~13:30) — budget voiture de la nuit sans EDF (bilan + controleur) ; fiche de l'agent allegee (25 831 car.), concierge sans silence, calendrier JEEP mar/sam ; nuit du 28-29 verifiee, achat EDF annonce = maison en HC + manque batterie ; regle HC active, vehicule branche a midi (JEEP/VW/ZERO), prevision de nuit a trois verdicts, nuits superposees et bilan recale sur la soiree* diff --git a/dashboards/bilan_energetique.yaml b/dashboards/bilan_energetique.yaml index 0aecb4a..797a83a 100644 --- a/dashboards/bilan_energetique.yaml +++ b/dashboards/bilan_energetique.yaml @@ -472,6 +472,43 @@ views: path: production icon: mdi:solar-power cards: + # Budget voiture de la nuit (30/09/2026) : ce que la batterie peut donner + # a la voiture sans EDF ; reglage de la nuit (sans_edf par defaut, remis + # chaque jour a 12:00 ; complete = budget batterie puis EDF en HC). + - type: entities + title: Voiture cette nuit — sans EDF ou charge complète + entities: + - entity: input_select.victron_evcs_night_charge_mode + name: Charge de la voiture cette nuit + - entity: input_number.evcs_balance_night_vehicle_budget_kwh + name: Budget batterie sans EDF (prévu par le bilan) + - entity: input_text.victron_evcs_night_budget + name: Budget figé à 01:24 (budget|index borne) + - type: markdown + content: | + {% set m = states('input_select.victron_evcs_night_charge_mode') %} + {% set b = states('input_number.evcs_balance_night_vehicle_budget_kwh') | float(0) %} + {% set f = states('input_text.victron_evcs_night_budget') %} + {% if '|' in f %} + {% set fb = f.split('|')[0] | float(0) %} + {% set ref = f.split('|')[1] | float(0) %} + {% set c = states('sensor.evcs_hq2239dnt7p_total_energy') | float(ref) %} + **Session en cours** : budget figé **{{ fb | round(1) }} kWh**, déjà + **{{ (c - ref) | round(1) }} kWh** donnés à la voiture. + {% else %} + Cette nuit, la voiture pourra prendre environ **{{ b | round(1) }} kWh** + sur la batterie sans EDF (profil prudent, plancher 22 %, la batterie + tient jusqu'à la nuit suivante). + {% endif %} + + {% if m == 'complete' %} + 🔌 **Charge complète** : le budget batterie d'abord, puis EDF en heures + creuses pour le reste. Retour automatique à « sans EDF » demain à 12:00. + {% else %} + 🔋 **Sans EDF** (par défaut) : la voiture s'arrête au budget et attend le + soleil. Si EDF se ferme de toute façon pour protéger la batterie, elle en + profite en heures creuses. + {% endif %} # Source de la prevision du bilan (28/09/2026) : VRM en essai, comparaison # prevue le 05/10. Revenir a forecast_solar = retour arriere immediat. - type: entities diff --git a/dashboards/evcs_master.yaml b/dashboards/evcs_master.yaml index 246f244..2733e09 100755 --- a/dashboards/evcs_master.yaml +++ b/dashboards/evcs_master.yaml @@ -225,6 +225,10 @@ views: name: 🔋 Niveau du véhicule branché (à saisir au branchement) - entity: input_datetime.victron_evcs_departure_ponctuel name: 🕒 Repart aujourd'hui à (départ ponctuel, VW / ZERO) + - entity: input_select.victron_evcs_night_charge_mode + name: 🌙 Charge cette nuit (sans EDF = budget batterie seul) + - entity: input_number.evcs_balance_night_vehicle_budget_kwh + name: 🔋 Budget batterie de la nuit sans EDF - entity: input_datetime.victron_evcs_jeep_midday_departure name: JEEP repart après midi à - entity: input_text.victron_evcs_jeep_midday_departure_days diff --git a/tests/diagnostics-agent/run.mjs b/tests/diagnostics-agent/run.mjs index f8bac8b..6bcb7ef 100644 --- a/tests/diagnostics-agent/run.mjs +++ b/tests/diagnostics-agent/run.mjs @@ -438,6 +438,33 @@ for (const [nom, r, test] of imp) { (r) => r.etat === 'indisponible' && /etat « erreur »/.test(r.attributs.explication)], ['bilan perime -> indisponible, dit depuis combien de temps', previsionNuit(base(), new Date('2026-09-28T00:30:00Z')), (r) => r.etat === 'indisponible' && /recalcule depuis 65 min/.test(r.attributs.explication)], + // Budget voiture de la nuit (30/09) : ce que la batterie peut donner sans EDF. + ...(() => { + const nuit = (extra) => { const m = base(); for (const [k, v] of Object.entries({ + 'input_boolean.victron_evcs_night_grid_session_committed': 'off', 'input_boolean.evcs_balance_grid_needed': 'off', + 'input_number.evcs_balance_night_vehicle_budget_kwh': '6.46', ...extra, + })) m.set(k, { state: v }); return previsionNuit(m, t); }; + return [ + ['voiture branchee, sans EDF (defaut) : « environ 6,5 kWh sur la batterie … s arrete »', + nuit({ 'binary_sensor.victron_evcs_vehicle_connected': 'on' }), + (r) => r.attributs.mode_charge_nuit === 'sans_edf' && r.attributs.budget_voiture_kwh === 6.46 + && /Voiture branchee\. La voiture peut prendre environ 6,5 kWh sur la batterie sans EDF, puis elle s arrete/.test(r.attributs.texte_rapport)], + ['charge complete demandee : « puis EDF en heures creuses pour le reste »', + nuit({ 'binary_sensor.victron_evcs_vehicle_connected': 'on', 'input_select.victron_evcs_night_charge_mode': 'complete' }), + (r) => r.attributs.mode_charge_nuit === 'complete' && /puis EDF en heures creuses pour le reste/.test(r.attributs.texte_rapport)], + ['session : budget FIGE et deja donne (3,7 fige a l index 4000, compteur 4001,2)', + nuit({ 'binary_sensor.victron_evcs_vehicle_connected': 'on', 'input_text.victron_evcs_night_budget': '3.7|4000', + 'sensor.evcs_hq2239dnt7p_total_energy': '4001.2' }), + (r) => r.attributs.budget_voiture_fige && r.attributs.budget_voiture_kwh === 3.7 && r.attributs.budget_deja_donne_kwh === 1.2 + && /environ 3,7 kWh/.test(r.attributs.texte_rapport) && /Deja 1,2 kWh donnes/.test(r.attributs.texte_rapport)], + ['budget nul, sans EDF : « rien … elle attendra le soleil »', + nuit({ 'binary_sensor.victron_evcs_vehicle_connected': 'on', 'input_number.evcs_balance_night_vehicle_budget_kwh': '0' }), + (r) => /La batterie ne peut rien donner a la voiture cette nuit sans EDF : elle attendra le soleil/.test(r.attributs.texte_rapport)], + ['aucune voiture : le budget est annonce pour le cas ou on en branche une', + nuit({ 'binary_sensor.victron_evcs_vehicle_connected': 'off' }), + (r) => /Aucun vehicule branche\. S il y en a un cette nuit : la voiture peut prendre environ 6,5 kWh/.test(r.attributs.texte_rapport)], + ]; + })(), ['plus jamais d attribut reseau_necessaire_kwh (lu comme un achat le 28/09)', previsionNuit(base(), t), (r) => !('reseau_necessaire_kwh' in r.attributs) && 'import_prevu_kwh' in r.attributs], ['jamais d attribut qui ressemble a un SOC du matin (incident 21:16)', previsionNuit(base(), t), diff --git a/tools/diagnostics-agent.mjs b/tools/diagnostics-agent.mjs index 38fe683..c8bbe43 100644 --- a/tools/diagnostics-agent.mjs +++ b/tools/diagnostics-agent.mjs @@ -652,13 +652,37 @@ export function previsionNuit(st, maintenant = new Date()) { ? ' Sous reserve : l historique de production de ces heures de fin de journee est incomplet (batterie souvent pleine a cette heure) ; la decision reelle est reprise ' + (hcDebut ? `a ${hcDebut}` : 'a l ouverture des heures creuses') + '.' : ''; attributs.reserve_bilan = degrade ? 'forme de production minoree sur les heures de deficit' : null; - const voiture = attributs.voiture === 'aucune' - ? 'Aucun vehicule branche.' - : attributs.voiture === 'branchee' - ? 'Un vehicule est branche : sa charge s ajoute et sera decidee par le controleur.' - : 'Etat du vehicule inconnu.'; const hc = hcDebut ? `a ${hcDebut}` : 'a l ouverture des heures creuses'; const kwh = (x) => String(x).replace('.', ','); + // BUDGET VOITURE DE LA NUIT (30/09) : ce que la batterie peut donner sans EDF, + // et le reglage de la nuit (sans_edf par defaut, complete sur demande). Pendant + // la session, le budget FIGE par le controleur et ce qui a deja ete donne. + const modeNuit = txt('input_select.victron_evcs_night_charge_mode') === 'complete' ? 'complete' : 'sans_edf'; + const fige = (txt('input_text.victron_evcs_night_budget') || '').match(/^\s*([\d.]+)\|([\d.]+)\s*$/); + const compteur = nombre(st, 'sensor.evcs_hq2239dnt7p_total_energy'); + const budgetPublie = nombre(st, 'input_number.evcs_balance_night_vehicle_budget_kwh'); + const budgetKwh = fige ? Number(fige[1]) : budgetPublie; + const dejaDonne = fige && compteur !== null ? Math.max(0, Math.round((compteur - Number(fige[2])) * 10) / 10) : null; + const b = budgetKwh !== null ? kwh(Math.round(budgetKwh * 10) / 10) : null; + const suite = modeNuit === 'complete' + ? 'puis EDF en heures creuses pour le reste (charge complete demandee)' + : 'puis elle s arrete et attend le soleil (reglage « sans EDF »)'; + const phraseBudget = b === null ? '' + : budgetKwh < 0.1 + ? (modeNuit === 'complete' + ? 'La batterie ne peut rien donner a la voiture cette nuit : EDF la chargera en heures creuses (charge complete demandee).' + : 'La batterie ne peut rien donner a la voiture cette nuit sans EDF : elle attendra le soleil (reglage « sans EDF »).') + : `La voiture peut prendre environ ${b} kWh sur la batterie sans EDF, ${suite}.` + + (dejaDonne !== null ? ` Deja ${kwh(dejaDonne)} kWh donnes.` : ''); + attributs.mode_charge_nuit = modeNuit; + attributs.budget_voiture_kwh = budgetKwh; + attributs.budget_voiture_fige = !!fige; + attributs.budget_deja_donne_kwh = dejaDonne; + const voiture = attributs.voiture === 'aucune' + ? 'Aucun vehicule branche.' + (phraseBudget ? ` S il y en a un cette nuit : ${phraseBudget.charAt(0).toLowerCase()}${phraseBudget.slice(1)}` : '') + : attributs.voiture === 'branchee' + ? (phraseBudget ? `Voiture branchee. ${phraseBudget}` : 'Un vehicule est branche : sa charge s ajoute et sera decidee par le controleur.') + : 'Etat du vehicule inconnu.'; const jusqua = hcFin ? `jusqu a ${hcFin}` : 'jusqu a la fin des heures creuses'; const plage = hcDebut && hcFin ? `de ${hcDebut} a ${hcFin}` : 'en heures creuses'; const detail = maisonHcKwh === null ? '' @@ -693,7 +717,8 @@ export function previsionNuit(st, maintenant = new Date()) { const achatSi = maisonHcKwh !== null ? ` Si EDF s engage ${hc}, la maison y achetera environ ${kwh(Math.round(maisonHcKwh * 10) / 10)} kWh ${jusqua}.` : ''; const texteJour = `Il est trop tot pour la prevision de la nuit : elle se calcule le soir, sur la consommation reelle ` + - `de la soiree, et la decision d EDF se prend ${hc}.${repere}${achatSi} Batterie a ${soc !== null ? Math.round(soc) : '?'} % maintenant.`; + `de la soiree, et la decision d EDF se prend ${hc}.${repere}${achatSi} Batterie a ${soc !== null ? Math.round(soc) : '?'} % maintenant.` + + (phraseBudget ? ` ${phraseBudget}` : ''); return { etat: 'jour', attributs: { ...attributs, verdict: 'jour', statut: 'jour', prochaine_traversee: m ? `${m[1]} h -> ${m[2]} h` : null, prochaine_traversee_soc_requis_pct: m ? ndSoc : null, prochaine_traversee_kwh: m ? ndKwh : null,