def update(self, response) -> None: log.MainLogger().debug("Komponente "+self.component_config["name"]+" auslesen.") config = self.component_config["configuration"] power = float(jq.compile(config["jq_power"]).input(response).first()) if power >= 0: power = power * -1 if config["jq_counter"] == "": topic_str = "openWB/set/system/device/" + \ str(self.__device_id)+"/component/" + \ str(self.component_config["id"])+"/" _, counter = self.__sim_count.sim_count( power, topic=topic_str, data=self.simulation, prefix="pv") inverter_state = InverterState( power=power, counter=counter, currents=[0, 0, 0] ) else: counter = jq.compile(config["jq_counter"]).input(response).first() inverter_state = InverterState( power=power, counter=counter ) self.__store.set(inverter_state)
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 read_state(self): def read_scaled_int16(address: int, count: int): return scale_registers( self.__tcp_client.read_holding_registers( address, [ModbusDataType.INT_16] * (count + 1), unit=self.component_config.configuration.modbus_id)) def read_scaled_uint16(address: int, count: int): return scale_registers( self.__tcp_client.read_holding_registers( address, [ModbusDataType.UINT_16] * (count) + [ModbusDataType.INT_16], unit=self.component_config.configuration.modbus_id)) def read_scaled_uint32(address: int, count: int): return scale_registers( self.__tcp_client.read_holding_registers( address, [ModbusDataType.UINT_32] * (count) + [ModbusDataType.INT_16], unit=self.component_config.configuration.modbus_id)) # 40083 = AC Power value (Watt) # 40084 = AC Power scale factor power = read_scaled_int16(40083, 1)[0] * -1 # 40093 = AC Lifetime Energy production (Watt hours) # 40095 = AC Lifetime scale factor exported = read_scaled_uint32(40093, 1)[0] # 40072/40073/40074 = AC Phase A/B/C Current value (Amps) # 40075 = AC Current scale factor currents = read_scaled_uint16(40072, 3) return InverterState(power=power, exported=exported, currents=currents)
def update(self) -> None: """ liest die Werte des Moduls aus. """ with self.__tcp_client: powers, power = self.__client.get_power() version = self.component_config["configuration"]["version"] if version == 1: power = sum(powers) if power > 10: power = power*-1 currents = self.__client.get_currents() if isinstance(self.__client, Lovato): topic_str = "openWB/set/system/device/" + \ str(self.__device_id)+"/component/" + \ str(self.component_config["id"])+"/" _, counter = self.__sim_count.sim_count( power, topic=topic_str, data=self.__simulation, prefix="pv") else: counter = self.__client.get_counter() log.MainLogger().debug("PV-Kit Leistung[W]: "+str(power)) inverter_state = InverterState( power=power, counter=counter, currents=currents ) self.__store.set(inverter_state)
def update(self, client: PowerwallHttpClient, aggregate) -> None: pv_watt = aggregate["solar"]["instant_power"] if pv_watt > 5: pv_watt = pv_watt * -1 self.__store.set( InverterState(exported=aggregate["solar"]["energy_exported"], power=pv_watt))
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(self) -> None: log.MainLogger().debug("Komponente "+self.component_config["name"]+" auslesen.") vc_count = self.component_config["configuration"]["vc_count"] vc_type = self.component_config["configuration"]["vc_type"] if vc_type == 'VS': mb_unit = 40 mb_register = 20 # MB:20; ID: 15010; PV power kW elif vc_type == 'VT': mb_unit = 20 mb_register = 8 # MB:8; ID: 11004; Power of the PV generator kW else: raise FaultState.error("Unbekannter VC-Typ: "+str(vc_type)) power = 0 for i in range(1, vc_count+1): mb_unit_dev = mb_unit+i power += self.__tcp_client.read_input_registers(mb_register, ModbusDataType.FLOAT_32, unit=mb_unit_dev) power = power * -1000 if vc_type == 'VS': mb_register = 46 # MB:46; ID: 15023; Desc: Total PV produced energy MWh elif vc_type == 'VT': mb_register = 18 # MB:18; ID: 11009; Desc: Total produced energy MWh counter = 0 for i in range(1, vc_count + 1): mb_unit_dev = mb_unit + i counter += self.__tcp_client.read_input_registers(mb_register, ModbusDataType.FLOAT_32, unit=mb_unit_dev) counter = counter * 1000000 inverter_state = InverterState( power=power, counter=counter ) self.__store.set(inverter_state)
def update(self) -> float: log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") # Rückgabewert ist die aktuelle Wirkleistung in [W]. params = (('Scope', 'System'), ) response = req.get_http_session().get( 'http://' + self.device_config["ip_address"] + '/solar_api/v1/GetPowerFlowRealtimeData.fcgi', params=params, timeout=3) try: power = float(response.json()["Body"]["Data"]["Site"]["P_PV"]) except TypeError: # Ohne PV Produktion liefert der WR 'null', ersetze durch Zahl 0 power = 0 power2 = self.__get_wr2() power += power2 power1 = power power *= -1 topic = "openWB/set/system/device/" + str( self.__device_id) + "/component/" + str( self.component_config["id"]) + "/" _, counter = self.__sim_count.sim_count(power, topic=topic, data=self.__simulation, prefix="pv") inverter_state = InverterState(power=power, counter=counter) self.__store.set(inverter_state) # Rückgabe der Leistung des ersten WR ohne Vorzeichenumkehr return power1
def update(self) -> None: modbus_id = self.component_config.configuration.modbus_id with self.__tcp_client: if self.component_config.configuration.mppt: try: power = self.__tcp_client.read_holding_registers(789, ModbusDataType.UINT_16, unit=modbus_id) / -10 except Exception as e: if "GatewayPathUnavailable" in str(e): power = 0 log.debug(self.component_config.name + ": Reg 789 konnte nicht gelesen werden, Power auf 0 gesetzt.") else: raise else: # Adresse 808-810 ac output connected pv # Adresse 811-813 ac input connected pv # Adresse 850 mppt Leistung power_temp1 = self.__tcp_client.read_holding_registers(808, [ModbusDataType.UINT_16]*6, unit=100) power_temp2 = self.__tcp_client.read_holding_registers(850, ModbusDataType.UINT_16, unit=100) power = (sum(power_temp1)+power_temp2) * -1 topic_str = "openWB/set/system/device/" + str(self.__device_id)+"/component/" + \ str(self.component_config.id)+"/" _, exported = self.__sim_count.sim_count(power, topic=topic_str, data=self.simulation, prefix="pv%s" % ("" if self.component_config.id == 1 else "2")) inverter_state = InverterState( power=power, exported=exported ) self.__store.set(inverter_state)
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 update(self) -> None: vc_count = self.component_config.configuration.vc_count vc_type = self.component_config.configuration.vc_type with self.__tcp_client: if vc_type == 'VS': mb_unit = 40 mb_register = 20 # MB:20; ID: 15010; PV power kW elif vc_type == 'VT': mb_unit = 20 mb_register = 8 # MB:8; ID: 11004; Power of the PV generator kW else: raise FaultState.error("Unbekannter VC-Typ: " + str(vc_type)) power = 0 for i in range(1, vc_count + 1): mb_unit_dev = mb_unit + i power += self.__tcp_client.read_input_registers( mb_register, ModbusDataType.FLOAT_32, unit=mb_unit_dev) power = power * -1000 if vc_type == 'VS': mb_register = 46 # MB:46; ID: 15023; Desc: Total PV produced energy MWh elif vc_type == 'VT': mb_register = 18 # MB:18; ID: 11009; Desc: Total produced energy MWh exported = 0 for i in range(1, vc_count + 1): mb_unit_dev = mb_unit + i exported += self.__tcp_client.read_input_registers( mb_register, ModbusDataType.FLOAT_32, unit=mb_unit_dev) exported = exported * 1000000 inverter_state = InverterState(power=power, exported=exported) self.__store.set(inverter_state)
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 parse_kostal_piko_var2_html(html: str): result = re.search(r"aktuell</td>\s*<td[^>]*>\s*(\d+).*Gesamtenergie</td>\s*<td[^>]*>\s*(\d+)", html, re.DOTALL) if result is None: raise Exception("Given HTML does not match the expected regular expression. Ignoring.") return InverterState( counter=int(result.group(2)) * 1000, power=-int(result.group(1)) )
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 update(self) -> None: log.MainLogger().debug("Komponente "+self.component_config["name"]+" auslesen.") inverter_state = InverterState( power=self.__get_power(), counter=self.__get_counter() ) self.__store.set(inverter_state)
def update(self) -> None: log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") inverter_state = InverterState( # for compatibility: in 1.x power URL values are positive! power=(-self.__get_power() if compatibility.is_ramdisk_in_use() else self.__get_power()), counter=self.__get_counter()) self.__store.set(inverter_state)
def read_inverter_state(self) -> InverterState: log.debug("Komponente "+self.component_config["name"]+" auslesen.") data = {'RPC': '{"version": "1.0","proc": "GetPlantOverview","id": "1","format": "JSON"}'} response = req.get_http_session().post( 'http://' + self.__device_address + '/rpc', data=data, timeout=3).json() return InverterState( counter=float(response["result"]["overview"][2]["value"]) * 1000, power=-int(response["result"]["overview"][0]["value"]) )
def fill_inverter_state(self, power): topic_str = "openWB/set/system/device/" + str(self.__device_id) + \ "/component/" + str(self.component_config.id)+"/" _, exported = self.__sim_count.sim_count( power, topic=topic_str, data=self.simulation, prefix="pv%s" % ("" if self.component_config.id == 1 else "2")) return InverterState(power=power, exported=exported)
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(self, bat: bool) -> float: log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") gen24 = self.component_config["configuration"]["gen24"] # Rückgabewert ist die aktuelle Wirkleistung in [W]. params = (('Scope', 'System'), ) response = req.get_http_session().get( 'http://' + self.device_config["ip_address"] + '/solar_api/v1/GetPowerFlowRealtimeData.fcgi', params=params, timeout=3) try: power = float(response.json()["Body"]["Data"]["Site"]["P_PV"]) except TypeError: # Ohne PV Produktion liefert der WR 'null', ersetze durch Zahl 0 power = 0 power2, counter2 = self.__get_wr2() power += power2 power1 = power power *= -1 topic = "openWB/set/system/device/" + str( self.__device_id) + "/component/" + str( self.component_config["id"]) + "/" if gen24: _, counter = self.__sim_count.sim_count(power, topic=topic, data=self.__simulation, prefix="pv") else: counter = float(response.json()["Body"]["Data"]["Site"]["E_Total"]) daily_yield = float( response.json()["Body"]["Data"]["Site"]["E_Day"]) counter, counter_start, counter_offset = self.__calculate_offset( counter, daily_yield) counter = counter + counter2 if counter > 0: counter = self.__add_and_save_offset(daily_yield, counter, counter_start, counter_offset) if bat is True: _, counter = self.__sim_count.sim_count(power, topic=topic, data=self.__simulation, prefix="pv") inverter_state = InverterState(power=power, counter=counter, currents=[0, 0, 0]) self.__store.set(inverter_state) # Rückgabe der Leistung des ersten WR ohne Vorzeichenumkehr return power1
def update(self, response: Dict) -> None: power = float(response["statistics"]["pcs_pv_total_power"]) * -1 topic_str = "openWB/set/system/device/" + \ str(self.__device_id)+"/component/" + \ str(self.component_config.id)+"/" _, exported = self.__sim_count.sim_count(power, topic=topic_str, data=self.__simulation, prefix="pv") inverter_state = InverterState(exported=exported, power=power) self.__store.set(inverter_state)
def __update_variant_2(self) -> InverterState: # Auslesen einer Sonnenbatterie Eco 6 über die integrierte REST-API des Batteriesystems pv_power = -int(self.__read_variant_2_element("M03")) log.MainLogger().debug('Speicher PV Leistung: ' + str(pv_power)) topic_str = "openWB/set/system/device/" + str( self.__device_id) + "/component/" + str( self.component_config["id"]) + "/" _, exported = self.__sim_count.sim_count(pv_power, topic=topic_str, data=self.__simulation, prefix="pv") return InverterState(counter=exported, power=pv_power)
def update(self) -> None: with self.__tcp_client: power = sum([ self.__tcp_client.read_holding_registers( reg, ModbusDataType.UINT_32, unit=self.__modbus_id) for reg in [35105, 35109, 35113, 35117] ]) * -1 exported = self.__tcp_client.read_holding_registers( 35191, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 inverter_state = InverterState(power=power, exported=exported) self.__store.set(inverter_state)
def update(self) -> None: log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") with self.__tcp_client: power_temp = self.__tcp_client.read_input_registers( 10, [ModbusDataType.UINT_16] * 2) power = sum(power_temp) * -1 counter = self.__tcp_client.read_input_registers( 82, ModbusDataType.UINT_32, wordorder=Endian.Little) * 100 inverter_state = InverterState(power=power, counter=counter) self.__store.set(inverter_state)
def update(self) -> None: with self.__tcp_client: power_temp = self.__tcp_client.read_input_registers( 10, [ModbusDataType.UINT_16] * 2, unit=self.__modbus_id) power = sum(power_temp) * -1 exported = self.__tcp_client.read_input_registers( 82, ModbusDataType.UINT_32, wordorder=Endian.Little, unit=self.__modbus_id) * 100 inverter_state = InverterState(power=power, exported=exported) self.__store.set(inverter_state)
def read(self) -> InverterState: data = { 'RPC': '{"version": "1.0","proc": "GetPlantOverview","id": "1","format": "JSON"}' } response = req.get_http_session().post('http://' + self.__device_address + '/rpc', data=data, timeout=3).json() return InverterState( exported=float(response["result"]["overview"][2]["value"]) * 1000, power=-int(response["result"]["overview"][0]["value"]))
def update(self, resp: Dict) -> None: power = resp["1634"]["0"] * -1 topic_str = "openWB/set/system/device/" + str(self.__device_id) + \ "/component/" + str(self.component_config.id)+"/" _, exported = self.__sim_count.sim_count( power, topic=topic_str, data=self.simulation, prefix="pv%s" % ("" if self.component_config.id == 1 else "2")) inverter_state = InverterState(power=power, exported=exported) self.__store.set(inverter_state)
def read_state(self) -> InverterState: unit = self.component_config.configuration.modbus_id # 40380 = "Meter 2/Total Real Power (sum of active phases)" (Watt) power = self.__tcp_client.read_holding_registers(40380, ModbusDataType.INT_16, unit=unit) topic_str = "openWB/set/system/device/" + str(self.__device_id) + \ "/component/" + str(self.component_config.id)+"/" _, exported = self.__sim_count.sim_count(power, topic=topic_str, data=self.simulation, prefix="pv") return InverterState(exported=exported, power=power)
def update(self, unit_id: int) -> None: reg_p = self.__version_factory() power = self.__get_power(unit_id, reg_p) topic_str = "openWB/set/system/device/" + \ str(self.__device_id)+"/component/" + \ str(self.component_config.id)+"/" _, exported = self.__sim_count.sim_count( power, topic=topic_str, data=self.simulation, prefix="pv%s" % ("" if self.component_config.id == 1 else "2")) inverter_state = InverterState(power=power, exported=exported) self.__store.set(inverter_state)
def __update_variant_1(self) -> InverterState: # Auslesen einer Sonnenbatterie 8 oder 10 über die integrierte JSON-API v1 des Batteriesystems ''' example data: { "Apparent_output": 225, "BackupBuffer": "0", "BatteryCharging": false, "BatteryDischarging": false, "Consumption_Avg": 2114, "Consumption_W": 2101, "Fac": 49.97200393676758, "FlowConsumptionBattery": false, "FlowConsumptionGrid": true, "FlowConsumptionProduction": false, "FlowGridBattery": false, "FlowProductionBattery": false, "FlowProductionGrid": false, "GridFeedIn_W": -2106, "IsSystemInstalled": 1, "OperatingMode": "2", "Pac_total_W": -5, "Production_W": 0, "RSOC": 6, "RemainingCapacity_Wh": 2377, "Sac1": 75, "Sac2": 75, "Sac3": 75, "SystemStatus": "OnGrid", "Timestamp": "2021-12-13 07:54:48", "USOC": 0, "Uac": 231, "Ubat": 48, "dischargeNotAllowed": true, "generator_autostart": false, "NVM_REINIT_STATUS": 0 } ''' inverter_state = self.__read_variant_1() pv_power = -inverter_state["Production_W"] log.debug('Speicher PV Leistung: ' + str(pv_power)) topic_str = "openWB/set/system/device/" + str( self.__device_id) + "/component/" + str( self.component_config.id) + "/" _, exported = self.__sim_count.sim_count( pv_power, topic=topic_str, data=self.simulation, prefix="pv%s" % ("" if self.component_config.id == 1 else "2")) return InverterState(exported=exported, power=pv_power)