def __read_variant_2_element(self, element: str) -> str: response = req.get_http_session().get( 'http://' + self.__device_address + ':7979/rest/devices/battery/' + element, timeout=5) response.encoding = 'utf-8' return response.text.strip(" \n\r")
def update(self, bat: bool) -> Tuple[CounterState, MeterLocation]: variant = self.component_config["configuration"]["variant"] log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") session = req.get_http_session() if variant == 0 or variant == 1: counter_state, meter_location = self.__update_variant_0_1(session) elif variant == 2: counter_state, meter_location = self.__update_variant_2(session) else: raise FaultState.error("Unbekannte Variante: " + str(variant)) if meter_location == MeterLocation.load: response = session.get( 'http://' + self.device_config["ip_address"] + '/solar_api/v1/GetPowerFlowRealtimeData.fcgi', params=(('Scope', 'System'), ), timeout=5) counter_state.power = float( response.json()["Body"]["Data"]["Site"]["P_Grid"]) topic_str = "openWB/set/system/device/{}/component/{}/".format( self.__device_id, self.component_config["id"]) # Beim Energiebezug ist nicht klar, welcher Anteil aus dem Netz bezogen wurde, und was aus # dem Wechselrichter kam. # Beim Energieexport ist nicht klar, wie hoch der Eigenverbrauch während der Produktion war. counter_state.imported, counter_state.exported = self.__sim_count.sim_count( counter_state.power, topic=topic_str, data=self.simulation, prefix="bezug") return counter_state, meter_location
def powerwall_update(address: str, email: str, password: str, update_function: UpdateFunction): log.debug("Beginning update") cookies = None try: cookies = json.loads(COOKIE_FILE.read_text()) except FileNotFoundError: log.debug("Cookie-File <%s> does not exist. It will be created.", COOKIE_FILE) except JSONDecodeError as e: log.warning("Could not parse Cookie-File <%s>. It will be re-created.", COOKIE_FILE, exc_info=e) session = get_http_session() if cookies is None: _authenticate_and_update(session, address, email, password, update_function) return try: update_function(PowerwallHttpClient(address, session, cookies)) return except HTTPError as e: if e.response.status_code != 401 and e.response.status_code != 403: raise e log.warning( "Login to powerwall with existing cookie failed. Will retry with new cookie..." ) _authenticate_and_update(session, address, email, password, update_function) log.debug("Update completed successfully")
def __get_wr2(self) -> Tuple[float, float]: ip_address2 = self.component_config["configuration"]["ip_address2"] counter2 = 0 if ip_address2 != "none": try: params = (('Scope', 'System'), ) response = req.get_http_session().get( 'http://' + ip_address2 + '/solar_api/v1/GetPowerFlowRealtimeData.fcgi', params=params, timeout=3) response.raise_for_status() try: power2 = float( response.json()["Body"]["Data"]["Site"]["P_PV"]) except TypeError: # Ohne PV Produktion liefert der WR 'null', ersetze durch Zahl 0 power2 = 0 if not self.component_config["configuration"]["gen24"]: counter2 = float( response.json()["Body"]["Data"]["Site"]["E_Total"]) except (requests.ConnectTimeout, requests.ConnectionError): # Nachtmodus: WR ist ausgeschaltet power2 = 0 else: power2 = 0 return power2, counter2
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 request_value(url: str) -> Optional[float]: if "none" == url: return None else: response = req.get_http_session().get(url, timeout=5) response.encoding = 'utf-8' log.debug("Antwort auf " + str(url) + " " + str(response.text)) return float(response.text.replace("\n", ""))
def get_values(self) -> Tuple[float, float]: '''BYD Speicher bieten zwei HTML-Seiten, auf denen Informationen abgegriffen werden können: /asp/Home.asp und /asp/RunData.asp. Aktuell (2022-03) ist die Leistungsangabe (Power) auf der RunData.asp auf ganze kW gerundet und somit für openWB nicht brauchbar. ''' resp = req.get_http_session().get( 'http://' + self.__device_config.configuration.ip_address + '/asp/Home.asp', auth=(self.__device_config.configuration.username, self.__device_config.configuration.password)) return BydParser.parse(resp.text)
def get_values(self) -> Tuple[float, float]: params = (('dxsEntries', ['67109120', '251658753']),) resp = req.get_http_session().get('http://'+self.ip_address+'/api/dxs.json', params=params, timeout=3).json() power = float(resp["dxsEntries"][0]["value"]) if power > 5: power = power*-1 exported = float(resp["dxsEntries"][1]["value"]) * 1000 return power, exported
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 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) -> None: log.MainLogger().debug("Start device reading " + str(self._components)) if self._components: with MultiComponentUpdateContext(self._components): response = req.get_http_session().get(self.device_config["configuration"]["ip_address"], timeout=5) for component in self._components: self._components[component].update(response.json()) else: log.MainLogger().warning( self.device_config["name"] + ": Es konnten keine Werte gelesen werden, da noch keine Komponenten konfiguriert wurden." )
def get_values(self) -> Tuple[float, List[float]]: params = (('dxsEntries', ['83887106', '83887362', '83887618']), ) resp = req.get_http_session().get('http://' + self.ip_address + '/api/dxs.json', params=params, timeout=3).json()["dxsEntries"] powers = [ float(resp[0]["value"]), float(resp[1]["value"]), float(resp[2]["value"]) ] home_consumption = sum(powers) return home_consumption, powers
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) -> None: log.debug("Start device reading " + str(self.components)) if self.components: with MultiComponentUpdateContext(self.components): resp_json = req.get_http_session().get( 'http://' + self.device_config.configuration.ip_address + '/api.php?get=currentstate', timeout=5).json() for component in self.components: self.components[component].update(resp_json) else: log.warning( self.device_config.name + ": Es konnten keine Werte gelesen werden, da noch keine Komponenten konfiguriert wurden." )
def update(bydhvip: str, bydhvuser: str, bydhvpass: str): '''BYD Speicher bieten zwei HTML-Seiten, auf denen Informationen abgegriffen werden können: /asp/Home.asp und /asp/RunData.asp. Aktuell (2022-03) ist die Leistungsangabe (Power) auf der RunData.asp auf ganze kW gerundet und somit für openWB nicht brauchbar. ''' log.debug("Beginning update") bat_info = ComponentInfo(None, "BYD", "bat") with SingleComponentUpdateContext(bat_info): # response = req.get_http_session().get('http://' + bydhvip + '/asp/RunData.asp', auth=(bydhvuser, bydhvpass)) response = req.get_http_session().get('http://' + bydhvip + '/asp/Home.asp', auth=(bydhvuser, bydhvpass)) get_bat_value_store(1).set(BydParser.parse(response.text)) log.debug("Update completed successfully")
def fetch_soc(akey: str, token: str) -> Union[int, float]: response = req.get_http_session().get("https://app.evnotify.de/soc", params={ "akey": akey, "token": token }) try: soc_display = response.json()[_SOC_PROPERTY] if not isinstance(soc_display, (int, float)): raise Exception("Number expected, got <{}>, type={}".format( soc_display, type(soc_display))) return soc_display except Exception as e: raise Exception( "Expected object with numeric property <{}>. Got: <{}>".format( _SOC_PROPERTY, response.text)) from e
def update(self) -> None: params = ( ('CAN', '1'), ('HASH', '00200403'), ('TYPE', '1'), ) response = req.get_http_session().get( "http://" + self.ip_address + "/data/ajax.txt", params=params, auth=HTTPDigestAuth("customer", self.password)) values = response.text.split(';') inverter_state = InverterState(power=float(values[1].split(' ')[0]) * -1, exported=float(values[16]) * 1000) self.__store.set(inverter_state)
def update(self) -> None: log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") params = ( ('CAN', '1'), ('HASH', '00200403'), ('TYPE', '1'), ) response = req.get_http_session().get( "http://" + self.ip_address + "/data/ajax.txt", params=params, auth=HTTPDigestAuth("customer", self.password)) values = response.text.split(';') inverter_state = InverterState(power=float(values[1].split(' ')[0]) * -1, counter=float(values[16]) * 1000) self.__store.set(inverter_state)
def update(self) -> None: session = req.get_http_session() variant = self.component_config.configuration.variant if variant == 0 or variant == 1: counter_state = self.__update_variant_0_1(session) elif variant == 2: counter_state = self.__update_variant_2(session) else: raise FaultState.error("Unbekannte Variante: " + str(variant)) topic_str = "openWB/set/system/device/{}/component/{}/".format( self.__device_id, self.component_config.id) counter_state.imported, counter_state.exported = self.__sim_count.sim_count( counter_state.power, topic=topic_str, data=self.simulation, prefix="bezug") self.__store.set(counter_state)
def update(self) -> None: session = req.get_http_session() response = session.get('http://' + self.device_config.ip_address + '/solar_api/v1/GetPowerFlowRealtimeData.fcgi', timeout=5) # Wenn WR aus bzw. im Standby (keine Antwort), ersetze leeren Wert durch eine 0. power = float(response.json()["Body"]["Data"]["Site"]["P_Grid"]) or 0 topic_str = "openWB/set/system/device/{}/component/{}/".format( self.__device_id, self.component_config.id) imported, exported = self.__sim_count.sim_count(power, topic=topic_str, data=self.simulation, prefix="bezug") counter_state = CounterState(imported=imported, exported=exported, power=power) self.__store.set(counter_state)
def read_power(self) -> float: # Rückgabewert ist die aktuelle Wirkleistung in [W]. try: 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"]) * -1 except TypeError: # Ohne PV Produktion liefert der WR 'null', ersetze durch Zahl 0 power = 0 except (requests.ConnectTimeout, requests.ConnectionError): # Nachtmodus: WR ist ausgeschaltet power = 0 return power
def update(self) -> None: log.debug("Start device reading " + str(self.components)) if self.components: with MultiComponentUpdateContext(self.components): session = req.get_http_session() response = self._request_data(session) # missing "auth" in response indicates success if (response.get('auth') == "auth_key failed" or response.get('auth') == "auth timeout" or response.get('auth') == "not done"): self._update_session_key(session) response = self._request_data(session) for component in self.components: self.components[component].update(response) else: log.warning( self.device_config.name + ": Es konnten keine Werte gelesen werden, da noch keine Komponenten konfiguriert wurden." )
def update(self) -> BatState: log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") meter_id = str(self.device_config["meter_id"]) resp_json = req.get_http_session().get( 'http://' + self.device_config["ip_address"] + '/solar_api/v1/GetPowerFlowRealtimeData.fcgi', params=(('Scope', 'System'), ), timeout=5).json() try: power = int(resp_json["Body"]["Data"]["Site"]["P_Akku"]) * -1 except TypeError: # Wenn WR aus bzw. im Standby (keine Antwort), ersetze leeren Wert durch eine 0. power = 0 try: resp_json_id = dict(resp_json["Body"]["Data"]) if "Inverters" in resp_json_id: soc = float(resp_json_id["Inverters"]["1"]["SOC"]) else: soc = float( resp_json_id.get(meter_id)["Controller"] ["StateOfCharge_Relative"]) except TypeError: # Wenn WR aus bzw. im Standby (keine Antwort), ersetze leeren Wert durch eine 0. soc = 0 topic_str = "openWB/set/system/device/" + str( self.__device_id) + "/component/" + str( self.component_config["id"]) + "/" imported, exported = self.__sim_count.sim_count(power, topic=topic_str, data=self.__simulation, prefix="speicher") bat_state = BatState(power=power, soc=soc, imported=imported, exported=exported) return bat_state
def update(self) -> Tuple[CounterState, MeterLocation]: variant = self.component_config["configuration"]["variant"] log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") session = req.get_http_session() if variant == 0 or variant == 1: counter_state, meter_location = self.__update_variant_0_1(session) elif variant == 2: counter_state, meter_location = self.__update_variant_2(session) else: raise FaultState.error("Unbekannte Variante: " + str(variant)) topic_str = "openWB/set/system/device/{}/component/{}/".format( self.__device_id, self.component_config["id"]) counter_state.imported, counter_state.exported = self.__sim_count.sim_count( counter_state.power, topic=topic_str, data=self.simulation, prefix="bezug") return counter_state, meter_location
def update(self) -> None: log.debug("Beginning update") cookies = None address = self.device_config.configuration.ip_address email = self.device_config.configuration.email password = self.device_config.configuration.password with MultiComponentUpdateContext(self.components): try: cookies = json.loads(COOKIE_FILE.read_text()) except FileNotFoundError: log.debug( "Cookie-File <%s> does not exist. It will be created.", COOKIE_FILE) except JSONDecodeError: log.warning("Could not parse Cookie-File " + str(COOKIE_FILE) + ". It will be re-created.", exc_info=True) session = get_http_session() if cookies is None: self.__authenticate_and_update(session, address, email, password, self.__update_components) return try: self.__update_components( PowerwallHttpClient(address, session, cookies)) return except HTTPError as e: if e.response.status_code != 401 and e.response.status_code != 403: raise e log.warning( "Login to powerwall with existing cookie failed. Will retry with new cookie..." ) self.__authenticate_and_update(session, address, email, password, self.__update_components) log.debug("Update completed successfully")
def update(self, bat: bool) -> CounterState: log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") session = req.get_http_session() response = session.get('http://' + self.device_config["ip_address"] + '/solar_api/v1/GetPowerFlowRealtimeData.fcgi', timeout=5) # Wenn WR aus bzw. im Standby (keine Antwort), ersetze leeren Wert durch eine 0. power = float(response.json()["Body"]["Data"]["Site"]["P_Grid"]) or 0 # Summe der vom Netz bezogene Energie total in Wh # nur für Smartmeter im Einspeisepunkt! # bei Smartmeter im Verbrauchszweig entspricht das dem Gesamtverbrauch response = session.get('http://' + self.device_config["ip_address"] + '/solar_api/v1/GetMeterRealtimeData.cgi', params=(('Scope', 'System'), ), timeout=5) meter_id = str(self.device_config["meter_id"]) response_json_id = dict(response.json()["Body"]["Data"]).get(meter_id) if "EnergyReal_WAC_Minus_Absolute" in response_json_id and \ "EnergyReal_WAC_Plus_Absolute" in response_json_id: imported = float(response_json_id["EnergyReal_WAC_Minus_Absolute"]) exported = float(response_json_id["EnergyReal_WAC_Plus_Absolute"]) else: topic_str = "openWB/set/system/device/{}/component/{}/".format( self.__device_id, self.component_config["id"]) imported, exported = self.__sim_count.sim_count( power, topic=topic_str, data=self.simulation, prefix="bezug") counter_state = CounterState(imported=imported, exported=exported, power=power) log.MainLogger().debug("Fronius SM Leistung[W]: " + str(counter_state.power)) return counter_state
def update(self) -> CounterState: log.MainLogger().debug("Komponente " + self.component_config["name"] + " auslesen.") session = req.get_http_session() response = session.get('http://' + self.device_config["ip_address"] + '/solar_api/v1/GetPowerFlowRealtimeData.fcgi', timeout=5) # Wenn WR aus bzw. im Standby (keine Antwort), ersetze leeren Wert durch eine 0. power = float(response.json()["Body"]["Data"]["Site"]["P_Grid"]) or 0 topic_str = "openWB/set/system/device/{}/component/{}/".format( self.__device_id, self.component_config["id"]) imported, exported = self.__sim_count.sim_count(power, topic=topic_str, data=self.simulation, prefix="bezug") counter_state = CounterState(imported=imported, exported=exported, power=power) log.MainLogger().debug("Fronius S0 Leistung[W]: " + str(counter_state.power)) return counter_state
def __read_variant_1(self): return req.get_http_session().get("http://" + self.__device_address + "/api/v1/status", timeout=5).json()
def __read_variant_0(self): return req.get_http_session().get('http://' + self.__device_address + ':7979/rest/devices/battery', timeout=5).json()
def __init__(self, device_config: dict) -> None: settings = device_config["configuration"] self.__session = get_http_session() self.__session.auth = (settings["user"], settings["password"]) self._components = [] # type: List[DiscovergyComponent]