import logging import victronclient import mqttpublisher import tibberclient from enum import IntEnum logger = logging.getLogger('main') charge_max_price_level = 0.024 MAX_POWER=4000 MAX_CHARGE_POWER=4000 class Chargemode(IntEnum): FLOATING=0 CHARGING_MANUAL=1 CHARGING_MAX=2 DISCHARGING_MANUAL=3 DISCHARGING_MAX=4 CHARGING_CHEAP=5 BALANCE_DYN=6 CHARGE_DYN=7 DISCHARGE_DYN=8 class chargeman_controller: def __init__(self, victronconn: victronclient.client, tibber: tibberclient.client) -> None: self.chargemode=Chargemode.FLOATING self.chargevalue=0 self.consump_updated=False self.return_updated=False self.consumption=0 self.return_delivery=0 self.netp1usage =0 self.valuekwh = 0.0 self.battery_total_net_usage = 0.0 self.battery_total_net_delivery = 0.0 self.setpoint = 0 self.netp1usageupdated = False self.victronconn = victronconn self.tibber = tibber self.mqtt = mqttpublisher.publisher() def set_battery_total_net_delivery(self, value): self.battery_total_net_delivery = value def get_battery_total_net_delivery(self): return self.battery_total_net_delivery def set_battery_total_net_usage(self, value): self.battery_total_net_usage = value def get_battery_total_net_usage(self): return self.battery_total_net_usage def set_net_consumption(self, value): self.consumption = value self.consump_updated = True self.check_net_usage() def set_net_delivery(self, value): self.return_delivery = value self.return_updated = True self.check_net_usage() def check_net_usage(self): if self.return_updated and self.consump_updated: self.consump_updated=False self.return_updated=False if self.netp1usageupdated: # skip till next p1 cycle self.netp1usageupdated=False return self.netp1usage = self.consumption - self.return_delivery self.netp1usageupdated = True #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() setpoint = self.netp1usage+setpoint if self.chargemode==Chargemode.CHARGE_DYN and setpoint > 0: setpoint =0 elif self.chargemode==Chargemode.DISCHARGE_DYN and setpoint < 0: setpoint =0 self.setpoint = -1 * setpoint if self.setpoint > MAX_CHARGE_POWER: self.setpoint = MAX_CHARGE_POWER elif self.setpoint < -MAX_POWER: self.setpoint = MAX_POWER #logger.info ("Dynamic setpoint: {}".format(setpoint)) self.apply_charge_mode() def set_chargemode(self, mode: Chargemode): self.chargemode = mode self.apply_charge_mode() def set_chargemode_int(self, mode: int): self.chargemode = Chargemode(mode) self.apply_charge_mode() def set_chargevalue(self,value): self.chargevalue = value def get_chargevalue(self): return self.chargevalue def get_chargemode(self) -> Chargemode: return self.chargemode def get_chargemode_int(self) -> int: return int(self.chargemode) def get_chargevaluekwh(self) -> float: return self.valuekwh def set_chargevaluekwh(self,value: float): self.valuekwh = value def update(self): logger.debug("Run controller update") self.mqtt.loop() 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 self.apply_charge_mode() logger.debug("Controller update mqtt update batteries") for battery in self.victronconn.batteries: self.mqtt.publish_battery(battery) logger.debug("Controller update mqtt update done") logger.info('Mode={} CV={}W L1: SP={}W Net usage={}W Power={}W Battery: SoC={}% Current={:.2f}A TUsage={:.1f}KwH TDelivery{:.1f}KwH Tibber=€{}'.format( str(self.chargemode), int(self.chargevalue), self.victronconn.get_setpoint(), self.netp1usage, self.victronconn.get_power_usage(), self.victronconn.get_soc() , float(self.victronconn.get_battery_current() ), self.battery_total_net_usage, self.battery_total_net_delivery, self.tibber.getPrice() )) def apply_charge_mode(self): if self.chargemode in [Chargemode.CHARGING_MANUAL, Chargemode.DISCHARGING_MANUAL]: self.setpoint = self.chargevalue elif self.chargemode in [Chargemode.CHARGING_MAX]: self.setpoint = MAX_CHARGE_POWER elif self.chargemode in [Chargemode.DISCHARGING_MAX]: self.setpoint = -MAX_POWER elif self.chargemode == Chargemode.CHARGING_CHEAP: if self.tibber.getPrice() == None: logger.info("Tibber price unknown, doing nothing") self.setpoint = 0 elif round(self.tibber.getPrice(),2) > charge_max_price_level: logger.info("Energy prive {} too expensive, no charging".format(round(self.tibber.getPrice(),2))) self.setpoint = 0 else: logger.info("Energy prive {} ok, charging".format(round(self.tibber.getPrice(),2))) self.setpoint = MAX_CHARGE_POWER elif self.chargemode == Chargemode.FLOATING: self.setpoint = 0 if self.setpoint > 0: self.victronconn.set_charging() elif self.setpoint == 0: self.victronconn.set_idle() else: if self.setpoint < -MAX_POWER: self.setpoint = -MAX_POWER self.victronconn.set_discharging() self.victronconn.set_setpoint(self.setpoint) self.mqtt.publish_controller(self)