diff --git a/deploy/historisation/grafana/dashboards/vehicules.json b/deploy/historisation/grafana/dashboards/vehicules.json index 597c1ae..3c17df4 100644 --- a/deploy/historisation/grafana/dashboards/vehicules.json +++ b/deploy/historisation/grafana/dashboards/vehicules.json @@ -77,7 +77,7 @@ "format": "table", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT round(coalesce(sum(coalesce(energie_station_kwh, energie_kwh)) FILTER (WHERE vehicule = 'VW'), 0)::numeric, 1) AS \"VW\",\n round(coalesce(sum(coalesce(energie_station_kwh, energie_kwh)) FILTER (WHERE vehicule = 'JEEP'), 0)::numeric, 1) AS \"JEEP\",\n count(*) AS \"Sessions\",\n count(*) FILTER (WHERE NOT vehicule_sur OR vehicule IS NULL) AS \"Etiquette douteuse\"\nFROM session_charge WHERE $__timeFilter(debut)" + "rawSql": "SELECT round(coalesce(sum(coalesce(energie_station_kwh, energie_kwh)) FILTER (WHERE vehicule = 'VW'), 0)::numeric, 1) AS \"VW\",\n round(coalesce(sum(coalesce(energie_station_kwh, energie_kwh)) FILTER (WHERE vehicule = 'JEEP'), 0)::numeric, 1) AS \"JEEP\",\n count(*) AS \"Sessions\",\n count(*) FILTER (WHERE NOT vehicule_sur OR vehicule IS NULL) AS \"A confirmer\"\nFROM session_charge WHERE $__timeFilter(debut)" } ], "gridPos": { @@ -267,7 +267,7 @@ "format": "table", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT to_char(debut, 'Dy DD/MM HH24:MI') AS \"Debut\",\n coalesce(vehicule, '?') AS \"Vehicule\",\n CASE WHEN vehicule_sur THEN '' ELSE 'selecteur change' END AS \"Doute\",\n round((extract(epoch FROM fin - debut) / 60)::numeric) AS \"Minutes\",\n round(coalesce(energie_station_kwh, energie_kwh)::numeric, 2) AS \"kWh\",\n round(energie_kwh::numeric, 2) AS \"kWh integres\",\n round(energie_reseau_kwh::numeric, 2) AS \"dont reseau\",\n round(p_max_w::numeric) AS \"P max\",\n consigne_max_a AS \"Consigne max\",\n source_mode AS \"Source au depart\",\n to_char(debranche_a, 'Dy HH24:MI') AS \"Debranche\"\nFROM session_charge WHERE $__timeFilter(debut) ORDER BY debut DESC" + "rawSql": "SELECT to_char(debut, 'Dy DD/MM HH24:MI') AS \"Debut\",\n coalesce(vehicule, '?') AS \"Vehicule\",\n CASE WHEN vehicule_sur THEN 'confirme' ELSE 'a confirmer' END AS \"Etiquette\",\n round((extract(epoch FROM fin - debut) / 60)::numeric) AS \"Minutes\",\n round(coalesce(energie_station_kwh, energie_kwh)::numeric, 2) AS \"kWh\",\n round(energie_kwh::numeric, 2) AS \"kWh integres\",\n round(energie_reseau_kwh::numeric, 2) AS \"dont reseau\",\n round(p_max_w::numeric) AS \"P max\",\n consigne_max_a AS \"Consigne max\",\n source_mode AS \"Source au depart\",\n to_char(debranche_a, 'Dy HH24:MI') AS \"Debranche\"\nFROM session_charge WHERE $__timeFilter(debut) ORDER BY debut DESC" } ], "gridPos": { diff --git a/tools/gen-grafana-dashboards.py b/tools/gen-grafana-dashboards.py index 4c36f8e..9a4857f 100644 --- a/tools/gen-grafana-dashboards.py +++ b/tools/gen-grafana-dashboards.py @@ -1029,7 +1029,7 @@ vehicules = dashboard("evcs-vehicules", "Les vehicules", ( "SELECT round(coalesce(sum(coalesce(energie_station_kwh, energie_kwh)) FILTER (WHERE vehicule = 'VW'), 0)::numeric, 1) AS \"VW\",\n" " round(coalesce(sum(coalesce(energie_station_kwh, energie_kwh)) FILTER (WHERE vehicule = 'JEEP'), 0)::numeric, 1) AS \"JEEP\",\n" " count(*) AS \"Sessions\",\n" - " count(*) FILTER (WHERE NOT vehicule_sur OR vehicule IS NULL) AS \"Etiquette douteuse\"\n" + " count(*) FILTER (WHERE NOT vehicule_sur OR vehicule IS NULL) AS \"A confirmer\"\n" "FROM session_charge WHERE $__timeFilter(debut)", fmt="table")], PG, 0, 0, 24, 5, "« Etiquette douteuse » compte les sessions ou le selecteur a change en " @@ -1056,7 +1056,7 @@ vehicules = dashboard("evcs-vehicules", "Les vehicules", ( sql("A", "SELECT to_char(debut, 'Dy DD/MM HH24:MI') AS \"Debut\",\n" " coalesce(vehicule, '?') AS \"Vehicule\",\n" - " CASE WHEN vehicule_sur THEN '' ELSE 'selecteur change' END AS \"Doute\",\n" + " CASE WHEN vehicule_sur THEN 'confirme' ELSE 'a confirmer' END AS \"Etiquette\",\n" " round((extract(epoch FROM fin - debut) / 60)::numeric) AS \"Minutes\",\n" " round(coalesce(energie_station_kwh, energie_kwh)::numeric, 2) AS \"kWh\",\n" " round(energie_kwh::numeric, 2) AS \"kWh integres\",\n" diff --git a/tools/historise-evcs.mjs b/tools/historise-evcs.mjs index 301241e..91e148c 100644 --- a/tools/historise-evcs.mjs +++ b/tools/historise-evcs.mjs @@ -434,7 +434,7 @@ async function migrer(client) { fin timestamptz NOT NULL, terminee boolean NOT NULL DEFAULT false, vehicule text, - vehicule_sur boolean NOT NULL DEFAULT true, + vehicule_sur boolean NOT NULL DEFAULT false, energie_kwh double precision NOT NULL DEFAULT 0, p_max_w double precision, courant_max_a double precision, @@ -466,6 +466,35 @@ async function migrer(client) { await client.query( `ALTER TABLE session_charge ADD COLUMN IF NOT EXISTS ${col} double precision`); } + // --------------------------------------------------------------------- + // `vehicule_sur` CHANGE DE SENS (14/09/2026) + // --------------------------------------------------------------------- + // Avant : « le selecteur n'a pas change PENDANT la session ». Une propriete + // qui ne prouvait rien — le selecteur est reste sur VW du 07/09 au 13/09 et + // onze sessions fausses portaient toutes `true`. Mesure faite depuis : sur + // douze branchements, le selecteur n'a ete regle au bon moment QU'UNE FOIS. + // Desormais : « un humain a confirme », et seul le concierge le pose. + await client.query( + 'ALTER TABLE session_charge ALTER COLUMN vehicule_sur SET DEFAULT false'); + // Rattrapage des lignes anterieures, une seule fois. Rien n'ayant ete + // confirme par le canal tracable, tout retombe a faux — SAUF les six lignes + // que le proprietaire a lui-meme arbitrees le 13/09 (cf. la reparation + // ci-dessus) : c'est une vraie confirmation humaine, seulement donnee de + // vive voix. La perdre serait jeter l'information qu'il a fournie. + const { rowCount: bascules } = await client.query( + `UPDATE session_charge SET vehicule_sur = false + WHERE vehicule_sur AND debut < '2026-09-14'::date`); + if (bascules) { + await client.query( + `UPDATE session_charge SET vehicule_sur = true WHERE debut = ANY($1::timestamptz[])`, + [['2026-09-09 15:55:15.423+02', '2026-09-09 19:35:51.789+02', + '2026-09-10 01:25:24.359+02', '2026-09-11 16:54:58.101+02', + '2026-09-12 11:42:56.901+02', '2026-09-12 11:55:54.493+02']]); + await journaliser(client.query.bind(client), 'vehicule', + `Semantique de vehicule_sur inversee : ${bascules} lignes repassees a « non confirme »`, + { origine: 'proprietaire', motif: 'le drapeau dit desormais « un humain a confirme »', + confirmees_de_vive_voix: 6, date_arbitrage: '2026-09-13' }); + } await client.query( 'CREATE INDEX IF NOT EXISTS session_charge_debut_idx ON session_charge (debut DESC)'); @@ -710,8 +739,13 @@ async function ecrireSession(sql, client, st) { p_max_w = GREATEST(coalesce(p_max_w, 0), $3), courant_max_a = GREATEST(coalesce(courant_max_a, 0), coalesce($5, 0)), consigne_max_a = GREATEST(coalesce(consigne_max_a, 0), coalesce($6, 0)), - vehicule = coalesce($7, vehicule), - vehicule_sur = vehicule_sur AND ($7 IS NULL OR vehicule IS NULL OR vehicule = $7) + -- vehicule_sur n'est PLUS touche ici, et la virgule de la ligne + -- precedente a disparu avec lui. Le drapeau ne dit plus « le + -- selecteur n'a pas bouge » — une propriete qui ne prouvait rien, + -- un selecteur perime depuis six jours restant « sur » — mais + -- « UN HUMAIN A CONFIRME ». Seule une reponse du proprietaire, + -- via le concierge, peut le mettre a vrai. + vehicule = coalesce($7, vehicule) WHERE debut = $1`, [ouverte.debut, maintenant, p, dt, num(g('sensor.victron_evcs_current')?.state),