Files
EVCS/deploy/historisation/grafana/dashboards/telephone.json
Tallagrand Thierry fa5ad0d98a Un tableau pour la journee, et le report cesse de fabriquer des kilowattheures
Il manquait le bilan qu'on cherche en premier : ce qui a ete produit, consomme,
achete, et ou c'est alle. « La journee » (evcs-journee) le donne sur la fenetre
now/d — le jour civil, pas les 24 dernieres heures, sinon deux journees se
melangent et le total de production n'est plus comparable d'un jour a l'autre.

En controlant ses chiffres, un defaut bien plus large est apparu : la voiture
annoncait 6,9 kWh la ou le compteur cumulatif de la borne et la table des
sessions donnent tous deux 3,6.

Le report de la derniere valeur connue propageait la MOYENNE de la minute au
lieu de sa DERNIERE valeur. Le 07/09 a 14:28 la borne passe de 1577 a 0 W dans
la meme minute : cette minute vaut 788 W en moyenne, et le report la maintenait
pendant les heures sans ecriture qui suivaient — 0,79 kWh fabriques sur la
seule heure de 15 h, exactement 788 x 60 / 1000.

Le defaut ne se voyait ni sur le solaire ni sur la maison, ecrits en continu.
Il frappe les grandeurs intermittentes, au premier rang la borne qui passe
l'essentiel de la journee a zero. Les colonnes « Voiture » de « Le systeme » et
« Les vehicules » etaient donc fausses depuis leur creation, en surestimation.

Corrige dans les trois constructeurs de requetes : la minute observee garde sa
moyenne, juste pour l'energie consommee dans cette minute, mais le report
propage la derniere valeur reellement lue. Aucun seuil, aucune peremption
arbitraire. Apres correction la voiture tombe a 3,6 kWh, egal au centieme a la
somme des sessions.

verifier-dashboards annoncait 73/73 avec le chiffre faux : il prouve qu'une
requete s'execute, pas qu'elle rend la bonne grandeur. Deuxieme fois en trois
jours qu'une source independante est le seul controle qui attrape le defaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
2026-09-07 23:10:55 +02:00

680 lines
18 KiB
JSON

{
"templating": {
"list": []
},
"uid": "evcs-telephone",
"title": "Sur le telephone",
"description": "Une colonne, pensee pour un ecran de telephone. Repond a une seule question : est-ce que la nuit qui vient va bien se passer ?",
"tags": [
"evcs"
],
"timezone": "Europe/Paris",
"editable": true,
"schemaVersion": 39,
"version": 1,
"refresh": "1m",
"time": {
"from": "now-12h",
"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": "Maintenant",
"description": "Marge = ce que la batterie a, moins ce qu'il lui faut pour tenir jusqu'au relais du solaire. Negative, la nuit ne passe pas sans reseau.",
"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\n (SELECT valeur FROM mesure WHERE cle = 'sensor.victron_battery_soc'\n ORDER BY ts DESC LIMIT 1) AS \"Batterie\",\n (SELECT soc_pct - soc_requis_pct FROM bilan\n WHERE soc_requis_pct IS NOT NULL ORDER BY ts DESC LIMIT 1) AS \"Marge\",\n (SELECT valeur FROM mesure WHERE cle = 'sensor.victron_pv_power_system'\n ORDER BY ts DESC LIMIT 1)\n- (SELECT valeur FROM mesure WHERE cle = 'sensor.victron_consumption_power_l1'\n ORDER BY ts DESC LIMIT 1) AS \"Excedent\""
}
],
"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": "Batterie"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "orange",
"value": 25
},
{
"color": "yellow",
"value": 40
},
{
"color": "green",
"value": 60
}
]
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Marge"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "decimals",
"value": 1
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 0
}
]
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Excedent"
},
"properties": [
{
"id": "unit",
"value": "watt"
},
{
"id": "decimals",
"value": 0
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
},
{
"color": "green",
"value": 0
}
]
}
}
]
}
]
},
"options": {
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"orientation": "horizontal",
"textMode": "auto",
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"percentChangeColorMode": "standard",
"noValue": "—",
"wideLayout": true
}
},
{
"type": "stat",
"title": "La nuit qui vient",
"description": "Energie a couvrir d'ici l'heure ou la production repasse au-dessus de la consommation, et le niveau de batterie qu'il faudrait pour y arriver.",
"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 round(energie_a_couvrir_kwh::numeric, 1) AS \"A couvrir\",\n round(soc_requis_pct::numeric) AS \"SOC requis\",\n heure_croisement AS \"Relais du solaire\"\nFROM bilan ORDER BY ts DESC LIMIT 1"
}
],
"gridPos": {
"x": 0,
"y": 5,
"w": 24,
"h": 5
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "text",
"value": null
}
]
},
"mappings": []
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "A couvrir"
},
"properties": [
{
"id": "unit",
"value": "kwatth"
}
]
},
{
"matcher": {
"id": "byName",
"options": "SOC requis"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 60
},
{
"color": "orange",
"value": 85
},
{
"color": "red",
"value": 100
}
]
}
}
]
}
]
},
"options": {
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"orientation": "horizontal",
"textMode": "auto",
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"percentChangeColorMode": "standard",
"noValue": "—",
"wideLayout": true
}
},
{
"type": "timeseries",
"title": "Ce qu'elle a, ce qu'il lui faut",
"description": "Quand le vert passe sous l'orange, le reseau sera necessaire.",
"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,\n soc_pct AS \"Batterie\",\n soc_requis_pct AS \"Requis\"\nFROM bilan WHERE $__timeFilter(ts) ORDER BY 1"
}
],
"gridPos": {
"x": 0,
"y": 10,
"w": 24,
"h": 7
},
"fieldConfig": {
"defaults": {
"unit": "percent",
"color": {
"mode": "palette-classic"
},
"custom": {
"lineWidth": 2,
"fillOpacity": 8,
"showPoints": "never",
"spanNulls": true,
"axisBorderShow": false,
"gradientMode": "opacity"
},
"min": 0,
"max": 100
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Batterie"
},
"properties": [
{
"id": "color",
"value": {
"mode": "fixed",
"fixedColor": "green"
}
},
{
"id": "custom.lineWidth",
"value": 3
}
]
},
{
"matcher": {
"id": "byName",
"options": "Requis"
},
"properties": [
{
"id": "color",
"value": {
"mode": "fixed",
"fixedColor": "orange"
}
},
{
"id": "custom.fillOpacity",
"value": 0
}
]
}
]
},
"options": {
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": true,
"calcs": []
},
"tooltip": {
"mode": "multi",
"sort": "desc"
}
}
},
{
"type": "timeseries",
"title": "Ce qui reste apres la maison",
"description": "Au-dessus de zero, la voiture peut charger sans rien couter. En dessous, la charger revient a vider la batterie.",
"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 par_minute AS (\n SELECT date_trunc('minute', ts) AS t,\n avg(valeur) FILTER (WHERE cle = 'sensor.victron_pv_power_system') AS Production,\n (array_agg(valeur ORDER BY ts DESC) FILTER (WHERE cle = 'sensor.victron_pv_power_system'))[1] AS d_Production,\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 FROM mesure WHERE $__timeFilter(ts) GROUP BY 1\n),\npaliers AS (\n SELECT t,\n Production, count(d_Production) OVER (ORDER BY t) AS g_Production,\n Maison, count(d_Maison) OVER (ORDER BY t) AS g_Maison,\n d_Production,\n d_Maison\n FROM par_minute\n),\nreport AS (\n SELECT t,\n coalesce(Production, first_value(d_Production) OVER (PARTITION BY g_Production ORDER BY t)) AS Production,\n coalesce(Maison, first_value(d_Maison) OVER (PARTITION BY g_Maison ORDER BY t)) AS Maison\n FROM paliers\n)\nSELECT t AS time,\n Production AS \"Production\",\n Maison AS \"Maison\",\n Production - Maison AS \"Excedent\"\nFROM report ORDER BY t"
}
],
"gridPos": {
"x": 0,
"y": 17,
"w": 24,
"h": 7
},
"fieldConfig": {
"defaults": {
"unit": "watt",
"color": {
"mode": "palette-classic"
},
"custom": {
"lineWidth": 2,
"fillOpacity": 8,
"showPoints": "never",
"spanNulls": true,
"axisBorderShow": false,
"gradientMode": "opacity"
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Production"
},
"properties": [
{
"id": "color",
"value": {
"mode": "fixed",
"fixedColor": "yellow"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Maison"
},
"properties": [
{
"id": "color",
"value": {
"mode": "fixed",
"fixedColor": "text"
}
},
{
"id": "custom.fillOpacity",
"value": 0
}
]
},
{
"matcher": {
"id": "byName",
"options": "Excedent"
},
"properties": [
{
"id": "color",
"value": {
"mode": "fixed",
"fixedColor": "green"
}
},
{
"id": "custom.lineWidth",
"value": 3
}
]
}
]
},
"options": {
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": true,
"calcs": []
},
"tooltip": {
"mode": "multi",
"sort": "desc"
}
}
},
{
"type": "state-timeline",
"title": "Le reseau",
"description": "Une seule bascule par nuit : c'est le but de la session unique.",
"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,\n source_mode AS \"Source\",\n CASE WHEN session_engagee THEN 'engagee' ELSE '-' END AS \"Session\"\nFROM decision WHERE $__timeFilter(ts) ORDER BY 1"
}
],
"gridPos": {
"x": 0,
"y": 24,
"w": 24,
"h": 5
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"custom": {
"lineWidth": 0,
"fillOpacity": 80,
"insertNulls": false,
"spanNulls": true
},
"mappings": [
{
"type": "value",
"options": {
"-": {
"color": "dark-blue",
"index": 0
},
"engagee": {
"color": "orange",
"index": 1
}
}
}
]
},
"overrides": []
},
"options": {
"showValue": "auto",
"mergeValues": true,
"alignValue": "left",
"rowHeight": 0.85,
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"mode": "single"
}
}
},
{
"type": "table",
"title": "Ce qui s'est passe",
"description": "Les huit derniers evenements, quelle que soit la fenetre de temps.",
"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, 'DD/MM HH24:MI') AS \"Quand\",\n resume AS \"Quoi\"\nFROM evenement ORDER BY ts DESC LIMIT 8"
}
],
"gridPos": {
"x": 0,
"y": 29,
"w": 24,
"h": 7
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "left",
"cellOptions": {
"type": "auto"
},
"filterable": true
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Quand"
},
"properties": [
{
"id": "custom.width",
"value": 110
}
]
}
]
},
"options": {
"showHeader": true,
"cellHeight": "sm",
"footer": {
"show": false
}
}
}
]
}