Reshape boiler data
Deploy Solar Relay / deploy (push) Successful in 1m7s

This commit is contained in:
franzz committed 2026-11-03 01:48:01 +01:00
1 parent 9e0230731b
commit e7905c667d
10 files changed
+224 -27

No files matched your search

+34 -3
View File
@@ -94,14 +94,31 @@ def boiler_readings(response, pin):
return {"boiler_enabled": state["state"], "boiler_reason": reason}
def boiler_threshold(response, pin):
"""GEN24's UI reads rules.<pin>.thresholdOn from /api/config/emrs, in watts.
In pgrid mode, negative thresholds denote surplus exported to the grid.
The UI displays their absolute magnitude as the switch-on threshold.
"""
rule = response.get("rules", {}).get(pin)
if not isinstance(rule, dict):
raise ValueError("Boiler threshold rule is missing")
value = _number(rule.get("thresholdOn"))
if value is not None and rule.get("mode") == "pgrid":
value = abs(value)
if value is None or not 0 < value <= 99_999_999.99:
raise ValueError("Boiler switch-on threshold is missing or invalid")
return value
class FroniusClient:
def __init__(self, config: InverterConfig, *, http=None):
self.config = config
self.http = http or JsonHttp(config.request_timeout_seconds, local=True)
self.last_request_started = None
def _read(self, path):
for attempt in range(REQUEST_RETRIES + 1):
def _read(self, path, retries=REQUEST_RETRIES):
for attempt in range(retries + 1):
if self.last_request_started is not None:
wait = self.config.min_request_interval_seconds - (time.monotonic() - self.last_request_started)
if wait > 0:
@@ -110,7 +127,7 @@ class FroniusClient:
try:
return self.http.request(self.config.base_url.rstrip("/") + path)
except TransportError as error:
if attempt == REQUEST_RETRIES:
if attempt == retries:
raise
LOG.warning("Fronius GET %s failed (%s); retry %d/%d", path, error, attempt + 1, REQUEST_RETRIES)
@@ -151,4 +168,18 @@ class FroniusClient:
sample["source_timestamps"][name] = timestamp
except (TransportError, ValueError, KeyError, TypeError, AttributeError) as error:
sample["errors"][name] = str(error)
# These settings may require inverter authentication (HTTP 401). A failed
# settings read must not turn a valid boiler state into a missing sample.
sample["readings"]["boiler_threshold_w"] = self.config.boiler_threshold_w
sample["readings"]["boiler_threshold_source"] = "config"
try:
response = self._read("/api/config/emrs", retries=0)
threshold = boiler_threshold(response, self.config.boiler_pin)
sample["readings"]["boiler_threshold_w"] = threshold
sample["readings"]["boiler_threshold_source"] = "api"
# Keep only the relevant load-management settings, never an authentication response.
rule = response["rules"][self.config.boiler_pin]
sample["raw"]["boiler_config"] = {"thresholdOn": rule["thresholdOn"], "mode": rule.get("mode")}
except (TransportError, ValueError, KeyError, TypeError, AttributeError):
pass
return sample