JEEP branchee a midi : reseau en HC seulement si le solaire ne suffit pas avant son depart
Regle du proprietaire (28/09). Le bilan compare le besoin saisi (niveau x capacite mesuree - deja delivre) au solaire disponible avant le depart de 15:30 (mar/mer/ ven/sam) sans descendre la batterie sous le plancher « tenir jusqu'a la prochaine HC ». Le controleur relaie le verdict sous les gardes du bilan ; session de midi engagee -> voiture sur le reseau avec la batterie. Bilan 61/61, golden 121/121. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uJ47NxgfpGnuuMr3f2n7Z
This commit is contained in:
33
HANDOFF.md
33
HANDOFF.md
@@ -982,5 +982,38 @@ la prochaine HC (01:24) ; sinon la HC de midi la complete, voiture comprise.**
|
|||||||
consulte pas le besoin de la nuit ; le plancher du bilan n'agit au demarrage que
|
consulte pas le besoin de la nuit ; le plancher du bilan n'agit au demarrage que
|
||||||
dans la fenetre du soir.
|
dans la fenetre du soir.
|
||||||
|
|
||||||
|
### 28/09 midi — JEEP branchee : le reseau en HC de midi quand le solaire ne suffit pas avant son depart
|
||||||
|
Regle du proprietaire : **« JEEP branchee engage le reseau en HC midi si l'analyse de
|
||||||
|
production le justifie »**. La JEEP repart a **15:30 mar/mer/ven/sam** (MANUEL §0) ;
|
||||||
|
les autres jours elle reste branchee et la HC de 01:24 finit la charge au meme tarif —
|
||||||
|
fermer le contacteur a midi n'y gagnerait rien et etoufferait le solaire.
|
||||||
|
- **Donnees (HA, `victron_evcs_vehicles.yaml`)** : niveau de la voiture SAISI
|
||||||
|
(`input_number.victron_evcs_vehicle_level_pct`, aucun capteur ne le donne ; retenu
|
||||||
|
seulement s'il date du branchement, tolerance 1 h) ; compteur de la borne memorise a
|
||||||
|
la saisie par une automatisation (`..._level_ref_kwh`) pour deduire ce qui a deja ete
|
||||||
|
delivre ; capacites MESUREES au compteur (JEEP 8,7 = plus grosse session, VW 7,7) ;
|
||||||
|
jours et heure de depart. Carte EVCS Master.
|
||||||
|
- **Analyse (bilan, `vehicleMidday`)** : en fenetre HC avec un depart avant la prochaine
|
||||||
|
HC, besoin = (100 - niveau) % x capacite - deja delivre, borne a 3,7 kW x temps
|
||||||
|
restant ; solaire disponible = batterie au-dessus du plancher « tenir jusqu'a la
|
||||||
|
prochaine HC » evalue AU DEPART (difference SIGNEE : une batterie sous son plancher
|
||||||
|
est d'abord remontee) + net prevu d'ici le depart (p50). Verdict si manque > 0,5 kWh
|
||||||
|
(3x le cout d'une fermeture du contacteur). Publie `input_boolean.evcs_balance_vehicle_grid_needed`,
|
||||||
|
`evcs_balance_vehicle_need_kwh`, `evcs_balance_vehicle_solar_kwh` + phrase dans la raison.
|
||||||
|
- **Controleur** : `middayGridSessionUncovered` inclut le verdict (memes gardes statut/
|
||||||
|
fraicheur que la cible, voiture branchee) ; session engagee + contacteur ferme a midi
|
||||||
|
-> voiture en `grid_hc_allowed` meme au soleil (la consigne garde son credit solaire).
|
||||||
|
- Tests : bilan 61/61 (6 scenarios JEEP, cas ensoleille verifie a la main : 4,05 + 4,96
|
||||||
|
= 9,01 kWh >= 7,66), golden 121/121 (5 scenarios), mutations qui mordent (2 bilan,
|
||||||
|
3 controleur). Deploye 13:53, cycle 13:55 propre (lundi : verdict off).
|
||||||
|
- Rejeu du 28/09 13:55 « si c'etait un jour de depart », JEEP a 12 % : SOC 48 -> solaire
|
||||||
|
8,3 kWh pour 5,9 absorbables -> pas de reseau ; SOC 35 -> manque 1,4 -> reseau.
|
||||||
|
- ⚠️ Le point 1 envisage (« la JEEP bridee a 13 A par la priorite batterie ») etait une
|
||||||
|
ERREUR de lecture : c'etait la rampe de demarrage (+1 A / 30 s), 19 A six minutes plus
|
||||||
|
tard. Rien a changer.
|
||||||
|
- ⚠️ A surveiller : le niveau doit etre saisi a chaque branchement (sinon la regle se
|
||||||
|
tait) ; la capacite JEEP 8,7 est a affiner sur une charge complete depuis un niveau
|
||||||
|
connu.
|
||||||
|
|
||||||
---
|
---
|
||||||
*Document mis a jour le : 28/09/2026 (~02:15) — VRM remplace Forecast.Solar (comparaison le 05/10), tenir jusqu'a la prochaine HC, prevision de nuit pour l'agent*
|
*Document mis a jour le : 28/09/2026 (~02:15) — VRM remplace Forecast.Solar (comparaison le 05/10), tenir jusqu'a la prochaine HC, prevision de nuit pour l'agent*
|
||||||
|
|||||||
@@ -219,6 +219,18 @@ views:
|
|||||||
name: Heure départ VW
|
name: Heure départ VW
|
||||||
- entity: input_datetime.victron_evcs_departure_time_jeep
|
- entity: input_datetime.victron_evcs_departure_time_jeep
|
||||||
name: Heure départ JEEP
|
name: Heure départ JEEP
|
||||||
|
- entity: input_number.victron_evcs_vehicle_level_pct
|
||||||
|
name: 🔋 Niveau de la voiture (à saisir au branchement)
|
||||||
|
- entity: input_datetime.victron_evcs_jeep_midday_departure
|
||||||
|
name: JEEP repart après midi à
|
||||||
|
- entity: input_text.victron_evcs_jeep_midday_departure_days
|
||||||
|
name: Jours où la JEEP repart (lun mar mer jeu ven sam dim)
|
||||||
|
- entity: input_boolean.evcs_balance_vehicle_grid_needed
|
||||||
|
name: Réseau justifié pour la voiture à midi (verdict du bilan)
|
||||||
|
- entity: input_number.evcs_balance_vehicle_need_kwh
|
||||||
|
name: Besoin voiture avant départ
|
||||||
|
- entity: input_number.evcs_balance_vehicle_solar_kwh
|
||||||
|
name: Solaire disponible pour la voiture avant départ
|
||||||
- entity: input_boolean.victron_evcs_allow_grid_departure_urgent
|
- entity: input_boolean.victron_evcs_allow_grid_departure_urgent
|
||||||
name: ⚡ Réseau avant départ (P9)
|
name: ⚡ Réseau avant départ (P9)
|
||||||
- entity: input_select.victron_evcs_strategy
|
- entity: input_select.victron_evcs_strategy
|
||||||
@@ -615,3 +627,25 @@ views:
|
|||||||
attribute: cause
|
attribute: cause
|
||||||
name: Cause du dernier échec
|
name: Cause du dernier échec
|
||||||
icon: mdi:alert-outline
|
icon: mdi:alert-outline
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# Interventions borne & alertes (19/09/2026)
|
||||||
|
# Le swap de vehicule trop rapide (14 s) fige la borne et bloque le
|
||||||
|
# nouveau vehicule. P11-bis (last_changed) le corrige tout seul ; ce
|
||||||
|
# bouton reste le filet MANUEL si la borne se coince malgre tout.
|
||||||
|
# L'alerte "branchee mais ne charge pas" est OPT-IN, seuils a calibrer.
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
- type: entities
|
||||||
|
title: Interventions borne & alertes
|
||||||
|
entities:
|
||||||
|
- entity: input_boolean.evcs_reset_borne
|
||||||
|
name: Reset borne (forcer un retest)
|
||||||
|
icon: mdi:restart-alert
|
||||||
|
- type: divider
|
||||||
|
- entity: input_boolean.evcs_alerte_charge_enabled
|
||||||
|
name: Envoyer les alertes WhatsApp
|
||||||
|
icon: mdi:bell-alert
|
||||||
|
- entity: input_text.evcs_alerte_charge
|
||||||
|
name: Alerte en cours (vide = rien)
|
||||||
|
icon: mdi:ev-station
|
||||||
|
secondary_info: last-changed
|
||||||
|
|||||||
@@ -194,6 +194,14 @@ appliquer les 7,70 kWh de la VW.
|
|||||||
Exemples donnes : lundi midi la JEEP · mardi midi la JEEP puis la VW la nuit ·
|
Exemples donnes : lundi midi la JEEP · mardi midi la JEEP puis la VW la nuit ·
|
||||||
mercredi midi la JEEP.
|
mercredi midi la JEEP.
|
||||||
|
|
||||||
|
**Depart de la JEEP apres le dejeuner (donne le 28/09/2026)** : elle **repart a
|
||||||
|
15:30 le mardi, le mercredi, le vendredi et le samedi** ; elle **ne repart pas**
|
||||||
|
le lundi, le jeudi ni le dimanche (elle reste branchee). Ce calendrier vit dans
|
||||||
|
HA (`input_text.victron_evcs_jeep_midday_departure_days`,
|
||||||
|
`input_datetime.victron_evcs_jeep_midday_departure`) : les jours de depart, ce
|
||||||
|
que la JEEP n'a pas pris avant 15:30 ne pourra pas l'etre en HC la nuit — c'est
|
||||||
|
ce qui peut justifier le reseau en HC de midi (HANDOFF §81).
|
||||||
|
|
||||||
**LE WEEK-END, LA ROUTINE S'INVERSE.** Sa femme travaille le samedi matin et le
|
**LE WEEK-END, LA ROUTINE S'INVERSE.** Sa femme travaille le samedi matin et le
|
||||||
dimanche matin, retour a midi : la **JEEP** charge donc le midi ET la nuit du
|
dimanche matin, retour a midi : la **JEEP** charge donc le midi ET la nuit du
|
||||||
samedi au dimanche. La **VW** ne bouge pas du week-end — sauf sortie plongee —
|
samedi au dimanche. La **VW** ne bouge pas du week-end — sauf sortie plongee —
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ input_boolean:
|
|||||||
evcs_balance_grid_needed:
|
evcs_balance_grid_needed:
|
||||||
name: "Bilan — réseau nécessaire"
|
name: "Bilan — réseau nécessaire"
|
||||||
icon: mdi:transmission-tower
|
icon: mdi:transmission-tower
|
||||||
|
# Voiture branchee en HC avec un depart avant la prochaine HC (28/09) : le
|
||||||
|
# solaire ne suffit pas a lui donner son besoin avant le depart -> le
|
||||||
|
# controleur engage la session reseau de la fenetre HC.
|
||||||
|
evcs_balance_vehicle_grid_needed:
|
||||||
|
name: "Bilan — réseau justifié pour la voiture (départ avant la prochaine HC)"
|
||||||
|
icon: mdi:car-electric
|
||||||
evcs_balance_pv_censored:
|
evcs_balance_pv_censored:
|
||||||
name: "Bilan — production actuellement bridée"
|
name: "Bilan — production actuellement bridée"
|
||||||
icon: mdi:solar-power-variant-outline
|
icon: mdi:solar-power-variant-outline
|
||||||
@@ -75,6 +81,22 @@ input_number:
|
|||||||
icon: mdi:battery-clock
|
icon: mdi:battery-clock
|
||||||
# Regle « tenir jusqu'a la prochaine HC » (28/09) : SOC a avoir en fin de
|
# Regle « tenir jusqu'a la prochaine HC » (28/09) : SOC a avoir en fin de
|
||||||
# fenetre HC en cours. 0 = hors fenetre HC ou regle sans effet.
|
# fenetre HC en cours. 0 = hors fenetre HC ou regle sans effet.
|
||||||
|
evcs_balance_vehicle_need_kwh:
|
||||||
|
name: "Bilan — besoin de la voiture avant son départ"
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
step: 0.01
|
||||||
|
unit_of_measurement: kWh
|
||||||
|
mode: box
|
||||||
|
icon: mdi:car-arrow-left
|
||||||
|
evcs_balance_vehicle_solar_kwh:
|
||||||
|
name: "Bilan — solaire disponible pour la voiture avant son départ"
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
step: 0.01
|
||||||
|
unit_of_measurement: kWh
|
||||||
|
mode: box
|
||||||
|
icon: mdi:solar-power-variant
|
||||||
evcs_balance_hc_floor_pct:
|
evcs_balance_hc_floor_pct:
|
||||||
name: "Bilan — plancher HC (prochaine HC)"
|
name: "Bilan — plancher HC (prochaine HC)"
|
||||||
min: 0
|
min: 0
|
||||||
|
|||||||
@@ -37,8 +37,84 @@ input_datetime:
|
|||||||
name: EVCS - Heure départ JEEP
|
name: EVCS - Heure départ JEEP
|
||||||
has_date: false
|
has_date: false
|
||||||
has_time: true
|
has_time: true
|
||||||
|
victron_evcs_jeep_midday_departure:
|
||||||
|
name: EVCS - Heure de départ JEEP après midi
|
||||||
|
has_date: false
|
||||||
|
has_time: true
|
||||||
|
|
||||||
input_boolean:
|
input_boolean:
|
||||||
victron_evcs_allow_grid_departure_urgent:
|
victron_evcs_allow_grid_departure_urgent:
|
||||||
name: EVCS - Réseau avant départ (P9)
|
name: EVCS - Réseau avant départ (P9)
|
||||||
icon: mdi:car-clock
|
icon: mdi:car-clock
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# JEEP BRANCHEE A MIDI (28/09/2026, strategie du proprietaire)
|
||||||
|
# « JEEP branchee engage le reseau en HC midi si l'analyse de production le
|
||||||
|
# justifie ». Le bilan energetique compare le besoin de la voiture a ce que le
|
||||||
|
# solaire peut lui donner AVANT son depart sans descendre la batterie sous le
|
||||||
|
# plancher « tenir jusqu'a la prochaine HC ». Aucun capteur ne donne le niveau
|
||||||
|
# de la voiture : le proprietaire le saisit (tableau de bord ou agent vocal).
|
||||||
|
# Le calendrier reste ici : le bilan ne recoit qu'un instant de depart.
|
||||||
|
# =====================================================
|
||||||
|
input_number:
|
||||||
|
victron_evcs_vehicle_level_pct:
|
||||||
|
name: EVCS - Niveau de charge du véhicule (saisi)
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
step: 1
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
mode: box
|
||||||
|
icon: mdi:car-battery
|
||||||
|
# Compteur de la borne au moment de la saisie du niveau (ecrit par
|
||||||
|
# l'automatisation ci-dessous) : ce que la voiture a pris DEPUIS se deduit.
|
||||||
|
victron_evcs_vehicle_level_ref_kwh:
|
||||||
|
name: EVCS - Compteur borne à la saisie du niveau
|
||||||
|
min: 0
|
||||||
|
max: 1000000
|
||||||
|
step: 0.1
|
||||||
|
unit_of_measurement: kWh
|
||||||
|
mode: box
|
||||||
|
icon: mdi:counter
|
||||||
|
# Capacites MESUREES au compteur de la borne (charge complete depuis un
|
||||||
|
# niveau bas), pas les valeurs constructeur. VW 7,70 (15/09, MANUEL §0) ;
|
||||||
|
# JEEP 8,7 = plus grosse session relevee (22/09), a affiner.
|
||||||
|
victron_evcs_capacity_jeep_kwh:
|
||||||
|
name: EVCS - Capacité mesurée JEEP
|
||||||
|
min: 1
|
||||||
|
max: 100
|
||||||
|
step: 0.1
|
||||||
|
unit_of_measurement: kWh
|
||||||
|
mode: box
|
||||||
|
icon: mdi:car-battery
|
||||||
|
victron_evcs_capacity_vw_kwh:
|
||||||
|
name: EVCS - Capacité mesurée VW
|
||||||
|
min: 1
|
||||||
|
max: 100
|
||||||
|
step: 0.1
|
||||||
|
unit_of_measurement: kWh
|
||||||
|
mode: box
|
||||||
|
icon: mdi:car-battery
|
||||||
|
|
||||||
|
input_text:
|
||||||
|
# Jours ou la JEEP repart apres le dejeuner (lun mar mer jeu ven sam dim).
|
||||||
|
victron_evcs_jeep_midday_departure_days:
|
||||||
|
name: EVCS - Jours de départ JEEP après midi
|
||||||
|
max: 60
|
||||||
|
icon: mdi:calendar-week
|
||||||
|
|
||||||
|
automation:
|
||||||
|
- id: evcs_niveau_vehicule_reference
|
||||||
|
alias: "EVCS — mémoriser le compteur de la borne à la saisie du niveau"
|
||||||
|
mode: queued
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_number.victron_evcs_vehicle_level_pct
|
||||||
|
condition:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ states('sensor.evcs_hq2239dnt7p_total_energy') | is_number }}"
|
||||||
|
action:
|
||||||
|
- service: input_number.set_value
|
||||||
|
target:
|
||||||
|
entity_id: input_number.victron_evcs_vehicle_level_ref_kwh
|
||||||
|
data:
|
||||||
|
value: "{{ states('sensor.evcs_hq2239dnt7p_total_energy') | float }}"
|
||||||
|
|||||||
@@ -301,14 +301,14 @@ const hcNightReachActive = Number.isFinite(hcWindowEndMs) && Number.isFinite(nex
|
|||||||
// Energie a avoir en reserve a la fin de la fenetre : parcours A REBOURS des
|
// Energie a avoir en reserve a la fin de la fenetre : parcours A REBOURS des
|
||||||
// tranches comprises entre les deux instants (au prorata de leur recouvrement).
|
// tranches comprises entre les deux instants (au prorata de leur recouvrement).
|
||||||
// Un surplus ne rembourse qu'un deficit POSTERIEUR — d'ou le sens du parcours.
|
// Un surplus ne rembourse qu'un deficit POSTERIEUR — d'ou le sens du parcours.
|
||||||
function hcReachFloorPct(slots, untilMs = nextHcStartMs) {
|
function hcReachFloorPct(slots, untilMs = nextHcStartMs, fromMs = hcWindowEndMs) {
|
||||||
if (!Number.isFinite(hcWindowEndMs) || !Number.isFinite(untilMs) || untilMs <= hcWindowEndMs) return 0;
|
if (!Number.isFinite(fromMs) || !Number.isFinite(untilMs) || untilMs <= fromMs) return 0;
|
||||||
let needKwh = 0;
|
let needKwh = 0;
|
||||||
for (let i = slots.length - 1; i >= 0; i--) {
|
for (let i = slots.length - 1; i >= 0; i--) {
|
||||||
const s = slots[i];
|
const s = slots[i];
|
||||||
const debut = num(s.startMs, NaN);
|
const debut = num(s.startMs, NaN);
|
||||||
if (!Number.isFinite(debut)) continue;
|
if (!Number.isFinite(debut)) continue;
|
||||||
const a = Math.max(debut, hcWindowEndMs);
|
const a = Math.max(debut, fromMs);
|
||||||
const b = Math.min(debut + 3600000, untilMs);
|
const b = Math.min(debut + 3600000, untilMs);
|
||||||
if (b <= a) continue;
|
if (b <= a) continue;
|
||||||
needKwh = Math.max(0, needKwh - s.netKwh * (b - a) / 3600000);
|
needKwh = Math.max(0, needKwh - s.netKwh * (b - a) / 3600000);
|
||||||
@@ -348,6 +348,77 @@ const hcFloorPrudentPct = Math.max(hcReachFloorPct(slotsP80, nextHcStartMs), hcF
|
|||||||
const typical = coverage(slotsP50, hcFloorTypicalPct);
|
const typical = coverage(slotsP50, hcFloorTypicalPct);
|
||||||
const prudent = coverage(slotsP80, hcFloorPrudentPct);
|
const prudent = coverage(slotsP80, hcFloorPrudentPct);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// VOITURE BRANCHEE EN HC AVEC UN DEPART AVANT LA PROCHAINE HC (28/09/2026)
|
||||||
|
//
|
||||||
|
// Regle du proprietaire : « JEEP branchee engage le reseau en HC midi si
|
||||||
|
// l'analyse de production le justifie ». La JEEP repart a 15:30 certains jours
|
||||||
|
// (calendrier dans HA) : ce qu'elle n'a pas pris avant, elle ne le prendra pas
|
||||||
|
// en HC la nuit. Ailleurs, elle reste branchee et la HC de 01:24 la finit au
|
||||||
|
// meme tarif — fermer le contacteur a midi n'y gagnerait rien et etoufferait le
|
||||||
|
// solaire (installation sans injection).
|
||||||
|
//
|
||||||
|
// Ce que le solaire peut donner a la voiture avant son depart SANS toucher a
|
||||||
|
// la reserve de la nuit : l'energie de la batterie au-dessus du plancher
|
||||||
|
// « tenir jusqu'a la prochaine HC » evalue AU DEPART, plus le bilan net prevu
|
||||||
|
// (production - maison) d'ici le depart. Profil p50, comme required_soc.
|
||||||
|
// Le reseau est justifie quand le besoin de la voiture, borne par ce que son
|
||||||
|
// chargeur peut absorber avant le depart, depasse ce solaire.
|
||||||
|
//
|
||||||
|
// Besoin = (100 - niveau saisi) % x capacite MESUREE au compteur de la borne,
|
||||||
|
// moins ce que la borne a deja delivre depuis la saisie. Aucun capteur ne donne
|
||||||
|
// le niveau de la voiture : sans saisie valable, la regle se tait.
|
||||||
|
//
|
||||||
|
// Deux constantes, justifiees :
|
||||||
|
// - 3,7 kW : plateau MESURE des deux vehicules du foyer (3 655 et 3 680 W a
|
||||||
|
// consigne pleine, MANUEL §0) ; au-dela la voiture n'absorbe rien de plus.
|
||||||
|
// - 0,5 kWh : seuil de manque en dessous duquel une session ne se justifie
|
||||||
|
// pas — une fermeture du contacteur coute 50 a 150 Wh de transitoire (§57),
|
||||||
|
// on exige un gain d'au moins trois fois ce cout.
|
||||||
|
const VEHICLE_CHARGER_KW = 3.7;
|
||||||
|
const VEHICLE_GRID_MIN_SHORTFALL_KWH = 0.5;
|
||||||
|
const nowMsBalance = num(input.nowMs, Date.now());
|
||||||
|
const vehicleConnected = input.vehicle_connected === true || input.vehicle_connected === 'true';
|
||||||
|
const vehicleDepartureMs = num(input.vehicle_departure_ms, NaN);
|
||||||
|
const vehicleLevelPct = num(input.vehicle_level_pct, NaN);
|
||||||
|
const vehicleLevelValid = input.vehicle_level_valid === true || input.vehicle_level_valid === 'true';
|
||||||
|
const vehicleCapacityKwh = num(input.vehicle_capacity_kwh, NaN);
|
||||||
|
const vehicleDeliveredKwh = Math.max(0, num(input.vehicle_delivered_since_level_kwh, 0));
|
||||||
|
const vehicleNeedKwh =
|
||||||
|
vehicleLevelValid && Number.isFinite(vehicleLevelPct) && vehicleLevelPct >= 0 && vehicleLevelPct <= 100
|
||||||
|
&& Number.isFinite(vehicleCapacityKwh) && vehicleCapacityKwh > 0
|
||||||
|
? Math.max(0, (100 - vehicleLevelPct) / 100 * vehicleCapacityKwh - vehicleDeliveredKwh)
|
||||||
|
: NaN;
|
||||||
|
const vehicleRuleActive =
|
||||||
|
hcReachActive && vehicleConnected && Number.isFinite(vehicleNeedKwh)
|
||||||
|
&& Number.isFinite(vehicleDepartureMs) && vehicleDepartureMs > nowMsBalance
|
||||||
|
&& vehicleDepartureMs < nextHcStartMs;
|
||||||
|
let vehicleSolarKwh = null;
|
||||||
|
let vehicleReachableKwh = null;
|
||||||
|
let vehicleShortfallKwh = null;
|
||||||
|
let vehicleFloorAtDeparturePct = null;
|
||||||
|
if (vehicleRuleActive) {
|
||||||
|
vehicleFloorAtDeparturePct = hcReachFloorPct(slotsP50, nextHcStartMs, vehicleDepartureMs);
|
||||||
|
let netKwh = 0;
|
||||||
|
for (const sl of slotsP50) {
|
||||||
|
const debut = num(sl.startMs, NaN);
|
||||||
|
if (!Number.isFinite(debut)) continue;
|
||||||
|
const a = Math.max(debut, nowMsBalance);
|
||||||
|
const b = Math.min(debut + 3600000, vehicleDepartureMs);
|
||||||
|
if (b > a) netKwh += sl.netKwh * (b - a) / 3600000;
|
||||||
|
}
|
||||||
|
// Difference SIGNEE : une batterie SOUS son plancher doit d'abord etre
|
||||||
|
// remontee par ce meme surplus avant que la voiture n'en voie un kWh.
|
||||||
|
const plancherDepart = Math.max(socFloorPct, vehicleFloorAtDeparturePct);
|
||||||
|
const batterieAuDessusKwh = socPct >= plancherDepart
|
||||||
|
? energyBetweenSoc(plancherDepart, socPct)
|
||||||
|
: -energyBetweenSoc(socPct, plancherDepart);
|
||||||
|
vehicleSolarKwh = Math.max(0, batterieAuDessusKwh + netKwh);
|
||||||
|
vehicleReachableKwh = Math.min(vehicleNeedKwh, VEHICLE_CHARGER_KW * (vehicleDepartureMs - nowMsBalance) / 3600000);
|
||||||
|
vehicleShortfallKwh = Math.max(0, vehicleReachableKwh - vehicleSolarKwh);
|
||||||
|
}
|
||||||
|
const vehicleGridNeeded = vehicleRuleActive && vehicleShortfallKwh > VEHICLE_GRID_MIN_SHORTFALL_KWH;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// PROCHAINE FENÊTRE DE DÉFICIT (ajout du 24/08/2026)
|
// PROCHAINE FENÊTRE DE DÉFICIT (ajout du 24/08/2026)
|
||||||
//
|
//
|
||||||
@@ -537,6 +608,18 @@ return [{
|
|||||||
surplusAvailableKwh: round2(surplusAfterCrossingKwh),
|
surplusAvailableKwh: round2(surplusAfterCrossingKwh),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Voiture branchee en HC, depart avant la prochaine HC (28/09).
|
||||||
|
vehicleMidday: {
|
||||||
|
active: vehicleRuleActive,
|
||||||
|
needKwh: Number.isFinite(vehicleNeedKwh) ? round2(vehicleNeedKwh) : null,
|
||||||
|
departureMs: Number.isFinite(vehicleDepartureMs) ? vehicleDepartureMs : null,
|
||||||
|
solarKwh: vehicleSolarKwh === null ? null : round2(vehicleSolarKwh),
|
||||||
|
reachableKwh: vehicleReachableKwh === null ? null : round2(vehicleReachableKwh),
|
||||||
|
shortfallKwh: vehicleShortfallKwh === null ? null : round2(vehicleShortfallKwh),
|
||||||
|
floorAtDeparturePct: vehicleFloorAtDeparturePct === null ? null : round1(vehicleFloorAtDeparturePct),
|
||||||
|
gridNeeded: vehicleGridNeeded,
|
||||||
|
},
|
||||||
|
|
||||||
// Tenir jusqu'a la prochaine fenetre HC (28/09) : SOC a avoir en fin de
|
// Tenir jusqu'a la prochaine fenetre HC (28/09) : SOC a avoir en fin de
|
||||||
// fenetre HC en cours. 0 hors fenetre ou si la production suffit.
|
// fenetre HC en cours. 0 hors fenetre ou si la production suffit.
|
||||||
hcReach: {
|
hcReach: {
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ setText('input_text.evcs_balance_last_run', stampParis());
|
|||||||
// --- Le bilan lui-même ---------------------------------------------------
|
// --- Le bilan lui-même ---------------------------------------------------
|
||||||
if (!balance.usable) {
|
if (!balance.usable) {
|
||||||
setText('input_text.evcs_balance_status', 'indisponible');
|
setText('input_text.evcs_balance_status', 'indisponible');
|
||||||
|
// Jamais de verdict voiture perime quand le bilan ne sait plus rien.
|
||||||
|
setBool('input_boolean.evcs_balance_vehicle_grid_needed', false);
|
||||||
setText('input_text.evcs_balance_reason',
|
setText('input_text.evcs_balance_reason',
|
||||||
`Entrées manquantes : ${(balance.missing || []).join(', ')}. Les contrôleurs gardent leurs règles.`);
|
`Entrées manquantes : ${(balance.missing || []).join(', ')}. Les contrôleurs gardent leurs règles.`);
|
||||||
return calls;
|
return calls;
|
||||||
@@ -85,6 +87,11 @@ setNumber('input_number.evcs_balance_required_soc_prudent', c.prudent.requiredSo
|
|||||||
// Regle HC (28/09) publiee pour etre verifiable : 0 = hors fenetre HC.
|
// Regle HC (28/09) publiee pour etre verifiable : 0 = hors fenetre HC.
|
||||||
setNumber('input_number.evcs_balance_hc_floor_pct', balance.hcReach ? balance.hcReach.floorPct : 0);
|
setNumber('input_number.evcs_balance_hc_floor_pct', balance.hcReach ? balance.hcReach.floorPct : 0);
|
||||||
setNumber('input_number.evcs_balance_hc_floor_prudent_pct', balance.hcReach ? balance.hcReach.floorPrudentPct : 0);
|
setNumber('input_number.evcs_balance_hc_floor_prudent_pct', balance.hcReach ? balance.hcReach.floorPrudentPct : 0);
|
||||||
|
// Voiture branchee en HC, depart avant la prochaine HC (28/09). OFF hors regle.
|
||||||
|
const vm = balance.vehicleMidday || {};
|
||||||
|
setBool('input_boolean.evcs_balance_vehicle_grid_needed', !!vm.gridNeeded);
|
||||||
|
setNumber('input_number.evcs_balance_vehicle_need_kwh', vm.active ? vm.reachableKwh : 0);
|
||||||
|
setNumber('input_number.evcs_balance_vehicle_solar_kwh', vm.active ? vm.solarKwh : 0);
|
||||||
setNumber('input_number.evcs_balance_energy_to_cover_kwh', c.energyToCoverKwh);
|
setNumber('input_number.evcs_balance_energy_to_cover_kwh', c.energyToCoverKwh);
|
||||||
setNumber('input_number.evcs_balance_grid_energy_needed_kwh', c.gridEnergyNeededKwh);
|
setNumber('input_number.evcs_balance_grid_energy_needed_kwh', c.gridEnergyNeededKwh);
|
||||||
setNumber('input_number.evcs_balance_vehicle_surplus_kwh', balance.vehicle.surplusAvailableKwh);
|
setNumber('input_number.evcs_balance_vehicle_surplus_kwh', balance.vehicle.surplusAvailableKwh);
|
||||||
@@ -143,7 +150,11 @@ const hc = balance.hcReach || {};
|
|||||||
const hcText = hc.active && hc.raisesRequirement
|
const hcText = hc.active && hc.raisesRequirement
|
||||||
? ` Tenir jusqu'à la prochaine HC (${new Date(hc.nextHcStartMs).toLocaleTimeString('fr-FR', { timeZone: 'Europe/Paris', hour: '2-digit', minute: '2-digit' })}) → ${hc.floorPct}% en fin de fenêtre.`
|
? ` Tenir jusqu'à la prochaine HC (${new Date(hc.nextHcStartMs).toLocaleTimeString('fr-FR', { timeZone: 'Europe/Paris', hour: '2-digit', minute: '2-digit' })}) → ${hc.floorPct}% en fin de fenêtre.`
|
||||||
: '';
|
: '';
|
||||||
setText('input_text.evcs_balance_reason', reason + hcText + degradedNote + ndText);
|
const hhmm = ms => new Date(ms).toLocaleTimeString('fr-FR', { timeZone: 'Europe/Paris', hour: '2-digit', minute: '2-digit' });
|
||||||
|
const vehText = vm.active
|
||||||
|
? ` Voiture (départ ${hhmm(vm.departureMs)}) : ${vm.reachableKwh} kWh à donner, solaire ${vm.solarKwh} kWh → ${vm.gridNeeded ? 'réseau justifié' : 'solaire suffisant'}.`
|
||||||
|
: '';
|
||||||
|
setText('input_text.evcs_balance_reason', reason + hcText + vehText + degradedNote + ndText);
|
||||||
|
|
||||||
// Trace longue durée, pour comparer jour après jour le seuil calculé au seuil
|
// Trace longue durée, pour comparer jour après jour le seuil calculé au seuil
|
||||||
// fixe encore en vigueur — c'est cette comparaison qui décidera du câblage.
|
// fixe encore en vigueur — c'est cette comparaison qui décidera du câblage.
|
||||||
|
|||||||
@@ -20,8 +20,34 @@
|
|||||||
{%- if w[0] >= repere and ns.suivant == '' -%}{%- set ns.suivant = w[0] -%}{%- endif -%}
|
{%- if w[0] >= repere and ns.suivant == '' -%}{%- set ns.suivant = w[0] -%}{%- endif -%}
|
||||||
{%- if w[0] >= repere and w[2] == 'nuit' and ns.nuit == '' -%}{%- set ns.nuit = w[0] -%}{%- endif -%}
|
{%- if w[0] >= repere and w[2] == 'nuit' and ns.nuit == '' -%}{%- set ns.nuit = w[0] -%}{%- endif -%}
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
|
{#- Voiture branchee a midi (28/09/2026). Le calendrier (jours et heure de
|
||||||
|
depart de la JEEP apres le dejeuner) reste dans HA : le bilan recoit un
|
||||||
|
instant de depart, vide si la voiture ne repart pas aujourd'hui. Le niveau
|
||||||
|
saisi n'est retenu que s'il date de ce branchement (tolerance 1 h avant). -#}
|
||||||
|
{%- set vh = namespace(dep='', valide=false, cap=none) -%}
|
||||||
|
{%- set jours = ['lun', 'mar', 'mer', 'jeu', 'ven', 'sam', 'dim'] -%}
|
||||||
|
{%- if is_state('input_select.victron_evcs_vehicle', 'JEEP') -%}
|
||||||
|
{%- set vh.cap = states('input_number.victron_evcs_capacity_jeep_kwh') -%}
|
||||||
|
{%- if jours[now().weekday()] in states('input_text.victron_evcs_jeep_midday_departure_days') | lower -%}
|
||||||
|
{%- set d = today_at(states('input_datetime.victron_evcs_jeep_midday_departure')) -%}
|
||||||
|
{%- if d > now() -%}{%- set vh.dep = d -%}{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- elif is_state('input_select.victron_evcs_vehicle', 'VW') -%}
|
||||||
|
{%- set vh.cap = states('input_number.victron_evcs_capacity_vw_kwh') -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- set branche = states.binary_sensor.victron_evcs_vehicle_connected -%}
|
||||||
|
{%- set niveau = states.input_number.victron_evcs_vehicle_level_pct -%}
|
||||||
|
{%- if branche is not none and niveau is not none and branche.state == 'on' -%}
|
||||||
|
{%- set vh.valide = as_timestamp(niveau.last_changed) >= as_timestamp(branche.last_changed) - 3600 -%}
|
||||||
|
{%- endif -%}
|
||||||
{{ {
|
{{ {
|
||||||
"ts": now().isoformat(),
|
"ts": now().isoformat(),
|
||||||
|
"vehicle_connected": is_state("binary_sensor.victron_evcs_vehicle_connected", "on"),
|
||||||
|
"vehicle_departure_ms": (as_timestamp(vh.dep) * 1000) if vh.dep != '' else none,
|
||||||
|
"vehicle_level_pct": states("input_number.victron_evcs_vehicle_level_pct"),
|
||||||
|
"vehicle_level_valid": vh.valide,
|
||||||
|
"vehicle_capacity_kwh": vh.cap,
|
||||||
|
"vehicle_delivered_since_level_kwh": ((states("sensor.evcs_hq2239dnt7p_total_energy") | float(0)) - (states("input_number.victron_evcs_vehicle_level_ref_kwh") | float(0))) if states("sensor.evcs_hq2239dnt7p_total_energy") | is_number else none,
|
||||||
"hc_window_end_ms": (as_timestamp(ns.fin) * 1000) if ns.fin != '' else none,
|
"hc_window_end_ms": (as_timestamp(ns.fin) * 1000) if ns.fin != '' else none,
|
||||||
"next_hc_start_ms": (as_timestamp(ns.suivant) * 1000) if ns.suivant != '' else none,
|
"next_hc_start_ms": (as_timestamp(ns.suivant) * 1000) if ns.suivant != '' else none,
|
||||||
"next_night_hc_start_ms": (as_timestamp(ns.nuit) * 1000) if ns.nuit != '' else none,
|
"next_night_hc_start_ms": (as_timestamp(ns.nuit) * 1000) if ns.nuit != '' else none,
|
||||||
|
|||||||
@@ -60,6 +60,13 @@ return [{
|
|||||||
// Plancher de charge HC manuelle (18/09) : oublie ici jusqu'au 28/09, le
|
// Plancher de charge HC manuelle (18/09) : oublie ici jusqu'au 28/09, le
|
||||||
// bouton n'avait donc jamais atteint le bilan.
|
// bouton n'avait donc jamais atteint le bilan.
|
||||||
manual_hc_floor_percent: snapshot.manual_hc_floor_percent,
|
manual_hc_floor_percent: snapshot.manual_hc_floor_percent,
|
||||||
|
// Voiture branchee a midi (28/09) : depart du jour et besoin saisi.
|
||||||
|
vehicle_connected: snapshot.vehicle_connected,
|
||||||
|
vehicle_departure_ms: snapshot.vehicle_departure_ms,
|
||||||
|
vehicle_level_pct: snapshot.vehicle_level_pct,
|
||||||
|
vehicle_level_valid: snapshot.vehicle_level_valid,
|
||||||
|
vehicle_capacity_kwh: snapshot.vehicle_capacity_kwh,
|
||||||
|
vehicle_delivered_since_level_kwh: snapshot.vehicle_delivered_since_level_kwh,
|
||||||
hc_window_end_ms: snapshot.hc_window_end_ms,
|
hc_window_end_ms: snapshot.hc_window_end_ms,
|
||||||
next_hc_start_ms: snapshot.next_hc_start_ms,
|
next_hc_start_ms: snapshot.next_hc_start_ms,
|
||||||
next_night_hc_start_ms: snapshot.next_night_hc_start_ms,
|
next_night_hc_start_ms: snapshot.next_night_hc_start_ms,
|
||||||
|
|||||||
@@ -153,6 +153,7 @@
|
|||||||
"dvcc_max_charge_current_a": states("sensor.victron_max_charge_current_setting"),
|
"dvcc_max_charge_current_a": states("sensor.victron_max_charge_current_setting"),
|
||||||
"balance_status": states("input_text.evcs_balance_status"),
|
"balance_status": states("input_text.evcs_balance_status"),
|
||||||
"balance_last_run": states("input_text.evcs_balance_last_run"),
|
"balance_last_run": states("input_text.evcs_balance_last_run"),
|
||||||
|
"balance_vehicle_grid_needed": states("input_boolean.evcs_balance_vehicle_grid_needed"),
|
||||||
"allow_grid_hc_midday": states("input_boolean.victron_evcs_allow_grid_hc_midday"),
|
"allow_grid_hc_midday": states("input_boolean.victron_evcs_allow_grid_hc_midday"),
|
||||||
"probe_enabled": states("input_boolean.victron_evcs_probe_enabled"),
|
"probe_enabled": states("input_boolean.victron_evcs_probe_enabled"),
|
||||||
|
|
||||||
|
|||||||
@@ -487,6 +487,9 @@ const params = {
|
|||||||
// BILAN ENERGETIQUE (03/09/2026). Meme format d'horodatage que l'advisor
|
// BILAN ENERGETIQUE (03/09/2026). Meme format d'horodatage que l'advisor
|
||||||
// ("DD/MM HH:mm"), d'ou la reutilisation du parseur.
|
// ("DD/MM HH:mm"), d'ou la reutilisation du parseur.
|
||||||
balanceRequiredSocPct: num(input.balance_required_soc, NaN),
|
balanceRequiredSocPct: num(input.balance_required_soc, NaN),
|
||||||
|
// Voiture branchee en HC avec un depart avant la prochaine HC (28/09) : le
|
||||||
|
// bilan juge que le solaire ne lui donnera pas son besoin avant le depart.
|
||||||
|
balanceVehicleGridNeeded: low(input.balance_vehicle_grid_needed || 'off') === 'on',
|
||||||
// P12 — charge nuit sur batterie. Le plancher est le SOC requis PRUDENT (p80)
|
// P12 — charge nuit sur batterie. Le plancher est le SOC requis PRUDENT (p80)
|
||||||
// et non le p50 : la marge est mesuree, pas inventee. On descend la voiture
|
// et non le p50 : la marge est mesuree, pas inventee. On descend la voiture
|
||||||
// jusqu'a ce que la batterie n'ait plus que ce qu'il lui faut pour tenir
|
// jusqu'a ce que la batterie n'ait plus que ce qu'il lui faut pour tenir
|
||||||
|
|||||||
@@ -739,10 +739,24 @@ const nightGridSessionUncovered =
|
|||||||
// `required_soc` = 22,0 pour `energy_to_cover` = 0,0. Il ne remonte qu'un jour
|
// `required_soc` = 22,0 pour `energy_to_cover` = 0,0. Il ne remonte qu'un jour
|
||||||
// sombre — ou quand le proprietaire pose un plancher manuel, ce qui est
|
// sombre — ou quand le proprietaire pose un plancher manuel, ce qui est
|
||||||
// exactement le cas d'usage.
|
// exactement le cas d'usage.
|
||||||
|
//
|
||||||
|
// ET LA VOITURE (28/09/2026, strategie du proprietaire) : « JEEP branchee
|
||||||
|
// engage le reseau en HC midi si l'analyse de production le justifie ». Le
|
||||||
|
// bilan fait l'analyse (besoin saisi contre solaire disponible avant le depart,
|
||||||
|
// batterie gardee pour tenir jusqu'a 01:24) et publie un verdict ; on le relaie,
|
||||||
|
// sous les memes gardes de fraicheur et de statut que la cible.
|
||||||
|
const middayVehicleGridNeeded =
|
||||||
|
balanceMorningTargetApplied &&
|
||||||
|
params.balanceVehicleGridNeeded &&
|
||||||
|
parsed.evcsVehicleConnected;
|
||||||
|
|
||||||
const middayGridSessionUncovered =
|
const middayGridSessionUncovered =
|
||||||
balanceMorningTargetApplied &&
|
balanceMorningTargetApplied &&
|
||||||
Number.isFinite(params.balanceRequiredSocPct) &&
|
(
|
||||||
parsed.soc < params.balanceRequiredSocPct;
|
(Number.isFinite(params.balanceRequiredSocPct) &&
|
||||||
|
parsed.soc < params.balanceRequiredSocPct) ||
|
||||||
|
middayVehicleGridNeeded
|
||||||
|
);
|
||||||
|
|
||||||
const gridSessionInHcWindow =
|
const gridSessionInHcWindow =
|
||||||
profile === 'night_hc' || profile === 'midday_hc_or_solar';
|
profile === 'night_hc' || profile === 'midday_hc_or_solar';
|
||||||
@@ -931,7 +945,15 @@ else if (profile === 'night_hc') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
else if (profile === 'midday_hc_or_solar' || profile === 'morning_solar') {
|
else if (profile === 'midday_hc_or_solar' || profile === 'morning_solar') {
|
||||||
if (strongSolarSignal) {
|
// Session de midi engagee et contacteur ferme : la voiture charge sur le
|
||||||
|
// reseau avec la batterie (« batterie et voiture ensemble », 28/09), meme au
|
||||||
|
// soleil. La consigne garde son credit solaire (HA_SERVICE_CALLS_OUTPUT) :
|
||||||
|
// elle ne preleve au reseau que ce que les chargeurs ne donnent pas.
|
||||||
|
if (profile === 'midday_hc_or_solar' && nightGridSessionCommitted && parsed.gridAvailable) {
|
||||||
|
sourceMode = 'grid_hc_allowed';
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strongSolarSignal) {
|
||||||
sourceMode = 'solar_only';
|
sourceMode = 'solar_only';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1168,6 +1190,7 @@ return [{
|
|||||||
nightBatterySurplusAvailable,
|
nightBatterySurplusAvailable,
|
||||||
nightBatterySurplusExhausted,
|
nightBatterySurplusExhausted,
|
||||||
nightGridSessionCommitted,
|
nightGridSessionCommitted,
|
||||||
|
middayVehicleGridNeeded,
|
||||||
nightGridSessionUncovered,
|
nightGridSessionUncovered,
|
||||||
balanceTargetAgeMin:
|
balanceTargetAgeMin:
|
||||||
balanceTargetAgeMin === null ? null : Math.round(balanceTargetAgeMin * 10) / 10,
|
balanceTargetAgeMin === null ? null : Math.round(balanceTargetAgeMin * 10) / 10,
|
||||||
|
|||||||
@@ -106,6 +106,13 @@ const SUMMARIZE = {
|
|||||||
hcReachActive: o.hcReach ? o.hcReach.active : false,
|
hcReachActive: o.hcReach ? o.hcReach.active : false,
|
||||||
hcFloorPct: o.hcReach ? o.hcReach.floorPct : null,
|
hcFloorPct: o.hcReach ? o.hcReach.floorPct : null,
|
||||||
hcNightFloorPrudentPct: o.hcReach ? o.hcReach.nightFloorPrudentPct : null,
|
hcNightFloorPrudentPct: o.hcReach ? o.hcReach.nightFloorPrudentPct : null,
|
||||||
|
// Voiture branchee en HC, depart avant la prochaine HC (28/09).
|
||||||
|
vehicleActive: o.vehicleMidday ? o.vehicleMidday.active : false,
|
||||||
|
vehicleNeedKwh: o.vehicleMidday ? o.vehicleMidday.needKwh : null,
|
||||||
|
vehicleReachableKwh: o.vehicleMidday ? o.vehicleMidday.reachableKwh : null,
|
||||||
|
vehicleSolarKwh: o.vehicleMidday ? o.vehicleMidday.solarKwh : null,
|
||||||
|
vehicleFloorAtDeparturePct: o.vehicleMidday ? o.vehicleMidday.floorAtDeparturePct : null,
|
||||||
|
vehicleGridNeeded: o.vehicleMidday ? o.vehicleMidday.gridNeeded : false,
|
||||||
}) : ({ usable: false, missing: o.missing.join(',') }),
|
}) : ({ usable: false, missing: o.missing.join(',') }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ function snap2709(nowIso, soc, fenetre, facteur27 = 1) {
|
|||||||
const NUIT_2709 = ['2026-09-27T07:24:00+02:00', '2026-09-27T12:24:00+02:00', '2026-09-28T01:24:00+02:00'];
|
const NUIT_2709 = ['2026-09-27T07:24:00+02:00', '2026-09-27T12:24:00+02:00', '2026-09-28T01:24:00+02:00'];
|
||||||
const MIDI_2709 = ['2026-09-27T14:24:00+02:00', '2026-09-28T01:24:00+02:00', '2026-09-28T01:24:00+02:00'];
|
const MIDI_2709 = ['2026-09-27T14:24:00+02:00', '2026-09-28T01:24:00+02:00', '2026-09-28T01:24:00+02:00'];
|
||||||
const SOMBRE = 18 / 25.1;
|
const SOMBRE = 18 / 25.1;
|
||||||
|
// JEEP branchee a midi, depart 15:30 (28/09) : niveau saisi, capacite mesuree
|
||||||
|
// 8,7 kWh, rien de delivre depuis la saisie sauf mention.
|
||||||
|
function jeep(snapshot, niveau, extra = {}) {
|
||||||
|
return Object.assign(snapshot, { vehicle_connected: true,
|
||||||
|
vehicle_departure_ms: Date.parse('2026-09-27T15:30:00+02:00'), vehicle_level_pct: niveau,
|
||||||
|
vehicle_level_valid: true, vehicle_capacity_kwh: 8.7, vehicle_delivered_since_level_kwh: 0 }, extra);
|
||||||
|
}
|
||||||
|
|
||||||
export const balanceScenarios = [
|
export const balanceScenarios = [
|
||||||
{
|
{
|
||||||
@@ -432,5 +439,54 @@ export const balanceScenarios = [
|
|||||||
label: 'Hors fenetre HC : aucune exigence ajoutee (22 %, pas de reseau)',
|
label: 'Hors fenetre HC : aucune exigence ajoutee (22 %, pas de reseau)',
|
||||||
snapshot: snap2709('2026-09-27T12:30:00+02:00', 30, null, SOMBRE),
|
snapshot: snap2709('2026-09-27T12:30:00+02:00', 30, null, SOMBRE),
|
||||||
expect: { usable: true, requiredSocPct: 22, gridNeeded: false, hcReachActive: false, hcFloorPct: 0 },
|
expect: { usable: true, requiredSocPct: 22, gridNeeded: false, hcReachActive: false, hcFloorPct: 0 },
|
||||||
|
}, // ======== JEEP BRANCHEE A MIDI, DEPART 15:30 (28/09/2026) ========
|
||||||
|
{
|
||||||
|
node: 'BALANCE',
|
||||||
|
id: 'bilan-jeep-midi-soleil-solaire-suffit',
|
||||||
|
label: 'JEEP a 12 %, midi ensoleille (prevision 25,1 kWh), SOC 47 : le solaire couvre, pas de reseau',
|
||||||
|
// Verifie a la main : 88 % x 8,7 = 7,66 kWh. Plancher au depart 33,5 % ->
|
||||||
|
// 13,5 points x 0,30 = 4,05 kWh de batterie + 4,96 kWh de surplus net
|
||||||
|
// 12:30-15:30 = 9,01 kWh >= 7,66.
|
||||||
|
snapshot: jeep(snap2709('2026-09-27T12:30:00+02:00', 47, MIDI_2709), 12),
|
||||||
|
expect: { usable: true, vehicleActive: true, vehicleNeedKwh: 7.66, vehicleSolarKwh: 9,
|
||||||
|
vehicleFloorAtDeparturePct: 33.5, vehicleGridNeeded: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: 'BALANCE',
|
||||||
|
id: 'bilan-jeep-midi-sombre-reseau-justifie',
|
||||||
|
label: 'JEEP a 12 %, midi sombre (18 kWh), SOC 30 : le solaire ne couvre pas avant 15:30 -> reseau justifie',
|
||||||
|
// La batterie est SOUS son plancher de depart (41,4 %) : il lui manque
|
||||||
|
// 3,42 kWh, plus que les 2,26 kWh de surplus -> rien pour la voiture.
|
||||||
|
snapshot: jeep(snap2709('2026-09-27T12:30:00+02:00', 30, MIDI_2709, SOMBRE), 12),
|
||||||
|
expect: { usable: true, vehicleActive: true, vehicleSolarKwh: 0, vehicleReachableKwh: 7.66,
|
||||||
|
vehicleGridNeeded: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: 'BALANCE',
|
||||||
|
id: 'bilan-jeep-midi-sans-depart-regle-muette',
|
||||||
|
label: 'Jour sans depart (lundi, jeudi, dimanche) : la regle se tait, la HC de 01:24 finira la charge',
|
||||||
|
snapshot: jeep(snap2709('2026-09-27T12:30:00+02:00', 30, MIDI_2709, SOMBRE), 12, { vehicle_departure_ms: null }),
|
||||||
|
expect: { usable: true, vehicleActive: false, vehicleGridNeeded: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: 'BALANCE',
|
||||||
|
id: 'bilan-jeep-midi-niveau-perime-regle-muette',
|
||||||
|
label: 'Niveau saisi avant ce branchement : on ne devine pas le besoin, la regle se tait',
|
||||||
|
snapshot: jeep(snap2709('2026-09-27T12:30:00+02:00', 30, MIDI_2709, SOMBRE), 12, { vehicle_level_valid: false }),
|
||||||
|
expect: { usable: true, vehicleActive: false, vehicleGridNeeded: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: 'BALANCE',
|
||||||
|
id: 'bilan-jeep-midi-deja-chargee-par-le-solaire',
|
||||||
|
label: 'Saisie 12 % puis 7,5 kWh deja delivres : besoin residuel 0,16 kWh, sous le seuil -> pas de reseau',
|
||||||
|
snapshot: jeep(snap2709('2026-09-27T12:30:00+02:00', 30, MIDI_2709, SOMBRE), 12, { vehicle_delivered_since_level_kwh: 7.5 }),
|
||||||
|
expect: { usable: true, vehicleActive: true, vehicleNeedKwh: 0.16, vehicleGridNeeded: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: 'BALANCE',
|
||||||
|
id: 'bilan-jeep-hors-fenetre-hc-regle-muette',
|
||||||
|
label: 'Hors fenetre HC (15:00) : la regle ne s engage jamais hors HC',
|
||||||
|
snapshot: jeep(snap2709('2026-09-27T15:00:00+02:00', 30, null, SOMBRE), 12),
|
||||||
|
expect: { usable: true, vehicleActive: false, vehicleGridNeeded: false },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -2562,6 +2562,103 @@ export const scenarios = [
|
|||||||
takeoverNeeded: false,
|
takeoverNeeded: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// ======== JEEP BRANCHEE A MIDI, DEPART 15:30 (28/09/2026) ========
|
||||||
|
// « JEEP branchee engage le reseau en HC midi si l'analyse de production le
|
||||||
|
// justifie » : le BILAN fait l'analyse et publie un verdict, le controleur le
|
||||||
|
// relaie sous les memes gardes (statut, fraicheur) que la cible. Ici la
|
||||||
|
// batterie n'a rien a reclamer (SOC 45 > 30) : seul le verdict voiture engage.
|
||||||
|
{
|
||||||
|
id: 'hc-midi-jeep-verdict-bilan-engage-la-session',
|
||||||
|
label: 'MIDI 28/09 — JEEP branchee, le bilan juge le solaire insuffisant avant 15:30 : la session HC de midi s engage',
|
||||||
|
time: MIDDAY,
|
||||||
|
overrides: {
|
||||||
|
evcs_vehicle_connected: 'on',
|
||||||
|
evcs_device_connected: 'on',
|
||||||
|
soc_percent: '45', battery_current_a: '10',
|
||||||
|
pv_power_w: '3000', pv_power_10m_avg_w: '3000', pv_trend_w: '0',
|
||||||
|
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||||||
|
evcs_ac_power_w: '0',
|
||||||
|
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||||||
|
balance_grid_energy_needed_kwh: '0',
|
||||||
|
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||||||
|
balance_vehicle_grid_needed: 'on',
|
||||||
|
},
|
||||||
|
expect: { profile: 'midday_hc_or_solar', sessionCommittedService: 'turn_on', takeoverNeeded: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hc-midi-jeep-sans-verdict-aucun-engagement',
|
||||||
|
label: 'CONTREFACTUEL — meme midi, le bilan juge le solaire suffisant : aucune session',
|
||||||
|
time: MIDDAY,
|
||||||
|
overrides: {
|
||||||
|
evcs_vehicle_connected: 'on',
|
||||||
|
evcs_device_connected: 'on',
|
||||||
|
soc_percent: '45', battery_current_a: '10',
|
||||||
|
pv_power_w: '3000', pv_power_10m_avg_w: '3000', pv_trend_w: '0',
|
||||||
|
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||||||
|
evcs_ac_power_w: '0',
|
||||||
|
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||||||
|
balance_grid_energy_needed_kwh: '0',
|
||||||
|
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||||||
|
balance_vehicle_grid_needed: 'off',
|
||||||
|
},
|
||||||
|
expect: { profile: 'midday_hc_or_solar', takeoverNeeded: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hc-midi-jeep-verdict-voiture-debranchee',
|
||||||
|
label: 'CONTREFACTUEL — verdict encore a ON mais voiture debranchee : aucune session',
|
||||||
|
time: MIDDAY,
|
||||||
|
overrides: {
|
||||||
|
evcs_vehicle_connected: 'off',
|
||||||
|
evcs_device_connected: 'on',
|
||||||
|
soc_percent: '45', battery_current_a: '10',
|
||||||
|
pv_power_w: '3000', pv_power_10m_avg_w: '3000', pv_trend_w: '0',
|
||||||
|
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||||||
|
evcs_ac_power_w: '0',
|
||||||
|
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||||||
|
balance_grid_energy_needed_kwh: '0',
|
||||||
|
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||||||
|
balance_vehicle_grid_needed: 'on',
|
||||||
|
},
|
||||||
|
expect: { profile: 'midday_hc_or_solar', takeoverNeeded: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hc-midi-jeep-verdict-bilan-perime',
|
||||||
|
label: 'CONTREFACTUEL — verdict a ON mais bilan vieux de 2 h : on ne relaie pas un verdict perime',
|
||||||
|
time: MIDDAY,
|
||||||
|
overrides: {
|
||||||
|
evcs_vehicle_connected: 'on',
|
||||||
|
evcs_device_connected: 'on',
|
||||||
|
soc_percent: '45', battery_current_a: '10',
|
||||||
|
pv_power_w: '3000', pv_power_10m_avg_w: '3000', pv_trend_w: '0',
|
||||||
|
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||||||
|
evcs_ac_power_w: '0',
|
||||||
|
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||||||
|
balance_grid_energy_needed_kwh: '0',
|
||||||
|
balance_status: 'ok', balance_last_run: '08/08 11:00',
|
||||||
|
balance_vehicle_grid_needed: 'on',
|
||||||
|
},
|
||||||
|
expect: { profile: 'midday_hc_or_solar', takeoverNeeded: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hc-midi-session-engagee-voiture-sur-reseau',
|
||||||
|
label: 'MIDI — session engagee, contacteur ferme, plein soleil : la voiture passe sur le reseau (batterie et voiture ensemble)',
|
||||||
|
time: MIDDAY,
|
||||||
|
overrides: {
|
||||||
|
evcs_vehicle_connected: 'on',
|
||||||
|
grid_state: 'on', victron_acin1_available: '1',
|
||||||
|
night_grid_session_committed_prev: 'on',
|
||||||
|
evcs_device_connected: 'on',
|
||||||
|
soc_percent: '45', battery_current_a: '10',
|
||||||
|
pv_power_w: '6000', pv_power_10m_avg_w: '6000', pv_trend_w: '0',
|
||||||
|
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
|
||||||
|
evcs_ac_power_w: '0',
|
||||||
|
balance_required_soc: '30', balance_energy_to_cover_kwh: '0',
|
||||||
|
balance_grid_energy_needed_kwh: '0',
|
||||||
|
balance_status: 'ok', balance_last_run: '08/08 12:50',
|
||||||
|
balance_vehicle_grid_needed: 'on',
|
||||||
|
},
|
||||||
|
expect: { profile: 'midday_hc_or_solar', sourceMode: 'grid_hc_allowed', takeoverNeeded: true },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'consigne-bilan-plancher-nuage-exclut-la-batterie',
|
id: 'consigne-bilan-plancher-nuage-exclut-la-batterie',
|
||||||
label: 'CONSIGNE 18/09 — midi, soleil bride, manque batterie, PAS de voiture : le terme batterie ne doit PAS remonter le plancher nuage',
|
label: 'CONSIGNE 18/09 — midi, soleil bride, manque batterie, PAS de voiture : le terme batterie ne doit PAS remonter le plancher nuage',
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user