Le systeme : replique locale de la vue generale VRM
La console VRM est hebergee dans le cloud Victron et disparait quand une coupure d'orage emporte Internet. Meme vision, lue sur le reseau local : reseau, charges, batterie, borne, production, detail par tracker, les flux dans le temps et l'energie heure par heure. Tout dans Grafana, rien dans Home Assistant — et pour une raison trouvee en verifiant si son tableau Energie pouvait servir : gx_device_pv_energy et les compteurs d'energie batterie du GX se remettent a zero sans le signaler. state_class total sans last_reset, donc HA n'auto-detecte pas la remise a zero et a enregistre -373,35 kWh sur l'heure de 07:00. Les energies sont donc obtenues en INTEGRANT LA PUISSANCE, methode qui ignore le probleme. Trois versions du calcul avant la bonne : somme ponderee (laissait des trous, une grandeur stable n'etant jamais reecrite), moyenne des minutes presentes (sur-comptait : 5,13 kWh a 01 h pour 22 minutes observees), puis axe des minutes complet + report + avg x count/60 = energie reellement accumulee, correcte y compris pour l'heure en cours. Recoupement avec la capture VRM du proprietaire : 8,07 kWh de reseau contre "Depuis reseau 9,0 kWh", l'ecart valant exactement les treize minutes entre l'ouverture des heures creuses et le demarrage du service d'historisation. 45/45 requetes verifiees contre les bases reelles. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
69
HANDOFF.md
69
HANDOFF.md
@@ -3567,3 +3567,72 @@ savait de la nuit.
|
||||
|
||||
---
|
||||
*Document mis a jour le : 04/09/2026 (~09:00) — trois defauts de grandeur corriges dans les tableaux, 19/19*
|
||||
|
||||
## 46. Session du 04/09/2026 — « Le systeme » : replique locale de la vue VRM
|
||||
|
||||
### Pourquoi
|
||||
La console VRM est hebergee dans le cloud Victron. Elle disparait exactement
|
||||
quand on en a le plus besoin : une coupure d'orage sur ce site emporte souvent
|
||||
Internet. Demande du proprietaire : la meme vision, en local.
|
||||
|
||||
### Ou, et pourquoi pas ailleurs
|
||||
Tout dans **Grafana**, rien dans Home Assistant. Le tableau Energie de HA
|
||||
etait le candidat naturel pour la moitie basse (barres horaires) — il est
|
||||
declare mais **entierement vide** (aucune source, aucun appareil). En verifiant
|
||||
s'il pouvait servir, defaut majeur trouve :
|
||||
|
||||
| compteur | etat |
|
||||
|---|---|
|
||||
| `gx_device_pv_energy` | **RESET non signale** : −373,35 kWh enregistres sur l'heure de 07:00 |
|
||||
| `gx_device_dc_battery_charge/discharge_energy` | **RESET** : −134,7 et −142,6 kWh |
|
||||
| `airtria_mppt1_total_yield`, `mppt_450_200_..._total_yield`, `v1_total_yield` | sains, monotones |
|
||||
| `airtria_mpii_v1_energy_from_ac_in_1_to_*` et retours | sains, monotones |
|
||||
|
||||
`state_class: total` sans `last_reset` : Home Assistant n'auto-detecte PAS la
|
||||
remise a zero (c'est le comportement de `total_increasing`). Chaque redemarrage
|
||||
de HA injecterait donc un trou geant dans les barres du tableau Energie.
|
||||
=> Les energies du tableau Grafana sont obtenues **en integrant la puissance**,
|
||||
methode immunisee contre ce probleme. Aucune ecriture dans HA.
|
||||
|
||||
### Trois versions du calcul d'energie horaire avant d'arriver a la bonne
|
||||
1. **Somme ponderee par l'intervalle** — laisse des TROUS : une grandeur qui ne
|
||||
change pas n'est jamais reecrite, donc la production nulle de la nuit ne
|
||||
produit qu'UNE ligne et les heures sans ligne sortaient vides au lieu de
|
||||
zero.
|
||||
2. **Moyenne des minutes presentes** — SUR-COMPTE : l'heure 01 annoncait
|
||||
5,13 kWh de reseau alors que le service n'avait demarre qu'a 01:37 —
|
||||
22 minutes comptees comme 60.
|
||||
3. **Retenu** : axe des minutes complet (`generate_series`), report de la
|
||||
derniere valeur connue, puis `avg x count / 60` = energie REELLEMENT
|
||||
accumulee. Correct pour une heure pleine ET pour l'heure en cours, qu'un
|
||||
simple rejet des heures incompletes laissait toujours vide — or c'est celle
|
||||
qu'on regarde le plus.
|
||||
|
||||
### Verification par recoupement avec la capture VRM
|
||||
Total reseau du jour : **8,07 kWh** cote Grafana contre « Depuis reseau
|
||||
9,0 kWh » cote VRM au meme moment. L'ecart de 0,9 kWh correspond exactement aux
|
||||
treize minutes entre l'ouverture des heures creuses (01:24) et le demarrage du
|
||||
service d'historisation (01:37). Detail par tracker recoupe aussi : la chaine
|
||||
que j'ai etiquetee « Ouest » produit 132 W a 9 h contre 173-183 W pour les trois
|
||||
chaines sud — et VRM montre bien Solar 4 comme la plus faible des quatre.
|
||||
|
||||
### Pieges InfluxDB notes au passage
|
||||
- L'integration `influxdb` de HA n'ecrit **que sur changement d'etat**. Une
|
||||
fenetre `range(start: -30m)` laisse donc vides les cases dont la valeur est
|
||||
stable — la borne a 0 A, voiture debranchee. Fenetre portee a **7 jours**,
|
||||
`last()` reste peu couteux.
|
||||
- `pivot` rend les colonnes par ordre alphabetique (A, V, W) : une
|
||||
transformation `organize` retablit tension / courant / puissance.
|
||||
|
||||
### Ce qui reste ouvert
|
||||
- Le tableau Energie de HA pourrait etre configure avec les compteurs SAINS
|
||||
(trois `*_total_yield` en sources solaires, quatre compteurs MultiPlus pour
|
||||
le reseau). Il manquerait la batterie, faute de compteur monotone — a creer
|
||||
par integration de Riemann sur `sensor.victron_battery_power` si on le veut.
|
||||
Non fait : le tableau Grafana couvre le besoin sans rien ecrire dans HA.
|
||||
- Le suiveur de trois panneaux (« V1 », visible dans VRM a 84 V / 0,4 A / 33 W)
|
||||
n'est pas dans les globs d'entites de `ha-influxdb.yaml` : il manque au
|
||||
tableau des trackers. A ajouter si le detail compte.
|
||||
|
||||
---
|
||||
*Document mis a jour le : 04/09/2026 (~10:00) — tableau « Le systeme », 45/45 requetes verifiees*
|
||||
|
||||
@@ -226,6 +226,18 @@ Mot de passe perdu :
|
||||
mode déclaré par chacun des sept chargeurs.
|
||||
- **Prévision contre réel** — le biais de forme de la prévision, question
|
||||
ouverte qui demande des semaines de données.
|
||||
- **Le système** — réplique locale de la vue générale VRM : réseau, charges,
|
||||
batterie, borne, production, **détail par tracker**, les flux dans le temps
|
||||
et l'énergie heure par heure. Existe parce que la console Victron est
|
||||
hébergée dans le cloud et disparaît quand une coupure d'orage emporte
|
||||
Internet. Tout y est lu sur le réseau local.
|
||||
|
||||
⚠️ Les énergies sont obtenues **en intégrant la puissance**, jamais en lisant
|
||||
les compteurs kWh du GX : `gx_device_pv_energy` et les compteurs d'énergie
|
||||
batterie se remettent à zéro sans le signaler, et Home Assistant enregistre
|
||||
alors une variation de −373 kWh sur une heure (vérifié le 04/09). Les
|
||||
compteurs `*_total_yield` des chargeurs et ceux du MultiPlus, eux, sont
|
||||
monotones et sains.
|
||||
- **Sur le téléphone** — une seule colonne, six panneaux, et la réponse en
|
||||
premier. À épingler sur l'écran d'accueil de l'iPhone :
|
||||
`http://10.10.10.22:3001/d/evcs-telephone?kiosk` (Safari → Partager → Sur
|
||||
|
||||
1046
deploy/historisation/grafana/dashboards/systeme.json
Normal file
1046
deploy/historisation/grafana/dashboards/systeme.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -597,9 +597,251 @@ telephone = dashboard("evcs-telephone", "Sur le telephone", (
|
||||
overrides=[sur("Quand", [{"id": "custom.width", "value": 110}])]),
|
||||
])
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# 5. LE SYSTEME — replique locale de la vue generale VRM
|
||||
# =====================================================================
|
||||
# Raison d'etre : la console VRM est hebergee dans le cloud Victron. Elle
|
||||
# disparait exactement quand on en a le plus besoin — une coupure d'orage sur
|
||||
# ce site emporte souvent Internet avec elle. Tout ce qui suit est lu sur le
|
||||
# reseau local, sans exception.
|
||||
#
|
||||
# Choix de methode : les energies horaires sont obtenues en INTEGRANT LA
|
||||
# PUISSANCE, et non en lisant les compteurs kWh de Victron. Verifie le
|
||||
# 04/09 : `gx_device_pv_energy` et les compteurs d'energie batterie du GX se
|
||||
# remettent a zero sans le signaler, et Home Assistant enregistre alors une
|
||||
# variation de -373 kWh sur une heure. Integrer la puissance ignore
|
||||
# completement ce probleme.
|
||||
|
||||
def flux_dernier(refid, mesure, entite, libelle, champ="value"):
|
||||
"""Derniere valeur connue d'une entite, renommee pour l'affichage."""
|
||||
# Fenetre de 7 jours et non de quelques minutes : l'integration InfluxDB
|
||||
# de Home Assistant n'ecrit QUE sur changement d'etat. Une grandeur stable
|
||||
# — la borne a 0 A, voiture debranchee — n'a donc aucun point recent, et
|
||||
# une fenetre courte affiche une case vide la ou la valeur est simplement
|
||||
# constante. `last()` reste peu couteux sur une fenetre large.
|
||||
return flux(refid,
|
||||
f'from(bucket: "evcs")\n'
|
||||
f' |> range(start: -7d)\n'
|
||||
f' |> filter(fn: (r) => r._measurement == "{mesure}"'
|
||||
f' and r.entity_id == "{entite}" and r._field == "{champ}")\n'
|
||||
f' |> last()\n'
|
||||
f' |> keep(columns: ["_time", "_value"])\n'
|
||||
f' |> rename(columns: {{_value: "{libelle}"}})')
|
||||
|
||||
# Energie horaire par integration ponderee par la duree, plafonnee a 300 s
|
||||
# pour qu'un arret du service n'extrapole pas une valeur perimee.
|
||||
ENERGIE_CLES = [
|
||||
('sensor.victron_pv_power_system', 'Solaire'),
|
||||
('sensor.victron_grid_power_l1', 'Reseau'),
|
||||
('sensor.victron_consumption_power_l1', 'Maison'),
|
||||
('sensor.victron_evcs_ac_power', 'Voiture'),
|
||||
]
|
||||
|
||||
# Energie horaire = moyenne de la puissance sur l'heure, en kWh.
|
||||
#
|
||||
# Deux pieges, tous deux rencontres le 04/09 avant d'arriver a cette version :
|
||||
#
|
||||
# 1. Somme des echantillons ponderee par leur intervalle : laisse des TROUS.
|
||||
# « Solaire » n'existait qu'a 00 h, 07 h, 08 h et 09 h, parce qu'une
|
||||
# grandeur qui ne change pas n'est jamais reecrite — la production nulle
|
||||
# de la nuit ne produit qu'UNE ligne, et les heures sans ligne sortaient
|
||||
# vides au lieu de sortir zero.
|
||||
#
|
||||
# 2. Moyenne des minutes PRESENTES : sur-compte. L'heure 01 annoncait
|
||||
# 5,13 kWh de reseau alors que la fenetre heures creuses ne s'ouvre qu'a
|
||||
# 01:24 — la moyenne portait sur 36 minutes et les comptait comme 60.
|
||||
#
|
||||
# D'ou l'axe des minutes COMPLET par `generate_series`, la valeur reportee
|
||||
# depuis le dernier point connu, puis la moyenne. Chaque heure pese alors ses
|
||||
# soixante minutes, qu'il y ait eu une mesure ou non.
|
||||
ENERGIE_SQL = """WITH axe AS (
|
||||
SELECT generate_series(date_trunc('minute', $__timeFrom()::timestamptz),
|
||||
date_trunc('minute', $__timeTo()::timestamptz),
|
||||
interval '1 minute') AS t
|
||||
),
|
||||
releve AS (
|
||||
SELECT date_trunc('minute', ts) AS t,
|
||||
""" + ",\n".join(
|
||||
f" avg(valeur) FILTER (WHERE cle = '{c}') AS {n}" for c, n in ENERGIE_CLES) + """
|
||||
FROM mesure WHERE $__timeFilter(ts) GROUP BY 1
|
||||
),
|
||||
par_minute AS (
|
||||
SELECT axe.t, """ + ", ".join(f"releve.{n}" for _, n in ENERGIE_CLES) + """
|
||||
FROM axe LEFT JOIN releve USING (t)
|
||||
),
|
||||
paliers AS (
|
||||
SELECT t, """ + ", ".join(n for _, n in ENERGIE_CLES) + ",\n" + ",\n".join(
|
||||
f" count({n}) OVER (ORDER BY t) AS g_{n}" for _, n in ENERGIE_CLES) + """
|
||||
FROM par_minute
|
||||
),
|
||||
report AS (
|
||||
SELECT t,
|
||||
""" + ",\n".join(
|
||||
f" first_value({n}) OVER (PARTITION BY g_{n} ORDER BY t) AS {n}"
|
||||
for _, n in ENERGIE_CLES) + """
|
||||
FROM paliers
|
||||
)
|
||||
SELECT to_char(t, 'HH24') || ' h' AS "Heure",
|
||||
""" + ",\n".join(
|
||||
# Energie REELLEMENT accumulee : puissance moyenne x duree observee.
|
||||
# Pas la moyenne seule, qui comptait 22 minutes comme 60 et annoncait
|
||||
# 5,13 kWh de reseau a 01 h le 04/09 — le service n'avait demarre qu'a
|
||||
# 01:37. Pas non plus un rejet des heures incompletes, qui laissait
|
||||
# toujours vide l'heure EN COURS, celle qu'on regarde le plus.
|
||||
# Contrepartie assumee : une heure passee amputee par un arret du service
|
||||
# sous-estime — mais un tel arret est journalise et apparait en marqueur
|
||||
# sur le graphique.
|
||||
f' round((avg({n}) * count({n}) / 60.0 / 1000.0)::numeric, 2) AS "{n}"'
|
||||
for _, n in ENERGIE_CLES) + """
|
||||
FROM report
|
||||
GROUP BY date_trunc('hour', t), to_char(t, 'HH24')
|
||||
HAVING """ + " OR ".join(f"count({n}) > 0" for _, n in ENERGIE_CLES) + """
|
||||
ORDER BY date_trunc('hour', t)"""
|
||||
|
||||
# Detail par tracker. Les quatre entrees du RS 450/200 et le MPPT1 ne suivent
|
||||
# pas la meme convention de nommage : deux branches, reunies puis pivotees.
|
||||
TRACKERS_FLUX = '''import "strings"
|
||||
|
||||
base = from(bucket: "evcs")
|
||||
|> range(start: -7d)
|
||||
|> filter(fn: (r) => r._field == "value")
|
||||
|> filter(fn: (r) => r._measurement == "V" or r._measurement == "A" or r._measurement == "W")
|
||||
|> last()
|
||||
|
||||
rs = base
|
||||
|> filter(fn: (r) => strings.containsStr(v: r.entity_id, substr: "pv_tracker_solar_"))
|
||||
|> map(fn: (r) => ({ r with tracker:
|
||||
if strings.containsStr(v: r.entity_id, substr: "solar_1") then "Sud 1 (10 pan.)"
|
||||
else if strings.containsStr(v: r.entity_id, substr: "solar_2") then "Sud 2 (10 pan.)"
|
||||
else if strings.containsStr(v: r.entity_id, substr: "solar_3") then "Sud 3 (10 pan.)"
|
||||
else "Ouest (9 pan.)" }))
|
||||
|
||||
se = base
|
||||
|> filter(fn: (r) => r.entity_id == "airtria_mppt1_pv_bus_voltage"
|
||||
or r.entity_id == "airtria_mppt1_pv_current"
|
||||
or r.entity_id == "airtria_mppt1_pv_yield_power")
|
||||
|> map(fn: (r) => ({ r with tracker: "Sud-est (10 pan.)" }))
|
||||
|
||||
union(tables: [rs, se])
|
||||
|> keep(columns: ["tracker", "_measurement", "_value"])
|
||||
|> pivot(rowKey: ["tracker"], columnKey: ["_measurement"], valueColumn: "_value")
|
||||
|> sort(columns: ["tracker"])'''
|
||||
|
||||
def col(nom, unite, largeur=None, dec=0):
|
||||
props = [{"id": "unit", "value": unite}, {"id": "decimals", "value": dec}]
|
||||
if largeur:
|
||||
props.append({"id": "custom.width", "value": largeur})
|
||||
return sur(nom, props)
|
||||
|
||||
systeme = dashboard("evcs-systeme", "Le systeme", (
|
||||
"Replique locale de la vue generale VRM. La console Victron est hebergee "
|
||||
"dans le cloud : elle disparait quand une coupure d'orage emporte "
|
||||
"Internet. Tout ce qui est affiche ici est lu sur le reseau local."),
|
||||
frm="now/d", refresh="30s", panels=[
|
||||
|
||||
stat("Reseau", [
|
||||
flux_dernier("A", "W", "victron_grid_power_l1", "Puissance"),
|
||||
flux_dernier("B", "V", "airtria_mpii_v1_input_voltage_l1", "Tension"),
|
||||
flux_dernier("C", "A", "gx_device_grid_current_l1", "Courant")],
|
||||
IN, 0, 0, 6, 5,
|
||||
"Positif : le reseau alimente la maison. Le contacteur EDF est pilote par "
|
||||
"le workflow d'hysteresis, pas depuis ici.",
|
||||
overrides=[col("Puissance", "watt"), col("Tension", "volt"), col("Courant", "amp", dec=1)]),
|
||||
|
||||
stat("Charges CA", [
|
||||
flux_dernier("A", "W", "victron_consumption_power_l1", "Consommation"),
|
||||
flux_dernier("B", "Hz", "airtria_mpii_v1_output_frequency_l1", "Frequence")],
|
||||
IN, 6, 0, 5, 5,
|
||||
"Ce que la maison consomme, voiture comprise.",
|
||||
overrides=[col("Consommation", "watt"), col("Frequence", "hertz", dec=1)]),
|
||||
|
||||
stat("Batterie", [
|
||||
flux_dernier("A", "%", "victron_battery_soc", "Charge"),
|
||||
flux_dernier("B", "W", "victron_battery_power", "Puissance"),
|
||||
flux_dernier("C", "V", "victron_battery_voltage", "Tension"),
|
||||
flux_dernier("D", "A", "victron_battery_current", "Courant"),
|
||||
flux_dernier("E", "°C", "victron_battery_temperature", "Temperature")],
|
||||
IN, 11, 0, 8, 5,
|
||||
"Puissance positive : la batterie se charge. Sept modules actifs sur huit, "
|
||||
"un pack bloquant la charge vers 94 %.",
|
||||
overrides=[
|
||||
sur("Charge", [{"id": "unit", "value": "percent"}, {"id": "decimals", "value": 0},
|
||||
{"id": "thresholds", "value": seuils(
|
||||
("red", None), ("orange", 25), ("yellow", 40), ("green", 60))}]),
|
||||
col("Puissance", "watt"), col("Tension", "volt", dec=2),
|
||||
col("Courant", "amp", dec=1), col("Temperature", "celsius")]),
|
||||
|
||||
stat("Chargeur VE", [
|
||||
flux_dernier("A", "W", "victron_evcs_ac_power", "Puissance"),
|
||||
flux_dernier("B", "A", "victron_evcs_current", "Courant"),
|
||||
flux_dernier("C", "A", "victron_evcs_set_current", "Consigne")],
|
||||
IN, 19, 0, 5, 5,
|
||||
"Consigne = ce que le controleur demande a la borne, reecrit toutes les "
|
||||
"30 s. Courant = ce que la voiture prend reellement.",
|
||||
overrides=[col("Puissance", "watt"), col("Courant", "amp", dec=1),
|
||||
col("Consigne", "amp", dec=0)]),
|
||||
|
||||
stat("Chargeur PV", [
|
||||
flux_dernier("A", "W", "victron_pv_power_system", "Production"),
|
||||
flux_dernier("B", "W", "victron_pv_power_10m_avg", "Moyenne 10 min")],
|
||||
IN, 0, 5, 6, 8,
|
||||
"Rappel qui vaut pour toute lecture de cette valeur : sans injection, la "
|
||||
"production MESUREE vaut min(ressource, consommation + absorption "
|
||||
"batterie). Basse ne veut pas dire mauvais temps.",
|
||||
overrides=[col("Production", "watt"), col("Moyenne 10 min", "watt")]),
|
||||
|
||||
dict(table("Detail par tracker", [flux("A", TRACKERS_FLUX)],
|
||||
IN, 6, 5, 18, 8,
|
||||
"Les 52 panneaux en quatre plans : sud-est 40 degres, trois chaines sud "
|
||||
"35 degres, ouest 45 degres. Le suiveur de trois panneaux ne publie pas "
|
||||
"de mode de fonctionnement et n'apparait donc pas ici.",
|
||||
overrides=[sur("tracker", [{"id": "displayName", "value": "Tracker"},
|
||||
{"id": "custom.width", "value": 200}]),
|
||||
sur("V", [{"id": "displayName", "value": "Tension"},
|
||||
{"id": "unit", "value": "volt"}, {"id": "decimals", "value": 0}]),
|
||||
sur("A", [{"id": "displayName", "value": "Courant"},
|
||||
{"id": "unit", "value": "amp"}, {"id": "decimals", "value": 1}]),
|
||||
sur("W", [{"id": "displayName", "value": "Puissance"},
|
||||
{"id": "unit", "value": "watt"}, {"id": "decimals", "value": 0},
|
||||
{"id": "custom.cellOptions",
|
||||
"value": {"type": "gauge", "mode": "gradient"}},
|
||||
{"id": "max", "value": 3100}])]),
|
||||
# `pivot` rend les colonnes par ordre alphabetique (A, V, W). On retablit
|
||||
# l'ordre de lecture : tension, courant, puissance.
|
||||
transformations=[{"id": "organize", "options": {
|
||||
"indexByName": {"tracker": 0, "V": 1, "A": 2, "W": 3}}}]),
|
||||
|
||||
ts("Les flux", [
|
||||
sql_grandeurs("A", [
|
||||
('sensor.victron_pv_power_system', 'Solaire'),
|
||||
('sensor.victron_grid_power_l1', 'Reseau'),
|
||||
('sensor.victron_battery_power', 'Batterie'),
|
||||
('sensor.victron_consumption_power_l1', 'Maison'),
|
||||
('sensor.victron_evcs_ac_power', 'Voiture')])],
|
||||
PG, 0, 13, 24, 9, "watt",
|
||||
"Batterie positive : elle se charge. Reseau positif : il alimente. "
|
||||
"Aux instants ou tout est a zero sauf la maison, c'est la batterie qui "
|
||||
"porte tout.",
|
||||
extra={"overrides": [
|
||||
sur("Solaire", [{"id": "color", "value": {"mode": "fixed", "fixedColor": "yellow"}}]),
|
||||
sur("Reseau", [{"id": "color", "value": {"mode": "fixed", "fixedColor": "red"}}]),
|
||||
sur("Batterie", [{"id": "color", "value": {"mode": "fixed", "fixedColor": "blue"}}]),
|
||||
sur("Maison", [{"id": "color", "value": {"mode": "fixed", "fixedColor": "text"}},
|
||||
{"id": "custom.fillOpacity", "value": 0}]),
|
||||
sur("Voiture", [{"id": "color", "value": {"mode": "fixed", "fixedColor": "green"}}])]}),
|
||||
|
||||
barchart("Energie heure par heure", [sql("A", ENERGIE_SQL, fmt="table")],
|
||||
PG, 0, 22, 24, 9, "kwatth",
|
||||
"Obtenue en integrant la puissance, PAS en lisant les compteurs kWh du "
|
||||
"GX : ceux-ci se remettent a zero sans le signaler (verifie le 04/09, "
|
||||
"-373 kWh enregistres sur une heure cote Home Assistant)."),
|
||||
])
|
||||
|
||||
base = pathlib.Path('deploy/historisation/grafana/dashboards')
|
||||
for nom, d in [('nuit.json', nuit), ('solaire.json', solaire),
|
||||
('prevision.json', prevision),
|
||||
('telephone.json', telephone)]:
|
||||
('telephone.json', telephone),
|
||||
('systeme.json', systeme)]:
|
||||
(base / nom).write_text(json.dumps(d, indent=2, ensure_ascii=False) + '\n')
|
||||
print(f"{nom:16s} {len(d['panels'])} panneaux")
|
||||
|
||||
Reference in New Issue
Block a user