feat: mark Modbus connection loss in InfluxDB

SolarEdge firmware is known to silently drop the Modbus TCP listener
(port refuses connections while still shown as enabled). Write a
connection_state=1 point on first failed poll and 0 on recovery so the
outage is visible in Grafana instead of just a data gap.

ConnectionException is re-raised from read() - it is fatal for the
whole cycle, not a flaky single block. Marker points carry no tags
(registers unreadable during outage), so the tile query skips the
serial filter.

Also includes threshold tweaks on the power stat tiles.
This commit is contained in:
dj7nt committed 2026-10-11 07:33:44 +02:00
1 parent d1aa17c05a
commit 154f953109
3 files changed
+128 -2

No files matched your search

+114 -1
View File
@@ -37,6 +37,19 @@
},
"fieldConfig": {
"defaults": {
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "text",
"value": null
},
{
"color": "green",
"value": 0
}
]
},
"unit": "watt"
},
"overrides": []
@@ -82,6 +95,19 @@
},
"fieldConfig": {
"defaults": {
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 0
}
]
},
"unit": "watt"
},
"overrides": []
@@ -117,7 +143,7 @@
"resultFormat": "time_series"
}
],
"title": "Netz aktuell (+ Bezug / - Einspeisung)",
"title": "Netz aktuell (+ Einspeisung / - Bezug)",
"type": "stat"
},
{
@@ -127,6 +153,19 @@
},
"fieldConfig": {
"defaults": {
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 0
}
]
},
"unit": "watt"
},
"overrides": []
@@ -2818,6 +2857,80 @@
],
"title": "Temperaturen",
"type": "timeseries"
},
{
"datasource": {
"type": "influxdb",
"uid": "${DS_INFLUXDB}"
},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "value",
"options": {
"0": {
"text": "OK",
"color": "green",
"index": 0
},
"1": {
"text": "Verbindung verloren",
"color": "red",
"index": 1
}
}
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 1
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 4,
"x": 0,
"y": 94
},
"id": 109,
"options": {
"colorMode": "background",
"graphMode": "none",
"textMode": "value",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
}
},
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "${DS_INFLUXDB}"
},
"query": "SELECT last(\"connection_state\") FROM \"$measurement\" WHERE $timeFilter",
"refId": "A",
"rawQuery": true,
"resultFormat": "time_series"
}
],
"title": "Modbus-Verbindung",
"type": "stat"
}
],
"refresh": "1m",
+10 -1
View File
@@ -81,15 +81,24 @@ if __name__ == '__main__':
unit=config['modbus']['unit']
)
modbus_ok = True
measurement = config['influx'].get('measurement', 'solaredge')
while True:
try:
raw = inverter.read()
if not modbus_ok:
publish(to_line_protocol({'connection_state': 0}, measurement), config['influx'])
modbus_ok = True
solar_data = SolarData(raw)
line = to_line_protocol(solar_data.data, config['influx'].get('measurement', 'solaredge'))
line = to_line_protocol(solar_data.data, measurement)
if line:
publish(line, config['influx'])
except ConnectionException as err:
print('Cannot connect to SolarEdge Modbus server on {0}:{1}, error: {2}'.format(
config['modbus']['host'], config['modbus']['port'], err))
if modbus_ok:
publish(to_line_protocol({'connection_state': 1}, measurement), config['influx'])
modbus_ok = False
time.sleep(config['poll_interval'])
+4
View File
@@ -3,6 +3,7 @@ import math
import time
from pymodbus.constants import Endian
from pymodbus.exceptions import ConnectionException
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.client.sync import ModbusSerialClient
@@ -322,6 +323,9 @@ class MyInverter:
data[rname] = self._decode_value(rawData, rlength, dtype, vtype)
offset += rlength
except ConnectionException:
# connection loss is fatal for the whole cycle, not a flaky block
raise
except Exception as err:
print('Modbus block {0} failed, skipping this cycle: {1}'.format(name, err))