Les pointilles s effacent, et une sauvegarde ne se faisait plus depuis juin

Trois choses vues en regardant deux captures.

La tache Hyper Backup « Synology NAS 1 » echoue en zero seconde : sa
destination, le dossier partage ARCHIVEFAMILLE sur 10.10.10.22, n existe pas —
ni en clair, ni chiffre, ni dans la configuration Samba. Sa jumelle, qui vise
le dossier video du meme hote, reussit. Dix notifications d echec retrouvees,
zero reussite, la plus ancienne du 6 juin : les photos 2008-2015 n ont aucune
sauvegarde depuis trois mois. Non corrige, la machine source n accepte pas ma
cle et le choix de destination appartient au proprietaire.

Les annotations Grafana deviennent opt-in, et le genre bilan en est ecarte : a
lui seul un quart des lignes, presque toutes des bascules d un drapeau qui
change plusieurs fois par heure. Un trait vertical marque ce qui arrive, pas ce
qui clignote. Au passage les deux panneaux de comparaison affichaient huit
courbes dont quatre mortes ; ils ne montrent plus que les modules ayant emis
dans les deux dernieres heures — un critere d activite, pas un prefixe de
code-barres.

Et la marge de 50 % de la nuit est verifiee : il fallait couvrir 2,22 kWh
jusqu au croisement solaire, le bilan en avait reserve 2,65. Dix-neuf pour cent
d assurance sur un parc de quatre modules.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
Tallagrand Thierry
2026-09-06 15:44:30 +02:00
parent 0ba3cb81ee
commit 3fe84e09d4
10 changed files with 118 additions and 18 deletions

View File

@@ -4948,6 +4948,78 @@ joignable depuis le telephone. Et une session Claude, meme pilotable a distance,
reste un poste de travail — ce n'est pas elle qui doit porter la surveillance de reste un poste de travail — ce n'est pas elle qui doit porter la surveillance de
nuit, d'ou le rapport par courriel loge dans le service d'historisation. nuit, d'ou le rapport par courriel loge dans le service d'historisation.
### La sauvegarde « Synology NAS 1 » echoue depuis au moins TROIS MOIS
Signalee par une notification DSM apercue dans une capture. Faits, lus dans le
courriel d'echec :
```
Tache de sauvegarde : Synology NAS 1
Destination : 10.10.10.22 / ARCHIVEFAMILLE / ARCHIVE_1_PHOTOS_2008_2015.hbk
Duree : 0 Second
```
**Duree zero** : la tache n'a pas pu demarrer, ce n'est pas une erreur de
donnees. Verifie en SSH sur 10.10.10.22 : le dossier partage
**`ARCHIVEFAMILLE` n'existe pas** — ni en clair, ni comme partage chiffre, ni
dans la configuration Samba. Les partages reels y sont `photo`, `video`,
`FILMS`, `PLONGEES`, `KEVIN`, `NetBackup`, `docker`, `projects`...
La tache jumelle « Synology NAS 2 » vise `10.10.10.22 / video /
ARCHIVE_1_VIDEO.hbk` et **reussit** : l'hote de destination va bien, c'est le
dossier qui a disparu.
Anciennete : dix notifications d'echec retrouvees, **zero reussite**, la plus
ancienne du **06/06/2026**. Les photos 2008-2015 de ARCHIVE (10.10.10.50) n'ont
donc aucune sauvegarde depuis au moins trois mois, et l'alerte quotidienne
partait dans une boite a 17 600 messages non lus.
⚠️ Non corrige : 10.10.10.50 n'accepte pas ma cle SSH, et recreer un partage
puis reconfigurer Hyper Backup est une decision qui appartient au proprietaire.
Deux voies : recreer un dossier partage `ARCHIVEFAMILLE` sur 10.10.10.22, ou
repointer la tache vers un partage existant (`NetBackup` est le nom
conventionnel). Dans les deux cas il faudra une sauvegarde COMPLETE au premier
passage, le `.hbk` n'existant pas.
### Les annotations Grafana etaient devenues illisibles
Sept jours d'historique produisaient une foret de pointilles orange masquant les
courbes. Deux corrections dans `tools/gen-grafana-dashboards.py` :
1. **`enable: False`** — les evenements deviennent OPT-IN. Le bouton
« Evenements » en tete de tableau les rallume quand on veut le contexte d'un
incident. C'est ce bouton qui etait allume par defaut.
2. **Le genre `bilan` est ecarte** de l'annotation : a lui seul **26 %** des
lignes (36 sur 137), presque toutes des bascules de « production bridee » qui
changent plusieurs fois par heure. Un trait vertical doit marquer ce qui
ARRIVE — bascule du contacteur, vehicule branche, changement de mode ACM,
redemarrage, intervention manuelle — pas un drapeau qui clignote.
Au passage : les deux panneaux qui COMPARENT les modules entre eux affichaient
**huit courbes dont quatre mortes**. Le releveur lit aussi les modules isoles
tant qu'ils sont sous tension ; le pack ancien a cesse d'emettre le 05/09 mais
restait trace sur une fenetre de sept jours. Filtre pose : « module ayant emis
dans les deux dernieres heures » — un critere d'activite, pas un prefixe de
code-barres, donc rebrancher un pack le fait revenir tout seul.
62/62 requetes toujours valides.
### La marge de 50 % de cette nuit, verifiee
Question du proprietaire : est-elle justifiee pour un demi-parc ? Mesure de
05:05 (fin de la recharge, SOC 55) a 08:45 (croisement PV) :
| | |
|---|---:|
| consommation maison | 2,57 kWh |
| production PV sur la periode | 0,35 kWh |
| **energie reellement a couvrir** | **2,22 kWh** |
| ce que le bilan avait reserve (49,4 % - plancher 22 %) | **2,65 kWh** |
| ce que la batterie a effectivement fourni | 1,00 kWh |
**Marge de 19 %** entre ce qui etait reserve et ce qu'il fallait : c'est une
assurance, pas un gaspillage. La batterie n'a fourni que 1,00 kWh parce que le
reseau a porte la maison de 05:05 a 06:47 — mais la reserve devait couvrir le
cas ou le contacteur disparait a 05:05, et dans ce cas les 2,22 kWh etaient
tous a sa charge. Sur un parc de quatre modules (7,1 kWh utiles de 100 a 22 %),
2,65 kWh representent 37 % du parc. Justifie.
### Reste ouvert ### Reste ouvert
- Detecteur de gel dans le service d'historisation : une grandeur qui ne bouge - Detecteur de gel dans le service d'historisation : une grandeur qui ne bouge
pas alors qu'une autre du meme bilan bouge = alerte. Non fait. pas alors qu'une autre du meme bilan bouge = alerte. Non fait.

View File

@@ -60,14 +60,14 @@
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "evcs-pg" "uid": "evcs-pg"
}, },
"enable": true, "enable": false,
"iconColor": "semi-dark-orange", "iconColor": "semi-dark-orange",
"hide": false, "hide": false,
"target": { "target": {
"format": "table", "format": "table",
"rawQuery": true, "rawQuery": true,
"editorMode": "code", "editorMode": "code",
"rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1"
} }
} }
] ]
@@ -166,7 +166,7 @@
"format": "time_series", "format": "time_series",
"rawQuery": true, "rawQuery": true,
"editorMode": "code", "editorMode": "code",
"rawSql": "SELECT ts AS time, module AS metric, max(mv) - min(mv) AS ecart\nFROM cellule WHERE $__timeFilter(ts)\nGROUP BY ts, module ORDER BY 1" "rawSql": "SELECT ts AS time, module AS metric, max(mv) - min(mv) AS ecart\nFROM cellule WHERE $__timeFilter(ts)\n AND module IN (SELECT module FROM cellule\n GROUP BY module HAVING max(ts) > now() - interval '2 hours')\nGROUP BY ts, module ORDER BY 1"
} }
], ],
"gridPos": { "gridPos": {
@@ -223,7 +223,7 @@
"format": "time_series", "format": "time_series",
"rawQuery": true, "rawQuery": true,
"editorMode": "code", "editorMode": "code",
"rawSql": "SELECT ts AS time, module || ' — cellule meneuse' AS metric,\n max(mv) AS mv\nFROM cellule WHERE $__timeFilter(ts)\nGROUP BY ts, module ORDER BY 1" "rawSql": "SELECT ts AS time, module || ' — cellule meneuse' AS metric,\n max(mv) AS mv\nFROM cellule WHERE $__timeFilter(ts)\n AND module IN (SELECT module FROM cellule\n GROUP BY module HAVING max(ts) > now() - interval '2 hours')\nGROUP BY ts, module ORDER BY 1"
}, },
{ {
"refId": "B", "refId": "B",

View File

@@ -46,14 +46,14 @@
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "evcs-pg" "uid": "evcs-pg"
}, },
"enable": true, "enable": false,
"iconColor": "semi-dark-orange", "iconColor": "semi-dark-orange",
"hide": false, "hide": false,
"target": { "target": {
"format": "table", "format": "table",
"rawQuery": true, "rawQuery": true,
"editorMode": "code", "editorMode": "code",
"rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1"
} }
} }
] ]

View File

@@ -46,14 +46,14 @@
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "evcs-pg" "uid": "evcs-pg"
}, },
"enable": true, "enable": false,
"iconColor": "semi-dark-orange", "iconColor": "semi-dark-orange",
"hide": false, "hide": false,
"target": { "target": {
"format": "table", "format": "table",
"rawQuery": true, "rawQuery": true,
"editorMode": "code", "editorMode": "code",
"rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1"
} }
} }
] ]

View File

@@ -46,14 +46,14 @@
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "evcs-pg" "uid": "evcs-pg"
}, },
"enable": true, "enable": false,
"iconColor": "semi-dark-orange", "iconColor": "semi-dark-orange",
"hide": false, "hide": false,
"target": { "target": {
"format": "table", "format": "table",
"rawQuery": true, "rawQuery": true,
"editorMode": "code", "editorMode": "code",
"rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1"
} }
} }
] ]

View File

@@ -89,14 +89,14 @@
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "evcs-pg" "uid": "evcs-pg"
}, },
"enable": true, "enable": false,
"iconColor": "semi-dark-orange", "iconColor": "semi-dark-orange",
"hide": false, "hide": false,
"target": { "target": {
"format": "table", "format": "table",
"rawQuery": true, "rawQuery": true,
"editorMode": "code", "editorMode": "code",
"rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1"
} }
} }
] ]

View File

@@ -46,14 +46,14 @@
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "evcs-pg" "uid": "evcs-pg"
}, },
"enable": true, "enable": false,
"iconColor": "semi-dark-orange", "iconColor": "semi-dark-orange",
"hide": false, "hide": false,
"target": { "target": {
"format": "table", "format": "table",
"rawQuery": true, "rawQuery": true,
"editorMode": "code", "editorMode": "code",
"rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1"
} }
} }
] ]

View File

@@ -46,14 +46,14 @@
"type": "grafana-postgresql-datasource", "type": "grafana-postgresql-datasource",
"uid": "evcs-pg" "uid": "evcs-pg"
}, },
"enable": true, "enable": false,
"iconColor": "semi-dark-orange", "iconColor": "semi-dark-orange",
"hide": false, "hide": false,
"target": { "target": {
"format": "table", "format": "table",
"rawQuery": true, "rawQuery": true,
"editorMode": "code", "editorMode": "code",
"rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1"
} }
} }
] ]

View File

@@ -448,3 +448,7 @@ heure_reelle,etat,courant_mA,c1_mV,c2_mV,c3_mV,c4_mV,c5_mV,c6_mV,c7_mV,c8_mV,c9_
2026-09-06T13:31:10.870Z,Y230202C30371425,,3312,3312,3312,3312,3311,3311,3312,3311,3312,3312,3312,3312,3311,3312,3311,1 2026-09-06T13:31:10.870Z,Y230202C30371425,,3312,3312,3312,3312,3311,3311,3312,3311,3312,3312,3312,3312,3311,3312,3311,1
2026-09-06T13:31:10.997Z,Y230202C30371431,,3312,3312,3312,3312,3312,3312,3312,3312,3312,3310,3312,3312,3312,3312,3312,2 2026-09-06T13:31:10.997Z,Y230202C30371431,,3312,3312,3312,3312,3312,3312,3312,3312,3312,3310,3312,3312,3312,3312,3312,2
2026-09-06T13:31:11.122Z,Y230202C30371427,,3301,3302,3302,3302,3300,3300,3302,3302,3302,3301,3301,3303,3303,3303,3300,3 2026-09-06T13:31:11.122Z,Y230202C30371427,,3301,3302,3302,3302,3300,3300,3302,3302,3302,3301,3301,3303,3303,3303,3300,3
2026-09-06T13:41:11.226Z,Y230202C30371426,,3312,3312,3311,3312,3312,3312,3312,3312,3310,3312,3312,3312,3312,3312,3311,2
2026-09-06T13:41:11.324Z,Y230202C30371425,,3309,3310,3310,3310,3309,3309,3310,3310,3310,3310,3309,3310,3310,3310,3310,1
2026-09-06T13:41:11.451Z,Y230202C30371431,,3310,3311,3311,3311,3311,3310,3311,3312,3311,3311,3310,3311,3312,3312,3311,2
2026-09-06T13:41:11.577Z,Y230202C30371427,,3318,3318,3318,3317,3317,3318,3318,3317,3317,3317,3318,3318,3318,3318,3317,1
1 heure_reelle etat courant_mA c1_mV c2_mV c3_mV c4_mV c5_mV c6_mV c7_mV c8_mV c9_mV c10_mV c11_mV c12_mV c13_mV c14_mV c15_mV ecart_mV
448 2026-09-06T13:31:10.870Z Y230202C30371425 3312 3312 3312 3312 3311 3311 3312 3311 3312 3312 3312 3312 3311 3312 3311 1
449 2026-09-06T13:31:10.997Z Y230202C30371431 3312 3312 3312 3312 3312 3312 3312 3312 3312 3310 3312 3312 3312 3312 3312 2
450 2026-09-06T13:31:11.122Z Y230202C30371427 3301 3302 3302 3302 3300 3300 3302 3302 3302 3301 3301 3303 3303 3303 3300 3
451 2026-09-06T13:41:11.226Z Y230202C30371426 3312 3312 3311 3312 3312 3312 3312 3312 3310 3312 3312 3312 3312 3312 3311 2
452 2026-09-06T13:41:11.324Z Y230202C30371425 3309 3310 3310 3310 3309 3309 3310 3310 3310 3310 3309 3310 3310 3310 3310 1
453 2026-09-06T13:41:11.451Z Y230202C30371431 3310 3311 3311 3311 3311 3310 3311 3312 3311 3311 3310 3311 3312 3312 3311 2
454 2026-09-06T13:41:11.577Z Y230202C30371427 3318 3318 3318 3317 3317 3318 3318 3317 3317 3317 3318 3318 3318 3318 3317 1

View File

@@ -153,12 +153,25 @@ def dashboard(uid, title, desc, panels, refresh="1m", frm="now-24h", variables=N
{"builtIn": 1, "datasource": {"type": "grafana", "uid": "-- Grafana --"}, {"builtIn": 1, "datasource": {"type": "grafana", "uid": "-- Grafana --"},
"enable": True, "hide": True, "name": "Annotations & Alerts", "enable": True, "hide": True, "name": "Annotations & Alerts",
"iconColor": "rgba(0, 211, 255, 1)", "type": "dashboard"}, "iconColor": "rgba(0, 211, 255, 1)", "type": "dashboard"},
{"name": "Evenements", "datasource": PG, "enable": True, # Les evenements sont OPT-IN : `enable: False`. Ils rendent un
# graphique illisible des qu'ils sont nombreux — le 06/09, sept
# jours d'historique donnaient une foret de pointillies orange qui
# masquait les courbes. Le bouton « Evenements » en tete de tableau
# les rallume quand on veut le contexte d'un incident.
#
# Et meme allumes, on ecarte le genre `bilan` : a lui seul 26 % des
# lignes, presque toutes des bascules de « production bridee » qui
# changent plusieurs fois par heure. Ce qui merite un trait vertical
# est ce qui ARRIVE — une bascule du contacteur, un vehicule
# branche, un changement de mode de charge, un redemarrage, une
# intervention manuelle — pas un drapeau qui clignote.
{"name": "Evenements", "datasource": PG, "enable": False,
"iconColor": "semi-dark-orange", "hide": False, "iconColor": "semi-dark-orange", "hide": False,
"target": {"format": "table", "rawQuery": True, "editorMode": "code", "target": {"format": "table", "rawQuery": True, "editorMode": "code",
"rawSql": "SELECT ts AS time, resume AS text, " "rawSql": "SELECT ts AS time, resume AS text, "
"genre || ',' || coalesce(source,'?') AS tags " "genre || ',' || coalesce(source,'?') AS tags "
"FROM evenement WHERE $__timeFilter(ts) ORDER BY 1"}}]}, "FROM evenement WHERE $__timeFilter(ts) "
"AND genre <> 'bilan' ORDER BY 1"}}]},
"panels": panels} "panels": panels}
# ===================================================================== # =====================================================================
@@ -1109,9 +1122,18 @@ batteries = dashboard("evcs-batteries", "Les batteries", (
"custom.thresholdsStyle": {"mode": "dashed"}}}}, "custom.thresholdsStyle": {"mode": "dashed"}}}},
legend="right"), legend="right"),
# ⚠️ Ces deux panneaux COMPARENT les modules entre eux : ils ne doivent
# montrer que les modules ENCORE SUR LA CHAINE. Le releveur lit aussi les
# modules isoles tant qu'ils sont sous tension — le pack ancien a cesse
# d'emettre le 05/09 mais restait affiche sur une fenetre de sept jours,
# soit huit courbes dont quatre mortes. Le critere est « a emis dans les
# deux dernieres heures », pas un prefixe de code-barres : rebrancher un
# pack le fait revenir tout seul.
ts("Ecart entre la plus haute et la plus basse", [ ts("Ecart entre la plus haute et la plus basse", [
sql("A", "SELECT ts AS time, module AS metric, max(mv) - min(mv) AS ecart\n" sql("A", "SELECT ts AS time, module AS metric, max(mv) - min(mv) AS ecart\n"
"FROM cellule WHERE $__timeFilter(ts)\n" "FROM cellule WHERE $__timeFilter(ts)\n"
" AND module IN (SELECT module FROM cellule\n"
" GROUP BY module HAVING max(ts) > now() - interval '2 hours')\n"
"GROUP BY ts, module ORDER BY 1")], "GROUP BY ts, module ORDER BY 1")],
PG, 0, 10, 12, 8, unit="mvolt", PG, 0, 10, 12, 8, unit="mvolt",
desc="L'indicateur qui resume un module en une courbe. Un module sain de " desc="L'indicateur qui resume un module en une courbe. Un module sain de "
@@ -1122,6 +1144,8 @@ batteries = dashboard("evcs-batteries", "Les batteries", (
sql("A", "SELECT ts AS time, module || ' — cellule meneuse' AS metric,\n" sql("A", "SELECT ts AS time, module || ' — cellule meneuse' AS metric,\n"
" max(mv) AS mv\n" " max(mv) AS mv\n"
"FROM cellule WHERE $__timeFilter(ts)\n" "FROM cellule WHERE $__timeFilter(ts)\n"
" AND module IN (SELECT module FROM cellule\n"
" GROUP BY module HAVING max(ts) > now() - interval '2 hours')\n"
"GROUP BY ts, module ORDER BY 1"), "GROUP BY ts, module ORDER BY 1"),
sql("B", "SELECT ts AS time, 'plafond de charge (A)' AS metric,\n" sql("B", "SELECT ts AS time, 'plafond de charge (A)' AS metric,\n"
" valeur * 20 AS mv\n" " valeur * 20 AS mv\n"