def update(num: int, speichermodul: str, wrkostalpikoip: str): log.debug('Wechselrichter Kostal Piko Var 1 Speicher: ' + speichermodul) log.debug('Wechselrichter Kostal Piko Var 1 IP: ' + wrkostalpikoip) # Auslesen eines Kostal Piko WR über die integrierte API des WR. Rückgabewert ist die aktuelle Wattleistung. if speichermodul != "none": params = (('dxsEntries', ['33556736', '251658753)']), ) pvwatttmp = requests.get('http://' + wrkostalpikoip + '/api/dxs.json', params=params, timeout=5).json() else: params = (('dxsEntries', ['67109120', '251658753)']), ) pvwatttmp = requests.get('http://' + wrkostalpikoip + '/api/dxs.json', params=params, timeout=5).json() # aktuelle Ausgangsleistung am WR [W] pvwatt = int(pvwatttmp["dxsEntries"][0]["value"]) if pvwatt > 5: pvwatt = pvwatt * -1 log.debug('WR Leistung: ' + str(pvwatt)) # Gesamtzählerstand am WR [kWh] pvkwh = int(pvwatttmp['dxsEntries'][1]['value']) get_inverter_value_store(num).set( InverterState(counter=pvkwh * 1000, power=pvwatt))
def read_legacy(ip1: str, webbox: int, ip2: str, ip3: str, ip4: str, version: int, num: int) -> None: def create_webbox_inverter(address: str): config = inverter_webbox.get_default_config() config["id"] = num return inverter_webbox.SmaWebboxInverter(0, address, config) def create_modbus_inverter(address: str): config = inverter_modbus_tcp.get_default_config() config["id"] = num config["configuration"]["version"] = SmaInverterVersion(version) return inverter_modbus_tcp.SmaModbusTcpInverter(0, address, config) inverter1 = (create_webbox_inverter if webbox else create_modbus_inverter)(ip1) inverters_additional = (create_modbus_inverter(address) for address in [ip2, ip3, ip4] if address != "none") # In legacy we were able to configure multiple IP-Addresses for a single SMA-component, effectively creating a # virtual component that represents the sum of its subcomponents. This was probably done in order to circumvent # the limitation to have a maximum of two inverters configured in legacy. # Since openWB 2 does not have a limitation on the number of inverters we do not implement this there. However # we still need to implement this for the read_legacy-bridge. # Here we act like we only update the first inverter, while we actually query all inverters and sum them up: with SingleComponentUpdateContext(inverter1.component_info): total_power = 0 total_energy = 0 for inverter in itertools.chain((inverter1,), inverters_additional): state = inverter.read_inverter_state() total_power += state.power total_energy += state.counter get_inverter_value_store(num).set(InverterState(counter=total_energy, power=total_power))
def update_sma_modbus(addresses: Iterable[str]): power_total = 0 energy_total = 0 for ipaddress in addresses: with ModbusClient(ipaddress) as client: # AC Wirkleistung über alle Phasen (W) [Pac]: power = client.read_holding_registers(30775, ModbusDataType.INT_32, unit=3) # Gesamtertrag (Wh) [E-Total]: energy = client.read_holding_registers(30529, ModbusDataType.UINT_32, unit=3) log.debug("%s: power = %d W, energy = %d Wh", ipaddress, power, energy) if power == SMA_INT32_NAN: log.debug("Power value is NaN - ignoring") else: power_total += power energy_total += energy power_total = -max(power_total, 0) get_inverter_value_store(1).set( InverterState(counter=energy_total, power=power_total))
def update_using_cookie(address: str, cookie): aggregate = read_aggregate(address, cookie) pv_watt = aggregate["solar"]["instant_power"] if pv_watt > 5: pv_watt = pv_watt * -1 get_inverter_value_store(1).set( InverterState(counter=aggregate["solar"]["energy_exported"], power=pv_watt))
def update_using_powerwall_client(client: PowerwallHttpClient): aggregate = client.get_json("/api/meters/aggregates") pv_watt = aggregate["solar"]["instant_power"] if pv_watt > 5: pv_watt = pv_watt * -1 get_inverter_value_store(1).set( InverterState(counter=aggregate["solar"]["energy_exported"], power=pv_watt))
def read_legacy(component_type: str, address: str, bat_module: str, bat_ip: str, bat_username: str, bat_password: str, num: Optional[int] = None) -> None: dev = Device( KostalPiko(configuration=KostalPikoConfiguration(ip_address=address))) if component_type in COMPONENT_TYPE_TO_MODULE: component_config = COMPONENT_TYPE_TO_MODULE[ component_type].component_descriptor.configuration_factory() else: raise Exception("illegal component type " + component_type + ". Allowed values: " + ','.join(COMPONENT_TYPE_TO_MODULE.keys())) component_config.id = num dev.add_component(component_config) log.debug('KostalPiko IP-Adresse: ' + address) log.debug('KostalPiko Speicher: ' + bat_module) if component_type == "inverter": with SingleComponentUpdateContext( dev.components["component" + str(num)].component_info): power, exported = dev.components["component" + str(num)].get_values() if bat_module == "speicher_bydhv": bat_power = _get_byd_bat_power(bat_ip, bat_username, bat_password, num) power -= bat_power get_inverter_value_store(num).set( InverterState(power=power, exported=exported)) elif component_type == "counter": with SingleComponentUpdateContext( dev.components["componentNone"].component_info): home_consumption, powers = dev.components[ "componentNone"].get_values() if bat_module == "speicher_bydhv": bat_power = _get_byd_bat_power(bat_ip, bat_username, bat_password, num) home_consumption += bat_power dev.add_component(KostalPikoInverterSetup(id=num)) inverter_power, _ = dev.components["component" + str(num)].get_values() power = home_consumption + inverter_power imported, exported = simcount.SimCountFactory().get_sim_counter()( ).sim_count(power, topic="topic_str", data={}, prefix="bezug") counter_state = CounterState(imported=imported, exported=exported, power=power, powers=powers) get_counter_value_store(None).set(counter_state)
def update(num: int, wr_piko2_url: str, wr_piko2_user: str, wr_piko2_pass: str): log.debug("Beginning update") response = requests.get(wr_piko2_url, verify=False, auth=(wr_piko2_user, wr_piko2_pass), timeout=10) response.raise_for_status() get_inverter_value_store(num).set( parse_kostal_piko_var2_html(response.text)) log.debug("Update completed successfully")
def update_sma_webbox(address: str): data = { 'RPC': '{"version": "1.0","proc": "GetPlantOverview","id": "1","format": "JSON"}' } response = requests.post('http://' + address + '/rpc', data=data, timeout=3) response.raise_for_status() response_data = response.json() get_inverter_value_store(1).set( InverterState( counter=float(response_data["result"]["overview"][2]["value"]) * 1000, power=-int(response_data["result"]["overview"][0]["value"])))
def __init__(self, component_config: Union[Dict, TeslaInverterSetup]) -> None: self.component_config = dataclass_from_dict(TeslaInverterSetup, component_config) self.__store = get_inverter_value_store(self.component_config.id) self.component_info = ComponentInfo.from_component_config( self.component_config)
def __init__(self, component_config: dict, tcp_client: modbus.ModbusClient) -> None: self.component_config = component_config self.__tcp_client = tcp_client self.__store = get_inverter_value_store(component_config["id"]) self.component_info = ComponentInfo.from_component_config( component_config)
def __init__(self, device_id: int, component_config: dict) -> None: self.__device_id = device_id self.component_config = component_config self.__sim_count = simcount.SimCountFactory().get_sim_counter()() self.simulation = {} self.__store = get_inverter_value_store(component_config["id"]) self.component_info = ComponentInfo.from_component_config(component_config)
def __init__(self, device_id: int, device_address: str, component_config: dict) -> None: self.__device_address = device_address self.component_config = component_config self.__store = get_inverter_value_store(component_config["id"]) self.component_info = ComponentInfo.from_component_config( component_config)
def __init__(self, component_config: Union[Dict, StuderInverterSetup], tcp_client: modbus.ModbusTcpClient_) -> None: self.component_config = dataclass_from_dict(StuderInverterSetup, component_config) self.__tcp_client = tcp_client self.__store = get_inverter_value_store(self.component_config.id) self.component_info = ComponentInfo.from_component_config( self.component_config)
def __init__(self, device_id: int, component_config: dict, tcp_client: modbus.ModbusClient) -> None: self.component_config = component_config factory = kit_counter_inverter_version_factory( component_config["configuration"]["version"]) self.__client = factory(component_config["configuration"]["id"], tcp_client) self.__tcp_client = tcp_client self.__store = get_inverter_value_store(component_config["id"]) self.component_info = ComponentInfo.from_component_config(component_config)
def __init__(self, device_id: int, component_config: Union[Dict, KostalPikoInverterSetup], ip_address: str) -> None: self.component_config = dataclass_from_dict(KostalPikoInverterSetup, component_config) self.ip_address = ip_address self.__store = get_inverter_value_store(self.component_config.id) self.component_info = ComponentInfo.from_component_config(self.component_config)
def __init__(self, component_config: dict, ip_address: str, password: str) -> None: self.component_config = component_config self.ip_address = ip_address self.password = password self.__store = get_inverter_value_store(component_config["id"]) self.component_info = ComponentInfo.from_component_config( component_config)
def update_e3dc_battery(addresses: Iterable[str], read_external: int, pv_other: bool): soc = 0 count = 0 battery_power = 0 # pv_external - > pv Leistung die als externe Produktion an e3dc angeschlossen ist # nur auslesen wenn als relevant parametrisiert (read_external = 1) , sonst doppelte Auslesung pv_external = 0 # pv -> pv Leistung die direkt an e3dc angeschlossen ist pv = 0 for address in addresses: log.debug("Battery Ip: %s, read_external %d pv_other %s", address, read_external, pv_other) count += 1 with ModbusClient(address, port=502) as client: # 40082 SoC soc += client.read_holding_registers(40082, ModbusDataType.INT_16, unit=1) # 40069 Speicherleistung battery_power += client.read_holding_registers(40069, ModbusDataType.INT_32, wordorder=Endian.Little, unit=1) # 40067 PV Leistung pv += (client.read_holding_registers(40067, ModbusDataType.INT_32, wordorder=Endian.Little, unit=1) * -1) if read_external == 1: # 40075 externe PV Leistung pv_external += client.read_holding_registers(40075, ModbusDataType.INT_32, wordorder=Endian.Little, unit=1) soc = soc / count log.debug("Battery soc %d battery_power %d pv %d pv_external %d count ip %d", soc, battery_power, pv, pv_external, count) counter_import, counter_export = SimCountFactory().get_sim_counter()().sim_count(battery_power, prefix="speicher") get_bat_value_store(1).set(BatState(power=battery_power, soc=soc, imported=counter_import, exported=counter_export)) # pv_other sagt aus, ob WR definiert ist, und dessen PV Leistung auch gilt # wenn 0 gilt nur PV und pv_external aus e3dc pv_total = pv + pv_external # Wenn wr1 nicht definiert ist, gilt nur die PV Leistung die hier im Modul ermittelt wurde # als gesamte PV Leistung für wr1 if not pv_other or pv_total != 0: # Wenn wr1 definiert ist, gilt die bestehende PV Leistung aus Wr1 und das was hier im Modul ermittelt wurde # als gesamte PV Leistung für wr1 if pv_other: try: pv_total = pv_total + files.pv[0].power.read() except: pass log.debug("wr update pv_other %s pv_total %d", pv_other, pv_total) _, counter_pv = SimCountFactory().get_sim_counter()().sim_count(pv_total, prefix="pv") get_inverter_value_store(1).set(InverterState(counter=counter_pv, power=pv_total))
def read_inverter(ip1: str, webbox: int, ip2: str, ip3: str, ip4: str, version: int, hybrid: int, num: int, sunny_boy_smart_energy: int): def create_webbox_inverter(address: str): return SmaWebboxInverter(address, SmaWebboxInverterSetup(id=num)) def create_modbus_inverter(address: str): config = SmaSunnyBoyInverterSetup( id=num, configuration=SmaSunnyBoyInverterConfiguration( hybrid=bool(hybrid), version=SmaInverterVersion(version))) return inverter.SmaSunnyBoyInverter( 0, config, modbus.ModbusTcpClient_(address, 502)) inverter1 = (create_webbox_inverter if webbox else create_modbus_inverter)(ip1) inverters_additional = (create_modbus_inverter(address) for address in [ip2, ip3, ip4] if address != "none") # In legacy we were able to configure multiple IP-Addresses for a single SMA-component, effectively creating a # virtual component that represents the sum of its subcomponents. This was probably done in order to circumvent # the limitation to have a maximum of two inverters configured in legacy. # Since openWB 2 does not have a limitation on the number of inverters we do not implement this there. However # we still need to implement this for the read_legacy-bridge. # Here we act like we only update the first inverter, while we actually query all inverters and sum them up: with SingleComponentUpdateContext(inverter1.component_info): total_power = 0 total_energy = 0 for inv in itertools.chain((inverter1, ), inverters_additional): state = inv.read() total_power += state.power total_energy += state.exported if hybrid == 1: if sunny_boy_smart_energy == 0: bat_comp = bat.SunnyBoyBat(0, SmaSunnyBoyBatSetup(), modbus.ModbusTcpClient_(ip1, 502)) else: bat_comp = bat_smart_energy.SunnyBoySmartEnergyBat( 0, SmaSunnyBoySmartEnergyBatSetup(), modbus.ModbusTcpClient_(ip1, 502)) bat_state = bat_comp.read() total_power -= bat_state.power total_energy = total_energy + bat_state.imported - bat_state.exported get_inverter_value_store(num).set( InverterState(exported=total_energy, power=total_power))
def __init__( self, device_address: str, component_config: Union[Dict, SmaWebboxInverterSetup]) -> None: self.__device_address = device_address self.component_config = dataclass_from_dict(SmaWebboxInverterSetup, component_config) self.__store = get_inverter_value_store(self.component_config.id) self.component_info = ComponentInfo.from_component_config( self.component_config)
def __init__(self, device_id: int, component_config: dict) -> None: self.__device_id = device_id self.component_config = component_config ip_address = component_config["configuration"]["ip_address"] self.__tcp_client = modbus.ModbusClient(ip_address, 502) self.__sim_count = simcount.SimCountFactory().get_sim_counter()() self.__simulation = {} self.__store = get_inverter_value_store(component_config["id"]) self.component_info = ComponentInfo.from_component_config(component_config)
def __init__(self, component_config: dict, domain: str) -> None: self.__get_power = create_request_function( domain, component_config["configuration"]["power_path"]) self.__get_counter = create_request_function( domain, component_config["configuration"]["counter_path"]) self.component_config = component_config self.__store = get_inverter_value_store(component_config["id"]) self.component_info = ComponentInfo.from_component_config( component_config)
def __init__(self, device_id: int, component_config: Union[Dict, LgInverterSetup]) -> None: self.__device_id = device_id self.component_config = dataclass_from_dict(LgInverterSetup, component_config) self.__sim_count = simcount.SimCountFactory().get_sim_counter()() self.__simulation = {} self.__store = get_inverter_value_store(self.component_config.id) self.component_info = ComponentInfo.from_component_config( self.component_config)
def create_component(component_config: dict): store = get_inverter_value_store(component_config["id"]) def persister(reading: CounterState): store.set( InverterState(counter=reading.exported, power=reading.power, currents=reading.currents)) return DiscovergyComponent(component_config, persister)
def __init__(self, device_id: int, component_config: Union[Dict, SolaxInverterSetup], tcp_client: modbus.ModbusTcpClient_, modbus_id: int) -> None: self.component_config = dataclass_from_dict(SolaxInverterSetup, component_config) self.__modbus_id = modbus_id self.__tcp_client = tcp_client self.__store = get_inverter_value_store(self.component_config.id) self.component_info = ComponentInfo.from_component_config( self.component_config)
def update(bezug_solarlog_ip: str): log.debug('Wechselrichter Solarlog IP: ' + bezug_solarlog_ip) data = {"801": {"170": None}} data = json.dumps(data) response = requests.post("http://" + bezug_solarlog_ip + '/getjp', data=data, timeout=3).json() pv_watt = float(response["801"]["170"]["101"]) pv_kwh = float(response["801"]["170"]["109"]) if pv_watt > 5: pv_watt = pv_watt * -1 log.debug('WR Leistung: ' + str(pv_watt)) log.debug('WR Energie: ' + str(pv_kwh)) get_inverter_value_store(1).set( InverterState(exported=pv_kwh, power=pv_watt))
def __init__(self, component_config: Union[Dict, SunwaysInverterSetup], ip_address: str, password: str) -> None: self.component_config = dataclass_from_dict(SunwaysInverterSetup, component_config) self.ip_address = ip_address self.password = password self.__store = get_inverter_value_store(self.component_config.id) self.component_info = ComponentInfo.from_component_config( self.component_config)
def __init__(self, device_id: int, component_config: Union[Dict, VictronInverterSetup], tcp_client: modbus.ModbusTcpClient_) -> None: self.__device_id = device_id self.component_config = dataclass_from_dict(VictronInverterSetup, component_config) self.__tcp_client = tcp_client self.__sim_count = simcount.SimCountFactory().get_sim_counter()() self.simulation = {} self.__store = get_inverter_value_store(self.component_config.id) self.component_info = ComponentInfo.from_component_config(self.component_config)
def read_legacy( component_type: str, ip_address: str, meter_id: int, variant: int, ip_address2: str = "none", num: Optional[int] = None) -> None: device_config = Fronius() device_config.configuration.ip_address = ip_address dev = Device(device_config) if component_type in COMPONENT_TYPE_TO_MODULE: component_config = COMPONENT_TYPE_TO_MODULE[component_type].component_descriptor.configuration_factory() if component_type == "bat": component_config.configuration.meter_id = meter_id elif component_type == "counter_sm": component_config.configuration.variant = variant component_config.configuration.meter_id = meter_id else: raise Exception( "illegal component type " + component_type + ". Allowed values: " + ','.join(COMPONENT_TYPE_TO_MODULE.keys()) ) component_config.id = num dev.add_component(component_config) log.debug('Fronius IP-Adresse: ' + ip_address) if component_type == "bat" or "counter" in component_type: dev.update() elif component_type == "inverter" and num: inverter1 = inverter.FroniusInverter(num, component_config, dev.device_config.configuration) with SingleComponentUpdateContext(inverter1.component_info): total_power = inverter1.read_power() if ip_address2 != "none": dev.device_config.configuration.ip_address = ip_address2 inverter2 = inverter.FroniusInverter(num, component_config, dev.device_config.configuration) total_power += inverter2.read_power() get_inverter_value_store(num).set(inverter1.fill_inverter_state(total_power)) else: raise Exception("illegal component num " + str(num) + ". Should be an int if it is an inverter.")
def update(wr_smartme_url: str, wr_smartme_user: str, wr_smartme_pass: str): log.debug('Wechselrichter smartme URL: ' + wr_smartme_url) log.debug('Wechselrichter smartme User: '******'Wechselrichter smartme Passwort: ' + wr_smartme_pass) # Daten einlesen response = requests.get(wr_smartme_url, auth=(wr_smartme_user, wr_smartme_pass), timeout=3).json() # Aktuelle Leistung (kW --> W) wattwr = response["ActivePower"] wattwr = round(wattwr * 1000) # Zählerstand Export (kWh --> Wh) pvkwh = response["CounterReadingExport"] pvkwh = round(pvkwh * 1000, 3) log.debug('WR Leistung: ' + str(wattwr)) log.debug('WR Energie: ' + str(pvkwh)) get_inverter_value_store(1).set(InverterState(counter=pvkwh, power=wattwr))
def __init__( self, device_id: int, device_address: str, device_variant: int, component_config: Union[Dict, SonnenbatterieInverterSetup]) -> None: self.__device_id = device_id self.__device_address = device_address self.__device_variant = device_variant self.component_config = dataclass_from_dict( SonnenbatterieInverterSetup, component_config) self.__sim_count = simcount.SimCountFactory().get_sim_counter()() self.simulation = {} self.__store = get_inverter_value_store(self.component_config.id) self.component_info = ComponentInfo.from_component_config( self.component_config)