{ "templating": { "list": [] }, "uid": "evcs-journee", "title": "La journee", "description": "Le bilan de la journee en cours, de minuit a maintenant : ce qui a ete produit, ce qui a ete consomme, ce qui a ete achete, et ou c'est alle. Les energies sont integrees depuis les puissances mesurees.", "tags": [ "evcs" ], "timezone": "Europe/Paris", "editable": true, "schemaVersion": 39, "version": 1, "refresh": "1m", "time": { "from": "now/d", "to": "now" }, "timepicker": { "refresh_intervals": [ "30s", "1m", "5m", "15m", "1h" ] }, "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "name": "Annotations & Alerts", "iconColor": "rgba(0, 211, 255, 1)", "type": "dashboard" }, { "name": "Evenements", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "enable": false, "iconColor": "semi-dark-orange", "hide": false, "target": { "format": "table", "rawQuery": true, "editorMode": "code", "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1" } } ] }, "panels": [ { "type": "stat", "title": "Le bilan depuis minuit", "description": "Sept chiffres, une ligne. L'autonomie est la part de la consommation de la maison qui n'est PAS venue du reseau. « Batterie rendue » est ce qu'elle a restitue, donc du solaire differe et non une consommation supplementaire.", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "format": "table", "rawQuery": true, "editorMode": "code", "rawSql": "WITH bornes AS (\n SELECT min(ts) AS d, max(ts) AS f FROM mesure WHERE $__timeFilter(ts)\n),\naxe AS (\n SELECT generate_series(date_trunc('minute', (SELECT d FROM bornes)),\n date_trunc('minute', (SELECT f FROM bornes)),\n interval '1 minute') AS t\n),\nreleve AS (\n SELECT date_trunc('minute', ts) AS t,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_pv_power_system') AS pv,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_pv_power_system'))[1] AS d_pv,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_consumption_power_l1') AS maison,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_consumption_power_l1'))[1] AS d_maison,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_grid_power_l1') AS reseau,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_grid_power_l1'))[1] AS d_reseau,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_evcs_ac_power') AS voiture,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_evcs_ac_power'))[1] AS d_voiture,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_battery_power') AS batterie,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_battery_power'))[1] AS d_batterie\n FROM mesure WHERE $__timeFilter(ts) GROUP BY 1\n),\npar_minute AS (\n SELECT axe.t, releve.pv, releve.d_pv, releve.maison, releve.d_maison, releve.reseau, releve.d_reseau, releve.voiture, releve.d_voiture, releve.batterie, releve.d_batterie\n FROM axe LEFT JOIN releve USING (t)\n),\npaliers AS (\n SELECT t, pv, d_pv, maison, d_maison, reseau, d_reseau, voiture, d_voiture, batterie, d_batterie,\n count(d_pv) OVER (ORDER BY t) AS g_pv,\n count(d_maison) OVER (ORDER BY t) AS g_maison,\n count(d_reseau) OVER (ORDER BY t) AS g_reseau,\n count(d_voiture) OVER (ORDER BY t) AS g_voiture,\n count(d_batterie) OVER (ORDER BY t) AS g_batterie\n FROM par_minute\n),\nreport AS (\n SELECT t,\n coalesce(pv, first_value(d_pv) OVER (PARTITION BY g_pv ORDER BY t), 0) AS pv,\n coalesce(maison, first_value(d_maison) OVER (PARTITION BY g_maison ORDER BY t), 0) AS maison,\n coalesce(reseau, first_value(d_reseau) OVER (PARTITION BY g_reseau ORDER BY t), 0) AS reseau,\n coalesce(voiture, first_value(d_voiture) OVER (PARTITION BY g_voiture ORDER BY t), 0) AS voiture,\n coalesce(batterie, first_value(d_batterie) OVER (PARTITION BY g_batterie ORDER BY t), 0) AS batterie\n FROM paliers\n)\nSELECT\n round((sum(pv) / 60.0 / 1000.0)::numeric, 1) AS \"Solaire produit\",\n round((sum(maison) / 60.0 / 1000.0)::numeric, 1) AS \"Maison consommee\",\n round((sum(reseau) / 60.0 / 1000.0)::numeric, 1) AS \"Reseau importe\",\n round((sum(voiture) / 60.0 / 1000.0)::numeric, 1) AS \"Voiture\",\n round((sum(greatest(batterie, 0)) / 60.0 / 1000.0)::numeric, 1) AS \"Batterie chargee\",\n round((sum(greatest(-batterie, 0)) / 60.0 / 1000.0)::numeric, 1) AS \"Batterie rendue\",\n round((100.0 * greatest(sum(maison) - sum(reseau), 0)\n / nullif(sum(maison), 0))::numeric, 0) AS \"Autonomie\"\nFROM report GROUP BY (1 = 1)" } ], "gridPos": { "x": 0, "y": 0, "w": 24, "h": 5 }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "thresholds": { "mode": "absolute", "steps": [ { "color": "text", "value": null } ] }, "mappings": [] }, "overrides": [ { "matcher": { "id": "byName", "options": "Solaire produit" }, "properties": [ { "id": "unit", "value": "kwatth" } ] }, { "matcher": { "id": "byName", "options": "Maison consommee" }, "properties": [ { "id": "unit", "value": "kwatth" } ] }, { "matcher": { "id": "byName", "options": "Voiture" }, "properties": [ { "id": "unit", "value": "kwatth" } ] }, { "matcher": { "id": "byName", "options": "Batterie chargee" }, "properties": [ { "id": "unit", "value": "kwatth" } ] }, { "matcher": { "id": "byName", "options": "Batterie rendue" }, "properties": [ { "id": "unit", "value": "kwatth" } ] }, { "matcher": { "id": "byName", "options": "Reseau importe" }, "properties": [ { "id": "unit", "value": "kwatth" }, { "id": "thresholds", "value": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "#EAB839", "value": 3 }, { "color": "orange", "value": 8 } ] } } ] }, { "matcher": { "id": "byName", "options": "Autonomie" }, "properties": [ { "id": "unit", "value": "percent" }, { "id": "thresholds", "value": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "#EAB839", "value": 60 }, { "color": "green", "value": 85 } ] } } ] } ] }, "options": { "reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false }, "orientation": "horizontal", "textMode": "auto", "colorMode": "value", "graphMode": "none", "justifyMode": "center", "percentChangeColorMode": "standard", "noValue": "—", "wideLayout": true } }, { "type": "barchart", "title": "Energie heure par heure", "description": "La forme de la journee : quand le solaire a couvert la maison, et quand il a fallu acheter. Le pas suit la periode choisie.", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "format": "table", "rawQuery": true, "editorMode": "code", "rawSql": "WITH bornes AS (\n SELECT $__timeFrom()::timestamptz AS d, $__timeTo()::timestamptz AS f\n),\n-- Le pas s'adapte a la plage choisie. Sans cela, « 30 derniers jours » rend\n-- 720 barres illisibles — c'est exactement ce que fait VRM en passant de\n-- l'heure au jour puis au mois.\npas AS (\n SELECT CASE WHEN f - d > interval '70 days' THEN 'month'\n WHEN f - d > interval '4 days' THEN 'day'\n ELSE 'hour' END AS unite,\n CASE WHEN f - d > interval '70 days' THEN 'MM/YYYY'\n WHEN f - d > interval '4 days' THEN 'DD/MM'\n ELSE 'DD/MM HH24' END AS forme\n FROM bornes\n),\naxe AS (\n SELECT generate_series(date_trunc('minute', (SELECT d FROM bornes)),\n date_trunc('minute', (SELECT f FROM bornes)),\n interval '1 minute') AS t\n),\nreleve AS (\n SELECT date_trunc('minute', ts) AS t,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_pv_power_system') AS Solaire,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_pv_power_system'))[1] AS d_Solaire,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_grid_power_l1') AS Reseau,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_grid_power_l1'))[1] AS d_Reseau,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_consumption_power_l1') AS Maison,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_consumption_power_l1'))[1] AS d_Maison,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_evcs_ac_power') AS Voiture,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_evcs_ac_power'))[1] AS d_Voiture\n FROM mesure WHERE $__timeFilter(ts) GROUP BY 1\n),\npar_minute AS (\n SELECT axe.t, releve.Solaire, releve.d_Solaire, releve.Reseau, releve.d_Reseau, releve.Maison, releve.d_Maison, releve.Voiture, releve.d_Voiture\n FROM axe LEFT JOIN releve USING (t)\n),\npaliers AS (\n SELECT t, Solaire, d_Solaire, Reseau, d_Reseau, Maison, d_Maison, Voiture, d_Voiture,\n count(d_Solaire) OVER (ORDER BY t) AS g_Solaire,\n count(d_Reseau) OVER (ORDER BY t) AS g_Reseau,\n count(d_Maison) OVER (ORDER BY t) AS g_Maison,\n count(d_Voiture) OVER (ORDER BY t) AS g_Voiture\n FROM par_minute\n),\nreport AS (\n SELECT t,\n coalesce(Solaire, first_value(d_Solaire) OVER (PARTITION BY g_Solaire ORDER BY t)) AS Solaire,\n coalesce(Reseau, first_value(d_Reseau) OVER (PARTITION BY g_Reseau ORDER BY t)) AS Reseau,\n coalesce(Maison, first_value(d_Maison) OVER (PARTITION BY g_Maison ORDER BY t)) AS Maison,\n coalesce(Voiture, first_value(d_Voiture) OVER (PARTITION BY g_Voiture ORDER BY t)) AS Voiture\n FROM paliers\n)\nSELECT to_char(date_trunc(pas.unite, t), pas.forme) AS \"Heure\",\n round((avg(Solaire) * count(Solaire) / 60.0 / 1000.0)::numeric, 2) AS \"Solaire\",\n round((avg(Reseau) * count(Reseau) / 60.0 / 1000.0)::numeric, 2) AS \"Reseau\",\n round((avg(Maison) * count(Maison) / 60.0 / 1000.0)::numeric, 2) AS \"Maison\",\n round((avg(Voiture) * count(Voiture) / 60.0 / 1000.0)::numeric, 2) AS \"Voiture\"\nFROM report, pas\nGROUP BY date_trunc(pas.unite, t), pas.forme\nHAVING count(Solaire) > 0 OR count(Reseau) > 0 OR count(Maison) > 0 OR count(Voiture) > 0\nORDER BY date_trunc(pas.unite, t)" } ], "gridPos": { "x": 0, "y": 5, "w": 24, "h": 9 }, "fieldConfig": { "defaults": { "unit": "kwatth", "color": { "mode": "palette-classic" }, "custom": { "lineWidth": 1, "fillOpacity": 80, "gradientMode": "none", "axisBorderShow": false } }, "overrides": [] }, "options": { "orientation": "auto", "xTickLabelRotation": -45, "showValue": "never", "stacking": "none", "groupWidth": 0.7, "barWidth": 0.9, "legend": { "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "mode": "multi", "sort": "none" } } }, { "type": "timeseries", "title": "Les flux de la journee", "description": "Les cinq puissances sur le meme axe. La batterie est signee : au-dessus de zero elle se remplit, en dessous elle porte la maison. Quand « Reseau » decolle alors que la production est forte, c'est une fermeture du contacteur qu'il faut expliquer.", "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": "WITH bornes AS (\n SELECT min(ts) AS d, max(ts) AS f FROM mesure WHERE $__timeFilter(ts)\n),\naxe AS (\n SELECT generate_series(date_trunc('minute', (SELECT d FROM bornes)),\n date_trunc('minute', (SELECT f FROM bornes)),\n interval '1 minute') AS t\n),\nreleve AS (\n SELECT date_trunc('minute', ts) AS t,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_pv_power_system') AS pv,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_pv_power_system'))[1] AS d_pv,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_consumption_power_l1') AS maison,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_consumption_power_l1'))[1] AS d_maison,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_grid_power_l1') AS reseau,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_grid_power_l1'))[1] AS d_reseau,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_evcs_ac_power') AS voiture,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_evcs_ac_power'))[1] AS d_voiture,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_battery_power') AS batterie,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_battery_power'))[1] AS d_batterie\n FROM mesure WHERE $__timeFilter(ts) GROUP BY 1\n),\npar_minute AS (\n SELECT axe.t, releve.pv, releve.d_pv, releve.maison, releve.d_maison, releve.reseau, releve.d_reseau, releve.voiture, releve.d_voiture, releve.batterie, releve.d_batterie\n FROM axe LEFT JOIN releve USING (t)\n),\npaliers AS (\n SELECT t, pv, d_pv, maison, d_maison, reseau, d_reseau, voiture, d_voiture, batterie, d_batterie,\n count(d_pv) OVER (ORDER BY t) AS g_pv,\n count(d_maison) OVER (ORDER BY t) AS g_maison,\n count(d_reseau) OVER (ORDER BY t) AS g_reseau,\n count(d_voiture) OVER (ORDER BY t) AS g_voiture,\n count(d_batterie) OVER (ORDER BY t) AS g_batterie\n FROM par_minute\n),\nreport AS (\n SELECT t,\n coalesce(pv, first_value(d_pv) OVER (PARTITION BY g_pv ORDER BY t), 0) AS pv,\n coalesce(maison, first_value(d_maison) OVER (PARTITION BY g_maison ORDER BY t), 0) AS maison,\n coalesce(reseau, first_value(d_reseau) OVER (PARTITION BY g_reseau ORDER BY t), 0) AS reseau,\n coalesce(voiture, first_value(d_voiture) OVER (PARTITION BY g_voiture ORDER BY t), 0) AS voiture,\n coalesce(batterie, first_value(d_batterie) OVER (PARTITION BY g_batterie ORDER BY t), 0) AS batterie\n FROM paliers\n)\nSELECT t AS time,\n pv AS \"Production solaire\",\n maison AS \"Consommation maison\",\n voiture AS \"Charge du vehicule\",\n reseau AS \"Reseau\",\n batterie AS \"Batterie (+ charge, - decharge)\"\nFROM report ORDER BY t" } ], "gridPos": { "x": 0, "y": 14, "w": 24, "h": 10 }, "fieldConfig": { "defaults": { "unit": "watt", "color": { "mode": "palette-classic" }, "custom": { "lineWidth": 2, "fillOpacity": 8, "showPoints": "never", "spanNulls": true, "axisBorderShow": false, "gradientMode": "opacity" } }, "overrides": [] }, "options": { "legend": { "displayMode": "list", "placement": "bottom", "showLegend": true, "calcs": [] }, "tooltip": { "mode": "multi", "sort": "desc" } } }, { "type": "timeseries", "title": "Etat de charge, et ce que la nuit exigera", "description": "Le SOC reel contre ce que le bilan energetique reclame pour traverser la nuit jusqu'au croisement production/consommation. Quand le reel passe sous la ligne en fin de journee, le reseau sera necessaire avant le matin.", "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 \"SOC reel\"\nFROM mesure WHERE cle = 'sensor.victron_battery_soc'\n AND $__timeFilter(ts) ORDER BY 1" }, { "refId": "B", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "format": "time_series", "rawQuery": true, "editorMode": "code", "rawSql": "SELECT ts AS time, soc_requis_pct AS \"SOC requis par le bilan\"\nFROM bilan WHERE $__timeFilter(ts) AND statut = 'ok' ORDER BY 1" } ], "gridPos": { "x": 0, "y": 24, "w": 12, "h": 9 }, "fieldConfig": { "defaults": { "unit": "percent", "color": { "mode": "palette-classic" }, "custom": { "lineWidth": 2, "fillOpacity": 8, "showPoints": "never", "spanNulls": true, "axisBorderShow": false, "gradientMode": "opacity" } }, "overrides": [] }, "options": { "legend": { "displayMode": "list", "placement": "bottom", "showLegend": true, "calcs": [] }, "tooltip": { "mode": "multi", "sort": "desc" } } }, { "type": "state-timeline", "title": "Reseau, source et decisions", "description": "Trois bandes qui se lisent ensemble. « Production bridee » signale que le solaire n'avait plus de preneur : l'energie manquante ce moment-la a ete refusee, pas absente.", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "format": "table", "rawQuery": true, "editorMode": "code", "rawSql": "SELECT ts AS time, source_mode AS \"Source\"\nFROM decision WHERE $__timeFilter(ts) ORDER BY 1" }, { "refId": "B", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "format": "table", "rawQuery": true, "editorMode": "code", "rawSql": "SELECT ts AS time, etat AS \"Etat du controleur\"\nFROM decision WHERE $__timeFilter(ts) ORDER BY 1" }, { "refId": "C", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "format": "table", "rawQuery": true, "editorMode": "code", "rawSql": "SELECT ts AS time,\n CASE WHEN pv_censure THEN 'production bridee'\n ELSE 'production libre' END AS \"Production\"\nFROM bilan WHERE $__timeFilter(ts) ORDER BY 1" } ], "gridPos": { "x": 12, "y": 24, "w": 12, "h": 9 }, "fieldConfig": { "defaults": { "color": { "mode": "continuous-GrYlRd" }, "custom": { "lineWidth": 0, "fillOpacity": 80, "insertNulls": false, "spanNulls": true }, "mappings": [] }, "overrides": [] }, "options": { "showValue": "auto", "mergeValues": true, "alignValue": "left", "rowHeight": 0.85, "legend": { "displayMode": "list", "placement": "bottom", "showLegend": false }, "tooltip": { "mode": "single" } } }, { "type": "table", "title": "Les charges du jour", "description": "⚠️ L'etiquette du vehicule vient d'un selecteur manuel : elle est fausse s'il n'a pas ete positionne au branchement. Les deux voitures sont electriquement indiscernables (3 680 W contre 3 655 W a consigne egale).", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "format": "table", "rawQuery": true, "editorMode": "code", "rawSql": "SELECT to_char(debut AT TIME ZONE 'Europe/Paris', 'HH24:MI') AS \"Debut\",\n coalesce(to_char(fin AT TIME ZONE 'Europe/Paris', 'HH24:MI'), 'en cours') AS \"Fin\",\n coalesce(vehicule, '?') AS \"Vehicule\",\n round(coalesce(energie_compteur_kwh, energie_kwh)::numeric, 2) AS \"Energie\",\n round(coalesce(p_max_w, 0)::numeric) AS \"Pointe\",\n coalesce(source_mode, '?') AS \"Source\"\nFROM session_charge WHERE $__timeFilter(debut) ORDER BY debut DESC" } ], "gridPos": { "x": 0, "y": 33, "w": 12, "h": 8 }, "fieldConfig": { "defaults": { "custom": { "align": "left", "cellOptions": { "type": "auto" }, "filterable": true } }, "overrides": [ { "matcher": { "id": "byName", "options": "Energie" }, "properties": [ { "id": "unit", "value": "kwatth" } ] }, { "matcher": { "id": "byName", "options": "Pointe" }, "properties": [ { "id": "unit", "value": "watt" } ] } ] }, "options": { "showHeader": true, "cellHeight": "sm", "footer": { "show": false } } }, { "type": "table", "title": "Ce qui est arrive aujourd'hui", "description": "Le genre `bilan` est ecarte : ses bascules de production bridee changent plusieurs fois par heure et noieraient le reste. Elles sont lisibles dans la frise ci-dessus.", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, "format": "table", "rawQuery": true, "editorMode": "code", "rawSql": "SELECT to_char(ts AT TIME ZONE 'Europe/Paris', 'HH24:MI:SS') AS \"Heure\",\n genre AS \"Genre\", resume AS \"Evenement\"\nFROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan'\nORDER BY ts DESC" } ], "gridPos": { "x": 12, "y": 33, "w": 12, "h": 8 }, "fieldConfig": { "defaults": { "custom": { "align": "left", "cellOptions": { "type": "auto" }, "filterable": true } }, "overrides": [] }, "options": { "showHeader": true, "cellHeight": "sm", "footer": { "show": false } } } ] }