fix: setpoint clamp, thread safety, error handling, real-time kWh counters; cleanup repo

This commit is contained in:
bart
2026-11-11 18:57:25 +01:00
parent 0b17034b68
commit 651676afda
12 changed files with 89 additions and 27996 deletions
+41 -7
View File
@@ -1,5 +1,8 @@
import logging
import threading
import time
import functools
import victronclient
import mqttpublisher
import tibberclient
@@ -7,6 +10,15 @@ from enum import IntEnum
logger = logging.getLogger('main')
def _locked(method):
# Serialise access to controller state: MQTT callbacks and the main loop run in different threads
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
with self.lock:
return method(self, *args, **kwargs)
return wrapper
charge_max_price_level = 0.024
MAX_POWER=4000
@@ -27,6 +39,8 @@ class Chargemode(IntEnum):
class chargeman_controller:
def __init__(self, victronconn: victronclient.client, tibber: tibberclient.client) -> None:
self.lock = threading.RLock()
self.last_update = None
self.chargemode=Chargemode.FLOATING
self.chargevalue=0
self.consump_updated=False
@@ -57,17 +71,20 @@ class chargeman_controller:
return self.battery_total_net_usage
@_locked
def set_net_consumption(self, value):
self.consumption = value
self.consump_updated = True
self.check_net_usage()
@_locked
def set_net_delivery(self, value):
self.return_delivery = value
self.return_updated = True
self.check_net_usage()
@_locked
def check_net_usage(self):
@@ -86,7 +103,12 @@ class chargeman_controller:
#logger.info("Update P1 usage: {}".format(netp1usage))
if self.chargemode in [Chargemode.BALANCE_DYN,Chargemode.CHARGE_DYN,Chargemode.DISCHARGE_DYN]:
setpoint = -1 * self.victronconn.get_power_usage()
power_usage = self.victronconn.get_power_usage()
if power_usage is None:
logger.warning("Victron power usage unknown, skipping dynamic setpoint")
return
setpoint = -1 * power_usage
setpoint = self.netp1usage+setpoint
@@ -101,22 +123,25 @@ class chargeman_controller:
if self.setpoint > MAX_CHARGE_POWER:
self.setpoint = MAX_CHARGE_POWER
elif self.setpoint < -MAX_POWER:
self.setpoint = MAX_POWER
self.setpoint = -MAX_POWER
#logger.info ("Dynamic setpoint: {}".format(setpoint))
self.apply_charge_mode()
@_locked
def set_chargemode(self, mode: Chargemode):
self.chargemode = mode
self.apply_charge_mode()
@_locked
def set_chargemode_int(self, mode: int):
self.chargemode = Chargemode(mode)
self.apply_charge_mode()
@_locked
def set_chargevalue(self,value):
self.chargevalue = value
@@ -142,6 +167,7 @@ class chargeman_controller:
self.valuekwh = value
@_locked
def update(self):
logger.debug("Run controller update")
@@ -150,10 +176,17 @@ class chargeman_controller:
logger.debug("Controller update mqtt loop done")
if self.victronconn.get_power_usage() > 0:
self.battery_total_net_usage += self.victronconn.get_power_usage() /60/60*10
elif self.victronconn.get_power_usage() < 0:
self.battery_total_net_delivery += (self.victronconn.get_power_usage() * -1 ) /60/60*10
now = time.monotonic()
elapsed = 0.0 if self.last_update is None else now - self.last_update
self.last_update = now
power_usage = self.victronconn.get_power_usage()
if power_usage is not None:
# W * s / 3600 = Wh, as kWh counter: elapsed real time instead of a fixed interval
if power_usage > 0:
self.battery_total_net_usage += power_usage / 60 / 60 * elapsed
elif power_usage < 0:
self.battery_total_net_delivery += (power_usage * -1) / 60 / 60 * elapsed
self.apply_charge_mode()
@@ -175,7 +208,7 @@ class chargeman_controller:
self.netp1usage,
self.victronconn.get_power_usage(),
self.victronconn.get_soc() ,
float(self.victronconn.get_battery_current() ),
float(self.victronconn.get_battery_current() or 0),
self.battery_total_net_usage,
self.battery_total_net_delivery,
self.tibber.getPrice() ))
@@ -187,6 +220,7 @@ class chargeman_controller:
@_locked
def apply_charge_mode(self):