La prevision entre dans « La journee » et sur le telephone

Suite du correctif de l onglet Home Assistant. « La journee » avait le meme
angle mort : cadre sur le jour civil, il s arretait a l heure en cours.

Fenetre portee a la journee ENTIERE (dashboard() gagne un parametre `jusqu`),
serie « Encore attendu » en gris translucide a la suite des barres mesurees,
et colonne « Reste attendu » dans le bandeau du haut, a cote de « Solaire
produit ».

Sur le telephone, aucun panneau ajoute — un panneau de plus y est un ecran de
plus a faire defiler. « Soleil restant » rejoint le bandeau Maintenant, a cote
de Excedent ; « Soleil demain » rejoint La nuit qui vient, puisque c est lui
qui decide jusqu ou la batterie peut descendre.

Trois pieges, tous dans la meme direction. La mesure ne doit pas suivre la
fenetre dans le futur : ENERGIE_SQL construisait son axe entre les deux macros
puis reportait la derniere valeur connue, ce qui aurait rempli les heures a
venir avec la puissance de l instant et gonfle l energie du jour — sa borne
haute est desormais least($__timeTo(), now()). FULL OUTER JOIN et non LEFT :
une periode a venir n a aucune ligne de mesure. Et borne haute stricte, sans
quoi une barre « 18/09 00 h » apparaissait dans La journee.

L heure EN COURS n a volontairement pas de barre de prevision : elle est deja
mesuree pour ses minutes ecoulees.

73/73 requetes valides — mais ce controle prouve l execution, pas la grandeur.
Les deux requetes de La journee ont donc ete rejouees a la main avec la vraie
fenetre : 24 lignes, mesures jusqu a 02 h, prevision de 03 h a 23 h, somme des
barres 36,95 kWh contre 37,0 annonces par le bandeau. C est ce recoupement qui
a fait apparaitre le troisieme piege.

Corrige au passage la note du 08/09 sur l acces a l API Grafana : les
apostrophes du .env sont SIMPLES et non doubles, verifie a l octet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
This commit is contained in:
Tallagrand Thierry
2026-09-17 02:37:43 +02:00
parent a6baad54dc
commit 1541ccf7b4
5 changed files with 316 additions and 37 deletions

View File

@@ -1245,7 +1245,7 @@
"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)"
"rawSql": "WITH bornes AS (\n -- ⚠️ LA MESURE NE PEUT PAS DEPASSER MAINTENANT. « La journee » s'etend\n -- desormais jusqu'a la fin du jour pour montrer la prevision ; sans ce\n -- garde-fou, le report de la derniere valeur connue (plus bas) remplirait les\n -- heures a venir avec la puissance de l'instant et gonflerait l'energie.\n SELECT $__timeFrom()::timestamptz AS d,\n least($__timeTo()::timestamptz, now()) 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),\nmesure_par_periode AS (\n SELECT date_trunc(pas.unite, t) AS b,\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\"\n FROM report, pas\n GROUP BY date_trunc(pas.unite, t)\n HAVING count(Solaire) > 0 OR count(Reseau) > 0 OR count(Maison) > 0 OR count(Voiture) > 0\n)\n\nSELECT to_char(b, (SELECT forme FROM pas)) AS \"Heure\",\n \"Solaire\",\n \"Reseau\",\n \"Maison\",\n \"Voiture\"\nFROM mesure_par_periode\nORDER BY b"
}
],
"gridPos": {