def test_dataUpdate_global(self): """Listener can announce catch-all wildcard""" testdata = { '/some/path': 1, '/another/path/x': 2 } Scheduler().registerData(['*'], self.listener1) Scheduler.simulated = True Scheduler().dataUpdate(DataPackage(Listener, testdata)) Scheduler().dataRunner.join() self.assertDictEqual(testdata, self.listener1.data[0])
def test_dummySpeicher(self): OpenWBconfig('resources/test.conf') data = openWBValues() module = SpeicherModule() self.assertEqual(2, len(module.modules)) self.assertEqual(1, data.get('housebattery/boolHouseBatteryConfigured'), 'Speicher enabled') BAT1, BAT2 = module.modules BAT1.P = 1000 BAT2.P = 2000 BAT1.soc = 25 BAT2.soc = 75 Scheduler().test_callAll() self.assertEqual(BAT1.P, data.get('housebattery/1/W'), "BAT1 power has been reported") self.assertEqual(BAT2.P, data.get('housebattery/2/W'), "BAT2 power has been reported") self.assertEqual(BAT1.P + BAT2.P, data.get('housebattery/W'), "sum power has been reported") self.assertEqual(50, data.get('housebattery/%Soc'), "sum SOC has been reported") Scheduler().test_callAll() BAT1.kwhOut = 5 BAT2.kwhOut = 10 BAT1.kwhIn = 15 BAT2.kwhIn = 20 Scheduler().test_callAll() self.assertEqual(BAT1.kwhIn + BAT2.kwhIn, data.get('housebattery/WhImported'), "EVU energy imported has been reported") self.assertEqual(BAT1.kwhOut + BAT2.kwhOut, data.get('housebattery/WhExported'), "EVU energy exported has been reported") self.assertEqual(BAT1.kwhIn, data.get('housebattery/1/dailykwhIn'), "BAT1 consumption has been reported (Daily)") self.assertEqual(BAT1.kwhOut, data.get('housebattery/1/monthlykwhOut'), "BAT1 generation has been reported (Monthly)") self.assertEqual(BAT1.kwhIn + BAT2.kwhIn, data.get('housebattery/DailyYieldImportKwh'), "Sum consumption has been reported (Daily)") self.assertEqual(BAT1.kwhOut + BAT2.kwhOut, data.get('housebattery/MonthlyYieldExportKwh'), "Sum generation has been reported (Monthly)") Scheduler().signalEvent(OpenWBEvent(EventType.resetDaily)) BAT1.kwhOut += 10 BAT2.kwhIn += 20 Scheduler().test_callAll() self.assertEqual(10, data.get('housebattery/MonthlyYieldExportKwh'), "Sum generation daily is offsetted") self.assertEqual(20, data.get('housebattery/DailyYieldImportKwh'), "Sum consumption daily is offsetted")
def setup(self, config): self.P = 0 self.soc = 0 self.kwhOut = 0 self.kwhIn = 0 self.dailyOffsetOut = 0 self.dailyOffsetIn = 0 Scheduler().registerTimer(1, self.loop) Scheduler().registerEvent(EventType.resetDaily, self.daily)
def test_eventListener(self): self.daily = False self.monthly = False def dailyReset(event): self.daily = True def monthlyReset(event): self.monthly = True Scheduler().registerEvent(EventType.resetDaily, dailyReset) Scheduler().registerEvent(EventType.resetMonthly, monthlyReset) Scheduler().signalEvent(OpenWBEvent(EventType.resetDaily, info="Info")) self.assertTrue(self.daily) self.assertFalse(self.monthly)
def test_charging(self): """Erkenne Ladezustand""" OpenWBconfig('resources/test.conf') data = openWBValues() module = LPModule() LP1 = module.modules[0] LP1.actP = 200 Scheduler().test_callAll() self.assertFalse(data.get('lp/1/boolChargeStat'), "Fahrzeug lädt nicht") LP1.actP = 500 Scheduler().test_callAll() self.assertTrue(data.get('lp/1/boolChargeStat'), "Fahrzeug lädt")
def setUp(self): RamdiskValues._inst = FakeRamdisk() if '_inst' in vars(OpenWBconfig): del OpenWBconfig._inst if '_inst' in vars(Scheduler): del Scheduler._inst Scheduler(simulated=True)
def test_dataUpdate_1listener(self): Scheduler().registerData(['/some/path/*', '/another/path/*'], self.listener1) Scheduler().dataUpdate(DataPackage(Listener, { '/some/other/path': 1, '/some/path/a': 2})) Scheduler().dataUpdate(DataPackage(Listener, { '/some/path/subpath/b': 3, '/another/path/c': 4 })) Scheduler.simulated = True Scheduler().dataRunner.join() self.assertEqual(1, len(self.listener1.data), "Listener 1 has received one package") self.assertDictEqual({ '/some/path/a': 2, '/some/path/subpath/b': 3, '/another/path/c': 4 }, self.listener1.data[0], "Listener shall have received the registered data branches")
def setup(self, config): self.ip = config.get(self.configprefix + '_ip') self.timeout = config.get(self.configprefix + '_timeout', 2) self.laststate = {} self.setter = GO_E_SET('http://%s/mqtt' % self.ip, self.timeout, self) self.setter.start() Scheduler().registerTimer(5, self.loop) super().setup(config)
def setup(self, config): self.ip = config.get(self.configprefix + '_ip') self.swname = config.get(self.configprefix + '_name') self.power = config.get(self.configprefix + '_power', 2000) self.blockcnt = 0 self.on_delay = 0 self.setP = 0 Scheduler().registerTimer(10, self.loop) super().setup(config)
def setup(self, config): self.P = 0 self.kwhOut = 0 self.kwhIn = 0 self.offsetPV = config.get('offsetpv') # Weitere Attribute: # A1..A3 # V1..V3 Scheduler().registerTimer(1, self.loop)
def test_plugged(self): """Erkenne Plugged Status""" OpenWBconfig('resources/test.conf') data = openWBValues() module = LPModule() LP1 = module.modules[0] LP1.A = 6 LP1.A1 = 1 Scheduler().test_callAll(2) self.assertFalse(data.get('lp/1/boolPlugStat'), "Fahrzeug nicht eingesteckt") LP1.A1 = 5.2 Scheduler().test_callAll(2) self.assertTrue(data.get('lp/1/boolPlugStat'), "Fahrzeug eingesteckt") LP1.A1 = 4.9 Scheduler().test_callAll(2) self.assertFalse(data.get('lp/1/boolPlugStat'), "Fahrzeug nicht eingesteckt")
def setup(self, config) -> None: self.ip = config.get(self.configprefix + '_ip') self.device = ModbusDevice(self.ip) if config.get(self.configprefix + '_bms') == "batrium": from .bms_batrium import BATRIUM self.bms = BATRIUM(1) self.bms.setup(self) self.bms.start() else: self.bms = None super().setup(config) Scheduler().registerTimer(10, self.loop)
def test_phasecount(self): """Zähle angeschlossene Phasen""" OpenWBconfig('resources/test.conf') data = openWBValues() module = LPModule() LP1 = module.modules[0] LP1.A = 6 LP1.A1 = 1 Scheduler().test_callAll() LP1.zaehle_phasen() Scheduler().test_callAll() self.assertEqual(1, data.get('lp/1/countPhasesInUse'), "1 Phase wird erkannt") LP1.A2 = 1 Scheduler().test_callAll() LP1.zaehle_phasen() Scheduler().test_callAll() self.assertEqual(1, data.get('lp/1/countPhasesInUse'), "2 Phasen werden erkannt") LP1.A3 = 1 Scheduler().test_callAll() LP1.zaehle_phasen() Scheduler().test_callAll() self.assertEqual(1, data.get('lp/1/countPhasesInUse'), "3 Phasen werden erkannt")
def setup(self): """Subscribe to set topics""" self.bulk_config() self.logger.debug('Subscribing.') self.client.subscribe("openWB/set/#", 2) self.client.subscribe("openWB/config/set/#", 2) scheduler = Scheduler() scheduler.registerData(["*"], self) scheduler.registerTimer( 10, self.publishLiveData ) # TODO: React on Chargepoint end-of-loop event scheduler.registerEvent(EventType.configupdate, self.newconfig) scheduler.registerEvent(EventType.resetDaily, self.cut_live)
def setup(self, config): super().setup(config) host = config[self.configprefix + '_ip'] assert host is not None, "Host für %s notwenig! (Setting %s_ip)" % ( self.configprefix, self.configprefix) self.instances = config.get(self.configprefix + '_instances', 1) if config.get(self.configprefix + '_type') == 'modbus': self.instance = ModbusWR(host, self.instances) else: # dashboard self.instance = SMADASH(host) assert self.instances == 1, "Dashboard Tripower can only have one instance." super().setup(config) Scheduler().registerTimer(10, self.loop)
def test_dummyEVU(self): OpenWBconfig('resources/test.conf') module = EVUModule() self.assertIsNotNone(module.modul) EVU = module.modul data = openWBValues() EVU.P = 1000 Scheduler().test_callAll() self.assertEqual(EVU.P, data.get('evu/W'), "EVU power has been reported") self.assertIsNone(data.get('evu/ASchieflast', None), "Keine Schieflast ohne Phasenströme") EVU.A1 = 1 EVU.A2 = 3 EVU.A3 = 6 Scheduler().test_callAll() self.assertEqual(5, data.get('evu/ASchieflast'), "EVU Schieflast has been calculated") EVU.kwhIn = 5 EVU.kwhOut = 10 Scheduler().test_callAll() self.assertEqual(EVU.kwhIn, data.get('evu/WhImported'), "EVU energy imported has been reported") self.assertEqual(EVU.kwhOut, data.get('evu/WhExported'), "EVU energy exported has been reported") self.assertEqual(EVU.kwhIn, data.get('evu/DailyYieldImportKwh'), "EVU energy imported has been reported (Daily)") self.assertEqual(EVU.kwhOut, data.get('evu/DailyYieldExportKwh'), "EVU energy exported has been reported(Daily)") self.assertEqual(EVU.kwhIn, data.get('evu/MonthlyYieldImportKwh'), "EVU energy imported has been reported (Monthly)") self.assertEqual(EVU.kwhOut, data.get('evu/MonthlyYieldExportKwh'), "EVU energy exported has been reported (Monthly)")
def test_data(self): OpenWBconfig('resources/test.conf') module = PVModule() self.assertEqual(2, len(module.modules)) WR1, WR2 = module.modules data = openWBValues() WR1.P = 1000 WR2.P = 2000 Scheduler().test_callAll() self.assertEqual(WR1.P, data.get('pv/1/W'), "WR1 power has been reported") self.assertEqual(WR2.P, data.get('pv/2/W'), "WR2 power has been reported") self.assertEqual(WR1.P + WR2.P, data.get('pv/W'), "sum power has been reported") Scheduler().test_callAll() WR1.Wh = 5 WR2.Wh = 10 Scheduler().test_callAll() self.assertEqual(WR1.Wh, data.get('pv/1/kwh'), "WR1 generation has been reported") self.assertEqual(WR2.Wh, data.get('pv/2/kwh'), "WR2 generation has been reported") self.assertEqual(WR1.Wh + WR2.Wh, data.get('pv/WhCounter'), "Sum generation has been reported") self.assertEqual(WR1.Wh, data.get('pv/1/DailyKwh'), "WR1 generation has been reported (Daily)") self.assertEqual(WR1.Wh, data.get('pv/1/MonthlyKwh'), "WR1 generation has been reported (Monthly)") self.assertEqual(WR1.Wh + WR2.Wh, data.get('pv/DailyYieldKwh'), "Sum generation has been reported (Daily)") self.assertEqual(WR1.Wh + WR2.Wh, data.get('pv/MonthlyYieldKwh'), "Sum generation has been reported (Monthly)")
def __init__(self, instance_id: int): super().__init__(daemon=True) name = self.__class__.__name__ self.trigger = Event() # A trigger to start the module self.finished = Event() # A trigger to signal that the module has run self.trigger.clear() self.finished.clear() if hasattr(self.__class__, 'multiinstance') and self.__class__.multiinstance: self.id = instance_id name += '_' + str(instance_id) self.name = name self.logger = logging.getLogger(self.name) self.configprefix = None # Provided during setup self.offsets = {} # Place for storing offsets for daily data if hasattr(self, 'event'): # Register general event handler Scheduler().registerEvent(None, self.event)
def send(self, data: dict) -> None: if 'W' in data: # Only for normal data packages if "boolPlugStat" not in data: data["boolPlugStat"] = not self.is_blocked if "boolChargeStat" not in data: data["boolChargeStat"] = self.is_charging if "countPhasesInUse" not in data: data['countPhasesInUse'] = self.phasen if "kwh" not in data: data['kwh'] = 0 # Handle Ladung seit Plug / Ladung seit Chargestart plugged = data['boolPlugStat'] charging = data['boolChargeStat'] chargedkwh = data['kwh'] self.offsets['chargedW'] += data['W'] data['kWhChargedSincePlugged'] = self.offsetted( 'plugged', 'kwh', chargedkwh) if plugged else 0 data['kWhActualCharged'] = self.offsets[ 'chargedW'] / 720000 # Einheit: W*Zykluszeit => /(3600/t)/1000 # self.offsetted('charge', 'kwh', chargedkwh) if plugged and not self.plugged: self.reset_offset('plugged', 'kwh') self.logger.info(f'LP{self.id} plugged in at {chargedkwh} kwh') Scheduler().signalEvent( OpenWBEvent(EventType.resetEnergy, self.id)) if charging and not self.charging: self.reset_offset('charge', 'kwh') self.offsets['chargedW'] = 0 self.logger.info('Start charging at %i kwh' % chargedkwh) # self.setP = self.actP # Initialisiere setP falls externer Start self.plugged = plugged self.charging = charging data["DailyKwh"] = self.offsetted('daily', 'kwh', data['kwh']) self.master.send(DataPackage(self, data))
def messagehandler(self, msg): """Handle incoming requests""" republish = False getter_topic = msg.topic.replace('openWB/', '').replace('/set/', '/get/') # self.logger.info("receive: %s = %s" % (repr(msg.topic), repr(msg.payload))) try: val = int(msg.payload) # self.logger.info("Value: %i" % val) except ValueError: val = None try: if msg.topic == "openWB/config/set/pv/regulationPoint": # Offset (PV) if val is not None and -300000 <= val <= 300000: republish = True self.core.setconfig('offsetpv', val) elif msg.topic == "openWB/config/set/pv/priorityModeEVBattery": # Priorität Batt/EV if val is not None and 0 <= val <= 2: republish = True self.core.setconfig('speicherpveinbeziehen', val) elif msg.topic == "openWB/config/set/pv/nurpv70dynw": republish = True self.core.setconfig('offsetpvpeak', val) elif re.match("openWB/set/lp/(\\d)/ChargePointEnabled", msg.topic): # Chargepoint en/disable device = int(re.search('/lp/(\\d)/', msg.topic).group(1)) republish = True self.core.data.update( DataPackage(self, {'lp/%i/ChargePointEnabled' % device: val})) elif msg.topic.startswith( "openWB/config/set/sofort/"): # Sofortladen... device = int(re.search('/lp/(\\d)/', msg.topic).group(1)) if 1 <= device <= 8: republish = True if msg.topic.endswith('current'): if val is not None and 6 <= val <= 32: self.core.setconfig('lpmodul%i_sofortll' % device, val) elif msg.topic.endswith( 'chargeLimitation'): # Limitierung Modus if val is not None and 0 <= val <= 2: self.core.setconfig('msmoduslp%i' % device, val) self.publish_config( "lp/%i/boolDirectModeChargekWh" % device, 1 if val == 1 else 0) self.publish_config( "lp/%i/boolDirectChargeModeSoc" % device, 1 if val == 2 else 0) elif msg.topic.endswith( 'energyToCharge'): # Modus 1: Lademenge [kWh] if val is not None and 0 <= val <= 100: self.core.setconfig('lademkwh%i' % device, val) elif msg.topic.endswith( 'socToChargeTo'): # Modus 2: SOC [%] if val is not None and 0 <= val <= 100: self.core.setconfig('sofortsoclp%i' % device, val) elif msg.topic.endswith('resetEnergyToCharge'): republish = False if msg.payload: Scheduler().signalEvent( OpenWBEvent(EventType.resetEnergy, device)) elif msg.topic == "openWB/config/set/pv/stopDelay": if val is not None and 0 <= val <= 10000: republish = True self.core.setconfig('abschaltverzoegerung', val) elif msg.topic.startswith( 'openWB/config/set/lp/'): # Ladepunkt Konfiguration self.logger.info("LP message") device = int(re.search('/lp/(\\d)', msg.topic).group(1)) if re.search("/ChargeMode", msg.topic): # Chargemode mode = Chargemap(val) self.logger.info(f'ChargeMode lp{device} = {mode}') if 1 <= device <= 8: self.core.setconfig('lpmodul%i_mode' % device, mode.name) elif re.search("/alwaysOn", msg.topic): self.logger.info(f'AlwaysOn lp{device} = {msg.payload}') if 1 <= device <= 8: republish = True self.core.setconfig('lpmodul%i_alwayson' % device, bool(int(msg.payload))) elif msg.topic == "openWB/set/graph/RequestDayGraph": # Anforderung eines Daily graphs. # Format Wert: yyyymmdd # Antwort: openWB/system/DayGraphData1<n>, n=1..12 je 25 Zeilen # Herkunft: web/logging/data/<yyyymm>.csv erzeugt von Cronjob "cron5min.sh" # echo $(date +%H%M),$bezug,$einspeisung,$pv,$ll1,$ll2,$ll3,$llg,$speicheri,$speichere,$verbraucher1,$verbrauchere1,$verbraucher2,$verbrauchere2,$verbraucher3,$ll4,$ll5,$ll6,$ll7,$ll8,$speichersoc,$soc,$soc1,$temp1,$temp2,$temp3,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$d10,$temp4,$temp5,$temp6 >> $dailyfile.csv if 1 <= val <= 20501231: subprocess.run([ basePath + '../../runs/senddaygraphdata.sh', msg.payload ]) elif msg.topic == 'openWB/set/graph/RequestMonthGraph': # Anforderung eines Month graphs. # Format Wert: yyyymm # Antwort: openWB/system/MonthGraphData<n>, n=1..12 je 25 Zeilen # Herkunft: web/logging/data/<yyyymm>.csv erzeugt von Cronjob "cronnightly.sh" # echo $(date +%Y%m%d),$bezug,$einspeisung,$pv,$ll1,$ll2,$ll3,$llg,$verbraucher1iwh,$verbraucher1ewh,$verbraucher2iwh,$verbraucher2ewh,$ll4,$ll5,$ll6,$ll7,$ll8,$speicherikwh,$speicherekwh,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$d10 >> $monthlyfile.csv if 1 <= val <= 20501231: subprocess.run([ basePath + '../../runs/sendmonthgraphdata.sh', msg.payload ]) elif msg.topic == "openWB/set/graph/RequestLiveGraph": if val == 1: subprocess.run(basePath + "../../runs/sendlivegraphdata.sh") else: self.publish_data("system/LiveGraphData", "empty", qos=0) elif msg.topic == "openWB/set/graph/RequestLLiveGraph": if val == 1: subprocess.run(basePath + "../../runs/sendllivegraphdata.sh") elif getter_topic in self.configmapping: if val is not None and 0 <= val <= 10000: republish = True self.core.setconfig(self.configmapping[getter_topic], val) else: self.logger.info("Nix gefunden.") except IOError as e: # Exception self.logger.exception("BAMM!", exc_info=e) if republish: # self.logger.info("Re-publish: %s = %s" % (getter_topic, msg.payload)) self.publish_config(getter_topic, msg.payload)
def setup(self): scheduler = Scheduler() scheduler.registerData(['*'], self)
import os import sys mypath = os.path.dirname(os.path.realpath(__file__)) + '/' sys.path.append(mypath + 'src') from openWB import OpenWBCore, api from openWB.Scheduling import Scheduler from openWB.startup import init_system if 'http_proxy' in os.environ: os.environ.pop('http_proxy') if 'https_proxy' in os.environ: os.environ.pop('https_proxy') level = logging.DEBUG if os.environ.get("DEBUG") == "1" else logging.INFO logging.basicConfig(level=level, format='%(asctime)-15s %(message)s', filename="/var/log/openWB.log") infologgers = ['Adafruit_I2C.Device.Bus.1.Address.0X40', 'pymodbus'] for logger in infologgers: logging.getLogger(logger).setLevel(logging.INFO) init_system() core = OpenWBCore.OpenWBCore().setup() # Start the API #api = api.OpenWBAPI(core) #api.start() Scheduler().run()
def setup(self, config): super().setup(config) self.kwh = 0 self.minI = config['minimalstromstaerke'] self.maxI = config['maximalstromstaerke'] Scheduler().registerTimer(1, self.loop)
def setup(self, config): self.P = 0 self.Wh = 0 Scheduler().registerTimer(1, self.loop)
def test_dummyLP(self): OpenWBconfig('resources/test.conf') data = openWBValues() module = LPModule() self.assertEqual(1, len(module.modules)) self.assertEqual(1, data.get('lp/1/boolChargePointConfigured'), 'LP1 configured') self.assertEqual(1, data.get('lp/1/ChargePointEnabled'), 'LP1 enabled') LP1 = module.modules[0] LP1.set(1300) with self.subTest('Geringe Leistung'): LP1.actP = 200 Scheduler().test_callAll() self.assertFalse(data.get('lp/1/boolChargeStat'), "Fahrzeug lädt nicht") with self.subTest('Höhere Leistung'): LP1.actP = 1200 LP1.A = 6 LP1.A1 = 5.5 Scheduler().test_callAll() self.assertEqual(LP1.actP, data.get('lp/1/W'), "LP1 Leistung wird übertragen") self.assertEqual(LP1.A, data.get('lp/1/AConfigured'), "LP1 Strom wird übertragen") self.assertEqual(LP1.actP, data.get('global/WAllChargePoints'), "Summenleistung wird übertragen") self.assertTrue(data.get('lp/1/boolChargeStat'), "Fahrzeug lädt") with self.subTest('Lademenge'): LP1.kwh = 10 Scheduler().test_callAll() self.assertEqual(LP1.kwh, data.get('lp/1/kwh'), "Energie (Gesamt) wird übertragen") self.assertEqual(LP1.kwh, data.get('lp/1/DailyKwh'), "Energie (Daily) wird übertragen") self.assertEqual(LP1.kwh, data.get('lp/1/kWhActualCharged'), "Energie (Seit Ladebeginn) wird übertragen") self.assertEqual(LP1.kwh, data.get('lp/WhCounter'), "Energie (Gesamtsumme) wird übertragen") self.assertEqual(LP1.kwh, data.get('lp/DailyYieldKwh'), "Energie (Daily Summe) wird übertragen") with self.subTest('Neuer Ladezyklus'): LP1.actP = 10 Scheduler().test_callAll() self.assertFalse(data.get('lp/1/boolChargeStat'), "Fahrzeug lädt nicht") LP1.actP = 2400 Scheduler().test_callAll() self.assertTrue(data.get('lp/1/boolChargeStat'), "Fahrzeug lädt") LP1.kwh += 5 Scheduler().test_callAll() self.assertEqual(15, data.get('lp/1/kwh'), "Energie (Gesamt) wird übertragen") self.assertEqual(15, data.get('lp/1/DailyKwh'), "Energie (Daily) wird übertragen") self.assertEqual(5, data.get('lp/1/kWhActualCharged'), "Energie (Seit Ladebeginn) wird übertragen") self.assertEqual(15, data.get('lp/1/kWhChargedSincePlugged'), "Energie (Seit Plugged) wird übertragen") self.assertEqual(15, data.get('lp/WhCounter'), "Energie (Gesamtsumme) wird übertragen") self.assertEqual(15, data.get('lp/DailyYieldKwh'), "Energie (Daily Summe) wird übertragen")
def loop(self) -> None: properties = [lp.get_props() for lp in self.regler.values()] arbitriert = dict([(id, 0) for id in self.regler.keys()]) self.logger.debug(f"Reglergruppe {self.mode} LP Props: {properties!r}") uberschuss = self.data.get('global/uberschuss') for id, regler in self.regler.items(): prefix = 'lp/%i/' % id limitierung = self.config.get('msmoduslp%i' % id) if self.mode not in ['standby', 'stop']: if limitierung == 1 and self.config.get( 'lademkwh%i' % id): # Limitierung: kWh if self.data.get(prefix + 'W') == 0: restzeit = "---" else: restzeit = int( (self.config.get('lademkwh%i' % id) - self.data.get(prefix + 'kWhActualCharged', 0)) * 1000 * 60 / self.data.get('lp/%i/W' % id)) # print(f"LP{id} Ziel: {self.config.get('lademkwh%i' % id)} Akt: {self.data.get(prefix + 'kWhActualCharged')} Leistung: {self.data.get(prefix + 'W')} Restzeit: {restzeit}") self.data.update( DataPackage( regler.wallbox, {prefix + 'TimeRemaining': f"{restzeit} min"})) if self.config.get( 'lademkwh%i' % id) <= self.data.get(prefix + 'kWhActualCharged'): self.logger.info( f"Lademenge erreicht: LP{id} {self.config.get('lademkwh%i' % id)}kwh" ) from openWB.OpenWBCore import OpenWBCore OpenWBCore().setconfig( regler.wallbox.configprefix + '_mode', "standby") Scheduler().signalEvent( OpenWBEvent(EventType.resetEnergy, id)) elif limitierung == 2: # Limitierung: SOC pass # TODO if self.mode == 'sofort': for id, regler in self.regler.items(): power = amp2power( self.config.get("lpmodul%i_sofortll" % id, 6), regler.wallbox.phasen) if regler.wallbox.setP != power: regler.wallbox.set(power) elif self.mode in ['stop', 'standby']: for regler in self.regler.values(): if regler.wallbox.setP != 0 or regler.wallbox.is_charging: self.logger.info( f"(LP {regler.wallbox.id}: {regler.wallbox.setP}W -> Reset" ) regler.wallbox.set(0) elif uberschuss > self.limit: # Leistungserhöhung deltaP = uberschuss - self.limit # Erhöhe eingeschaltete LPs for r in sorted(filter(lambda r: 'min+P' in r and 'on' in r.flags, properties), key=lambda r: r['min+P'].priority): p = self.get_increment(r, deltaP) if p is not None and self.schieflast_nicht_erreicht(r.id): # Erhöhe bei Asymmetrie nur 3-phasen-Boxen self.logger.debug(f"LP {r.id} bekommt +{p}W von {deltaP}W") arbitriert[r.id] = p deltaP -= p if deltaP <= 0: break # Schalte LPs mit höchster Prio ein candidates = unroll( groupby(filter(lambda r: 'min+P' in r and 'off' in r.flags, properties), key=lambda r: r['min+P'].priority)) if candidates: highest_prio = max(candidates.keys()) candidates = candidates[highest_prio] # Budget zum Einschalten: Erstmal der Überschuss budget = uberschuss - self.limit if self.mode == "pv": budget -= self.hysterese # Zusätzliches Budget kommt vom Regelpotential eingeschalteter LPs gleicher oder niedrigerer Prio budget += sum(r['max-P'].value for r in filter( lambda r: 'max-P' in r and 'min' not in r.flags and r[ 'max-P'].priority <= highest_prio, properties)) for r in candidates: if self.get_increment( r, budget ) is not None and self.schieflast_nicht_erreicht(r.id): self.logger.info( f"Budget: {budget}; LP {r.id} min+P {r['min+P'].value} passt noch" ) arbitriert[r.id] = r['min+P'].value budget -= r['min+P'].value elif uberschuss < self.limit: # Leistungsreduktion deltaP = self.limit - uberschuss for r in sorted(filter( lambda r: 'min-P' in r and 'min' not in r.flags, properties), key=lambda r: r['min-P'].priority): p = self.get_decrement(r, deltaP) if p is not None: self.logger.debug( f"LP {r.id} Reduzierung -{p}W von -{deltaP}W") arbitriert[r.id] = -p deltaP -= p if deltaP <= 0: break # Schalte LPs aus deltaP = self.limit - uberschuss if self.mode == "peak": deltaP -= self.hysterese for r in sorted(filter(lambda r: 'min' in r.flags, properties), key=lambda r: r['min-P'].priority): if self.get_decrement(r, deltaP) is not None: self.logger.info( f"Abschalten LP {r.id} soll {r['min-P'].value}W freigeben." ) arbitriert[r.id] = -r['min-P'].value deltaP -= r['min-P'].value for ID, inc in arbitriert.items(): self.regler[ID].request(inc)
def __init__(self): Scheduler().registerData( ['evu/W', 'pv/W', 'global/WAllChargePoints', 'housebattery/W'], self) self.data = openWBValues() self.config = OpenWBconfig()
def setup(self, config): self.pwm = Ada.PCA9685(address=config.get(self.configprefix + '_address')) self.pwm.set_pwm_freq(100) self.last = {'pv': 0, 'soc': 0, 'grid': -1000, 'batt': -1000, 'green': 0, 'red': 0} Scheduler().registerData(mapping.keys(), self) Scheduler().registerTimer(10, self.leds)
def test_scheduling(self): Scheduler().registerTimer(5, self.listener1.loop) Scheduler().registerTimer(10, self.listener2.loop) Scheduler().run(simulated=True) self.assertEqual(2, self.listener1.executed) self.assertEqual(1, self.listener2.executed)