HA Bilan energetique : nuage soiree/nuit (ApexCharts), points publies dans sensor.evcs_nuit_maison_prevue_kwh

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011uJ47NxgfpGnuuMr3f2n7Z
This commit is contained in:
Tallagrand Thierry
2026-09-29 00:00:09 +02:00
parent 7f3eda421f
commit 71a71e40d7
2 changed files with 84 additions and 5 deletions

View File

@@ -247,6 +247,81 @@ views:
pleine à cette heure : au-delà de 25 %, la production maximale pleine à cette heure : au-delà de 25 %, la production maximale
observée est minorée et le bilan le signale.* observée est minorée et le bilan le signale.*
# Nuage soiree / nuit (28/09/2026) : meme donnees que Grafana « La nuit »,
# publiees par le service d'historisation dans sensor.evcs_nuit_maison_prevue_kwh
# (attribut `nuage` = [soiree, nuit, voiture 0/1]). Graphique « ligne » a trait
# nul + marqueurs = nuage de points ; axe X numerique (kWh), pas le temps.
- type: custom:apexcharts-card
header:
show: true
title: Soirée contre nuit — la soirée annonce la nuit
show_states: false
graph_span: 1d
apex_config:
chart:
height: 300
zoom: {enabled: false}
xaxis:
type: numeric
min: 2
max: 10.5
tickAmount: 8
decimalsInFloat: 1
title: {text: 'Soirée 18:00-21:30 (kWh, voiture exclue)'}
yaxis:
- min: 7
max: 16
decimalsInFloat: 0
title: {text: 'Nuit 21:30-09:00 (kWh)'}
markers:
size: [6, 6, 0, 11]
tooltip:
x:
formatter: 'EVAL:function(v) { return "soirée " + v.toFixed(2) + " kWh"; }'
legend:
show: true
series:
- entity: sensor.evcs_nuit_maison_prevue_kwh
name: Nuit sans voiture
type: line
stroke_width: 0
color: '#4caf50'
data_generator: |
return (entity.attributes.nuage || []).filter(p => !p[2])
.map(p => [p[0], p[1]]).sort((a, b) => a[0] - b[0]);
- entity: sensor.evcs_nuit_maison_prevue_kwh
name: Nuit avec voiture
type: line
stroke_width: 0
color: '#2196f3'
data_generator: |
return (entity.attributes.nuage || []).filter(p => p[2])
.map(p => [p[0], p[1]]).sort((a, b) => a[0] - b[0]);
- entity: sensor.evcs_nuit_maison_prevue_kwh
name: Régression
type: line
stroke_width: 2
color: '#ff9800'
data_generator: |
const a = entity.attributes.a, b = entity.attributes.b, n = entity.attributes.nuage || [];
if (a === null || b === null || !n.length) return [];
const xs = n.map(p => p[0]), lo = Math.min(...xs), hi = Math.max(...xs);
return [[lo, a + b * lo], [hi, a + b * hi]];
- entity: sensor.evcs_nuit_maison_prevue_kwh
name: Ce soir (prévu)
type: line
stroke_width: 0
color: '#f44336'
data_generator: |
const s = entity.attributes.soiree_kwh, v = parseFloat(entity.state);
return (s !== null && s !== undefined && !isNaN(v)) ? [[s, v]] : [];
- type: markdown
content: |
{% set s = 'sensor.evcs_nuit_maison_prevue_kwh' %}
{% if is_number(states(s)) %}**Cette nuit** : soirée {{ state_attr(s, 'soiree_kwh') }} kWh → nuit prévue **{{ states(s) }} kWh** (moyenne {{ state_attr(s, 'nuit_moyenne_kwh') }}), soit **×{{ state_attr(s, 'facteur') }}** sur le profil du bilan.{% else %}Hors de la fenêtre 21:30 → 09:00 : pas de prévision de nuit en cours.{% endif %}
Modèle sur {{ state_attr(s, 'nuits') }} nuits : nuit = {{ state_attr(s, 'a') }} + {{ state_attr(s, 'b') }} × soirée (r² {{ state_attr(s, 'r2') }}).
- type: markdown - type: markdown
content: | content: |
### ⚠️ Taille du parc — en équivalents US2000C ### ⚠️ Taille du parc — en équivalents US2000C

View File

@@ -2015,19 +2015,23 @@ async function publierNuitMaison(client) {
if (DRY || !client) return 0; if (DRY || !client) return 0;
const maintenant = new Date(); const maintenant = new Date();
const cadre = nuitMaisonPrevue([], NaN, maintenant); const cadre = nuitMaisonPrevue([], NaN, maintenant);
let res = cadre; // Les nuits closes sont lues A CHAQUE fois, meme le jour : elles alimentent
// aussi le nuage du tableau HA (attribut `nuage`, [soiree, nuit, voiture 0/1]).
const nuits = (await client.query(NUITS_SQL +
'\nSELECT soiree_kwh AS soiree, maison_nuit_kwh AS nuit, voiture_kwh AS voiture FROM nuits ORDER BY j')).rows;
let soiree = NaN;
if (cadre.etat !== 'hors_fenetre') { if (cadre.etat !== 'hors_fenetre') {
const nuits = (await client.query(NUITS_SQL + '\nSELECT soiree_kwh AS soiree, maison_nuit_kwh AS nuit FROM nuits')).rows;
const debut = cadre.attributs.debut_ms; const debut = cadre.attributs.debut_ms;
const soir = (await client.query(SOIREE_SQL, [debut - 3.5 * 3600000, debut])).rows[0]; const soir = (await client.query(SOIREE_SQL, [debut - 3.5 * 3600000, debut])).rows[0];
// Une soiree sans mesure (service arrete) ne vaut pas zero kWh. // Une soiree sans mesure (service arrete) ne vaut pas zero kWh.
const soiree = soir && Number(soir.points) > 0 ? Number(soir.kwh) : NaN; soiree = soir && Number(soir.points) > 0 ? Number(soir.kwh) : NaN;
res = nuitMaisonPrevue(nuits, soiree, maintenant);
} }
const res = nuitMaisonPrevue(nuits, soiree, maintenant);
const nuage = nuits.map((n) => [Number(n.soiree), Number(n.nuit), Number(n.voiture) >= 0.5 ? 1 : 0]);
await publierCapteur('sensor.evcs_nuit_maison_prevue_kwh', res.etat, { await publierCapteur('sensor.evcs_nuit_maison_prevue_kwh', res.etat, {
friendly_name: 'EVCS — nuit de la maison prevue par la soiree', icon: 'mdi:chart-scatter-plot', friendly_name: 'EVCS — nuit de la maison prevue par la soiree', icon: 'mdi:chart-scatter-plot',
unit_of_measurement: typeof res.etat === 'number' ? 'kWh' : undefined, ...res.attributs, unit_of_measurement: typeof res.etat === 'number' ? 'kWh' : undefined, ...res.attributs,
calcule_a: maintenant.toISOString(), nuage, calcule_a: maintenant.toISOString(),
}); });
return 1; return 1;
} }