Harden Modbus/Influx paths, add Grafana dashboard

Short or garbled gateway responses are rejected on register count
instead of half-decoding (previously: 'unpack requires a buffer',
OverflowError on garbage scale factors). Scale factors outside
+/-10 and non-finite floats are dropped before they reach line
protocol.

offgrid is written as integer offgrid_state: the existing DB
cannot be migrated (mixed inmem/tsi1 shards block all deletes)
and the old boolean field conflicts with integer writes, which
poisoned the spool - HTTP 400 now drops the batch and logs the
InfluxDB error body instead of re-spooling it forever.

grafana_dashboard.json: fully German dashboard (InfluxQL,
InfluxDB 1.x) with off-grid state, SoC (SoE*SoH/100) and
consumption-source panels; status tiles use integers plus value
mappings because Grafana drops string/boolean fields from
InfluxQL time series.
This commit is contained in:
dj7nt committed 2026-10-10 16:58:44 +02:00
1 parent 34e77991e6
commit d266654696
4 files changed
+2944 -5

No files matched your search

+13
View File
@@ -5,6 +5,7 @@ import base64
import os
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
@@ -43,6 +44,18 @@ def publish(line, influx):
send_to_influx(backlog + [line], influx)
if backlog:
os.remove(SPOOL_FILE)
except urllib.error.HTTPError as err:
body = err.read().decode('utf-8', 'replace')[:200]
if err.code == 400:
# Permanently rejected (malformed line or field type conflict).
# Re-spooling would poison every future write, so drop the batch.
print('InfluxDB rejected the write, dropping {0} spooled line(s): {1}'.format(len(backlog) + 1, body))
if os.path.exists(SPOOL_FILE):
os.remove(SPOOL_FILE)
else:
print('InfluxDB write failed (HTTP {0}), spooling line: {1}'.format(err.code, body or err))
with open(SPOOL_FILE, 'a') as f:
f.write(line + '\n')
except Exception as err:
print('InfluxDB write failed, spooling line: {0}'.format(err))
with open(SPOOL_FILE, 'a') as f: