Memoire de mesures : PostgreSQL et InfluxDB sur le second Synology
Le proprietaire a tranche : on met les deux en service et on decidera a l usage s il faut garder les deux. Elles ne repondent pas a la meme question. PostgreSQL garde ce que n8n CALCULE — sorties du bilan, decisions du controleur, prevision telle qu elle a ete publiee — parce que ce ne sont pas des capteurs, donc qu aucune statistique Home Assistant ne les retient, et parce que ce sont des vecteurs, du texte et des jointures. Cinq tables, une vue de confort qui repond a la question restee sans reponse le 03/09 : ce que le bilan exigeait contre le SOC reellement atteint, heure par heure. InfluxDB garde ce que Home Assistant OBSERVE, a la resolution native et sur tout le perimetre energie, y compris les entites auxquelles personne n a encore pense. C est exactement ce qui a manque le 03/09, quand les modes des chargeurs MPPT se sont reveles etre le bon signal alors que personne ne les enregistrait. Les deux sur 10.10.10.22 : cette machine ne porte qu un service, quand 10.10.10.17 en porte quatre. Le volume n est pas l argument, les deux Synology ont des teraoctets libres. L alimentation de PostgreSQL ne passe volontairement PAS par n8n. Tout ce que n8n calcule, il le publie deja dans des helpers Home Assistant : les lire donne la meme information sans toucher aux trois workflows de production stabilises hier soir ni creer d identifiant dans n8n. L horodatage retenu est celui de Home Assistant et non celui du sondage, si bien qu une valeur inchangee porte la meme clef primaire et n est pas reecrite : on enregistre quand la grandeur bouge, pas quand on regarde. Essai a blanc verifie sur l installation reelle : 15 mesures, 1 ligne de bilan, 1 decision, 2 previsions, avec les bonnes dates. Le mode blanc comptait d abord les lignes ecrites, donc zero par construction — corrige pour compter ce qu il ecrirait, sans quoi il ne verifiait rien. Le branchement de Home Assistant sur InfluxDB demande un redemarrage et n est donc PAS applique : le fichier de configuration est pret, a poser en milieu de matinee, jamais entre 01:24 et 07:24. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
149
deploy/historisation/schema.sql
Normal file
149
deploy/historisation/schema.sql
Normal file
@@ -0,0 +1,149 @@
|
||||
-- =====================================================================
|
||||
-- Mémoire de mesures EVCS — schéma PostgreSQL (proposition A)
|
||||
-- Appliqué automatiquement à la création de la base par le compose.
|
||||
-- =====================================================================
|
||||
--
|
||||
-- Ce que cette base garde, et que ni l'historique de Home Assistant ni les
|
||||
-- statistiques long terme ne savent garder :
|
||||
--
|
||||
-- 1. ce que n8n CALCULE — SOC requis, heure de croisement, décisions et
|
||||
-- leurs motifs. Ce ne sont pas des capteurs, donc aucune statistique.
|
||||
-- 2. des VECTEURS — une prévision est 24 valeurs pour une journée.
|
||||
-- 3. du TEXTE — un motif de censure, une raison de décision.
|
||||
-- 4. des JOINTURES — « la prévision publiée à 04:00 contre la
|
||||
-- production réelle de 13 h » est ici une requête, ailleurs un exercice.
|
||||
--
|
||||
-- Ce qu'elle ne cherche PAS à faire : le flux brut de tous les états. C'est le
|
||||
-- rôle d'InfluxDB, à côté (voir compose.yaml).
|
||||
--
|
||||
-- Convention : tout horodatage est en timestamptz, écrit en UTC, lu en
|
||||
-- Europe/Paris. Ne jamais stocker d'heure locale nue — le passage à l'heure
|
||||
-- d'hiver du 25/10 rendrait deux heures indiscernables.
|
||||
|
||||
SET timezone = 'Europe/Paris';
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 1. MESURES — séries échantillonnées, format long
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Format long (une ligne par couple clé/instant) et non large : les grandeurs
|
||||
-- suivies changent au fil des sessions, et une table large impose une
|
||||
-- migration à chaque ajout. L'index couvre l'accès réel : « telle clé, sur
|
||||
-- telle plage de temps ».
|
||||
CREATE TABLE mesure (
|
||||
ts timestamptz NOT NULL,
|
||||
cle text NOT NULL,
|
||||
valeur double precision NOT NULL,
|
||||
PRIMARY KEY (cle, ts)
|
||||
);
|
||||
COMMENT ON TABLE mesure IS
|
||||
'Grandeurs numériques échantillonnées par n8n. Cadence = celle du workflow.';
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 2. BILAN — une ligne par exécution du bilan énergétique
|
||||
-- ---------------------------------------------------------------------
|
||||
CREATE TABLE bilan (
|
||||
ts timestamptz PRIMARY KEY,
|
||||
statut text,
|
||||
soc_pct double precision,
|
||||
energie_a_couvrir_kwh double precision,
|
||||
soc_requis_pct double precision,
|
||||
soc_requis_prudent_pct double precision,
|
||||
heure_croisement text,
|
||||
reseau_necessaire boolean,
|
||||
reseau_energie_kwh double precision,
|
||||
manque_stockage_kwh double precision,
|
||||
pv_censure boolean,
|
||||
motif_censure text,
|
||||
orage_niveau text,
|
||||
orage_reserve_kwh double precision,
|
||||
-- Le reste du bloc tel que publié, pour ne pas avoir à migrer la table
|
||||
-- chaque fois que le bilan gagne un champ.
|
||||
brut jsonb
|
||||
);
|
||||
COMMENT ON COLUMN bilan.soc_requis_pct IS
|
||||
'NON écrêté au-delà de 100 : une valeur supérieure est le signal de sous-dimensionnement du stockage (HANDOFF §26).';
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 3. DECISION — une ligne par changement de décision du contrôleur
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Pas une ligne toutes les 30 s : seulement quand la signature change. Sinon
|
||||
-- 2 880 lignes par jour dont 2 800 identiques, et la table devient illisible.
|
||||
CREATE TABLE decision (
|
||||
ts timestamptz PRIMARY KEY,
|
||||
profil text,
|
||||
source_mode text,
|
||||
etat text,
|
||||
decision text,
|
||||
charge_desiree boolean,
|
||||
courant_a double precision,
|
||||
soc_pct double precision,
|
||||
cible_matin_pct double precision,
|
||||
takeover_reseau boolean,
|
||||
session_engagee boolean,
|
||||
raison text,
|
||||
brut jsonb
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 4. PREVISION — une ligne par publication, avec le vecteur horaire
|
||||
-- ---------------------------------------------------------------------
|
||||
-- `publie_a` est l'instant de publication, `jour` le jour prévu. Les deux sont
|
||||
-- nécessaires : comparer une prévision à la réalité n'a de sens que si l'on
|
||||
-- sait QUAND elle a été faite. La clé primaire les porte donc toutes les deux —
|
||||
-- plusieurs prévisions du même jour, faites à des heures différentes, doivent
|
||||
-- coexister.
|
||||
CREATE TABLE prevision (
|
||||
publie_a timestamptz NOT NULL,
|
||||
jour date NOT NULL,
|
||||
source text NOT NULL DEFAULT 'forecast.solar',
|
||||
total_kwh double precision,
|
||||
horaire double precision[] NOT NULL, -- 24 valeurs, index 0 = 00 h
|
||||
PRIMARY KEY (publie_a, jour)
|
||||
);
|
||||
COMMENT ON TABLE prevision IS
|
||||
'Prévision TELLE QU''ELLE A ÉTÉ PUBLIÉE. Sans cet horodatage de publication, on ne peut pas mesurer le biais de la prévision.';
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 5. EVENEMENT — ce qu''on veut pouvoir retrouver un an après
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Déploiements, incidents, interventions manuelles, changements de matériel.
|
||||
-- Une courbe sans ses événements ne s'interprète pas : « le rendement a changé
|
||||
-- le 12 novembre » n'a de sens qu'avec la ligne qui dit qu'un pack a été
|
||||
-- rebranché ce jour-là.
|
||||
CREATE TABLE evenement (
|
||||
ts timestamptz NOT NULL DEFAULT now(),
|
||||
genre text NOT NULL, -- deploiement | incident | intervention | materiel
|
||||
source text, -- qui l'a écrit
|
||||
resume text NOT NULL,
|
||||
detail jsonb,
|
||||
id bigserial PRIMARY KEY
|
||||
);
|
||||
CREATE INDEX evenement_ts_idx ON evenement (ts DESC);
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Index de lecture
|
||||
-- ---------------------------------------------------------------------
|
||||
CREATE INDEX mesure_ts_idx ON mesure (ts DESC);
|
||||
CREATE INDEX bilan_ts_idx ON bilan (ts DESC);
|
||||
CREATE INDEX decision_ts_idx ON decision (ts DESC);
|
||||
CREATE INDEX prevision_jour_idx ON prevision (jour, publie_a DESC);
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Vue de confort : la question qui a bloqué l'analyse du 03/09
|
||||
-- ---------------------------------------------------------------------
|
||||
-- « Ce que le bilan exigeait, contre le SOC réellement atteint, heure par
|
||||
-- heure. » Trois lignes de SQL ici ; impossible sans cette base.
|
||||
CREATE VIEW bilan_contre_reel AS
|
||||
SELECT date_trunc('hour', ts) AS heure,
|
||||
round(avg(soc_requis_pct)::numeric, 1) AS soc_requis,
|
||||
round(avg(soc_pct)::numeric, 1) AS soc_reel,
|
||||
round(avg(soc_pct - soc_requis_pct)::numeric, 1) AS marge,
|
||||
round(avg(energie_a_couvrir_kwh)::numeric, 2) AS a_couvrir_kwh
|
||||
FROM bilan
|
||||
GROUP BY 1
|
||||
ORDER BY 1 DESC;
|
||||
|
||||
INSERT INTO evenement (genre, source, resume, detail) VALUES
|
||||
('deploiement', 'claude',
|
||||
'Création de la mémoire de mesures : PostgreSQL sur 10.10.10.22',
|
||||
'{"motif": "historique HA limité à ~20 j et sans valeurs calculées", "handoff": "§42"}');
|
||||
Reference in New Issue
Block a user