Le graphe « production prevue et realisee heure par heure » du troisieme onglet du bilan energetique ne montrait que du passe : sa courbe « prevue » etait la prevision publiee le matin de chaque jour ECOULE, et la fenetre s arretait a l heure en cours. C est un instrument de mesure du biais, pas une prevision. Deux courbes desormais, et elles n ont ni la meme provenance ni le meme usage. Pour un jour ecoule, ce qui avait ete annonce LE MATIN MEME reste la seule reference honnete : une prevision revisee a 18 h connait deja la journee, la comparer au reel ne mesure plus rien. Pour les heures a venir, c est la DERNIERE publication qui fait foi — aujourd hui jusqu au soir, demain en entier. Les deux se touchent sur l heure en cours au lieu de laisser un trou, et la seconde est tracee en pointilles. Au-dela de l heure en cours, « reel » et « bridage » valent NULL et non zero : un zero dessinerait une chute de production qui n a pas eu lieu. Defaut trouve en chemin : l ecart moyen comptait l heure EN COURS, mesuree sur ses seules minutes ecoulees. A 14h05 son reel vaut cinq minutes de production contre une prevision d heure pleine, soit -92 % qui n existent pas. La colonne `clos` l exclut. Et la serie mesuree s arrete desormais a la minute courante et non au debut de l heure, si bien que l heure en cours est reellement tracee au lieu de valoir zero. Le partage aujourd hui / demain se fait en SQL, dans le fuseau deja utilise par le reste de la requete : le conteneur repond en UTC a `date`, il ne doit pas servir d arbitre. Fenetre de la carte portee a hier / aujourd hui / demain (graph_span 3d, span end day offset +1d) : sans cela la prevision, qui est justement ce qu on veut voir, tombe hors du cadre. Verifie dans le paquet installe : `span.offset` exige bien un signe, et l offset decale les deux bornes. Verification en production : 47 points de prevision publies, du 17/09 01 h au 18/09 23 h, 33,3 kWh encore attendus aujourd hui et 34,5 annonces pour demain. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
451 lines
18 KiB
YAML
451 lines
18 KiB
YAML
views:
|
||
# ===================================================================
|
||
- title: Bilan
|
||
path: bilan
|
||
icon: mdi:scale-balance
|
||
cards:
|
||
- type: vertical-stack
|
||
cards:
|
||
- type: custom:mushroom-title-card
|
||
title: Bilan énergétique
|
||
subtitle: Mode ombre — observe et publie, ne pilote rien
|
||
|
||
- type: conditional
|
||
conditions:
|
||
- entity: input_text.evcs_balance_status
|
||
state: "indisponible"
|
||
card:
|
||
type: markdown
|
||
content: |
|
||
## ⚠️ Bilan indisponible
|
||
|
||
{{ states('input_text.evcs_balance_reason') }}
|
||
|
||
Les contrôleurs fonctionnent sur leurs règles historiques.
|
||
|
||
- type: conditional
|
||
conditions:
|
||
- entity: input_text.evcs_balance_status
|
||
state: "degrade"
|
||
card:
|
||
type: markdown
|
||
content: |
|
||
## 🟠 Bilan dégradé
|
||
|
||
Les chiffres sont publiés, mais ils reposent sur une base
|
||
incomplète : soit aucun croisement production/consommation
|
||
n'apparaît sur l'horizon, soit la forme de production des heures
|
||
de déficit vient d'un repli sur l'enveloppe mesurée.
|
||
|
||
{{ states('input_text.evcs_balance_reason') }}
|
||
|
||
# --- LA comparaison qui décidera du câblage ---------------------
|
||
- type: markdown
|
||
title: Seuil calculé contre seuil fixe
|
||
content: |
|
||
{% set req = states('input_number.evcs_balance_required_soc') | float(0) %}
|
||
{% set fixe = states('input_number.victron_grid_soc_off') | float(0) %}
|
||
{% set soc = states('sensor.victron_battery_soc') | float(0) %}
|
||
{% set ecart = req - fixe %}
|
||
| | |
|
||
|---|---:|
|
||
| SOC batterie **maintenant** | **{{ soc | round(0) }} %** |
|
||
| Seuil **calculé** par le bilan | **{{ req | round(1) }} %** |
|
||
| Seuil **fixe** encore en vigueur | {{ fixe | round(0) }} % |
|
||
| Écart | {{ '+' if ecart >= 0 else '' }}{{ ecart | round(1) }} points |
|
||
|
||
{% if req > fixe %}
|
||
Le seuil fixe est aujourd'hui **trop bas** : il laisserait la batterie
|
||
descendre sous ce qu'il faut pour tenir jusqu'au croisement.
|
||
{% elif req < fixe %}
|
||
Le seuil fixe est aujourd'hui **trop haut** : il fait importer du
|
||
réseau au-delà du nécessaire.
|
||
{% else %}
|
||
Les deux coïncident.
|
||
{% endif %}
|
||
|
||
*C'est l'accumulation de cet écart, jour après jour, qui décidera si
|
||
le bilan prend la main.*
|
||
|
||
# --- Décision immédiate ----------------------------------------
|
||
- type: entities
|
||
title: Décision immédiate
|
||
state_color: true
|
||
entities:
|
||
- entity: input_boolean.evcs_balance_grid_needed
|
||
name: Réseau nécessaire
|
||
- entity: input_text.evcs_balance_crossing_hour
|
||
name: Croisement production / consommation
|
||
- entity: input_number.evcs_balance_energy_to_cover_kwh
|
||
name: Énergie à couvrir d'ici là
|
||
- entity: input_number.evcs_balance_grid_energy_needed_kwh
|
||
name: Énergie réseau strictement nécessaire
|
||
- type: divider
|
||
- entity: input_number.evcs_balance_required_soc
|
||
name: SOC requis (décision)
|
||
- entity: input_number.evcs_balance_required_soc_prudent
|
||
name: SOC requis (planification)
|
||
|
||
# --- La nuit qui vient ------------------------------------------
|
||
- type: entities
|
||
title: Prochaine traversée déficitaire
|
||
entities:
|
||
- entity: input_text.evcs_balance_next_deficit_window
|
||
name: Fenêtre
|
||
- entity: input_number.evcs_balance_next_deficit_kwh
|
||
name: Énergie à fournir
|
||
- entity: input_number.evcs_balance_next_deficit_required_soc
|
||
name: SOC à atteindre avant
|
||
- entity: input_number.evcs_balance_storage_shortfall_kwh
|
||
name: Capacité de stockage manquante
|
||
|
||
- type: conditional
|
||
conditions:
|
||
- entity: input_number.evcs_balance_storage_shortfall_kwh
|
||
state_not: "0.0"
|
||
card:
|
||
type: markdown
|
||
content: |
|
||
### 🔋 Stockage insuffisant pour cette traversée
|
||
|
||
Il manque **{{ states('input_number.evcs_balance_storage_shortfall_kwh') }} kWh**
|
||
de capacité utile : même chargée à 100 %, la batterie actuelle
|
||
({{ states('input_number.evcs_balance_usable_capacity_kwh') }} kWh)
|
||
ne passe pas la fenêtre sans réseau.
|
||
|
||
*Un module US2000C ≈ 2,4 kWh nominal.*
|
||
|
||
# --- Ce qu'on jette ---------------------------------------------
|
||
- type: markdown
|
||
title: Production
|
||
content: |
|
||
{% set bride = is_state('input_boolean.evcs_balance_pv_censored', 'on') %}
|
||
|
||
{% if bride %}
|
||
### 🔻 Production actuellement **bridée**
|
||
|
||
Motif : *{{ states('input_text.evcs_balance_censor_reason') }}*
|
||
|
||
Sans injection réseau, l'onduleur ne produit que ce que le système
|
||
peut absorber. Le soleil est là, il manque un preneur.
|
||
{% else %}
|
||
### ☀️ Production **libre**
|
||
|
||
Rien ne bride l'onduleur en ce moment : la mesure de production
|
||
reflète la ressource réelle.
|
||
{% endif %}
|
||
|
||
Surplus attendu pour le véhicule aujourd'hui :
|
||
**{{ states('input_number.evcs_balance_vehicle_surplus_kwh') }} kWh**
|
||
|
||
- type: history-graph
|
||
title: SOC réel contre seuil calculé
|
||
hours_to_show: 48
|
||
entities:
|
||
- entity: sensor.victron_battery_soc
|
||
name: SOC batterie
|
||
- entity: input_number.evcs_balance_required_soc
|
||
name: Seuil calculé
|
||
- entity: input_number.victron_grid_soc_off
|
||
name: Seuil fixe
|
||
|
||
- type: history-graph
|
||
title: Bridage de la production
|
||
hours_to_show: 48
|
||
entities:
|
||
- entity: input_boolean.evcs_balance_pv_censored
|
||
name: Production bridée
|
||
- entity: switch.edf_general_ok_switch
|
||
name: Réseau EDF
|
||
|
||
- type: entities
|
||
title: Charge de la batterie — qui limite ?
|
||
entities:
|
||
- entity: sensor.victron_battery_soc
|
||
name: SOC
|
||
- entity: sensor.victron_battery_current
|
||
name: Courant pris par le parc
|
||
- entity: sensor.victron_max_charge_current_setting
|
||
name: ↳ plafond que NOUS imposons (DVCC)
|
||
- entity: sensor.airtria_8_us2000c_maximum_allowed_charge_current
|
||
name: ↳ plafond que le BMS autorise
|
||
- type: divider
|
||
- entity: sensor.victron_battery_voltage
|
||
name: Tension du parc
|
||
- entity: sensor.victron_max_charge_voltage_setting
|
||
name: ↳ consigne que NOUS imposons
|
||
- entity: sensor.airtria_8_us2000c_maximum_allowed_charging_voltage
|
||
name: ↳ tension que le BMS autorise
|
||
- entity: input_text.victron_charge_mode
|
||
name: Profil Auto Charge Manager
|
||
- type: divider
|
||
- entity: sensor.batterie_ecart_entre_cellules
|
||
name: Écart entre cellules
|
||
- entity: sensor.airtria_8_us2000c_tension_de_cellule_elevee
|
||
name: Alarme tension de cellule
|
||
- entity: sensor.airtria_8_us2000c_nombre_de_modules_bloquant_la_charge
|
||
name: Modules bloquant la charge
|
||
- type: divider
|
||
- entity: sensor.solaire_chargeurs_qui_se_retiennent
|
||
name: Chargeurs solaires qui se retiennent
|
||
- entity: sensor.victron_pv_power_system
|
||
name: Production
|
||
|
||
- type: markdown
|
||
content: >
|
||
**Comment lire cette carte.** Le courant pris contre le plafond DVCC
|
||
dit *qui* limite la charge. Collé au plafond : c'est nous. Loin en
|
||
dessous alors que la tension approche la consigne : c'est la fin de
|
||
charge normale. Loin en dessous avec une tension basse : il n'y a
|
||
simplement pas assez de soleil.
|
||
|
||
|
||
L'écart entre cellules est le garde-fou quand on relève un plafond —
|
||
quelques millivolts, le parc est sain ; s'il s'ouvre, on redescend.
|
||
À titre de repère, l'ancien module défaillant atteignait 168 mV.
|
||
|
||
|
||
Les chargeurs qui se retiennent sont le seul signal **direct** de
|
||
ressource refusée : sans injection, la production mesurée est
|
||
censurée par construction et ne dit rien de ce que le soleil pourrait
|
||
donner.
|
||
|
||
|
||
⚠️ *Nombre de modules* et *capacité installée* publiés par le BMS ne
|
||
comptent que le groupe maître — 4 modules et 400 Ah pour un parc qui
|
||
en a neuf. Ils ne sont pas sur cette carte volontairement.
|
||
|
||
# ===================================================================
|
||
- title: Profils & réglages
|
||
path: profils
|
||
icon: mdi:chart-bell-curve
|
||
cards:
|
||
- type: custom:mushroom-title-card
|
||
title: Profils mesurés
|
||
subtitle: Recalculés chaque nuit à 03:30 sur l'historique réel
|
||
|
||
- type: markdown
|
||
title: Profil horaire de consommation (kWh/h, hors véhicule)
|
||
content: |
|
||
{% set p50 = states('input_text.evcs_balance_consumption_p50').split(',') %}
|
||
{% set p80 = states('input_text.evcs_balance_consumption_p80').split(',') %}
|
||
{% set env = states('input_text.evcs_balance_envelope').split(',') %}
|
||
{% set cen = states('input_text.evcs_balance_censorship_rate').split(',') %}
|
||
{% set ok = p50 | length == 24 and p80 | length == 24 and env | length == 24 and cen | length == 24 %}
|
||
{% if not ok %}Profils indisponibles — le recalcul quotidien n'a pas encore publié.
|
||
{% else %}
|
||
| h | conso typique | conso prudente | production max | censure |
|
||
|---:|---:|---:|---:|---:|
|
||
{% for i in range(24) %}| {{ '%02d' | format(i) }} | {{ p50[i] }} | {{ p80[i] }} | {{ env[i] }} | {{ (cen[i] | float(1) * 100) | round(0) }} % |
|
||
{% endfor %}{% endif %}
|
||
|
||
*La colonne « censure » indique la part des jours où la batterie était
|
||
pleine à cette heure : au-delà de 25 %, la production maximale
|
||
observée est minorée et le bilan le signale.*
|
||
|
||
- type: markdown
|
||
content: |
|
||
### ⚠️ Taille du parc — en équivalents US2000C
|
||
|
||
Un **US5000 compte pour DEUX**, il fait 100 Ah contre 50.
|
||
|
||
| parc réel | valeur à saisir |
|
||
|---|---:|
|
||
| 4 × US2000C | **4** |
|
||
| 4 × US2000C + 4 × US5000 | **12** |
|
||
| 8 × US2000C | **8** |
|
||
|
||
Cette valeur alimente trois calculs à la fois : la capacité utile du
|
||
contrôleur, la conversion énergie/SOC du bilan, et l'échelle des
|
||
limites de courant batterie. À corriger **après** que les nouveaux
|
||
modules aient démarré et que les groupes se soient accouplés.
|
||
|
||
- type: markdown
|
||
title: Capacité utile — calculée, pas saisie
|
||
content: |
|
||
{% set n = states('input_number.evcs_balance_modules_en_service') | float(7) %}
|
||
{% set haut = 0.169 * n / 7 %}
|
||
{% set bas = 0.097 * n / 7 %}
|
||
{% set dc = 67 * haut + 11 * bas %}
|
||
**{{ (dc * 0.955) | round(1) }} kWh** réellement livrés à la maison,
|
||
de 100 % au plancher de 22 %.
|
||
|
||
Déduit de la courbe mesurée le 03/09 sur 576 paliers
|
||
({{ (haut * 1000) | round(0) }} Wh par point de SOC au-dessus de 33 %,
|
||
{{ (bas * 1000) | round(0) }} Wh en dessous) et du parc déclaré
|
||
ci-dessus. **Rien à saisir ici.**
|
||
|
||
*L'ancien réglage « capacité batterie utile » (15 kWh) ne pilote plus
|
||
rien depuis le 03/09 : il ne sert que de garde de complétude au
|
||
bilan. Il a été retiré de cette carte pour ne plus induire en erreur.*
|
||
|
||
- type: entities
|
||
title: Réglages
|
||
entities:
|
||
- entity: input_number.evcs_balance_modules_en_service
|
||
name: Parc batterie (équivalents US2000C)
|
||
- entity: input_boolean.evcs_balance_enabled
|
||
name: Bilan actif (kill-switch)
|
||
- type: divider
|
||
- entity: input_text.evcs_balance_status
|
||
name: Statut
|
||
- entity: input_text.evcs_balance_last_run
|
||
name: Dernière exécution
|
||
- entity: input_text.evcs_balance_reason
|
||
name: Explication
|
||
|
||
- type: markdown
|
||
content: |
|
||
### À propos
|
||
|
||
Ce bilan remplace des seuils fixes par des grandeurs mesurées. Il
|
||
**n'écrit que dans ses propres entités** `evcs_balance_*` : aucun
|
||
interrupteur, aucune consigne de borne, aucune stratégie. Cette
|
||
garantie est vérifiée par la suite de tests du dépôt, qui échoue si
|
||
une écriture sort de ce périmètre.
|
||
|
||
Détail de la démarche : `docs/CHANTIER_BILAN_ENERGETIQUE.md`.
|
||
|
||
# =====================================================
|
||
# PREVISION CONTRE REEL (15/09/2026)
|
||
# =====================================================
|
||
# Les trois series viennent de `sensor.evcs_prevision_contre_reel`, publie
|
||
# toutes les dix minutes par le service d'historisation depuis PostgreSQL —
|
||
# Home Assistant ne garde aucun historique des previsions, ses input_text
|
||
# etant ecrases a chaque publication.
|
||
- title: Prévision vs réel
|
||
path: prevision
|
||
icon: mdi:chart-timeline-variant
|
||
cards:
|
||
|
||
- type: markdown
|
||
content: >
|
||
## Comment lire ces courbes
|
||
|
||
|
||
**La courbe pleine orange est de l'histoire, la pointillée est la
|
||
prévision.** Pour un jour écoulé on affiche ce qui avait été annoncé
|
||
*le matin même* — c'est la seule référence qui mesure vraiment la
|
||
qualité de la prévision, une version révisée à 18 h connaissant déjà
|
||
la journée. Pour les heures à venir on affiche la **dernière**
|
||
publication : aujourd'hui jusqu'à ce soir, et demain en entier.
|
||
|
||
|
||
⚠️ **Une heure bridée n'est pas comparable.** Quand la batterie est
|
||
pleine et qu'aucun preneur ne se présente, la production se cale sur
|
||
la consommation : elle passe sous la prévision sans que la prévision
|
||
soit fausse. La bande grise du bas dit quand c'est le cas.
|
||
|
||
|
||
Le chiffre en tête — l'écart moyen sur sept jours — ne compte **que
|
||
les heures non bridées et déjà terminées**. C'est le seul qui juge
|
||
vraiment la prévision.
|
||
|
||
|
||
Deux exemples mesurés : le 13/09 à 15 h, prévu 5,32 kWh et réalisé
|
||
1,82 avec 100 % de bridage — l'écart n'est pas météorologique. Le
|
||
15/09 à 13 h, prévu 3,92 et réalisé 7,12 sans aucun bridage — là, la
|
||
prévision **sous-estime** de 82 %.
|
||
|
||
- type: entities
|
||
entities:
|
||
- type: attribute
|
||
entity: sensor.evcs_prevision_contre_reel
|
||
attribute: reste_aujourdhui_kwh
|
||
name: Encore attendu aujourd'hui
|
||
suffix: " kWh"
|
||
icon: mdi:weather-sunny
|
||
- type: attribute
|
||
entity: sensor.evcs_prevision_contre_reel
|
||
attribute: annonce_demain_kwh
|
||
name: Annoncé pour demain
|
||
suffix: " kWh"
|
||
icon: mdi:weather-sunset-up
|
||
- type: divider
|
||
- entity: sensor.evcs_prevision_contre_reel
|
||
name: Écart moyen (heures comparables, 7 j)
|
||
- type: attribute
|
||
entity: sensor.evcs_prevision_contre_reel
|
||
attribute: heures_comparables
|
||
name: Heures comparables
|
||
- type: attribute
|
||
entity: sensor.evcs_prevision_contre_reel
|
||
attribute: heures_total
|
||
name: Heures terminées dans la fenêtre
|
||
|
||
- type: custom:apexcharts-card
|
||
header:
|
||
show: true
|
||
title: Production réalisée, et ce qui est annoncé jusqu'à demain soir
|
||
show_states: false
|
||
# Hier, aujourd'hui, demain. La fenêtre s'arrête a la fin de DEMAIN et
|
||
# non a l'heure en cours : sans cela la prevision, qui est justement ce
|
||
# qu'on veut voir, tombe hors du cadre.
|
||
graph_span: 3d
|
||
span:
|
||
end: day
|
||
offset: '+1d'
|
||
apex_config:
|
||
chart:
|
||
height: 340
|
||
stroke:
|
||
width: [1, 2, 2]
|
||
dashArray: [0, 0, 5]
|
||
legend:
|
||
position: top
|
||
yaxis:
|
||
- title: {text: kWh par heure}
|
||
decimals: 1
|
||
min: 0
|
||
series:
|
||
- entity: sensor.evcs_prevision_contre_reel
|
||
name: Réalisée
|
||
type: area
|
||
opacity: 0.25
|
||
color: '#2d8cf0'
|
||
data_generator: |
|
||
return (entity.attributes.reelle || [])
|
||
.filter(p => p[1] !== null && p[1] !== undefined);
|
||
- entity: sensor.evcs_prevision_contre_reel
|
||
name: Prévue le matin même
|
||
type: line
|
||
color: '#f0a202'
|
||
data_generator: |
|
||
return (entity.attributes.predite || [])
|
||
.filter(p => p[1] !== null && p[1] !== undefined);
|
||
- entity: sensor.evcs_prevision_contre_reel
|
||
name: Prévision en cours
|
||
type: line
|
||
color: '#f05a02'
|
||
data_generator: |
|
||
return (entity.attributes.attendue || [])
|
||
.filter(p => p[1] !== null && p[1] !== undefined);
|
||
|
||
- type: custom:apexcharts-card
|
||
header:
|
||
show: true
|
||
title: Part de l'heure où la production était bridée
|
||
show_states: false
|
||
graph_span: 3d
|
||
span:
|
||
end: day
|
||
offset: '+1d'
|
||
apex_config:
|
||
chart:
|
||
height: 150
|
||
yaxis:
|
||
- min: 0
|
||
max: 100
|
||
title: {text: '% de l heure'}
|
||
legend:
|
||
show: false
|
||
series:
|
||
- entity: sensor.evcs_prevision_contre_reel
|
||
name: Bridage
|
||
type: area
|
||
opacity: 0.4
|
||
color: '#9e9e9e'
|
||
data_generator: |
|
||
return (entity.attributes.bridage || [])
|
||
.filter(p => p[1] !== null && p[1] !== undefined);
|