Files
EVCS/src/energy-balance/HA_SNAPSHOT_TEMPLATE.json.tpl

133 lines
8.1 KiB
Smarty

{#- Fenetres d'heures creuses (28/09/2026). Le calendrier tarifaire reste dans
Home Assistant (helpers victron_evcs_hc_*) ; le bilan ne recoit que deux
instants : la fin de la fenetre HC en cours (vide hors fenetre) et le debut
de la fenetre HC suivante. Strategie du proprietaire : la batterie doit
tenir jusqu'a la prochaine fenetre HC. -#}
{%- set ns = namespace(fin='', suivant='', nuit='', fen=[]) -%}
{%- for j in [0, 1, 2] -%}
{%- for a, b, t in [('victron_evcs_hc_night_start', 'victron_evcs_hc_night_stop', 'nuit'), ('victron_evcs_hc_midday_start', 'victron_evcs_hc_midday_stop', 'midi')] -%}
{%- set d = today_at(states('input_datetime.' ~ a)) + timedelta(days=j) -%}
{%- set f = today_at(states('input_datetime.' ~ b)) + timedelta(days=j) -%}
{%- set f = f + timedelta(days=1) if f <= d else f -%}
{%- set ns.fen = ns.fen + [[d, f, t]] -%}
{%- endfor -%}
{%- endfor -%}
{%- for w in ns.fen | sort(attribute='0') -%}
{%- if w[0] <= now() < w[1] and ns.fin == '' -%}{%- set ns.fin = w[1] -%}{%- endif -%}
{%- endfor -%}
{%- set repere = ns.fin if ns.fin != '' else now() -%}
{%- for w in ns.fen | sort(attribute='0') -%}
{%- 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 -%}
{%- endfor -%}
{#- Vehicule branche a midi (28/09/2026) — JEEP, VW ou ZERO, selon le
selecteur. Pour chacun, HA porte le calendrier (jours et heure de depart
apres le dejeuner), la capacite et la puissance de charge MESUREES ; le
bilan recoit un instant de depart, vide si le vehicule ne repart pas
aujourd'hui. Le niveau saisi n'est retenu que s'il date de ce branchement
(tolerance 1 h avant). Capacite ou puissance a 0 = inconnue. -#}
{%- set vh = namespace(dep='', valide=false, cap=none, kw=none) -%}
{%- set jours = ['lun', 'mar', 'mer', 'jeu', 'ven', 'sam', 'dim'] -%}
{%- set cle = {'JEEP': 'jeep', 'VW': 'vw', 'ZERO': 'zero'}.get(states('input_select.victron_evcs_vehicle')) -%}
{%- if cle -%}
{%- set vh.cap = states('input_number.victron_evcs_capacity_' ~ cle ~ '_kwh') -%}
{%- set vh.kw = states('input_number.victron_evcs_charger_kw_' ~ cle) -%}
{%- set heure = states('input_datetime.victron_evcs_' ~ cle ~ '_midday_departure') -%}
{%- if jours[now().weekday()] in states('input_text.victron_evcs_' ~ cle ~ '_midday_departure_days') | lower
and heure not in ['unknown', 'unavailable', ''] -%}
{%- set d = today_at(heure) -%}
{%- if d > now() -%}{%- set vh.dep = d -%}{%- endif -%}
{%- endif -%}
{%- endif -%}
{%- set branche = states.binary_sensor.victron_evcs_vehicle_connected -%}
{%- set niveau = states.input_number.victron_evcs_vehicle_level_pct -%}
{#- Depart ponctuel (VW, ZERO : pas de jours fixes) : pour aujourd'hui, a venir,
saisi depuis ce branchement. Le plus proche des deux departs l'emporte. -#}
{%- set ponctuel = states.input_datetime.victron_evcs_departure_ponctuel -%}
{%- if branche is not none and ponctuel is not none and branche.state == 'on'
and ponctuel.state not in ['unknown', 'unavailable', ''] -%}
{%- set p = as_datetime(ponctuel.attributes.timestamp) | as_local -%}
{%- if p.date() == now().date() and p > now()
and as_timestamp(ponctuel.last_changed) >= as_timestamp(branche.last_changed) - 3600 -%}
{%- set vh.dep = p if vh.dep == '' or p < vh.dep else vh.dep -%}
{%- endif -%}
{%- endif -%}
{%- if branche is not none and niveau is not none and branche.state == 'on' -%}
{#- Date de SAISIE ecrite par l'automatisation, pas last_changed : une saisie
identique ne change pas l'etat du helper mais compte comme une saisie. -#}
{%- set vh.valide = (as_timestamp(states('input_datetime.victron_evcs_vehicle_level_at'), 0)) >= as_timestamp(branche.last_changed) - 3600 -%}
{%- endif -%}
{{ {
"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_charger_kw": vh.kw,
"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,
"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,
"nowMs": as_timestamp(now()) * 1000,
"balance_enabled": states("input_boolean.evcs_balance_enabled"),
"usable_capacity_kwh": states("input_number.evcs_balance_usable_capacity_kwh"),
"modules_en_service": states("input_number.evcs_balance_modules_en_service"),
"consumption_p50_csv": states("input_text.evcs_balance_consumption_p50"),
"consumption_p80_csv": states("input_text.evcs_balance_consumption_p80"),
"envelope_csv": states("input_text.evcs_balance_envelope"),
"censorship_rate_csv": states("input_text.evcs_balance_censorship_rate"),
"forecast_source": states("input_select.evcs_balance_forecast_source"),
"forecast_day0": states("input_text.evcs_balance_vrm_day0") if is_state("input_select.evcs_balance_forecast_source", "vrm") else states("input_text.evcs_balance_forecast_day0"),
"forecast_today_csv": states("input_text.evcs_balance_vrm_today") if is_state("input_select.evcs_balance_forecast_source", "vrm") else states("input_text.evcs_balance_forecast_today"),
"forecast_tomorrow_csv": states("input_text.evcs_balance_vrm_tomorrow") if is_state("input_select.evcs_balance_forecast_source", "vrm") else states("input_text.evcs_balance_forecast_tomorrow"),
"forecast_updated": states("input_text.evcs_balance_vrm_updated") if is_state("input_select.evcs_balance_forecast_source", "vrm") else states("input_text.evcs_balance_forecast_updated"),
"grid_soc_off": states("input_number.victron_grid_soc_off"),
"manual_hc_floor_percent": states("input_number.evcs_battery_manual_hc_floor"),
"pv_power_w": states("sensor.victron_pv_power_system"),
"house_ac_power_w": states("sensor.victron_consumption_power_l1"),
"vebus_ac_out_l1_power_w": states("sensor.victron_ve_bus_ac_out_l1_power"),
"evcs_ac_power_w": states("sensor.victron_evcs_ac_power"),
"soc_percent": states("sensor.victron_battery_soc"),
"battery_current_a": states("sensor.victron_battery_current"),
"battery_voltage_v": states("sensor.victron_battery_voltage"),
"vebus_dc_power_w": states("sensor.victron_ve_bus_dc_power_system"),
"grid_switch": states("switch.edf_general_ok_switch"),
"acin1_available": states("sensor.victron_ac_in1_available"),
"grid_power_l1_w": states("sensor.victron_grid_power_l1"),
"multiplus_state": states("sensor.airtria_mpii_v1_state"),
"multiplus_overload_alarm": states("sensor.airtria_mpii_v1_overload_alarm"),
"system_state": states("sensor.gx_device_system_state"),
"ess_batterylife_state": states("sensor.gx_device_ess_batterylife_state"),
"dc_battery_state": states("sensor.gx_device_dc_battery_state"),
"mppt_1_state": states("sensor.airtria_mppt1_state"),
"mppt_2_state": states("sensor.mppt_450_200_hq2449yr9pt_state"),
"mppt_3_state": states("sensor.v1_state"),
"evcs_native_status": states("sensor.evcs_hq2239dnt7p_status"),
"forecast_today_remaining_kwh": states("sensor.energy_production_today_remaining"),
"forecast_today_kwh": states("sensor.energy_production_today"),
"forecast_tomorrow_kwh": states("sensor.energy_production_tomorrow"),
"forecast_next_hour_w": states("sensor.power_production_next_hour"),
"sun_elevation": state_attr("sun.sun", "elevation"),
"storm_alert_level": (states.sensor
| selectattr('entity_id', 'search', '_weather_alert')
| map(attribute='state') | list | first) | default('indisponible', true),
"storm_alert_detail": (states.sensor
| selectattr('entity_id', 'search', '_weather_alert')
| map(attribute='attributes') | list | first) | default({}, true)
} | tojson }}