From bb921a03945681a450f4861cd821c6d23073d365 Mon Sep 17 00:00:00 2001 From: Krisjanis Lejejs Date: Thu, 30 Dec 2021 18:10:05 +0200 Subject: [PATCH] Refactoring to more generic code to support more heat pumps --- README.md | 6 ++- ThermiaOnlineAPI/api/ThermiaAPI.py | 59 ++++++++++++++++++++---------- ThermiaOnlineAPI/model/HeatPump.py | 40 ++++++++++++++++---- 3 files changed, 77 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 4111b37..e61a5ab 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Thermia Online API -### A Python API for Thermia heat pumps using https://online.thermia.se +### A Python API for Thermia heat pumps using https://online.thermia.se + +## Confirmed heat pumps that API supports: +* Thermia Diplomat / Diplomat Duo +* Thermia iTec (still to be confirmed) ## Available properties within ThermiaHeatPump class: | Property | Description | diff --git a/ThermiaOnlineAPI/api/ThermiaAPI.py b/ThermiaOnlineAPI/api/ThermiaAPI.py index 9552356..1d4f945 100644 --- a/ThermiaOnlineAPI/api/ThermiaAPI.py +++ b/ThermiaOnlineAPI/api/ThermiaAPI.py @@ -10,11 +10,6 @@ THERMIA_API_CONFIG_URL = "https://online.thermia.se/api/configuration" -REGISTER_VALUES = { - "temperature": 50, - "operation_mode": 51, -} - class ThermiaAPI(): def __init__(self, email, password): self.__email = email @@ -65,32 +60,40 @@ def get_device_status(self, device): return request.json() - def get_temperature_status(self, device): + def get_temperature_status(self, device: ThermiaHeatPump): self.__check_token_validity() - url = self.configuration['apiBaseUrl'] + "/api/v1/Registers/Installations/" + str(device['id']) + "/Groups/REG_GROUP_TEMPERATURES" + url = self.configuration['apiBaseUrl'] + "/api/v1/Registers/Installations/" + str(device.id) + "/Groups/REG_GROUP_TEMPERATURES" request = requests.get(url, headers=DEFAULT_REQUEST_HEADERS) status = request.status_code if status != 200: - LOGGER.error("Error in getting device's operation mode. " + str(status)) + LOGGER.error("Error in getting device's temperature status. " + str(status)) return None - data = [d for d in request.json() if d['registerIndex'] == REGISTER_VALUES['temperature']] + device_temperature_register_index = device.get_register_indexes()["temperature"] + if device_temperature_register_index is None: + LOGGER.error("Error in getting device's temperature status. No temperature register index.") + return None + + data = [d for d in request.json() if d['registerIndex'] == device_temperature_register_index] if len(data) == 0: + LOGGER.error("Error in getting device's temperature status. Could not find temperature by register.") return None + data = data[0] + return { - "minValue": data[0]['minValue'], - "maxValue": data[0]['maxValue'], - "step": data[0]['step'], + "minValue": data['minValue'], + "maxValue": data['maxValue'], + "step": data['step'], } - def get_operation_mode(self, device): + def get_operation_mode(self, device: ThermiaHeatPump): self.__check_token_validity() - url = self.configuration['apiBaseUrl'] + "/api/v1/Registers/Installations/" + str(device['id']) + "/Groups/REG_GROUP_OPERATIONAL_OPERATION" + url = self.configuration['apiBaseUrl'] + "/api/v1/Registers/Installations/" + str(device.id) + "/Groups/REG_GROUP_OPERATIONAL_OPERATION" request = requests.get(url, headers=DEFAULT_REQUEST_HEADERS) status = request.status_code @@ -98,13 +101,18 @@ def get_operation_mode(self, device): LOGGER.error("Error in getting device's operation mode. " + str(status)) return None - data = [d for d in request.json() if d['registerIndex'] == REGISTER_VALUES['operation_mode']] + data = [d for d in request.json() if d['registerName'] == "REG_OPERATIONMODE"] if len(data) == 0: + LOGGER.error("Error in getting device's operation mode. Could not find operation mode by register name.") return None - current_operation_mode = int(data[0].get("registerValue")) - operation_modes_data = data[0].get("valueNames") + data = data[0] + + device.set_register_index_operation_mode(data['registerIndex']) + + current_operation_mode = int(data.get("registerValue")) + operation_modes_data = data.get("valueNames") if operation_modes_data is not None: operation_modes = list(map(lambda values: values.get("name").split("REG_VALUE_OPERATION_MODE_")[1], operation_modes_data)) @@ -116,13 +124,24 @@ def get_operation_mode(self, device): return None def set_temperature(self, device: ThermiaHeatPump, temperature): - self.__set_register_value(device, REGISTER_VALUES["temperature"], temperature) + device_temperature_register_index = device.get_register_indexes()["temperature"] + if device_temperature_register_index is None: + LOGGER.error("Error setting device's temperature. No temperature register index.") + return + + self.__set_register_value(device, device_temperature_register_index, temperature) def set_operation_mode(self, device: ThermiaHeatPump, mode): operation_mode_int = device.available_operation_modes.index(mode) - self.__set_register_value(device, REGISTER_VALUES["operation_mode"], operation_mode_int) - def __set_register_value(self, device: ThermiaHeatPump, register_index: REGISTER_VALUES, register_value: int): + device_operation_mode_register_index = device.get_register_indexes()["operation_mode"] + if device_operation_mode_register_index is None: + LOGGER.error("Error setting device's operation mode. No operation mode register index.") + return + + self.__set_register_value(device, device_operation_mode_register_index, operation_mode_int) + + def __set_register_value(self, device: ThermiaHeatPump, register_index: int, register_value: int): self.__check_token_validity() url = self.configuration['apiBaseUrl'] + "/api/v1/Registers/Installations/" + str(device.id) + "/Registers" diff --git a/ThermiaOnlineAPI/model/HeatPump.py b/ThermiaOnlineAPI/model/HeatPump.py index f309212..76690d2 100644 --- a/ThermiaOnlineAPI/model/HeatPump.py +++ b/ThermiaOnlineAPI/model/HeatPump.py @@ -8,6 +8,11 @@ LOGGER = logging.getLogger(__name__) +DEFAULT_REGISTER_INDEXES = { + "temperature": None, + "operation_mode": None, +} + class ThermiaHeatPump(): def __init__(self, device_data: json, api_interface: "ThermiaAPI"): self.__device_data = device_data @@ -16,14 +21,25 @@ def __init__(self, device_data: json, api_interface: "ThermiaAPI"): self.__status = None self.__temperature_state = None self.__operation_mode_state = None + + self.__register_indexes = DEFAULT_REGISTER_INDEXES self.refetch_data() def refetch_data(self): self.__info = self.__api_interface.get_device_info(self.__device_data) self.__status = self.__api_interface.get_device_status(self.__device_data) - self.__temperature_state = self.__api_interface.get_temperature_status(self.__device_data) - self.__operation_mode_state = self.__api_interface.get_operation_mode(self.__device_data) + + self.__register_indexes["temperature"] = self.__status.get("heatingEffectRegisters", [None, None])[1] + + self.__temperature_state = self.__api_interface.get_temperature_status(self) + self.__operation_mode_state = self.__api_interface.get_operation_mode(self) + + def get_register_indexes(self): + return self.__register_indexes + + def set_register_index_operation_mode(self, register_index: int): + self.__register_indexes["operation_mode"] = register_index def set_temperature(self, temperature: int): LOGGER.info("Setting temperature to " + str(temperature)) @@ -86,20 +102,30 @@ def heat_temperature(self): @property def heat_min_temperature_value(self): - return self.__temperature_state.get("minValue") + if self.__temperature_state is None: + return None + return self.__temperature_state.get("minValue", None) @property def heat_max_temperature_value(self): - return self.__temperature_state.get("maxValue") + if self.__temperature_state is None: + return None + return self.__temperature_state.get("maxValue", None) @property def heat_temperature_step(self): - return self.__temperature_state.get("step") + if self.__temperature_state is None: + return None + return self.__temperature_state.get("step", None) @property def operation_mode(self): - return self.__operation_mode_state["current"] + if self.__operation_mode_state is None: + return None + return self.__operation_mode_state.get("current", None) @property def available_operation_modes(self): - return self.__operation_mode_state["available"] + if self.__operation_mode_state is None: + return None + return self.__operation_mode_state.get("available", []) \ No newline at end of file