diff --git a/packages/modules/common/component_type.py b/packages/modules/common/component_type.py index ac4d6bc15..05eae75e8 100644 --- a/packages/modules/common/component_type.py +++ b/packages/modules/common/component_type.py @@ -18,7 +18,7 @@ def special_to_general_type_mapping(component_type: str) -> ComponentType: elif "cp" in component_type: return ComponentType.CHARGEPOINT else: - raise TypeError(f"Typ {component_type} konnte keinem bekannten Komponenten-Typ zugeordnet werden.") + raise TypeError("Typ "+component_type+" konnte keinem bekannten Komponenten-Typ zugeordnet werden.") def type_to_topic_mapping(component_type: str) -> str: diff --git a/packages/modules/fronius/device.py b/packages/modules/fronius/device.py index cbaed5f38..8d37d6c76 100644 --- a/packages/modules/fronius/device.py +++ b/packages/modules/fronius/device.py @@ -109,12 +109,11 @@ def read_legacy( dev.update() elif component_type == "inverter" and num: inverter1 = inverter.FroniusInverter(num, component_config, dev.device_config.configuration) - if ip_address2 != "none": - device_config["configuration"]["ip_address"] = ip_address2 - inverter2 = 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: diff --git a/packages/modules/http/api.py b/packages/modules/http/api.py index ca54032fd..eec748d1b 100644 --- a/packages/modules/http/api.py +++ b/packages/modules/http/api.py @@ -17,8 +17,8 @@ def request_value(url: str) -> Optional[float]: return float(response.text.replace("\n", "")) -def create_request_function(domain: str, path: str) -> Callable[[], Optional[float]]: - if path == "none": +def create_request_function(url: str, path: str) -> Callable[[], Optional[float]]: + if path == "none" or path is None: return lambda: 0 else: - return functools.partial(request_value, domain + path) + return functools.partial(request_value, url + path) diff --git a/packages/modules/http/bat.py b/packages/modules/http/bat.py index 50f36a815..3573ea05b 100644 --- a/packages/modules/http/bat.py +++ b/packages/modules/http/bat.py @@ -12,20 +12,20 @@ def get_default_config() -> dict: "id": 0, "type": "bat", "configuration": { - "power_path": "", - "imported_path": "none", - "exported_path": "none", - "soc_path": "" + "power_path": None, + "imported_path": None, + "exported_path": None, + "soc_path": None } } class HttpBat: - def __init__(self, device_id: int, component_config: dict, domain: str) -> None: - self.__get_power = create_request_function(domain, component_config["configuration"]["power_path"]) - self.__get_imported = create_request_function(domain, component_config["configuration"]["imported_path"]) - self.__get_exported = create_request_function(domain, component_config["configuration"]["exported_path"]) - self.__get_soc = create_request_function(domain, component_config["configuration"]["soc_path"]) + def __init__(self, device_id: int, component_config: dict, url: str) -> None: + self.__get_power = create_request_function(url, component_config["configuration"]["power_path"]) + self.__get_imported = create_request_function(url, component_config["configuration"]["imported_path"]) + self.__get_exported = create_request_function(url, component_config["configuration"]["exported_path"]) + self.__get_soc = create_request_function(url, component_config["configuration"]["soc_path"]) self.__device_id = device_id self.component_config = component_config diff --git a/packages/modules/http/counter.py b/packages/modules/http/counter.py index 16fca0a1b..fbb739230 100644 --- a/packages/modules/http/counter.py +++ b/packages/modules/http/counter.py @@ -12,23 +12,23 @@ def get_default_config() -> dict: "id": 0, "type": "counter", "configuration": { - "power_path": "", - "imported_path": "none", - "exported_path": "none", - "current_l1_path": "none", - "current_l2_path": "none", - "current_l3_path": "none", + "power_path": None, + "imported_path": None, + "exported_path": None, + "current_l1_path": None, + "current_l2_path": None, + "current_l3_path": None, } } class HttpCounter: - def __init__(self, device_id: int, component_config: dict, domain: str) -> None: - self.__get_power = create_request_function(domain, component_config["configuration"]["power_path"]) - self.__get_imported = create_request_function(domain, component_config["configuration"]["imported_path"]) - self.__get_exported = create_request_function(domain, component_config["configuration"]["exported_path"]) + def __init__(self, device_id: int, component_config: dict, url: str) -> None: + self.__get_power = create_request_function(url, component_config["configuration"]["power_path"]) + self.__get_imported = create_request_function(url, component_config["configuration"]["imported_path"]) + self.__get_exported = create_request_function(url, component_config["configuration"]["exported_path"]) self.__get_currents = [ - create_request_function(domain, + create_request_function(url, component_config["configuration"]["current_l" + str(i) + "_path"]) for i in range(1, 4) ] diff --git a/packages/modules/http/device.py b/packages/modules/http/device.py index b846f9f8e..3af141443 100644 --- a/packages/modules/http/device.py +++ b/packages/modules/http/device.py @@ -3,8 +3,6 @@ import re from typing import Dict, Union, List -from urllib3.util import parse_url - from helpermodules.cli import run_using_positional_cli_args from modules.common.abstract_device import AbstractDevice from modules.common.component_context import SingleComponentUpdateContext @@ -21,13 +19,52 @@ def get_default_config() -> dict: "type": "http", "id": 0, "configuration": { - "protocol": "http", - "domain": None, - "port": 80 + "url": None } } +class HttpConfiguration: + def __init__(self, url: str): + self.url = url + + @staticmethod + def from_dict(device_config: dict): + keys = ["url"] + try: + values = [device_config[key] for key in keys] + except KeyError as e: + raise Exception( + "Illegal configuration <{}>: Expected object with properties: {}".format(device_config, keys) + ) from e + return HttpConfiguration(*values) + + +class Http: + def __init__(self, name: str, type: str, id: int, configuration: HttpConfiguration) -> None: + self.name = name + self.type = type + self.id = id + self.configuration = configuration + + @staticmethod + def from_dict(device_config: dict): + keys = ["name", "type", "id", "configuration"] + try: + values = [device_config[key] for key in keys] + values = [] + for key in keys: + if isinstance(device_config[key], Dict): + values.append(HttpConfiguration.from_dict(device_config[key])) + else: + values.append(device_config[key]) + except KeyError as e: + raise Exception( + "Illegal configuration <{}>: Expected object with properties: {}".format(device_config, keys) + ) from e + return Http(*values) + + http_component_classes = Union[bat.HttpBat, counter.HttpCounter, inverter.HttpInverter] @@ -41,12 +78,9 @@ class Device(AbstractDevice): def __init__(self, device_config: dict) -> None: self.components = {} # type: Dict[str, http_component_classes] try: - self.device_config = device_config - port = self.device_config["configuration"]["port"] - self.domain = self.device_config["configuration"]["protocol"] + \ - "://" + self.device_config["configuration"]["domain"] - if port is not None: - self.domain = self.domain + ":" + str(port) + self.device_config = device_config \ + if isinstance(device_config, Http) \ + else Http.from_dict(device_config) except Exception: log.exception("Fehler im Modul "+device_config["name"]) @@ -54,7 +88,7 @@ def add_component(self, component_config: dict) -> None: component_type = component_config["type"] 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, self.domain) + self.device_config.id, component_config, self.device_config.configuration.url) else: raise Exception( "illegal component type " + component_type + ". Allowed values: " + @@ -70,7 +104,7 @@ def update(self) -> None: self.components[component].update() else: log.warning( - self.device_config["name"] + + self.device_config.name + ": Es konnten keine Werte gelesen werden, da noch keine Komponenten konfiguriert wurden." ) @@ -104,14 +138,13 @@ def run_device_legacy(device_config: dict, component_config: dict): def create_legacy_device_config(url: str): - parsed_url = parse_url(url) + regex = re.compile("^(https?://[^/]+)(.*)") + match = regex.search(url) + if match is None: + raise Exception("Invalid URL <" + url + ">: Absolute HTTP or HTTPS URL required") + host_scheme = match.group(1) device_config = get_default_config() - device_config["configuration"]["protocol"] = parsed_url.scheme - device_config["configuration"]["domain"] = parsed_url.hostname - if parsed_url.port is not None: - device_config["configuration"]["port"] = int(parsed_url.port) - else: - device_config["configuration"]["port"] = None + device_config["configuration"]["url"] = host_scheme return device_config diff --git a/packages/modules/http/inverter.py b/packages/modules/http/inverter.py index f782b5ac4..d94b6b09a 100644 --- a/packages/modules/http/inverter.py +++ b/packages/modules/http/inverter.py @@ -14,16 +14,16 @@ def get_default_config() -> dict: "id": 0, "type": "inverter", "configuration": { - "power_path": "", - "counter_path": "none", + "power_path": None, + "counter_path": None } } class HttpInverter: - def __init__(self, device_id: int, 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"]) + def __init__(self, device_id: int, component_config: dict, url: str) -> None: + self.__get_power = create_request_function(url, component_config["configuration"]["power_path"]) + self.__get_counter = create_request_function(url, component_config["configuration"]["counter_path"]) self.__device_id = device_id self.component_config = component_config diff --git a/packages/modules/json/device.py b/packages/modules/json/device.py index 8f916ce4b..545134771 100644 --- a/packages/modules/json/device.py +++ b/packages/modules/json/device.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import logging from typing import Dict, List, Union, Optional -from urllib3.util import parse_url from helpermodules.cli import run_using_positional_cli_args from modules.common import req @@ -20,13 +19,52 @@ def get_default_config() -> dict: "type": "json", "id": 0, "configuration": { - "protocol": "http", - "domain": None, - "port": 80 + "url": None } } +class JsonConfiguration: + def __init__(self, url: str): + self.url = url + + @staticmethod + def from_dict(device_config: dict): + keys = ["url"] + try: + values = [device_config[key] for key in keys] + except KeyError as e: + raise Exception( + "Illegal configuration <{}>: Expected object with properties: {}".format(device_config, keys) + ) from e + return JsonConfiguration(*values) + + +class Json: + def __init__(self, name: str, type: str, id: int, configuration: JsonConfiguration) -> None: + self.name = name + self.type = type + self.id = id + self.configuration = configuration + + @staticmethod + def from_dict(device_config: dict): + keys = ["name", "type", "id", "configuration"] + try: + values = [device_config[key] for key in keys] + values = [] + for key in keys: + if isinstance(device_config[key], Dict): + values.append(JsonConfiguration.from_dict(device_config[key])) + else: + values.append(device_config[key]) + except KeyError as e: + raise Exception( + "Illegal configuration <{}>: Expected object with properties: {}".format(device_config, keys) + ) from e + return Json(*values) + + json_component_classes = Union[bat.JsonBat, counter.JsonCounter, inverter.JsonInverter] @@ -40,11 +78,9 @@ class Device(AbstractDevice): def __init__(self, device_config: dict) -> None: self.components = {} # type: Dict[str, json_component_classes] try: - self.device_config = device_config - port = self.device_config["configuration"]["port"] - self.domain = self.device_config["configuration"]["protocol"] + \ - "://" + self.device_config["configuration"]["domain"] + \ - ":" + str(port) if port else "" + self.device_config = device_config \ + if isinstance(device_config, Json) \ + else Json.from_dict(device_config) except Exception: log.exception("Fehler im Modul "+device_config["name"]) @@ -52,7 +88,7 @@ def add_component(self, component_config: dict) -> None: component_type = component_config["type"] 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) + self.device_config.id, component_config) else: raise Exception( "illegal component type " + component_type + ". Allowed values: " + @@ -63,26 +99,22 @@ def update(self) -> None: log.debug("Start device reading " + str(self.components)) if self.components: with MultiComponentUpdateContext(self.components): - response = req.get_http_session().get(self.domain, timeout=5) + response = req.get_http_session().get(self.device_config.configuration.url, timeout=5) for component in self.components: self.components[component].update(response.json()) else: log.warning( - self.device_config["name"] + + self.device_config.name + ": Es konnten keine Werte gelesen werden, da noch keine Komponenten konfiguriert wurden." ) -def read_legacy(ip_address: str, component_config: dict, num: Optional[int] = None, **kwargs) -> None: +def read_legacy(url: str, component_config: dict, num: Optional[int] = None, **kwargs) -> None: component_config["configuration"].update(kwargs) component_config["id"] = num - parsed_url = parse_url(ip_address) device_config = get_default_config() - device_config["configuration"]["protocol"] = parsed_url.scheme - device_config["configuration"]["domain"] = parsed_url.hostname - device_config["configuration"]["port"] = int(parsed_url.port) - + device_config["configuration"]["url"] = url dev = Device(device_config) dev.add_component(component_config) dev.update()