-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
mqtt, powerdog, saxpower, siemens, lg (#1754)
* mqtt, powerdog, saxpower, siemens * lg
- Loading branch information
Showing
6 changed files
with
195 additions
and
426 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,146 +1,75 @@ | ||
#!/usr/bin/env python3 | ||
import json | ||
import logging | ||
import os | ||
from typing import Dict, Union, Optional, List | ||
from typing import Dict, Iterable, Union | ||
from requests import HTTPError, Session | ||
|
||
from dataclass_utils import dataclass_from_dict | ||
from helpermodules.cli import run_using_positional_cli_args | ||
from modules.common import req | ||
from modules.common.abstract_device import AbstractDevice, DeviceDescriptor | ||
from modules.common.component_context import MultiComponentUpdateContext | ||
from modules.devices.lg.config import LG, LgBatSetup, LgConfiguration, LgCounterSetup, LgInverterSetup | ||
from modules.devices.lg import bat, counter, inverter | ||
from modules.common.abstract_device import DeviceDescriptor | ||
from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater | ||
from modules.devices.lg.bat import LgBat | ||
from modules.devices.lg.config import LG, LgBatSetup, LgCounterSetup, LgInverterSetup | ||
from modules.devices.lg.counter import LgCounter | ||
from modules.devices.lg.inverter import LgInverter | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
lg_component_classes = Union[bat.LgBat, counter.LgCounter, inverter.LgInverter] | ||
|
||
|
||
class Device(AbstractDevice): | ||
"""Beispiel JSON-Objekte liegen im Ordner lgessv1/JSON-Beispiele.txt | ||
lg_ess_url: IP/URL des LG ESS V1.0 | ||
lg_ess_pass: Passwort, um sich in den LG ESS V1.0 einzuloggen. | ||
Das Passwort ist standardmäßig die Registrierungsnummer, | ||
die sich auf dem PCS (dem Hybridwechselrichter und | ||
Batteriemanagementsystem) befindet (Aufkleber!). Alter- | ||
nativ findet man die Registrierungsnummer in der App unter | ||
dem Menüpunkt "Systeminformationen". | ||
Mit der Registrierungsnummer kann man sich dann in der | ||
Rolle "installer" einloggen.""" | ||
COMPONENT_TYPE_TO_CLASS = { | ||
"bat": bat.LgBat, | ||
"counter": counter.LgCounter, | ||
"inverter": inverter.LgInverter | ||
} | ||
|
||
def __init__(self, device_config: Union[Dict, LG]) -> None: | ||
self.components = {} # type: Dict[str, lg_component_classes] | ||
self.session_key = " " | ||
try: | ||
self.device_config = dataclass_from_dict(LG, device_config) | ||
except Exception: | ||
log.exception("Fehler im Modul "+self.device_config.name) | ||
|
||
def add_component(self, component_config: Union[Dict, LgBatSetup, LgCounterSetup, LgInverterSetup]) -> None: | ||
if isinstance(component_config, Dict): | ||
component_type = component_config["type"] | ||
else: | ||
component_type = component_config.type | ||
component_config = dataclass_from_dict(COMPONENT_TYPE_TO_MODULE[ | ||
component_type].component_descriptor.configuration_factory, component_config) | ||
if component_type in self.COMPONENT_TYPE_TO_CLASS: | ||
self.components["component"+str(component_config.id)] = (self.COMPONENT_TYPE_TO_CLASS[component_type]( | ||
self.device_config.id, | ||
component_config)) | ||
else: | ||
raise Exception( | ||
"illegal component type " + component_type + ". Allowed values: " + | ||
','.join(self.COMPONENT_TYPE_TO_CLASS.keys()) | ||
) | ||
|
||
def update(self) -> None: | ||
log.debug("Start device reading " + str(self.components)) | ||
if self.components: | ||
with MultiComponentUpdateContext(self.components): | ||
session = req.get_http_session() | ||
try: | ||
response = self._request_data(session) | ||
except HTTPError: | ||
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_session_key(self, session: Session): | ||
try: | ||
headers = {'Content-Type': 'application/json', } | ||
data = json.dumps({"password": self.device_config.configuration.password}) | ||
response = session.put("https://"+self.device_config.configuration.ip_address+'/v1/login', headers=headers, | ||
data=data, verify=False, timeout=5).json() | ||
self.session_key = response["auth_key"] | ||
except (HTTPError, KeyError) as e: | ||
e.args += ("login failed! check password!", ) | ||
raise e | ||
|
||
def _request_data(self, session: Session) -> Dict: | ||
def _update_session_key(session: Session, ip_address: str, password: str) -> str: | ||
try: | ||
headers = {'Content-Type': 'application/json', } | ||
data = json.dumps({"auth_key": self.session_key}) | ||
return session.post("https://"+self.device_config.configuration.ip_address + "/v1/user/essinfo/home", | ||
headers=headers, | ||
data=data, | ||
verify=False, | ||
timeout=5).json() | ||
|
||
|
||
COMPONENT_TYPE_TO_MODULE = { | ||
"bat": bat, | ||
"counter": counter, | ||
"inverter": inverter | ||
} | ||
|
||
|
||
def read_legacy(component_type: str, ip: str, password: str, num: Optional[int] = None) -> None: | ||
dev = Device(LG(configuration=LgConfiguration(ip_address=ip, password=password))) | ||
|
||
if os.path.isfile("/var/www/html/openWB/ramdisk/ess_session_key"): | ||
with open("/var/www/html/openWB/ramdisk/ess_session_key", "r") as f: | ||
# erste Zeile ohne Zeilenumbruch lesen | ||
old_session_key = f.readline().strip() | ||
dev.session_key = old_session_key | ||
else: | ||
old_session_key = dev.session_key | ||
|
||
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()) | ||
) | ||
if component_type == "bat" or component_type == "counter": | ||
num = None | ||
component_config.id = num | ||
dev.add_component(component_config) | ||
log.debug('LG ESS V1.0 IP: ' + ip) | ||
log.debug('LG ESS V1.0 password: ' + password) | ||
dev.update() | ||
|
||
if dev.session_key != old_session_key: | ||
with open("/var/www/html/openWB/ramdisk/ess_session_key", "w") as f: | ||
f.write(str(dev.session_key)) | ||
|
||
|
||
def main(argv: List[str]): | ||
run_using_positional_cli_args(read_legacy, argv) | ||
data = json.dumps({"password": password}) | ||
response = session.put(f"https://{ip_address}/v1/login", headers=headers, | ||
data=data, verify=False, timeout=5).json() | ||
return response["auth_key"] | ||
except (HTTPError, KeyError) as e: | ||
e.args += ("login failed! check password!", ) | ||
raise e | ||
|
||
|
||
def _request_data(session: Session, session_key: str, ip_address: str) -> Dict: | ||
headers = {'Content-Type': 'application/json', } | ||
data = json.dumps({"auth_key": session_key}) | ||
return session.post(f"https://{ip_address}/v1/user/essinfo/home", | ||
headers=headers, | ||
data=data, | ||
verify=False, | ||
timeout=5).json() | ||
|
||
|
||
def create_device(device_config: LG): | ||
def create_bat_component(component_config: LgBatSetup): | ||
return LgBat(device_config.id, component_config) | ||
|
||
def create_counter_component(component_config: LgCounterSetup): | ||
return LgCounter(device_config.id, component_config) | ||
|
||
def create_inverter_component(component_config: LgInverterSetup): | ||
return LgInverter(device_config.id, component_config) | ||
|
||
def update_components(components: Iterable[Union[LgBat, LgCounter, LgInverter]]): | ||
nonlocal session_key | ||
session = req.get_http_session() | ||
try: | ||
response = _request_data(session, session_key, device_config.configuration.ip_address) | ||
except HTTPError: | ||
session_key = _update_session_key( | ||
session, device_config.configuration.ip_address, device_config.configuration.password) | ||
response = _request_data(session, session_key, device_config.configuration.ip_address) | ||
|
||
for component in components: | ||
component.update(response) | ||
|
||
session_key = " " | ||
return ConfigurableDevice( | ||
device_config=device_config, | ||
component_factory=ComponentFactoryByType( | ||
bat=create_bat_component, | ||
counter=create_counter_component, | ||
inverter=create_inverter_component, | ||
), | ||
component_updater=MultiComponentUpdater(update_components) | ||
) | ||
|
||
|
||
device_descriptor = DeviceDescriptor(configuration_factory=LG) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,37 @@ | ||
#!/usr/bin/env python3 | ||
from typing import Dict, Union | ||
from typing import Iterable, Union | ||
import logging | ||
|
||
from dataclass_utils import dataclass_from_dict | ||
from modules.common.abstract_device import AbstractDevice, DeviceDescriptor | ||
from modules.common.component_context import MultiComponentUpdateContext | ||
from modules.common.abstract_device import DeviceDescriptor | ||
from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater | ||
from modules.devices.mqtt import bat, counter, inverter | ||
from modules.devices.mqtt.config import Mqtt, MqttBatSetup, MqttCounterSetup, MqttInverterSetup | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
class Device(AbstractDevice): | ||
COMPONENT_TYPE_TO_CLASS = { | ||
"bat": bat.MqttBat, | ||
"counter": counter.MqttCounter, | ||
"inverter": inverter.MqttInverter | ||
} | ||
COMPONENT_TYPE_TO_MODULE = { | ||
"bat": bat, | ||
"counter": counter, | ||
"inverter": inverter | ||
} | ||
|
||
def __init__(self, device_config: Union[Dict, Mqtt]) -> None: | ||
self.components = {} | ||
try: | ||
self.device_config = dataclass_from_dict(Mqtt, device_config) | ||
except Exception: | ||
log.exception("Fehler im Modul " + self.device_config.name) | ||
|
||
def add_component(self, component_config: Union[Dict, MqttBatSetup, MqttCounterSetup, MqttInverterSetup]) -> None: | ||
if isinstance(component_config, Dict): | ||
component_type = component_config["type"] | ||
else: | ||
component_type = component_config.type | ||
component_config = dataclass_from_dict(self.COMPONENT_TYPE_TO_MODULE[ | ||
component_type].component_descriptor.configuration_factory, component_config) | ||
if component_type in self.COMPONENT_TYPE_TO_CLASS: | ||
self.components["component"+str(component_config.id) | ||
] = (self.COMPONENT_TYPE_TO_CLASS[component_type](component_config)) | ||
|
||
def update(self) -> None: | ||
if self.components: | ||
with MultiComponentUpdateContext(self.components): | ||
log.debug("MQTT-Module müssen nicht ausgelesen werden.") | ||
else: | ||
log.warning( | ||
self.device_config.name + | ||
": Es konnten keine Werte gelesen werden, da noch keine Komponenten konfiguriert wurden." | ||
) | ||
def create_device(device_config: Mqtt): | ||
def create_bat_component(component_config: MqttBatSetup): | ||
return bat.MqttBat(component_config) | ||
|
||
def create_counter_component(component_config: MqttCounterSetup): | ||
return counter.MqttCounter(component_config) | ||
|
||
def create_inverter_component(component_config: MqttInverterSetup): | ||
return inverter.MqttInverter(component_config) | ||
|
||
def update_components(components: Iterable[Union[bat.MqttBat, counter.MqttCounter, inverter.MqttInverter]]): | ||
log.debug("MQTT-Module müssen nicht ausgelesen werden.") | ||
|
||
return ConfigurableDevice( | ||
device_config=device_config, | ||
component_factory=ComponentFactoryByType( | ||
bat=create_bat_component, | ||
counter=create_counter_component, | ||
inverter=create_inverter_component, | ||
), | ||
component_updater=MultiComponentUpdater(update_components) | ||
) | ||
|
||
|
||
device_descriptor = DeviceDescriptor(configuration_factory=Mqtt) |
Oops, something went wrong.