From c387415c4f276fd3c714ec407c1d84fd96936738 Mon Sep 17 00:00:00 2001 From: Tallagrand Thierry Date: Tue, 8 Sep 2026 00:14:20 +0200 Subject: [PATCH] Le bilan n expose plus que des nombres MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'API Grafana, enfin accessible, a montre que le serveur renvoyait bien une ligne de valeurs au panneau : le defaut etait donc dans le rendu, pas dans la donnee. Restaient deux elements que le panneau « Sur la periode » des vehicules, lui fonctionnel, n'a pas : une colonne de texte dans le resultat, et un override `custom.hidden` pose pour la masquer — propriete qui n'existe pas sur un panneau stat. Les deux disparaissent. Le GROUP BY, lui, reste necessaire et s'ecrit desormais sur une expression constante, `GROUP BY (1 = 1)`, qui ne demande aucune colonne dans le SELECT. PostgreSQL refuse `GROUP BY true` (non-integer constant) mais accepte cette forme. Verifie par l'API sur la fenetre « Last 24 hours » : sept champs, tous de type number, valeurs presentes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj --- .../historisation/grafana/dashboards/journee.json | 14 +------------- tools/gen-grafana-dashboards.py | 8 +++----- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/deploy/historisation/grafana/dashboards/journee.json b/deploy/historisation/grafana/dashboards/journee.json index 7e16992..8fdd2cf 100644 --- a/deploy/historisation/grafana/dashboards/journee.json +++ b/deploy/historisation/grafana/dashboards/journee.json @@ -77,7 +77,7 @@ "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 'Journee' AS \"Periode\",\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" + "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": { @@ -103,18 +103,6 @@ "mappings": [] }, "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Periode" - }, - "properties": [ - { - "id": "custom.hidden", - "value": true - } - ] - }, { "matcher": { "id": "byName", diff --git a/tools/gen-grafana-dashboards.py b/tools/gen-grafana-dashboards.py index 1287751..8fa6563 100644 --- a/tools/gen-grafana-dashboards.py +++ b/tools/gen-grafana-dashboards.py @@ -1274,7 +1274,7 @@ report AS ( # Une seule ligne, six colonnes : c'est le resume que l'on lit en premier. # L'autonomie se calcule sur la maison SANS la voiture, sinon une grosse charge # solaire du vehicule gonfle artificiellement le taux. -# ⚠️ Le `GROUP BY 1` sur une colonne constante n'est pas cosmetique. Un agregat +# ⚠️ Le `GROUP BY (1 = 1)` n'est pas cosmetique. Un agregat # GLOBAL (sans GROUP BY) sur ces CTE rendait un panneau vide — sept « — » — # alors que la meme requete donnait les bons totaux en psql, que le fichier # servi par Grafana etait bien le dernier (md5 verifie) et que le serveur ne @@ -1282,7 +1282,6 @@ report AS ( # CTE mais groupe par heure, s'affichait lui sans probleme : le GROUP BY etait # la derniere difference structurelle entre les deux. BILAN_TOTAUX = BILAN_BASE + """SELECT - 'Journee' AS "Periode", round((sum(pv) / 60.0 / 1000.0)::numeric, 1) AS "Solaire produit", round((sum(maison) / 60.0 / 1000.0)::numeric, 1) AS "Maison consommee", round((sum(reseau) / 60.0 / 1000.0)::numeric, 1) AS "Reseau importe", @@ -1291,7 +1290,7 @@ BILAN_TOTAUX = BILAN_BASE + """SELECT round((sum(greatest(-batterie, 0)) / 60.0 / 1000.0)::numeric, 1) AS "Batterie rendue", round((100.0 * greatest(sum(maison) - sum(reseau), 0) / nullif(sum(maison), 0))::numeric, 0) AS "Autonomie" -FROM report GROUP BY 1""" +FROM report GROUP BY (1 = 1)""" BILAN_FLUX = BILAN_BASE + """SELECT t AS time, pv AS "Production solaire", @@ -1313,8 +1312,7 @@ journee = dashboard( "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.", - overrides=[sur("Periode", [{"id": "custom.hidden", "value": True}])] - + [sur(n, [{"id": "unit", "value": "kwatth"}]) for n in + overrides=[sur(n, [{"id": "unit", "value": "kwatth"}]) for n in ("Solaire produit", "Maison consommee", "Voiture", "Batterie chargee", "Batterie rendue")] + [sur("Reseau importe", [{"id": "unit", "value": "kwatth"},