Grafana : production du jour par chargeur ; suiveur V1 ajoute a l'export InfluxDB
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:
12
HANDOFF.md
12
HANDOFF.md
@@ -645,5 +645,15 @@ actifs), tous agents : timer systemd `openclaw-session-rotate.timer` (quotidien
|
||||
archives (0/89 au 25/09). ⚠️ Une mise a jour majeure de DSM a deja efface une
|
||||
config systeme (sudo NOPASSWD, §75) : reverifier le timer apres chaque MAJ DSM.
|
||||
|
||||
Grafana « Le solaire » : panneau **Production du jour par chargeur** (yield_today
|
||||
des trois chargeurs + Total, trace seulement si les trois publient). Le suiveur
|
||||
V1 manquait a InfluxDB : `sensor.v1_*` absent de `influxdb.include` de HA —
|
||||
ajoute le 25/09 (sauvegarde `configuration.yaml.bak-20260925-v1`, check_config
|
||||
valide), **effectif au prochain redemarrage de HA, que le proprio fait lui-meme**
|
||||
(apres : verifier le selecteur vehicule, §47). Le suiveur est aussi ajoute aux
|
||||
panneaux des modes (etiquette « Suiveur », sinon il tombait dans « Ensemble ») et
|
||||
du detail par tracker, dont la description attribuait a tort son absence a un
|
||||
defaut de publication.
|
||||
|
||||
---
|
||||
*Document mis a jour le : 25/09/2026 — bilan de nuit pour l'agent, retention 30 j des journaux OpenClaw*
|
||||
*Document mis a jour le : 25/09/2026 — bilan de nuit pour l'agent, retention des journaux, production par chargeur dans Grafana*
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
"type": "influxdb",
|
||||
"uid": "evcs-influx"
|
||||
},
|
||||
"query": "from(bucket: \"evcs\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._field == \"state\")\n |> filter(fn: (r) => r._measurement =~ /operation_mode$/)\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n |> map(fn: (r) => ({ r with tracker:\n if r._measurement =~ /solar_1/ then \"Sud 1\"\n else if r._measurement =~ /solar_2/ then \"Sud 2\"\n else if r._measurement =~ /solar_3/ then \"Sud 3\"\n else if r._measurement =~ /solar_4/ then \"Ouest\"\n else if r._measurement =~ /mppt1/ then \"Sud-est\"\n else \"Ensemble\" }))\n |> keep(columns: [\"_time\", \"_value\", \"tracker\"])\n |> pivot(rowKey: [\"_time\"], columnKey: [\"tracker\"], valueColumn: \"_value\")"
|
||||
"query": "from(bucket: \"evcs\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._field == \"state\")\n |> filter(fn: (r) => r._measurement =~ /operation_mode$/)\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n |> map(fn: (r) => ({ r with tracker:\n if r._measurement =~ /^v1_/ then \"Suiveur\"\n else if r._measurement =~ /solar_1/ then \"Sud 1\"\n else if r._measurement =~ /solar_2/ then \"Sud 2\"\n else if r._measurement =~ /solar_3/ then \"Sud 3\"\n else if r._measurement =~ /solar_4/ then \"Ouest\"\n else if r._measurement =~ /mppt1/ then \"Sud-est\"\n else \"Ensemble\" }))\n |> keep(columns: [\"_time\", \"_value\", \"tracker\"])\n |> pivot(rowKey: [\"_time\"], columnKey: [\"tracker\"], valueColumn: \"_value\")"
|
||||
}
|
||||
],
|
||||
"gridPos": {
|
||||
@@ -342,6 +342,92 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Production du jour par chargeur",
|
||||
"description": "Le rendement du jour que chaque chargeur declare lui-meme (yield_today), remis a zero chaque nuit. C'est la production REELLE (MANUEL §0), la meme que sensor.evcs_production_jour_kwh et que l'agent vocal. Le Total n'apparait que si les trois chargeurs publient : un chargeur muet donne un total partiel, qu'on ne trace pas. Les trois ne se remettent pas a zero a la meme minute (vers 00:40). Le suiveur n'est exporte que depuis le 25/09.",
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "evcs-influx"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "evcs-influx"
|
||||
},
|
||||
"query": "from(bucket: \"evcs\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\")\n |> filter(fn: (r) => r.entity_id == \"airtria_mppt1_yield_today\"\n or r.entity_id == \"mppt_450_200_hq2449yr9pt_yield_today\"\n or r.entity_id == \"v1_yield_today\")\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n |> map(fn: (r) => ({ r with chargeur:\n if r.entity_id =~ /^airtria/ then \"MPPT 250/70 sud-est\"\n else if r.entity_id =~ /^mppt_450/ then \"RS 450/200 sud + ouest\"\n else \"150/35 suiveur\" }))\n |> group()\n |> keep(columns: [\"_time\", \"_value\", \"chargeur\"])\n |> pivot(rowKey: [\"_time\"], columnKey: [\"chargeur\"], valueColumn: \"_value\")\n |> sort(columns: [\"_time\"])"
|
||||
},
|
||||
{
|
||||
"refId": "B",
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "evcs-influx"
|
||||
},
|
||||
"query": "from(bucket: \"evcs\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\")\n |> filter(fn: (r) => r.entity_id == \"airtria_mppt1_yield_today\"\n or r.entity_id == \"mppt_450_200_hq2449yr9pt_yield_today\"\n or r.entity_id == \"v1_yield_today\")\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n |> map(fn: (r) => ({ r with chargeur:\n if r.entity_id =~ /^airtria/ then \"MPPT 250/70 sud-est\"\n else if r.entity_id =~ /^mppt_450/ then \"RS 450/200 sud + ouest\"\n else \"150/35 suiveur\" }))\n |> group()\n |> keep(columns: [\"_time\", \"_value\", \"chargeur\"])\n |> pivot(rowKey: [\"_time\"], columnKey: [\"chargeur\"], valueColumn: \"_value\")\n |> sort(columns: [\"_time\"])\n |> fill(usePrevious: true, column: \"MPPT 250/70 sud-est\")\n |> fill(usePrevious: true, column: \"RS 450/200 sud + ouest\")\n |> fill(usePrevious: true, column: \"150/35 suiveur\")\n |> filter(fn: (r) => exists r[\"MPPT 250/70 sud-est\"] and exists r[\"RS 450/200 sud + ouest\"]\n and exists r[\"150/35 suiveur\"])\n |> map(fn: (r) => ({ _time: r._time, Total: r[\"MPPT 250/70 sud-est\"]\n + r[\"RS 450/200 sud + ouest\"] + r[\"150/35 suiveur\"] }))"
|
||||
}
|
||||
],
|
||||
"gridPos": {
|
||||
"x": 0,
|
||||
"y": 23,
|
||||
"w": 24,
|
||||
"h": 8
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "kwatth",
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"lineWidth": 2,
|
||||
"fillOpacity": 8,
|
||||
"showPoints": "never",
|
||||
"spanNulls": true,
|
||||
"axisBorderShow": false,
|
||||
"gradientMode": "opacity"
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Total"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "color",
|
||||
"value": {
|
||||
"mode": "fixed",
|
||||
"fixedColor": "yellow"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "custom.lineWidth",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"id": "custom.fillOpacity",
|
||||
"value": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true,
|
||||
"calcs": []
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "multi",
|
||||
"sort": "desc"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Batterie",
|
||||
|
||||
@@ -796,7 +796,7 @@
|
||||
{
|
||||
"type": "table",
|
||||
"title": "Detail par tracker",
|
||||
"description": "Les 52 panneaux en quatre plans : sud-est 40 degres, trois chaines sud 35 degres, ouest 45 degres. Le suiveur de trois panneaux ne publie pas de mode de fonctionnement et n'apparait donc pas ici.",
|
||||
"description": "Les 52 panneaux en quatre plans : sud-est 40 degres, trois chaines sud 35 degres, ouest 45 degres, et le suiveur de trois panneaux. Le suiveur n'est exporte vers InfluxDB que depuis le 25/09 (il manquait a la liste d'export de Home Assistant, et non faute de publier).",
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
"uid": "evcs-influx"
|
||||
@@ -808,7 +808,7 @@
|
||||
"type": "influxdb",
|
||||
"uid": "evcs-influx"
|
||||
},
|
||||
"query": "import \"strings\"\n\nbase = from(bucket: \"evcs\")\n |> range(start: -30m)\n |> filter(fn: (r) => r._field == \"value\")\n |> filter(fn: (r) => r._measurement == \"V\" or r._measurement == \"A\" or r._measurement == \"W\")\n |> last()\n\nrs = base\n |> filter(fn: (r) => strings.containsStr(v: r.entity_id, substr: \"pv_tracker_solar_\"))\n |> map(fn: (r) => ({ r with tracker:\n if strings.containsStr(v: r.entity_id, substr: \"solar_1\") then \"Sud 1 (10 pan.)\"\n else if strings.containsStr(v: r.entity_id, substr: \"solar_2\") then \"Sud 2 (10 pan.)\"\n else if strings.containsStr(v: r.entity_id, substr: \"solar_3\") then \"Sud 3 (10 pan.)\"\n else \"Ouest (9 pan.)\" }))\n\nse = base\n |> filter(fn: (r) => r.entity_id == \"airtria_mppt1_pv_bus_voltage\"\n or r.entity_id == \"airtria_mppt1_pv_current\"\n or r.entity_id == \"airtria_mppt1_pv_yield_power\")\n |> map(fn: (r) => ({ r with tracker: \"Sud-est (10 pan.)\" }))\n\nunion(tables: [rs, se])\n |> keep(columns: [\"tracker\", \"_measurement\", \"_value\"])\n |> pivot(rowKey: [\"tracker\"], columnKey: [\"_measurement\"], valueColumn: \"_value\")\n |> sort(columns: [\"tracker\"])"
|
||||
"query": "import \"strings\"\n\nbase = from(bucket: \"evcs\")\n |> range(start: -30m)\n |> filter(fn: (r) => r._field == \"value\")\n |> filter(fn: (r) => r._measurement == \"V\" or r._measurement == \"A\" or r._measurement == \"W\")\n |> last()\n\nrs = base\n |> filter(fn: (r) => strings.containsStr(v: r.entity_id, substr: \"pv_tracker_solar_\"))\n |> map(fn: (r) => ({ r with tracker:\n if strings.containsStr(v: r.entity_id, substr: \"solar_1\") then \"Sud 1 (10 pan.)\"\n else if strings.containsStr(v: r.entity_id, substr: \"solar_2\") then \"Sud 2 (10 pan.)\"\n else if strings.containsStr(v: r.entity_id, substr: \"solar_3\") then \"Sud 3 (10 pan.)\"\n else \"Ouest (9 pan.)\" }))\n\nse = base\n |> filter(fn: (r) => r.entity_id == \"airtria_mppt1_pv_bus_voltage\"\n or r.entity_id == \"airtria_mppt1_pv_current\"\n or r.entity_id == \"airtria_mppt1_pv_yield_power\")\n |> map(fn: (r) => ({ r with tracker: \"Sud-est (10 pan.)\" }))\n\nsv = base\n |> filter(fn: (r) => r.entity_id == \"v1_pv_bus_voltage\"\n or r.entity_id == \"v1_pv_current\"\n or r.entity_id == \"v1_pv_yield_power\")\n |> map(fn: (r) => ({ r with tracker: \"Suiveur (3 pan.)\" }))\n\nunion(tables: [rs, se, sv])\n |> keep(columns: [\"tracker\", \"_measurement\", \"_value\"])\n |> pivot(rowKey: [\"tracker\"], columnKey: [\"_measurement\"], valueColumn: \"_value\")\n |> sort(columns: [\"tracker\"])"
|
||||
}
|
||||
],
|
||||
"gridPos": {
|
||||
|
||||
@@ -34,6 +34,9 @@ influxdb:
|
||||
- sensor.evcs_*
|
||||
- sensor.mppt_*
|
||||
- sensor.airtria_*
|
||||
# Le suiveur (MPPT 150/35, 3 panneaux) : absent jusqu'au 25/09, donc ni
|
||||
# son rendement journalier ni son mode dans les tableaux Grafana.
|
||||
- sensor.v1_*
|
||||
- sensor.gx_device_*
|
||||
- sensor.sun_solar_elevation
|
||||
- binary_sensor.victron_*
|
||||
|
||||
@@ -341,6 +341,22 @@ nuit = dashboard("evcs-nuit", "La nuit", (
|
||||
# =====================================================================
|
||||
# 2. LE SOLAIRE — excedent, censure, et ce que les chargeurs disent
|
||||
# =====================================================================
|
||||
# Rendement journalier declare par chaque chargeur (MANUEL §0 : la production
|
||||
# reelle du jour est leur SOMME, jamais un sous-tracker du 450/200 en plus).
|
||||
RENDEMENTS_FLUX = (
|
||||
'from(bucket: "evcs")\n'
|
||||
' |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n'
|
||||
' |> filter(fn: (r) => r._measurement == "kWh" and r._field == "value")\n'
|
||||
' |> filter(fn: (r) => r.entity_id == "airtria_mppt1_yield_today"\n'
|
||||
' or r.entity_id == "mppt_450_200_hq2449yr9pt_yield_today"\n'
|
||||
' or r.entity_id == "v1_yield_today")\n'
|
||||
' |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n'
|
||||
' |> map(fn: (r) => ({ r with chargeur:\n'
|
||||
' if r.entity_id =~ /^airtria/ then "MPPT 250/70 sud-est"\n'
|
||||
' else if r.entity_id =~ /^mppt_450/ then "RS 450/200 sud + ouest"\n'
|
||||
' else "150/35 suiveur" }))\n'
|
||||
' |> group()\n')
|
||||
|
||||
solaire = dashboard("evcs-solaire", "Le solaire", (
|
||||
"La production mesuree n'est jamais la production disponible : sans "
|
||||
"injection, le MPPT ne produit que ce que le systeme peut absorber. "
|
||||
@@ -393,7 +409,8 @@ solaire = dashboard("evcs-solaire", "Le solaire", (
|
||||
' |> filter(fn: (r) => r._measurement =~ /operation_mode$/)\n'
|
||||
' |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n'
|
||||
' |> map(fn: (r) => ({ r with tracker:\n'
|
||||
' if r._measurement =~ /solar_1/ then "Sud 1"\n'
|
||||
' if r._measurement =~ /^v1_/ then "Suiveur"\n'
|
||||
' else if r._measurement =~ /solar_1/ then "Sud 1"\n'
|
||||
' else if r._measurement =~ /solar_2/ then "Sud 2"\n'
|
||||
' else if r._measurement =~ /solar_3/ then "Sud 3"\n'
|
||||
' else if r._measurement =~ /solar_4/ then "Ouest"\n'
|
||||
@@ -411,6 +428,35 @@ solaire = dashboard("evcs-solaire", "Le solaire", (
|
||||
"voltage_current_limited": {"color": "orange", "index": 1},
|
||||
"off": {"color": "dark-blue", "index": 2}}}]),
|
||||
|
||||
ts("Production du jour par chargeur", [
|
||||
flux("A", RENDEMENTS_FLUX + ' |> keep(columns: ["_time", "_value", "chargeur"])\n'
|
||||
' |> pivot(rowKey: ["_time"], columnKey: ["chargeur"], valueColumn: "_value")\n'
|
||||
' |> sort(columns: ["_time"])'),
|
||||
flux("B", RENDEMENTS_FLUX +
|
||||
' |> keep(columns: ["_time", "_value", "chargeur"])\n'
|
||||
' |> pivot(rowKey: ["_time"], columnKey: ["chargeur"], valueColumn: "_value")\n'
|
||||
' |> sort(columns: ["_time"])\n'
|
||||
' |> fill(usePrevious: true, column: "MPPT 250/70 sud-est")\n'
|
||||
' |> fill(usePrevious: true, column: "RS 450/200 sud + ouest")\n'
|
||||
' |> fill(usePrevious: true, column: "150/35 suiveur")\n'
|
||||
' |> filter(fn: (r) => exists r["MPPT 250/70 sud-est"] and exists r["RS 450/200 sud + ouest"]\n'
|
||||
' and exists r["150/35 suiveur"])\n'
|
||||
' |> map(fn: (r) => ({ _time: r._time, Total: r["MPPT 250/70 sud-est"]\n'
|
||||
' + r["RS 450/200 sud + ouest"] + r["150/35 suiveur"] }))')],
|
||||
IN, 0, 23, 24, 8, "kwatth",
|
||||
"Le rendement du jour que chaque chargeur declare lui-meme (yield_today), "
|
||||
"remis a zero chaque nuit. C'est la production REELLE (MANUEL §0), la meme "
|
||||
"que sensor.evcs_production_jour_kwh et que l'agent vocal. Le Total "
|
||||
"n'apparait que si les trois chargeurs publient : un chargeur muet donne "
|
||||
"un total partiel, qu'on ne trace pas. Les trois ne se remettent pas a "
|
||||
"zero a la meme minute (vers 00:40). Le suiveur n'est exporte que depuis "
|
||||
"le 25/09.",
|
||||
extra={"overrides": [
|
||||
{"matcher": {"id": "byName", "options": "Total"},
|
||||
"properties": [{"id": "color", "value": {"mode": "fixed", "fixedColor": "yellow"}},
|
||||
{"id": "custom.lineWidth", "value": 3},
|
||||
{"id": "custom.fillOpacity", "value": 0}]}]}),
|
||||
|
||||
ts("Batterie", [
|
||||
sql("A", "SELECT date_trunc('minute', ts) AS time, avg(valeur) AS \"SOC\"\n"
|
||||
"FROM mesure WHERE cle = 'sensor.victron_battery_soc'\n"
|
||||
@@ -852,7 +898,13 @@ se = base
|
||||
or r.entity_id == "airtria_mppt1_pv_yield_power")
|
||||
|> map(fn: (r) => ({ r with tracker: "Sud-est (10 pan.)" }))
|
||||
|
||||
union(tables: [rs, se])
|
||||
sv = base
|
||||
|> filter(fn: (r) => r.entity_id == "v1_pv_bus_voltage"
|
||||
or r.entity_id == "v1_pv_current"
|
||||
or r.entity_id == "v1_pv_yield_power")
|
||||
|> map(fn: (r) => ({ r with tracker: "Suiveur (3 pan.)" }))
|
||||
|
||||
union(tables: [rs, se, sv])
|
||||
|> keep(columns: ["tracker", "_measurement", "_value"])
|
||||
|> pivot(rowKey: ["tracker"], columnKey: ["_measurement"], valueColumn: "_value")
|
||||
|> sort(columns: ["tracker"])'''
|
||||
@@ -968,8 +1020,9 @@ systeme = dashboard("evcs-systeme", "Le systeme", (
|
||||
dict(table("Detail par tracker", [flux("A", TRACKERS_FLUX)],
|
||||
IN, 5, 7, 13, 8,
|
||||
"Les 52 panneaux en quatre plans : sud-est 40 degres, trois chaines sud "
|
||||
"35 degres, ouest 45 degres. Le suiveur de trois panneaux ne publie pas "
|
||||
"de mode de fonctionnement et n'apparait donc pas ici.",
|
||||
"35 degres, ouest 45 degres, et le suiveur de trois panneaux. Le suiveur "
|
||||
"n'est exporte vers InfluxDB que depuis le 25/09 (il manquait a la liste "
|
||||
"d'export de Home Assistant, et non faute de publier).",
|
||||
overrides=[sur("tracker", [{"id": "displayName", "value": "Tracker"},
|
||||
{"id": "custom.width", "value": 200}]),
|
||||
sur("V", [{"id": "displayName", "value": "Tension"},
|
||||
|
||||
Reference in New Issue
Block a user