Files
EVCS/deploy/historisation/compose.yaml
Tallagrand Thierry fe029cfdff Grafana : les deux bases se consultent, et les requetes sont verifiees
Trois tableaux de bord provisionnes depuis git, sur le modele de la regle R2
(le depot est la source de verite, l'interface ne peut pas les ecraser) :

  La nuit      SOC reel contre SOC exige par le bilan, et la marge entre les
               deux. C'est la question qui avait demande une nuit d'archeologie
               le 03/09.
  Le solaire   l'excedent (§36), la censure, et le mode declare par chacun des
               sept chargeurs (§37) — le signal le plus direct dont on dispose.
  Prevision    l'ecart heure par heure, avec la bande de censure dessous : une
               production sous la prevision peut vouloir dire mauvaise
               prevision OU absence de preneur.

Les evenements de la table `evenement` deviennent des marqueurs verticaux sur
tous les graphiques — une courbe sans ses evenements ne s'interprete pas.

tools/verifier-dashboards.mjs rejoue CHAQUE requete pour de vrai (SQL contre
PostgreSQL, Flux contre InfluxDB, macros Grafana substituees), annotations
comprises puisqu'elles s'executent sur tous les panneaux. Sans lui une requete
cassee ne se voit qu'a l'ecran, panneau par panneau. 19/19 au deploiement.

Port 3001 et non 3000 : sonde avant d'ecrire la ligne, quelque chose ecoute
deja sur 3000 — la lecon du 5432 deja pris par le PostgreSQL de DSM.

Connexion PostgreSQL par le role evcs_lecture : un tableau de bord ne doit pas
pouvoir ecrire dans la memoire. Aucun secret duplique, aucun secret commite :
le provisionnement les reference par $VARIABLE depuis les deux .env.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-09-04 08:06:25 +02:00

177 lines
6.8 KiB
YAML

# Mémoire de mesures EVCS — à déployer sur le SECOND Synology (10.10.10.22).
#
# Deux bases, deux rôles distincts, décidés le 04/09/2026 :
#
# postgres (proposition A) — ce que n8n CALCULE : sorties du bilan,
# décisions du contrôleur, prévision telle qu'elle a été publiée.
# Relationnel, donc capable de stocker un vecteur de 24 valeurs,
# une raison en texte, un événement daté — et de les JOINDRE.
#
# influxdb (proposition B) — ce que Home Assistant OBSERVE : chaque
# changement d'état, à la résolution native, y compris les entités
# auxquelles personne n'a encore pensé. C'est ce qui a manqué le
# 03/09, quand les modes des chargeurs MPPT se sont révélés être le
# bon signal alors que personne ne les enregistrait.
#
# ⚠️ NE PAS héberger sur 10.10.10.17 : cette machine porte déjà Home Assistant,
# n8n, openclaw et open-webui. Une mémoire qui meurt avec la machine qu'elle
# documente ne sert à rien (cf. HANDOFF §32).
#
# Mise en service :
# cd /volume2/projects/EVCS/EVCS/deploy/historisation
# bash preparer-env.sh (genere .env avec des secrets aleatoires)
# docker compose up -d
# docker compose logs -f
#
# Vérification :
# docker compose exec postgres psql -U evcs -d evcs -c '\dt'
# curl -s http://10.10.10.22:8086/health
#
# Volumes NOMMÉS et non bind-mounts : le démon Docker de DSM refuse un
# bind-mount dont le dossier hôte est absent, et une base de données sur un
# partage réseau est une mauvaise idée (verrouillage, latence, fsync).
services:
postgres:
image: postgres:16-alpine
container_name: evcs-postgres
restart: always
ports:
# 5433 et non 5432 : DSM fait deja tourner son propre PostgreSQL sur le
# port standard, pour ses services internes. On ne le deloge pas — on se
# met a cote. Ce port n'est publie que pour l'analyse depuis un poste de
# travail ; l'historisation, elle, passe par le reseau interne.
- "5433:5432"
env_file:
- .env
environment:
- TZ=Europe/Paris
- POSTGRES_USER=evcs
- POSTGRES_DB=evcs
# POSTGRES_PASSWORD vient de historisation.env
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
- evcs-pgdata:/var/lib/postgresql/data
# Appliqué UNE SEULE FOIS, à la création de la base. Pour faire évoluer
# le schéma ensuite, écrire une migration et la passer à psql.
- ./schema.sql:/docker-entrypoint-initdb.d/10-schema.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U evcs -d evcs"]
interval: 1m
timeout: 10s
retries: 5
influxdb:
image: influxdb:2.7-alpine
container_name: evcs-influxdb
restart: always
ports:
# Home Assistant, sur 10.10.10.17, écrit ici.
- "8086:8086"
env_file:
- .env
environment:
- TZ=Europe/Paris
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_ORG=airtria
- DOCKER_INFLUXDB_INIT_BUCKET=evcs
# Rétention 0 = illimitée sur le seau brut. Le sous-échantillonnage se
# fait par tâches (voir README), pas en jetant la donnée d'entrée.
- DOCKER_INFLUXDB_INIT_RETENTION=0
# DOCKER_INFLUXDB_INIT_USERNAME / _PASSWORD / _ADMIN_TOKEN : env file
volumes:
- evcs-influx-data:/var/lib/influxdb2
- evcs-influx-config:/etc/influxdb2
healthcheck:
test: ["CMD", "influx", "ping"]
interval: 1m
timeout: 10s
retries: 5
# Alimentation de PostgreSQL depuis Home Assistant. Volontairement PAS
# depuis n8n : tout ce que n8n calcule, il le publie deja dans des helpers HA
# — les lire ici donne la meme information sans toucher aux trois workflows
# de production ni creer d'identifiant dans n8n.
historisation:
image: node:22-alpine
container_name: evcs-historisation
restart: always
working_dir: /app
depends_on:
postgres:
condition: service_healthy
volumes:
# Depot en LECTURE SEULE : ce service n'ecrit que dans la base.
- /volume2/projects/EVCS/EVCS:/app:ro
env_file:
- .env
environment:
- TZ=Europe/Paris
- HA_URL=http://10.10.10.17:8123
# Nom de service sur le reseau interne du compose : ni IP en dur, ni
# dependance au port publie sur l'hote.
- PGHOST=postgres
- PGPORT=5432
- PGUSER=evcs
- PGDATABASE=evcs
# Reprise du mot de passe unique par substitution : il n'y a qu'un seul
# secret PostgreSQL a renseigner, pas deux a tenir egaux a la main.
- PGPASSWORD=${POSTGRES_PASSWORD}
- HISTORISE_INTERVAL_S=60
command: ["node", "tools/historise-evcs.mjs"]
# Consultation des DEUX bases au meme endroit. Grafana ne stocke rien de la
# mesure : il interroge PostgreSQL et InfluxDB a la demande. Le perdre ne
# perd aucune donnee — c'est une fenetre, pas un coffre.
grafana:
image: grafana/grafana-oss:11.3.0
container_name: evcs-grafana
restart: always
depends_on:
postgres:
condition: service_healthy
ports:
# 3001 et NON 3000 : quelque chose ecoute deja sur 3000 sur cette
# machine (verifie avant d'ecrire cette ligne, comme le port 5432 deja
# pris par le PostgreSQL interne de DSM).
- "3001:3000"
env_file:
# Deux fichiers : les secrets de la pile (mot de passe admin Grafana,
# mot de passe PostgreSQL) et ceux du depot (role de lecture, jeton
# InfluxDB). Aucun secret n'est duplique — c'etait le piege du
# POSTGRES_PASSWORD/PGPASSWORD tenus egaux a la main.
- .env
- ../../.env
environment:
- TZ=Europe/Paris
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
# Rien ne sort de la maison : ni telemetrie, ni verification de version,
# ni recuperation de plugins. Cette installation doit fonctionner
# pendant une coupure Internet d'orage (HANDOFF §32).
- GF_ANALYTICS_REPORTING_ENABLED=false
- GF_ANALYTICS_CHECK_FOR_UPDATES=false
- GF_ANALYTICS_CHECK_FOR_PLUGIN_UPDATES=false
- GF_NEWS_NEWS_FEED_ENABLED=false
- GF_USERS_ALLOW_SIGN_UP=false
- GF_DATE_FORMATS_DEFAULT_TIMEZONE=Europe/Paris
volumes:
# Provisionnement en LECTURE SEULE : sources de donnees et tableaux de
# bord viennent du depot git, comme le code des nodes n8n (regle R2).
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
# Base interne de Grafana : sessions, preferences, copies de travail.
- evcs-grafana-data:/var/lib/grafana
healthcheck:
test: ["CMD-SHELL", "wget -q -O- http://localhost:3000/api/health || exit 1"]
interval: 1m
timeout: 10s
retries: 5
volumes:
evcs-pgdata:
evcs-influx-data:
evcs-influx-config:
evcs-grafana-data: