From 696f384ef83f8eb9552f59e3110ecee5f9ee5224 Mon Sep 17 00:00:00 2001 From: MartinRinas Date: Fri, 13 Dec 2024 10:03:02 +0000 Subject: [PATCH 1/8] json soc module --- packages/modules/vehicles/json/config.py | 21 +++++++++ packages/modules/vehicles/json/soc.py | 55 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 packages/modules/vehicles/json/config.py create mode 100644 packages/modules/vehicles/json/soc.py diff --git a/packages/modules/vehicles/json/config.py b/packages/modules/vehicles/json/config.py new file mode 100644 index 0000000000..0ad51b783c --- /dev/null +++ b/packages/modules/vehicles/json/config.py @@ -0,0 +1,21 @@ +from helpermodules.auto_str import auto_str + + +@auto_str +class JsonSocConfiguration: + def __init__(self, soc_url=None, range_url=None, soc_pattern=None, range_pattern=None): + self.soc_url = soc_url + self.soc_pattern = soc_pattern + self.range_url = range_url + self.range_pattern = range_pattern + + +@auto_str +class JsonSocSetup(): + def __init__(self, + name: str = "Json SOC Module", + type: str = "json", + configuration: JsonSocConfiguration = None) -> None: + self.name = name + self.type = type + self.configuration = configuration or JsonSocConfiguration() diff --git a/packages/modules/vehicles/json/soc.py b/packages/modules/vehicles/json/soc.py new file mode 100644 index 0000000000..e6800b16e0 --- /dev/null +++ b/packages/modules/vehicles/json/soc.py @@ -0,0 +1,55 @@ +from typing import List +import logging +import jq + +from helpermodules.cli import run_using_positional_cli_args +from modules.common import req +from modules.common import store +from modules.common.abstract_device import DeviceDescriptor +from modules.common.abstract_vehicle import VehicleUpdateData +from modules.common.component_state import CarState +from modules.common.configurable_vehicle import ConfigurableVehicle +from modules.vehicles.json.config import JsonSocSetup, JsonSocConfiguration + + +log = logging.getLogger(__name__) + + +def fetch_soc(config: JsonSocSetup) -> CarState: + soc_url = config.configuration.soc_url + soc_pattern = config.configuration.soc_pattern + range_url = config.configuration.range_url + range_pattern = config.configuration.range_pattern + + if soc_url is None or soc_url == "none": + log.warning("http_soc: soc_url not defined - set soc to 0") + soc = 0 + else: + raw_soc = req.get_http_session().get(soc_url, timeout=5).json() + soc = float(jq.compile(soc_pattern).input(raw_soc).first()) + if range_url is None or range_url == "none": + log.warning("http_soc: range_url not defined - set range to null") + range = 0 + else: + raw_range = req.get_http_session().get(range_url, timeout=5).json() + range = int(jq.compile(range_pattern).input(raw_range).first()) + return CarState(soc, range) + + +def create_vehicle(vehicle_config: JsonSocSetup, vehicle: int): + def updater(vehicle_update_data: VehicleUpdateData) -> CarState: + return fetch_soc(vehicle_config) + return ConfigurableVehicle(vehicle_config=vehicle_config, component_updater=updater, vehicle=vehicle) + + +def http_update(soc_url: str, range_url: str, charge_point: int): + log.debug("http_soc: soc_url="+soc_url+"range_url="+range_url+"charge_point="+str(charge_point)) + store.get_car_value_store(charge_point).store.set( + fetch_soc(JsonSocSetup(configuration=JsonSocConfiguration(soc_url, range_url)))) + + +def main(argv: List[str]): + run_using_positional_cli_args(http_update, argv) + + +device_descriptor = DeviceDescriptor(configuration_factory=JsonSocSetup) From 902cb90c07b3a8cf7e0629b5bd7a0eded30b2095 Mon Sep 17 00:00:00 2001 From: MartinRinas Date: Fri, 13 Dec 2024 15:24:53 +0000 Subject: [PATCH 2/8] rename module --- packages/modules/vehicles/json/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/modules/vehicles/json/config.py b/packages/modules/vehicles/json/config.py index 0ad51b783c..8574df72b5 100644 --- a/packages/modules/vehicles/json/config.py +++ b/packages/modules/vehicles/json/config.py @@ -13,7 +13,7 @@ def __init__(self, soc_url=None, range_url=None, soc_pattern=None, range_pattern @auto_str class JsonSocSetup(): def __init__(self, - name: str = "Json SOC Module", + name: str = "JSON", type: str = "json", configuration: JsonSocConfiguration = None) -> None: self.name = name From 99997ca40db2432abe2c9ac7422fcaf31eb12ffd Mon Sep 17 00:00:00 2001 From: MartinRinas Date: Mon, 16 Dec 2024 21:29:26 +0000 Subject: [PATCH 3/8] configurable timeout, fetch only once if URLs are identical --- packages/modules/vehicles/json/config.py | 3 +- packages/modules/vehicles/json/soc.py | 36 ++++++++++++++++++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/modules/vehicles/json/config.py b/packages/modules/vehicles/json/config.py index 8574df72b5..c77bb1038c 100644 --- a/packages/modules/vehicles/json/config.py +++ b/packages/modules/vehicles/json/config.py @@ -3,11 +3,12 @@ @auto_str class JsonSocConfiguration: - def __init__(self, soc_url=None, range_url=None, soc_pattern=None, range_pattern=None): + def __init__(self, soc_url=None, range_url=None, soc_pattern=None, range_pattern=None, timeout=None): self.soc_url = soc_url self.soc_pattern = soc_pattern self.range_url = range_url self.range_pattern = range_pattern + self.timeout = timeout @auto_str diff --git a/packages/modules/vehicles/json/soc.py b/packages/modules/vehicles/json/soc.py index e6800b16e0..95eaa2c417 100644 --- a/packages/modules/vehicles/json/soc.py +++ b/packages/modules/vehicles/json/soc.py @@ -15,24 +15,44 @@ log = logging.getLogger(__name__) +def fetch_data(url, timeout=5): + if url is None or url == "none": + return None + raw_data = req.get_http_session().get(url, timeout=timeout).json() + return raw_data + + +def parse_data(data, pattern): + if pattern is None: + return data + return jq.compile(pattern).input(data).first() + + def fetch_soc(config: JsonSocSetup) -> CarState: soc_url = config.configuration.soc_url soc_pattern = config.configuration.soc_pattern range_url = config.configuration.range_url range_pattern = config.configuration.range_pattern + timeout = config.configuration.timeout if isinstance(config.configuration.timeout, int) else None - if soc_url is None or soc_url == "none": - log.warning("http_soc: soc_url not defined - set soc to 0") + soc_data = fetch_data(soc_url, timeout) + if soc_data is None: + log.warning("soc_url not defined, set soc to 0") soc = 0 else: - raw_soc = req.get_http_session().get(soc_url, timeout=5).json() - soc = float(jq.compile(soc_pattern).input(raw_soc).first()) - if range_url is None or range_url == "none": - log.warning("http_soc: range_url not defined - set range to null") + soc = float(parse_data(soc_data, soc_pattern)) + + if range_url is None or range_url == "none" or range_url == "": + log.warning("range_url not defined, set range to null") range = 0 else: - raw_range = req.get_http_session().get(range_url, timeout=5).json() - range = int(jq.compile(range_pattern).input(raw_range).first()) + if range_url == soc_url: # avoid duplicate http requests if range_url is the same as soc_url + log.debug('range_url "{}" is same as soc_url "{}", reusing existing data.'.format(range_url, soc_url)) + range_data = soc_data + else: + range_data = fetch_data(range_url, timeout) + range = int(parse_data(range_data, range_pattern)) + return CarState(soc, range) From 6341bbdeef854217bf1136a580f32dc4b9fa6b52 Mon Sep 17 00:00:00 2001 From: MartinRinas Date: Mon, 16 Dec 2024 21:41:16 +0000 Subject: [PATCH 4/8] add soc calc while charging --- packages/modules/vehicles/json/config.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/modules/vehicles/json/config.py b/packages/modules/vehicles/json/config.py index c77bb1038c..43aae35556 100644 --- a/packages/modules/vehicles/json/config.py +++ b/packages/modules/vehicles/json/config.py @@ -3,12 +3,15 @@ @auto_str class JsonSocConfiguration: - def __init__(self, soc_url=None, range_url=None, soc_pattern=None, range_pattern=None, timeout=None): + def __init__( + self, soc_url=None, range_url=None, soc_pattern=None, range_pattern=None, timeout=None, calculate_soc=False + ): self.soc_url = soc_url self.soc_pattern = soc_pattern self.range_url = range_url self.range_pattern = range_pattern self.timeout = timeout + self.calculate_soc = calculate_soc @auto_str From 45ea2adca544614f82e9a16ee9ae9b4b1915702d Mon Sep 17 00:00:00 2001 From: MartinRinas Date: Mon, 16 Dec 2024 21:54:27 +0000 Subject: [PATCH 5/8] type definition --- packages/modules/vehicles/json/config.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/modules/vehicles/json/config.py b/packages/modules/vehicles/json/config.py index 43aae35556..5fb6c28ccb 100644 --- a/packages/modules/vehicles/json/config.py +++ b/packages/modules/vehicles/json/config.py @@ -4,7 +4,13 @@ @auto_str class JsonSocConfiguration: def __init__( - self, soc_url=None, range_url=None, soc_pattern=None, range_pattern=None, timeout=None, calculate_soc=False + self, + soc_url: str = None, + range_url: str = None, + soc_pattern: str = None, + range_pattern: str = None, + timeout: int = None, + calculate_soc: bool = False ): self.soc_url = soc_url self.soc_pattern = soc_pattern From 0e0ebf09a8a9f58fe48f467100d00b03233104e4 Mon Sep 17 00:00:00 2001 From: MartinRinas Date: Tue, 17 Dec 2024 07:14:12 +0000 Subject: [PATCH 6/8] soc_while_charging --- packages/modules/vehicles/json/soc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/modules/vehicles/json/soc.py b/packages/modules/vehicles/json/soc.py index 95eaa2c417..1e8c551639 100644 --- a/packages/modules/vehicles/json/soc.py +++ b/packages/modules/vehicles/json/soc.py @@ -59,7 +59,8 @@ def fetch_soc(config: JsonSocSetup) -> CarState: def create_vehicle(vehicle_config: JsonSocSetup, vehicle: int): def updater(vehicle_update_data: VehicleUpdateData) -> CarState: return fetch_soc(vehicle_config) - return ConfigurableVehicle(vehicle_config=vehicle_config, component_updater=updater, vehicle=vehicle) + return ConfigurableVehicle(vehicle_config=vehicle_config, component_updater=updater, vehicle=vehicle, + calc_while_charging=vehicle_config.configuration.calculate_soc) def http_update(soc_url: str, range_url: str, charge_point: int): From 92a7270d44d5318dfe9008210e378755977c6c80 Mon Sep 17 00:00:00 2001 From: MartinRinas Date: Tue, 17 Dec 2024 08:09:11 +0000 Subject: [PATCH 7/8] single url --- packages/modules/vehicles/json/config.py | 6 ++--- packages/modules/vehicles/json/soc.py | 32 ++++++++---------------- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/packages/modules/vehicles/json/config.py b/packages/modules/vehicles/json/config.py index 5fb6c28ccb..a9856d8893 100644 --- a/packages/modules/vehicles/json/config.py +++ b/packages/modules/vehicles/json/config.py @@ -5,16 +5,14 @@ class JsonSocConfiguration: def __init__( self, - soc_url: str = None, - range_url: str = None, + url: str = None, soc_pattern: str = None, range_pattern: str = None, timeout: int = None, calculate_soc: bool = False ): - self.soc_url = soc_url + self.url = url self.soc_pattern = soc_pattern - self.range_url = range_url self.range_pattern = range_pattern self.timeout = timeout self.calculate_soc = calculate_soc diff --git a/packages/modules/vehicles/json/soc.py b/packages/modules/vehicles/json/soc.py index 1e8c551639..67f25815be 100644 --- a/packages/modules/vehicles/json/soc.py +++ b/packages/modules/vehicles/json/soc.py @@ -15,13 +15,6 @@ log = logging.getLogger(__name__) -def fetch_data(url, timeout=5): - if url is None or url == "none": - return None - raw_data = req.get_http_session().get(url, timeout=timeout).json() - return raw_data - - def parse_data(data, pattern): if pattern is None: return data @@ -29,29 +22,24 @@ def parse_data(data, pattern): def fetch_soc(config: JsonSocSetup) -> CarState: - soc_url = config.configuration.soc_url + url = config.configuration.url soc_pattern = config.configuration.soc_pattern - range_url = config.configuration.range_url range_pattern = config.configuration.range_pattern timeout = config.configuration.timeout if isinstance(config.configuration.timeout, int) else None - soc_data = fetch_data(soc_url, timeout) - if soc_data is None: - log.warning("soc_url not defined, set soc to 0") - soc = 0 + if url is None or url == "": + log.warning("url not defined, set soc to 0") + return CarState(0, 0) else: - soc = float(parse_data(soc_data, soc_pattern)) + raw_data = req.get_http_session().get(url, timeout=timeout).json() + + soc = float(parse_data(raw_data, soc_pattern)) - if range_url is None or range_url == "none" or range_url == "": - log.warning("range_url not defined, set range to null") + if range_pattern is None or range_pattern == "": + log.warning("range_pattern not defined, set range to 0") range = 0 else: - if range_url == soc_url: # avoid duplicate http requests if range_url is the same as soc_url - log.debug('range_url "{}" is same as soc_url "{}", reusing existing data.'.format(range_url, soc_url)) - range_data = soc_data - else: - range_data = fetch_data(range_url, timeout) - range = int(parse_data(range_data, range_pattern)) + range = int(parse_data(raw_data, range_pattern)) return CarState(soc, range) From 7947b2036299aed13f2a518867f7f952c79e3e82 Mon Sep 17 00:00:00 2001 From: MartinRinas Date: Tue, 17 Dec 2024 11:43:26 +0000 Subject: [PATCH 8/8] type definition, error handling --- packages/modules/vehicles/json/config.py | 9 +++++---- packages/modules/vehicles/json/soc.py | 20 ++++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/modules/vehicles/json/config.py b/packages/modules/vehicles/json/config.py index a9856d8893..b557a34f3f 100644 --- a/packages/modules/vehicles/json/config.py +++ b/packages/modules/vehicles/json/config.py @@ -1,14 +1,15 @@ from helpermodules.auto_str import auto_str +from typing import Optional @auto_str class JsonSocConfiguration: def __init__( self, - url: str = None, - soc_pattern: str = None, - range_pattern: str = None, - timeout: int = None, + url: Optional[str] = None, + soc_pattern: Optional[str] = None, + range_pattern: Optional[str] = None, + timeout: Optional[int] = None, calculate_soc: bool = False ): self.url = url diff --git a/packages/modules/vehicles/json/soc.py b/packages/modules/vehicles/json/soc.py index 67f25815be..0272cb9bda 100644 --- a/packages/modules/vehicles/json/soc.py +++ b/packages/modules/vehicles/json/soc.py @@ -10,15 +10,23 @@ from modules.common.component_state import CarState from modules.common.configurable_vehicle import ConfigurableVehicle from modules.vehicles.json.config import JsonSocSetup, JsonSocConfiguration +from typing import Any, Dict log = logging.getLogger(__name__) -def parse_data(data, pattern): - if pattern is None: - return data - return jq.compile(pattern).input(data).first() +def parse_data(data: Dict[str, Any], pattern: str) -> float: + log.debug(f"parse_data: data='{data}' pattern='{pattern}'") + + if pattern == "": + raise ValueError("Please provide pattern to parse data") + + result = jq.compile(pattern).input(data).first() + if result is None: + raise ValueError(f"Pattern {pattern} did not match any data in {data}") + + return float(result) def fetch_soc(config: JsonSocSetup) -> CarState: @@ -31,9 +39,9 @@ def fetch_soc(config: JsonSocSetup) -> CarState: log.warning("url not defined, set soc to 0") return CarState(0, 0) else: - raw_data = req.get_http_session().get(url, timeout=timeout).json() + raw_data: Dict[str, Any] = req.get_http_session().get(url, timeout=timeout).json() - soc = float(parse_data(raw_data, soc_pattern)) + soc = parse_data(raw_data, soc_pattern) if range_pattern is None or range_pattern == "": log.warning("range_pattern not defined, set range to 0")