From 8a6c7479d405c66962af71267269bbd1a633dcb5 Mon Sep 17 00:00:00 2001 From: Tallagrand Thierry Date: Mon, 12 Oct 2026 14:36:49 +0200 Subject: [PATCH] Kilometrage : niveaux deduits de l'autonomie marques (photo_autonomie), exposes par capacite_estimee.niveau_estime --- tests/kilometrage/run.mjs | 17 +++++++++++++++++ tools/kilometrage.mjs | 24 +++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/kilometrage/run.mjs b/tests/kilometrage/run.mjs index 4778c17..b824463 100644 --- a/tests/kilometrage/run.mjs +++ b/tests/kilometrage/run.mjs @@ -156,6 +156,23 @@ if (process.argv.includes('--vues')) { await c.query(`INSERT INTO decision (ts, etat) VALUES ('2020-03-02T01:42Z','night_recovery_done_idle')`); const v = (await c.query("SELECT * FROM capacite_estimee WHERE branche >= '2020-03-01' AND branche < '2020-03-02'")).rows[0]; proche('sans compteur : sessions du branchement 7,7 / 0,95', v.capacite_kwh, 7.7 / 0.95); + eq('niveau lu, pas estime', v.niveau_estime, false); + // 12/10 : niveau deduit de l'autonomie (photo VW) -> la saisie sans source est marquee. + const { marquerNiveauxEstimes } = await import('../../tools/kilometrage.mjs'); + await ev('2020-04-01T10:00Z', 'on'); await ev('2020-04-01T14:00Z', 'off'); + await c.query(`INSERT INTO niveau_saisi (saisi_a, niveau_pct, ref_kwh) VALUES ('2020-04-01T10:05Z', 33, 200.0)`); + await c.query(`INSERT INTO niveau_saisi (saisi_a, niveau_pct, ref_kwh) VALUES ('2020-04-01T09:50Z', 50, 199.0)`); // lue, autre niveau + await c.query(`INSERT INTO releve_km (vehicule, ts, km, niveau_pct, source) VALUES ('TESTV','2020-04-01T10:00Z',9000,33,'photo_autonomie:14/42')`); + await c.query(`INSERT INTO releve_km (vehicule, ts, km, niveau_pct, source) VALUES ('TESTV','2020-04-01T10:00:30Z',9000,40,'photo_autonomie:17/42')`); + await marquerNiveauxEstimes((q, p) => c.query(q, p)); + const ns = (await c.query("SELECT source FROM niveau_saisi WHERE saisi_a = '2020-04-01T10:05Z'")).rows[0]; + eq('saisie marquee estimee (meme niveau, 5 min)', ns.source, 'photo_autonomie:14/42'); + eq('saisie d un autre niveau non marquee', (await c.query("SELECT source FROM niveau_saisi WHERE saisi_a = '2020-04-01T09:50Z'")).rows[0].source, null); + await c.query(`INSERT INTO session_charge (debut, fin, terminee, vehicule, vehicule_sur, energie_kwh, energie_station_kwh) + VALUES ('2020-04-01T10:10Z','2020-04-01T12:00Z',true,'TESTV',true,5,5.5)`); + await c.query(`INSERT INTO decision (ts, etat) VALUES ('2020-04-01T12:01Z','day_vehicle_full_idle')`); + const w = (await c.query("SELECT * FROM capacite_estimee WHERE branche >= '2020-04-01' AND branche < '2020-04-02'")).rows[0]; + eq('vue : niveau estime expose', w.niveau_estime, true); } finally { await c.query('ROLLBACK'); await c.end(); } } console.log(`${ok}/${ok + ko}`); diff --git a/tools/kilometrage.mjs b/tools/kilometrage.mjs index 0855968..91089d7 100644 --- a/tools/kilometrage.mjs +++ b/tools/kilometrage.mjs @@ -202,13 +202,18 @@ WITH tr AS ( ORDER BY r.ts LIMIT 1) AS niveau_releve_apres, (SELECT nf.niveau_pct FROM niveau_fin nf WHERE nf.saisi_a >= v.debranche - interval '15 minutes' AND nf.saisi_a <= v.debranche + interval '1 hour' - ORDER BY nf.saisi_a LIMIT 1) AS niveau_fin_saisi + ORDER BY nf.saisi_a LIMIT 1) AS niveau_fin_saisi, + (SELECT nf.source FROM niveau_fin nf + WHERE nf.saisi_a >= v.debranche - interval '15 minutes' AND nf.saisi_a <= v.debranche + interval '1 hour' + ORDER BY nf.saisi_a LIMIT 1) AS niveau_fin_source FROM v ), f AS ( SELECT k.*, coalesce(niveau_fin_saisi, CASE WHEN plein THEN 100 ELSE niveau_releve_apres END) AS niveau_fin FROM k ) SELECT branche, debranche, coalesce(vehicule, '?') AS vehicule, niveau_pct, niveau_fin, kwh_livres, plein, + -- Un des deux niveaux est deduit de l'autonomie affichee (pas une lecture). + (coalesce(source, '') ILIKE '%autonomie%' OR coalesce(niveau_fin_source, '') ILIKE '%autonomie%') AS niveau_estime, CASE WHEN vehicule IS NOT NULL AND niveau_fin - niveau_pct >= 10 AND kwh_livres > 0 THEN kwh_livres / ((niveau_fin - niveau_pct) / 100.0) END AS capacite_kwh FROM f`; @@ -268,9 +273,26 @@ export async function ingererNiveauSaisi(sql, st) { const r = await sql( 'INSERT INTO niveau_saisi (saisi_a, niveau_pct, ref_kwh) VALUES ($1,$2,$3) ON CONFLICT DO NOTHING', [new Date(at), niveau, ref]); + await marquerNiveauxEstimes(sql); return r?.rowCount || 0; } +// Niveau ESTIME (12/10/2026, avec SPARK) : un niveau deduit de l'autonomie +// affichee (photo du tableau de bord VW, regle de trois sur l'autonomie pleine +// de reference) arrive dans le releve avec la source « photo_autonomie:/ », +// mais aussi dans le helper du niveau, donc dans niveau_saisi SANS source. +// On recopie la source sur la saisie correspondante (meme niveau, a 20 min pres), +// dans un sens comme dans l'autre de l'ordre d'arrivee. La vue capacite_estimee +// l'expose (niveau_estime) : un niveau deduit de l'autonomie depend de la +// reference, du chauffage et de la conduite, et biaise justement l'usure. +export const SOURCE_ESTIMEE = 'photo_autonomie:'; +export async function marquerNiveauxEstimes(sql) { + await sql( + `UPDATE niveau_saisi n SET source = r.source FROM releve_km r + WHERE n.source IS NULL AND r.source LIKE '${SOURCE_ESTIMEE}%' AND r.niveau_pct = n.niveau_pct + AND n.saisi_a BETWEEN r.ts - interval '20 minutes' AND r.ts + interval '20 minutes'`); +} + // --------------------------------------------------------------------- // JEEP LUE DIRECTEMENT (04/10/2026) : integration HACS hass-uconnect (API // Stellantis non officielle, compte du proprietaire, intervalle 15 min, sans