diff --git a/HANDOFF.md b/HANDOFF.md index f4520a5..93ffe9d 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -1307,6 +1307,15 @@ autoriser une charge complete avec EDF.** Decisions (30/09) : profil **prudent + A verifier le 01/10 : index fige, kWh reellement donnes, heure d'arret, SOC a 9 h contre le plancher, et si le debit sur batterie (gardes de courant par bande) a permis de donner tout le budget. +- **Grafana « La nuit »** : panneau **« Voiture sur la batterie : budget contre donne »** + (sous « Les puissances de la nuit ») — budget prevu par le bilan (sa valeur a 01:24 est + celle figee), kWh donnes depuis 01:24 (compteur de la borne, 01:24-09:00), SOC et plancher + prudent sur l'axe de droite. Pour l'alimenter, la table `mesure` historise desormais + `input_number.evcs_balance_night_vehicle_budget_kwh` et `sensor.evcs_hq2239dnt7p_total_energy`, + et le journal des evenements le mode de nuit et le budget fige. Requete « donne depuis + 01:24 » verifiee sur le compteur EDF de la nuit du 28-29/09 : 4,40 kWh (4,61 par + integration de la puissance, le compteur fait foi). Charge dans Grafana, requetes OK + (la courbe « donne » se remplit a partir de 01:24). --- *Document mis a jour le : 30/09/2026 (~18:30) — commande vocale du mode de nuit validee, premiere nuit budget a suivre ; budget voiture de la nuit sans EDF (bilan + controleur) ; fiche de l'agent allegee (25 831 car.), concierge sans silence, calendrier JEEP mar/sam ; nuit du 28-29 verifiee, achat EDF annonce = maison en HC + manque batterie ; regle HC active, vehicule branche a midi (JEEP/VW/ZERO), prevision de nuit a trois verdicts, nuits superposees et bilan recale sur la soiree* diff --git a/deploy/historisation/grafana/dashboards/nuit.json b/deploy/historisation/grafana/dashboards/nuit.json index 4552ab8..ebdde71 100644 --- a/deploy/historisation/grafana/dashboards/nuit.json +++ b/deploy/historisation/grafana/dashboards/nuit.json @@ -519,6 +519,200 @@ } } }, + { + "type": "timeseries", + "title": "Voiture sur la batterie : budget contre donne", + "description": "Le budget est ce que la voiture peut prendre sur la batterie SANS EDF (profil prudent, plancher 22 %, la batterie tient jusqu'a la nuit suivante). Le controleur le FIGE a 01:24 : sa valeur a 01:24 est celle qui compte. « Donne depuis 01:24 » = compteur de la borne depuis l'ouverture des heures creuses. En « sans EDF », la charge doit s'arreter quand la courbe bleue atteint le budget ; en « charge complete », EDF finit le reste. Axe de droite : la batterie et son plancher prudent.", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "evcs-pg" + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "evcs-pg" + }, + "format": "time_series", + "rawQuery": true, + "editorMode": "code", + "rawSql": "SELECT ts AS time, valeur AS \"Budget prevu (bilan)\"\nFROM mesure\nWHERE cle = 'input_number.evcs_balance_night_vehicle_budget_kwh'\n AND $__timeFilter(ts)\nORDER BY 1" + }, + { + "refId": "B", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "evcs-pg" + }, + "format": "time_series", + "rawQuery": true, + "editorMode": "code", + "rawSql": "SELECT m.ts AS time, m.valeur - b.valeur AS \"Donne depuis 01:24\"\nFROM mesure m\nJOIN LATERAL (\n SELECT valeur FROM mesure\n WHERE cle = m.cle\n AND ts <= ((((m.ts AT TIME ZONE 'Europe/Paris') - interval '84 minutes')::date\n + time '01:24') AT TIME ZONE 'Europe/Paris')\n ORDER BY ts DESC LIMIT 1) b ON true\nWHERE m.cle = 'sensor.evcs_hq2239dnt7p_total_energy'\n AND $__timeFilter(m.ts)\n AND (m.ts AT TIME ZONE 'Europe/Paris')::time BETWEEN time '01:24' AND time '09:00'\nORDER BY 1" + }, + { + "refId": "C", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "evcs-pg" + }, + "format": "time_series", + "rawQuery": true, + "editorMode": "code", + "rawSql": "SELECT ts AS time,\n soc_pct AS \"SOC batterie\",\n soc_requis_prudent_pct AS \"Plancher prudent\"\nFROM bilan\nWHERE $__timeFilter(ts)\nORDER BY 1" + } + ], + "gridPos": { + "x": 0, + "y": 24, + "w": 24, + "h": 9 + }, + "fieldConfig": { + "defaults": { + "unit": "kwatth", + "color": { + "mode": "palette-classic" + }, + "custom": { + "lineWidth": 2, + "fillOpacity": 0, + "showPoints": "never", + "spanNulls": true + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Budget prevu (bilan)" + }, + "properties": [ + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "orange" + } + }, + { + "id": "custom.lineInterpolation", + "value": "stepAfter" + }, + { + "id": "custom.lineStyle", + "value": { + "fill": "dash", + "dash": [ + 8, + 6 + ] + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Donne depuis 01:24" + }, + "properties": [ + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "blue" + } + }, + { + "id": "custom.lineWidth", + "value": 3 + }, + { + "id": "custom.fillOpacity", + "value": 15 + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "SOC batterie|Plancher prudent" + }, + "properties": [ + { + "id": "unit", + "value": "percent" + }, + { + "id": "min", + "value": 0 + }, + { + "id": "max", + "value": 100 + }, + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "SOC batterie" + }, + "properties": [ + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "green" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Plancher prudent" + }, + "properties": [ + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "red" + } + }, + { + "id": "custom.lineStyle", + "value": { + "fill": "dash", + "dash": [ + 4, + 4 + ] + } + } + ] + } + ] + }, + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true, + "calcs": [] + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + } + }, { "type": "table", "title": "Journal des evenements", @@ -542,7 +736,7 @@ ], "gridPos": { "x": 0, - "y": 24, + "y": 33, "w": 24, "h": 8 }, @@ -654,7 +848,7 @@ }, "gridPos": { "x": 0, - "y": 32, + "y": 41, "w": 14, "h": 11 }, @@ -878,7 +1072,7 @@ ], "gridPos": { "x": 14, - "y": 32, + "y": 41, "w": 10, "h": 11 }, @@ -925,7 +1119,7 @@ ], "gridPos": { "x": 0, - "y": 43, + "y": 52, "w": 24, "h": 9 }, diff --git a/tools/gen-grafana-dashboards.py b/tools/gen-grafana-dashboards.py index 51fc129..fa2e014 100644 --- a/tools/gen-grafana-dashboards.py +++ b/tools/gen-grafana-dashboards.py @@ -263,7 +263,7 @@ NUIT_XY = {"type": "xychart", "pluginVersion": "11.3.0", "title": "Soiree contre "droite orange est la regression sur toutes les nuits ; le gros point rouge " "est CE SOIR, place sur la droite : une prevision, pas une mesure. " "1 point de SOC = 0,30 kWh sur le parc actuel."), - "datasource": PG, "gridPos": {"x": 0, "y": 32, "w": 14, "h": 11}, + "datasource": PG, "gridPos": {"x": 0, "y": 41, "w": 14, "h": 11}, "targets": [sql("A", NUITS_CTE.rstrip() + NUAGE, fmt="table")], "fieldConfig": {"defaults": {"unit": "kwatth", "color": {"mode": "palette-classic"}, "custom": {"show": "points", "pointSize": {"fixed": 9}, "pointShape": "circle", @@ -297,13 +297,13 @@ NUIT_TABLE = table("Les nuits, une ligne par nuit", [ 'maison_hc_kwh AS "Maison 21:30-01:24", maison_nuit_kwh AS "Maison 21:30-09:00", ' 'voiture_kwh AS "Voiture", reseau_kwh AS "Reseau", pv_avant_9h_kwh AS "PV avant 9h" ' 'FROM nuits ORDER BY j DESC'), fmt="table")], - PG, 14, 32, 10, 11, + PG, 14, 41, 10, 11, "kWh ; « Maison » = voiture exclue. La nuit en cours apparait le lendemain a " "09:00, une fois close. C'est la matiere du nuage de gauche.") NUIT_PROFIL = barchart("Profil horaire de la maison : 20 nuits contre cette nuit", [ sql("A", PROFIL_SQL, fmt="table")], - PG, 0, 43, 24, 9, "kwatth", + PG, 0, 52, 24, 9, "kwatth", "kWh consommes par la maison (voiture exclue) heure par heure, de 18 h a 17 h. " "Mediane et valeur haute (8 nuits sur 10 en dessous) sur 20 jours, et la nuit en " "cours par-dessus — l'heure en cours est partielle. C'est ce profil moyen que le " @@ -424,13 +424,68 @@ nuit = dashboard("evcs-nuit", "La nuit", ( "showPoints": "never", "spanNulls": True, "gradientMode": "opacity"}}}), + # Budget voiture de la nuit (30/09/2026) : ce que la batterie peut donner sans + # EDF, fige a 01:24, contre ce que la borne a reellement donne. + ts("Voiture sur la batterie : budget contre donne", [ + sql("A", "SELECT ts AS time, valeur AS \"Budget prevu (bilan)\"\n" + "FROM mesure\n" + "WHERE cle = 'input_number.evcs_balance_night_vehicle_budget_kwh'\n" + " AND $__timeFilter(ts)\n" + "ORDER BY 1"), + sql("B", "SELECT m.ts AS time, m.valeur - b.valeur AS \"Donne depuis 01:24\"\n" + "FROM mesure m\n" + "JOIN LATERAL (\n" + " SELECT valeur FROM mesure\n" + " WHERE cle = m.cle\n" + " AND ts <= ((((m.ts AT TIME ZONE 'Europe/Paris') - interval '84 minutes')::date\n" + " + time '01:24') AT TIME ZONE 'Europe/Paris')\n" + " ORDER BY ts DESC LIMIT 1) b ON true\n" + "WHERE m.cle = 'sensor.evcs_hq2239dnt7p_total_energy'\n" + " AND $__timeFilter(m.ts)\n" + " AND (m.ts AT TIME ZONE 'Europe/Paris')::time BETWEEN time '01:24' AND time '09:00'\n" + "ORDER BY 1"), + sql("C", "SELECT ts AS time,\n" + " soc_pct AS \"SOC batterie\",\n" + " soc_requis_prudent_pct AS \"Plancher prudent\"\n" + "FROM bilan\n" + "WHERE $__timeFilter(ts)\n" + "ORDER BY 1")], + PG, 0, 24, 24, 9, "kwatth", + "Le budget est ce que la voiture peut prendre sur la batterie SANS EDF " + "(profil prudent, plancher 22 %, la batterie tient jusqu'a la nuit " + "suivante). Le controleur le FIGE a 01:24 : sa valeur a 01:24 est celle " + "qui compte. « Donne depuis 01:24 » = compteur de la borne depuis " + "l'ouverture des heures creuses. En « sans EDF », la charge doit " + "s'arreter quand la courbe bleue atteint le budget ; en « charge " + "complete », EDF finit le reste. Axe de droite : la batterie et son " + "plancher prudent.", + extra={"defaults": {"custom": {"lineWidth": 2, "fillOpacity": 0, + "showPoints": "never", "spanNulls": True}}, + "overrides": [ + {"matcher": {"id": "byName", "options": "Budget prevu (bilan)"}, + "properties": [{"id": "color", "value": {"mode": "fixed", "fixedColor": "orange"}}, + {"id": "custom.lineInterpolation", "value": "stepAfter"}, + {"id": "custom.lineStyle", "value": {"fill": "dash", "dash": [8, 6]}}]}, + {"matcher": {"id": "byName", "options": "Donne depuis 01:24"}, + "properties": [{"id": "color", "value": {"mode": "fixed", "fixedColor": "blue"}}, + {"id": "custom.lineWidth", "value": 3}, + {"id": "custom.fillOpacity", "value": 15}]}, + {"matcher": {"id": "byRegexp", "options": "SOC batterie|Plancher prudent"}, + "properties": [{"id": "unit", "value": "percent"}, + {"id": "min", "value": 0}, {"id": "max", "value": 100}, + {"id": "custom.axisPlacement", "value": "right"}]}, + {"matcher": {"id": "byName", "options": "SOC batterie"}, + "properties": [{"id": "color", "value": {"mode": "fixed", "fixedColor": "green"}}]}, + {"matcher": {"id": "byName", "options": "Plancher prudent"}, + "properties": [{"id": "color", "value": {"mode": "fixed", "fixedColor": "red"}}, + {"id": "custom.lineStyle", "value": {"fill": "dash", "dash": [4, 4]}}]}]}), table("Journal des evenements", [ sql("A", "SELECT ts AS \"Quand\", genre AS \"Genre\", source AS \"Par\",\n" " resume AS \"Ce qui s'est passe\"\n" "FROM evenement\n" "WHERE $__timeFilter(ts)\n" "ORDER BY ts DESC", fmt="table")], - PG, 0, 24, 24, 8, + PG, 0, 33, 24, 8, "Une courbe sans ses evenements ne s'interprete pas. Ces lignes " "apparaissent aussi en marqueurs verticaux sur tous les graphiques.", overrides=[{"matcher": {"id": "byName", "options": "Quand"}, diff --git a/tools/historise-evcs.mjs b/tools/historise-evcs.mjs index f02c5e3..af5e484 100644 --- a/tools/historise-evcs.mjs +++ b/tools/historise-evcs.mjs @@ -88,6 +88,10 @@ const MESURES = [ 'sensor.airtria_mpii_v1_energy_from_ac_in_1_to_ac_out', 'sensor.airtria_mpii_v1_energy_from_ac_in_1_to_inverter', 'sensor.edf_general_ok_energie_totale', + // Budget voiture de la nuit (30/09) : le budget publie par le bilan et le + // compteur de la borne, pour tracer « donne depuis 01:24 » contre le budget. + 'input_number.evcs_balance_night_vehicle_budget_kwh', + 'sensor.evcs_hq2239dnt7p_total_energy', ]; // --------------------------------------------------------------------- @@ -117,6 +121,8 @@ const TRANSITIONS_SUIVIES = [ ['binary_sensor.victron_evcs_vehicle_connected', 'vehicule', 'Vehicule branche'], ['input_select.victron_evcs_vehicle', 'intervention','Vehicule selectionne'], ['input_select.victron_evcs_strategy', 'intervention','Strategie'], + ['input_select.victron_evcs_night_charge_mode', 'intervention','Charge de la voiture la nuit'], + ['input_text.victron_evcs_night_budget', 'vehicule', 'Budget voiture de la nuit fige (kWh|index borne)'], ['sensor.gx_device_ess_batterylife_state', 'ess', 'Etat BatteryLife de l ESS'], ['input_text.victron_charge_mode', 'ess', 'Mode Auto Charge Manager'], ['input_boolean.evcs_balance_grid_needed', 'bilan', 'Reseau necessaire'],