From 03993720436d938be2e8c4c1d375de05714f7ccc Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 30 Aug 2021 19:07:52 +0200 Subject: [PATCH 01/65] Introduce more classes - part 1 --- plugwise/helper.py | 100 ++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 55 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 658924291..9bb8da3ba 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -1,11 +1,12 @@ """Use of this source code is governed by the MIT license found in the LICENSE file. Plugwise Smile protocol helpers. """ +import aiohttp import asyncio import datetime as dt import logging -import async_timeout +from async_timeout import timeout from dateutil import tz from dateutil.parser import parse from defusedxml import ElementTree as etree @@ -217,38 +218,37 @@ def power_data_energy_diff(measurement, net_string, f_val, direct_data): return direct_data -class SmileHelper: - """The SmileHelper class.""" +class SmileComm: + """The SmileComm class.""" - def __init__(self): + def __init__( + self, + host, + password, + username, + port, + timeout, + websession, + ): """Set the constructor for this class.""" - self._active_device_present = None - self._appl_data = {} - self._appliances = None - self._auth = None - self._cp_state = None - self._domain_objects = None - self._endpoint = None - self._heater_id = None - self._home_location = None - self._locations = None - self._modules = None - self._smile_legacy = False - self._host = None - self._loc_data = {} - self._port = None - self._stretch_v2 = False - self._stretch_v3 = False - self._thermo_locs = None - self._timeout = None - self._websession = None - - self.gateway_id = None - self.notifications = {} - self.smile_hostname = None - self.smile_name = None - self.smile_type = None - self.smile_version = () + if not websession: + + async def _create_session() -> aiohttp.ClientSession: + return aiohttp.ClientSession() # pragma: no cover + + loop = asyncio.get_event_loop() + if loop.is_running(): + self._websession = aiohttp.ClientSession() + else: + self._websession = loop.run_until_complete( + _create_session() + ) # pragma: no cover + else: + self._websession = websession + + self._auth = aiohttp.BasicAuth(username, password=password) + self._endpoint = f"http://{host}:{str(port)}" + self._timeout = timeout async def _request_validate(self, resp, method): """Helper-function for _request(): validate the returned data.""" @@ -289,7 +289,7 @@ async def _request( url = f"{self._endpoint}{command}" try: - with async_timeout.timeout(self._timeout): + with timeout(self._timeout): if method == "get": # Work-around for Stretchv2, should not hurt the other smiles headers = {"Accept-Encoding": "gzip"} @@ -311,6 +311,19 @@ async def _request( return await self._request_validate(resp, method) + async def close_connection(self): + """Close the Plugwise connection.""" + await self._websession.close() + + +class SmileHelper: + """The SmileHelper class.""" + + def __init__(self): + + self._cp_state = None + self._loc_data = {} + def _locations_legacy(self): """Helper-function for _all_locations(). Create locations for legacy devices. @@ -680,29 +693,6 @@ def _rule_ids_by_tag(self, tag, loc_id): if schema_ids != {}: return schema_ids - async def _update_domain_objects(self): - """Helper-function for smile.py: full_update_device() and update_gw_devices(). - Request domain_objects data. - """ - self._domain_objects = await self._request(DOMAIN_OBJECTS) - - # If Plugwise notifications present: - self.notifications = {} - url = f"{self._endpoint}{DOMAIN_OBJECTS}" - notifications = self._domain_objects.findall(".//notification") - for notification in notifications: - try: - msg_id = notification.attrib["id"] - msg_type = notification.find("type").text - msg = notification.find("message").text - self.notifications.update({msg_id: {msg_type: msg}}) - _LOGGER.debug("Plugwise notifications: %s", self.notifications) - except AttributeError: # pragma: no cover - _LOGGER.info( - "Plugwise notification present but unable to process, manually investigate: %s", - url, - ) - def _appliance_measurements(self, appliance, data, measurements): """Helper-function for _get_appliance_data() - collect appliance measurement data.""" for measurement, attrs in measurements: From c95b978952666f229ddf666c874dc1f2f9b8a76e Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 30 Aug 2021 19:09:16 +0200 Subject: [PATCH 02/65] Introduce more classes - part 2 --- plugwise/smile.py | 463 ++++++++++++++++++++++++++-------------------- 1 file changed, 258 insertions(+), 205 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index c641fbec2..b8340e95a 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -33,6 +33,7 @@ ) from .exceptions import ConnectionFailedError, InvalidXMLError, UnsupportedDeviceError from .helper import ( + SmileComm, SmileHelper, device_state_updater, pw_notification_updater, @@ -42,8 +43,8 @@ _LOGGER = logging.getLogger(__name__) -class Smile(SmileHelper): - """The Plugwise Smile main class.""" +class SmileConnect(SmileComm): + """The Plugwise SmileConnect class.""" # pylint: disable=too-many-instance-attributes, too-many-public-methods @@ -51,40 +52,22 @@ def __init__( self, host, password, - username=DEFAULT_USERNAME, - port=DEFAULT_PORT, - timeout=DEFAULT_TIMEOUT, - websession: aiohttp.ClientSession = None, + username, + port, + timeout, + websession, ): """Set the constructor for this class.""" - super().__init__() - - if not websession: - - async def _create_session() -> aiohttp.ClientSession: - return aiohttp.ClientSession() # pragma: no cover - - loop = asyncio.get_event_loop() - if loop.is_running(): - self._websession = aiohttp.ClientSession() - else: - self._websession = loop.run_until_complete( - _create_session() - ) # pragma: no cover - else: - self._websession = websession - - self._auth = aiohttp.BasicAuth(username, password=password) - - self._devices = {} - self._host = host - self._port = port - self._endpoint = f"http://{self._host}:{str(self._port)}" - self._timeout = timeout - - self.gw_devices = {} + super().__init__( + host, + password, + username, + port, + timeout, + websession, + ) - async def connect(self): + async def _connect(self): """Connect to Plugwise device and determine its name, type and version.""" names = [] @@ -198,10 +181,6 @@ async def _smile_detect(self, result, dsmrmain): self._stretch_v2 = self.smile_version[1].major == 2 self._stretch_v3 = self.smile_version[1].major == 3 - async def close_connection(self): - """Close the Plugwise connection.""" - await self._websession.close() - async def _full_update_device(self): """Perform a first fetch of all XML data, needed for initialization.""" self._locations = await self._request(LOCATIONS) @@ -215,6 +194,29 @@ async def _full_update_device(self): if self.smile_type != "power": await self._update_domain_objects() + async def _update_domain_objects(self): + """Helper-function for smile.py: full_update_device() and update_gw_devices(). + Request domain_objects data. + """ + self._domain_objects = await self._request(DOMAIN_OBJECTS) + + # If Plugwise notifications present: + self.notifications = {} + url = f"{self._endpoint}{DOMAIN_OBJECTS}" + notifications = self._domain_objects.findall(".//notification") + for notification in notifications: + try: + msg_id = notification.attrib["id"] + msg_type = notification.find("type").text + msg = notification.find("message").text + self.notifications.update({msg_id: {msg_type: msg}}) + _LOGGER.debug("Plugwise notifications: %s", self.notifications) + except AttributeError: # pragma: no cover + _LOGGER.info( + "Plugwise notification present but unable to process, manually investigate: %s", + url, + ) + async def update_gw_devices(self): """Perform an incremental update for updating the various device states.""" if self.smile_type != "power": @@ -251,6 +253,225 @@ async def update_gw_devices(self): data, self.gw_devices, dev_dict, dev_id, "switches", key ) + async def _set_schedule_state_legacy(self, name, state): + """Helper-function for set_schedule_state().""" + schema_rule_id = None + for rule in self._domain_objects.findall("rule"): + if rule.find("name").text == name: + schema_rule_id = rule.attrib["id"] + + if schema_rule_id is None: + return False + + template_id = None + state = str(state) + locator = f'.//*[@id="{schema_rule_id}"]/template' + for rule in self._domain_objects.findall(locator): + template_id = rule.attrib["id"] + + uri = f"{RULES};id={schema_rule_id}" + data = ( + "{state}' + ) + + await self._request(uri, method="put", data=data) + return True + + async def set_schedule_state(self, loc_id, name, state): + """Set the Schedule, with the given name, on the relevant Thermostat. + Determined from - DOMAIN_OBJECTS. + """ + if self._smile_legacy: + return await self._set_schedule_state_legacy(name, state) + + schema_rule_ids = self._rule_ids_by_name(str(name), loc_id) + if schema_rule_ids == {} or schema_rule_ids is None: + return False + + for schema_rule_id, location_id in schema_rule_ids.items(): + template_id = None + if location_id == loc_id: + state = str(state) + locator = f'.//*[@id="{schema_rule_id}"]/template' + for rule in self._domain_objects.findall(locator): + template_id = rule.attrib["id"] + + uri = f"{RULES};id={schema_rule_id}" + data = ( + "{state}' + ) + + await self._request(uri, method="put", data=data) + + return True + + async def set_preset(self, loc_id, preset): + """Set the given Preset on the relevant Thermostat - from LOCATIONS.""" + if self._smile_legacy: + return await self._set_preset_legacy(preset) + + current_location = self._locations.find(f'location[@id="{loc_id}"]') + location_name = current_location.find("name").text + location_type = current_location.find("type").text + + if preset not in self._presets(loc_id): + return False + + uri = f"{LOCATIONS};id={loc_id}" + data = ( + "{location_name}{location_type}' + f"{preset}" + ) + + await self._request(uri, method="put", data=data) + return True + + async def set_temperature(self, loc_id, temperature): + """Set the given Temperature on the relevant Thermostat.""" + temperature = str(temperature) + uri = self._temperature_uri(loc_id) + data = ( + "" + f"{temperature}" + ) + + await self._request(uri, method="put", data=data) + return True + + async def _set_groupswitch_member_state(self, members, state, switch): + """Helper-function for set_switch_state() . + Set the given State of the relevant Switch within a group of members. + """ + for member in members: + locator = f'appliance[@id="{member}"]/{switch.actuator}/{switch.func_type}' + switch_id = self._appliances.find(locator).attrib["id"] + uri = f"{APPLIANCES};id={member}/{switch.device};id={switch_id}" + if self._stretch_v2: + uri = f"{APPLIANCES};id={member}/{switch.device}" + state = str(state) + data = f"<{switch.func_type}><{switch.func}>{state}" + + await self._request(uri, method="put", data=data) + + return True + + async def set_switch_state(self, appl_id, members, model, state): + """Set the given State of the relevant Switch.""" + switch = Munch() + switch.actuator = "actuator_functionalities" + switch.device = "relay" + switch.func_type = "relay_functionality" + switch.func = "state" + if model == "dhw_cm_switch": + switch.device = "toggle" + switch.func_type = "toggle_functionality" + + if model == "lock": + switch.func = "lock" + state = "false" if state == "off" else "true" + + if self._stretch_v2: + switch.actuator = "actuators" + switch.func_type = "relay" + + if members is not None: + return await self._set_groupswitch_member_state(members, state, switch) + + locator = f'appliance[@id="{appl_id}"]/{switch.actuator}/{switch.func_type}' + switch_id = self._appliances.find(locator).attrib["id"] + uri = f"{APPLIANCES};id={appl_id}/{switch.device};id={switch_id}" + if self._stretch_v2: + uri = f"{APPLIANCES};id={appl_id}/{switch.device}" + data = f"<{switch.func_type}><{switch.func}>{state}" + + if model == "relay": + locator = ( + f'appliance[@id="{appl_id}"]/{switch.actuator}/{switch.func_type}/lock' + ) + lock_state = self._appliances.find(locator).text + # Don't bother switching a relay when the corresponding lock-state is true + if lock_state == "true": + return False + await self._request(uri, method="put", data=data) + return True + + await self._request(uri, method="put", data=data) + return True + + async def _set_preset_legacy(self, preset): + """Set the given Preset on the relevant Thermostat - from DOMAIN_OBJECTS.""" + locator = f'rule/directives/when/then[@icon="{preset}"].../.../...' + rule = self._domain_objects.find(locator) + if rule is None: + return False + + uri = f"{RULES}" + data = f'true' + + await self._request(uri, method="put", data=data) + return True + + async def delete_notification(self): + """Delete the active Plugwise Notification.""" + uri = f"{NOTIFICATIONS}" + + await self._request(uri, method="delete") + return True + + +class Smile(SmileConnect, SmileHelper): + """The Plugwise Smile main class.""" + def __init__( + self, + host, + password, + username=DEFAULT_USERNAME, + port=DEFAULT_PORT, + timeout=DEFAULT_TIMEOUT, + websession: aiohttp.ClientSession = None, + ): + super().__init__( + host, + password, + username, + port, + timeout, + websession, + ) + + self._active_device_present = None + self._appl_data = {} + self._appliances = None + self._domain_objects = None + self._heater_id = None + self._home_location = None + self._locations = None + self._modules = None + self._smile_legacy = False + self._stretch_v2 = False + self._stretch_v3 = False + self._thermo_locs = None + + self.gateway_id = None + self.notifications = {} + self.smile_hostname = None + self.smile_name = None + self.smile_type = None + self.smile_version = () + + self.gw_devices = {} + + async def connect(self): + """Connect to Plugwise device and determine its name, type and version.""" + + connected = await self._connect() + return connected + def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). When conditions are met, the plugwise_notification binary_sensor @@ -447,172 +668,4 @@ def single_master_thermostat(self): return True return False - async def _set_schedule_state_legacy(self, name, state): - """Helper-function for set_schedule_state().""" - schema_rule_id = None - for rule in self._domain_objects.findall("rule"): - if rule.find("name").text == name: - schema_rule_id = rule.attrib["id"] - - if schema_rule_id is None: - return False - - template_id = None - state = str(state) - locator = f'.//*[@id="{schema_rule_id}"]/template' - for rule in self._domain_objects.findall(locator): - template_id = rule.attrib["id"] - - uri = f"{RULES};id={schema_rule_id}" - data = ( - "{state}' - ) - - await self._request(uri, method="put", data=data) - return True - - async def set_schedule_state(self, loc_id, name, state): - """Set the Schedule, with the given name, on the relevant Thermostat. - Determined from - DOMAIN_OBJECTS. - """ - if self._smile_legacy: - return await self._set_schedule_state_legacy(name, state) - - schema_rule_ids = self._rule_ids_by_name(str(name), loc_id) - if schema_rule_ids == {} or schema_rule_ids is None: - return False - - for schema_rule_id, location_id in schema_rule_ids.items(): - template_id = None - if location_id == loc_id: - state = str(state) - locator = f'.//*[@id="{schema_rule_id}"]/template' - for rule in self._domain_objects.findall(locator): - template_id = rule.attrib["id"] - - uri = f"{RULES};id={schema_rule_id}" - data = ( - "{state}' - ) - - await self._request(uri, method="put", data=data) - - return True - - async def set_preset(self, loc_id, preset): - """Set the given Preset on the relevant Thermostat - from LOCATIONS.""" - if self._smile_legacy: - return await self._set_preset_legacy(preset) - - current_location = self._locations.find(f'location[@id="{loc_id}"]') - location_name = current_location.find("name").text - location_type = current_location.find("type").text - - if preset not in self._presets(loc_id): - return False - - uri = f"{LOCATIONS};id={loc_id}" - data = ( - "{location_name}{location_type}' - f"{preset}" - ) - - await self._request(uri, method="put", data=data) - return True - - async def set_temperature(self, loc_id, temperature): - """Set the given Temperature on the relevant Thermostat.""" - temperature = str(temperature) - uri = self._temperature_uri(loc_id) - data = ( - "" - f"{temperature}" - ) - - await self._request(uri, method="put", data=data) - return True - - async def _set_groupswitch_member_state(self, members, state, switch): - """Helper-function for set_switch_state() . - Set the given State of the relevant Switch within a group of members. - """ - for member in members: - locator = f'appliance[@id="{member}"]/{switch.actuator}/{switch.func_type}' - switch_id = self._appliances.find(locator).attrib["id"] - uri = f"{APPLIANCES};id={member}/{switch.device};id={switch_id}" - if self._stretch_v2: - uri = f"{APPLIANCES};id={member}/{switch.device}" - state = str(state) - data = f"<{switch.func_type}><{switch.func}>{state}" - - await self._request(uri, method="put", data=data) - - return True - - async def set_switch_state(self, appl_id, members, model, state): - """Set the given State of the relevant Switch.""" - switch = Munch() - switch.actuator = "actuator_functionalities" - switch.device = "relay" - switch.func_type = "relay_functionality" - switch.func = "state" - if model == "dhw_cm_switch": - switch.device = "toggle" - switch.func_type = "toggle_functionality" - - if model == "lock": - switch.func = "lock" - state = "false" if state == "off" else "true" - - if self._stretch_v2: - switch.actuator = "actuators" - switch.func_type = "relay" - - if members is not None: - return await self._set_groupswitch_member_state(members, state, switch) - - locator = f'appliance[@id="{appl_id}"]/{switch.actuator}/{switch.func_type}' - switch_id = self._appliances.find(locator).attrib["id"] - uri = f"{APPLIANCES};id={appl_id}/{switch.device};id={switch_id}" - if self._stretch_v2: - uri = f"{APPLIANCES};id={appl_id}/{switch.device}" - data = f"<{switch.func_type}><{switch.func}>{state}" - if model == "relay": - locator = ( - f'appliance[@id="{appl_id}"]/{switch.actuator}/{switch.func_type}/lock' - ) - lock_state = self._appliances.find(locator).text - # Don't bother switching a relay when the corresponding lock-state is true - if lock_state == "true": - return False - await self._request(uri, method="put", data=data) - return True - - await self._request(uri, method="put", data=data) - return True - - async def _set_preset_legacy(self, preset): - """Set the given Preset on the relevant Thermostat - from DOMAIN_OBJECTS.""" - locator = f'rule/directives/when/then[@icon="{preset}"].../.../...' - rule = self._domain_objects.find(locator) - if rule is None: - return False - - uri = f"{RULES}" - data = f'true' - - await self._request(uri, method="put", data=data) - return True - - async def delete_notification(self): - """Delete the active Plugwise Notification.""" - uri = f"{NOTIFICATIONS}" - - await self._request(uri, method="delete") - return True From d9e7e7d4aef5579cc159b23884e96dae6374a82b Mon Sep 17 00:00:00 2001 From: autoblack Date: Mon, 30 Aug 2021 17:11:08 +0000 Subject: [PATCH 03/65] fixup: more_classes Python code reformatted using Black --- plugwise/helper.py | 2 +- plugwise/smile.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 9bb8da3ba..5afb46c1b 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -320,7 +320,7 @@ class SmileHelper: """The SmileHelper class.""" def __init__(self): - + self._cp_state = None self._loc_data = {} diff --git a/plugwise/smile.py b/plugwise/smile.py index b8340e95a..f68496826 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -426,6 +426,7 @@ async def delete_notification(self): class Smile(SmileConnect, SmileHelper): """The Plugwise Smile main class.""" + def __init__( self, host, @@ -434,7 +435,7 @@ def __init__( port=DEFAULT_PORT, timeout=DEFAULT_TIMEOUT, websession: aiohttp.ClientSession = None, - ): + ): super().__init__( host, password, @@ -667,5 +668,3 @@ def single_master_thermostat(self): if count == 1: return True return False - - From 703e7fec4b8d71d90f182b76615d5861a4e56b99 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 30 Aug 2021 19:17:52 +0200 Subject: [PATCH 04/65] Isort fix --- plugwise/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 5afb46c1b..331d918ba 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -1,11 +1,11 @@ """Use of this source code is governed by the MIT license found in the LICENSE file. Plugwise Smile protocol helpers. """ -import aiohttp import asyncio import datetime as dt import logging +import aiohttp from async_timeout import timeout from dateutil import tz from dateutil.parser import parse From d717a6385df2a30a092946fb8cffc39e39ef2f9d Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 30 Aug 2021 19:34:58 +0200 Subject: [PATCH 05/65] Move close_connection --- plugwise/helper.py | 4 ---- plugwise/smile.py | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 331d918ba..181204a91 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -311,10 +311,6 @@ async def _request( return await self._request_validate(resp, method) - async def close_connection(self): - """Close the Plugwise connection.""" - await self._websession.close() - class SmileHelper: """The SmileHelper class.""" diff --git a/plugwise/smile.py b/plugwise/smile.py index f68496826..59a31e81f 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -95,6 +95,10 @@ async def _connect(self): return True + async def close_connection(self): + """Close the Plugwise connection.""" + await self._websession.close() + async def _smile_detect_legacy(self, result, dsmrmain): """Helper-function for _smile_detect().""" network = result.find(".//module/protocols/network_router/network") From 6a003525feb6dc65df7e30b487231c29aaba91de Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 30 Aug 2021 19:49:04 +0200 Subject: [PATCH 06/65] Revert async_timeout change --- plugwise/helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 181204a91..4b9593515 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -6,7 +6,7 @@ import logging import aiohttp -from async_timeout import timeout +import async_timeout from dateutil import tz from dateutil.parser import parse from defusedxml import ElementTree as etree @@ -289,7 +289,7 @@ async def _request( url = f"{self._endpoint}{command}" try: - with timeout(self._timeout): + with async_timeout.timeout(self._timeout): if method == "get": # Work-around for Stretchv2, should not hurt the other smiles headers = {"Accept-Encoding": "gzip"} From 3d500d8ffac63f168e5c46246d10447602fcf29f Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 30 Aug 2021 19:34:58 +0200 Subject: [PATCH 07/65] Revert "Move close_connection" This reverts commit d717a6385df2a30a092946fb8cffc39e39ef2f9d. --- plugwise/helper.py | 4 ++++ plugwise/smile.py | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 4b9593515..c8a98935b 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -311,6 +311,10 @@ async def _request( return await self._request_validate(resp, method) + async def close_connection(self): + """Close the Plugwise connection.""" + await self._websession.close() + class SmileHelper: """The SmileHelper class.""" diff --git a/plugwise/smile.py b/plugwise/smile.py index 59a31e81f..f68496826 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -95,10 +95,6 @@ async def _connect(self): return True - async def close_connection(self): - """Close the Plugwise connection.""" - await self._websession.close() - async def _smile_detect_legacy(self, result, dsmrmain): """Helper-function for _smile_detect().""" network = result.find(".//module/protocols/network_router/network") From 279f9674c0c0fba38bfe2624d1a3cbbe997a4eed Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 31 Aug 2021 09:30:35 +0200 Subject: [PATCH 08/65] Correct line format --- plugwise/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index c8a98935b..4031fc1ee 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -289,7 +289,7 @@ async def _request( url = f"{self._endpoint}{command}" try: - with async_timeout.timeout(self._timeout): + async with async_timeout.timeout(self._timeout): if method == "get": # Work-around for Stretchv2, should not hurt the other smiles headers = {"Accept-Encoding": "gzip"} From 48179f6b5ff0180d7a1f71b9d8d206364ff92f1f Mon Sep 17 00:00:00 2001 From: Bouwe Date: Thu, 26 Aug 2021 18:11:08 +0200 Subject: [PATCH 09/65] Provide return for use of DUC --- plugwise/smile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugwise/smile.py b/plugwise/smile.py index c641fbec2..cd23821b7 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -251,6 +251,8 @@ async def update_gw_devices(self): data, self.gw_devices, dev_dict, dev_id, "switches", key ) + return self.gw_devices + def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). When conditions are met, the plugwise_notification binary_sensor From 2ebe778e3f1095fd428f735426acc6b68e4127a0 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Thu, 26 Aug 2021 18:34:49 +0200 Subject: [PATCH 10/65] Simplify update-function name --- plugwise/helper.py | 8 ++++---- plugwise/smile.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index ac77ac0b0..766b2ad5e 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -68,7 +68,7 @@ def device_state_updater(data, devs, d_id, d_dict): - """Helper-function for _update_gw_devices(). + """Helper-function for update(). Update the Device_State sensor state. """ for idx, item in enumerate(d_dict["sensors"]): @@ -114,7 +114,7 @@ def update_device_state(data, d_dict): def pw_notification_updater(devs, d_id, d_dict, notifs): - """Helper-function for _update_gw_devices(). + """Helper-function for update(). Update the PW_Notification binary_sensor state. """ for idx, item in enumerate(d_dict["binary_sensors"]): @@ -123,7 +123,7 @@ def pw_notification_updater(devs, d_id, d_dict, notifs): def update_helper(data, devs, d_dict, d_id, e_type, key): - """Helper-function for _update_gw_devices().""" + """Helper-function for update().""" for dummy in d_dict[e_type]: if key != dummy[ATTR_ID]: continue @@ -681,7 +681,7 @@ def _rule_ids_by_tag(self, tag, loc_id): return schema_ids async def _update_domain_objects(self): - """Helper-function for smile.py: full_update_device() and update_gw_devices(). + """Helper-function for smile.py: full_update_device() and update(). Request domain_objects data. """ self._domain_objects = await self._request(DOMAIN_OBJECTS) diff --git a/plugwise/smile.py b/plugwise/smile.py index cd23821b7..231e78650 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -215,7 +215,7 @@ async def _full_update_device(self): if self.smile_type != "power": await self._update_domain_objects() - async def update_gw_devices(self): + async def update(self): """Perform an incremental update for updating the various device states.""" if self.smile_type != "power": await self._update_domain_objects() @@ -391,7 +391,7 @@ def _device_data_climate(self, details, device_data): return device_data def _get_device_data(self, dev_id): - """Helper-function for _all_device_data() and update_gw_devices(). + """Helper-function for _all_device_data() and update(). Provide device-data, based on Location ID (= dev_id), from APPLIANCES. """ devices = self._devices From 2c4f1e44a1c3e50565eab674354f76037e528d8f Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 16:48:00 +0200 Subject: [PATCH 11/65] Extend output of update() --- plugwise/smile.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 231e78650..cb97c2df7 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -226,6 +226,8 @@ async def update(self): if not (self.smile_type == "power" and self._smile_legacy): self._appliances = await self._request(APPLIANCES) + self.gw_data["notifications"] = self.notifications + for dev_id, dev_dict in self.gw_devices.items(): data = self._get_device_data(dev_id) for key, value in list(data.items()): @@ -251,7 +253,7 @@ async def update(self): data, self.gw_devices, dev_dict, dev_id, "switches", key ) - return self.gw_devices + return {**self.gw_data, **self.gw_devices} def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). @@ -270,6 +272,7 @@ def _all_device_data(self): """Helper-function for get_all_devices(). Collect initial data for each device and add to self.gw_devices. """ + self.gw_data = {} dev_id_list = [] dev_and_data_list = [] for dev_id, dev_dict in self._devices.items(): @@ -294,6 +297,12 @@ def _all_device_data(self): self.gw_devices = dict(zip(dev_id_list, dev_and_data_list)) + self.gw_data["gateway"] = self.gateway_id + self.gw_data["heater"] = self._heater_id + self.gw_data["name"] = self.smile_name + self.gw_data["active_device"] = self._active_device_present + self.gw_data["single_master"] = self.single_master_thermostat() + def get_all_devices(self): """Determine the devices present from the obtained XML-data.""" self._devices = {} From fdc00d1710e37fb85833b0f97a3e2de88d98eb49 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:17:45 +0200 Subject: [PATCH 12/65] Use coordinator.data instead of api --- plugwise/entities.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugwise/entities.py b/plugwise/entities.py index 926d83d4f..5359aa185 100644 --- a/plugwise/entities.py +++ b/plugwise/entities.py @@ -21,10 +21,10 @@ class GWBinarySensor: """ Represent the Plugwise Smile/Stretch binary_sensor.""" - def __init__(self, api, dev_id, binary_sensor): + def __init__(self, coordinator, dev_id, binary_sensor): """Initialize the Gateway.""" - self._api = api self._binary_sensor = binary_sensor + self._cdata = coordinator.data self._dev_id = dev_id self._attributes = {} self._icon = None @@ -65,7 +65,7 @@ def _icon_selector(arg, state): def update_data(self): """Handle update callbacks.""" - data = self._api.gw_devices[self._dev_id] + data = self._cdata[self._dev_id] for key, _ in data.items(): if key != "binary_sensors": @@ -81,7 +81,7 @@ def update_data(self): if self._binary_sensor != "plugwise_notification": continue - notify = self._api.notifications + notify = self._cdata["notifications"] self._notification = {} for severity in SEVERITIES: self._attributes[f"{severity.upper()}_msg"] = [] @@ -98,10 +98,10 @@ def update_data(self): class GWThermostat: """Represent a Plugwise Thermostat Device.""" - def __init__(self, api, dev_id): + def __init__(self, coordinator, dev_id): """Initialize the Thermostat.""" - self._api = api + self._cdata = coordinator.data self._compressor_state = None self._cooling_state = None self._dev_id = dev_id @@ -120,9 +120,9 @@ def __init__(self, api, dev_id): self._smile_class = None self._temperature = None - self._active_device = self._api._active_device_present - self._heater_id = self._api._heater_id - self._sm_thermostat = self._api.single_master_thermostat() + self._active_device = self._cdata["active_device"] + self._heater_id = self._cdata["heater"] + self._sm_thermostat = self._cdata["single_master"] @property def compressor_state(self): @@ -186,7 +186,7 @@ def extra_state_attributes(self): def update_data(self): """Handle update callbacks.""" - data = self._api.gw_devices[self._dev_id] + data = self._cdata[self._dev_id] # current & target_temps, heater_central data when required s_list = data["sensors"] @@ -197,7 +197,7 @@ def update_data(self): self._setpoint = s_list[idx][ATTR_STATE] self._schedule_temp = data.get("schedule_temperature") if self._active_device: - hc_data = self._api.gw_devices[self._heater_id] + hc_data = self._cdata[self._heater_id] self._compressor_state = hc_data.get("compressor_state") if self._sm_thermostat: self._cooling_state = hc_data.get("cooling_state") From 22afa27b33e65b9909e1be059ed40707af095d31 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:22:06 +0200 Subject: [PATCH 13/65] Adapt testing to changes in update() --- tests/test_smile.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index 6dccdf686..ed4ead7cf 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -407,14 +407,13 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): ] bsw_list = ["binary_sensors", "sensors", "switches"] smile.get_all_devices() - await smile.update_gw_devices() - device_list = smile.gw_devices + device_list = await smile.update() self._write_json("all_devices", device_list) - self._write_json("notifications", smile.notifications) + self._write_json("notifications", device_list["notifications"] location_list = smile._thermo_locs - _LOGGER.info("Gateway id = %s", smile.gateway_id) + _LOGGER.info("Gateway id = %s", device_list["gateway"]) _LOGGER.info("Hostname = %s", smile.smile_hostname) self.show_setup(location_list, device_list) pp4 = PrettyPrinter(indent=4) @@ -432,7 +431,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): for dev_id, details in device_list.items(): if testdevice == dev_id: thermostat = None - data = smile.gw_devices[dev_id] + data = device_list[dev_id] _LOGGER.info( "%s", "- Testing data for device {} ({})".format( @@ -441,7 +440,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): ) _LOGGER.info(" + Device data: %s", data) if data["class"] in MASTER_THERMOSTATS: - thermostat = pw_entities.GWThermostat(smile, dev_id) + thermostat = pw_entities.GWThermostat(device_list, dev_id) thermostat.update_data() _LOGGER.info( "%s", @@ -476,7 +475,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): if measure_key == "binary_sensors": b_sensor = None b_sensor = pw_entities.GWBinarySensor( - smile, dev_id, a_item["id"] + device_list, dev_id, a_item["id"] ) b_sensor.update_data() assert ( From 38a28e25822750a1f5cab152853bae2163933952 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:23:10 +0200 Subject: [PATCH 14/65] Add missing ")" --- tests/test_smile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index ed4ead7cf..dcb04da38 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -409,7 +409,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): smile.get_all_devices() device_list = await smile.update() self._write_json("all_devices", device_list) - self._write_json("notifications", device_list["notifications"] + self._write_json("notifications", device_list["notifications"]) location_list = smile._thermo_locs From f133547fd48f7d96d37892cafa58c6978315bcf0 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:30:19 +0200 Subject: [PATCH 15/65] Return of list of dicts, easier to separate --- plugwise/smile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index cb97c2df7..1f88c72db 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -253,7 +253,7 @@ async def update(self): data, self.gw_devices, dev_dict, dev_id, "switches", key ) - return {**self.gw_data, **self.gw_devices} + return [self.gw_data, self.gw_devices] def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). From 31b0fffc570da122814de976b49f92f04ce35e52 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:35:48 +0200 Subject: [PATCH 16/65] Adapt testing for output as list --- tests/test_smile.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index dcb04da38..9a128bda0 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -407,13 +407,15 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): ] bsw_list = ["binary_sensors", "sensors", "switches"] smile.get_all_devices() - device_list = await smile.update() + data = await smile.update() + extra = data[0] + device_list = data[1] self._write_json("all_devices", device_list) - self._write_json("notifications", device_list["notifications"]) + self._write_json("notifications", extra["notifications"]) location_list = smile._thermo_locs - _LOGGER.info("Gateway id = %s", device_list["gateway"]) + _LOGGER.info("Gateway id = %s", extra["gateway"]) _LOGGER.info("Hostname = %s", smile.smile_hostname) self.show_setup(location_list, device_list) pp4 = PrettyPrinter(indent=4) @@ -440,7 +442,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): ) _LOGGER.info(" + Device data: %s", data) if data["class"] in MASTER_THERMOSTATS: - thermostat = pw_entities.GWThermostat(device_list, dev_id) + thermostat = pw_entities.GWThermostat(extra, dev_id) thermostat.update_data() _LOGGER.info( "%s", @@ -475,7 +477,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): if measure_key == "binary_sensors": b_sensor = None b_sensor = pw_entities.GWBinarySensor( - device_list, dev_id, a_item["id"] + extra, dev_id, a_item["id"] ) b_sensor.update_data() assert ( From 02b59d31131ac6969eadbf9e260a918dc7ec2f12 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:39:54 +0200 Subject: [PATCH 17/65] Entities.py: correct input --- plugwise/entities.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugwise/entities.py b/plugwise/entities.py index 5359aa185..320aa90a3 100644 --- a/plugwise/entities.py +++ b/plugwise/entities.py @@ -21,10 +21,10 @@ class GWBinarySensor: """ Represent the Plugwise Smile/Stretch binary_sensor.""" - def __init__(self, coordinator, dev_id, binary_sensor): + def __init__(self, data, dev_id, binary_sensor): """Initialize the Gateway.""" self._binary_sensor = binary_sensor - self._cdata = coordinator.data + self._data = data self._dev_id = dev_id self._attributes = {} self._icon = None @@ -65,7 +65,7 @@ def _icon_selector(arg, state): def update_data(self): """Handle update callbacks.""" - data = self._cdata[self._dev_id] + data = self._data[self._dev_id] for key, _ in data.items(): if key != "binary_sensors": @@ -81,7 +81,7 @@ def update_data(self): if self._binary_sensor != "plugwise_notification": continue - notify = self._cdata["notifications"] + notify = self._data["notifications"] self._notification = {} for severity in SEVERITIES: self._attributes[f"{severity.upper()}_msg"] = [] @@ -98,12 +98,12 @@ def update_data(self): class GWThermostat: """Represent a Plugwise Thermostat Device.""" - def __init__(self, coordinator, dev_id): + def __init__(self, data, dev_id): """Initialize the Thermostat.""" - self._cdata = coordinator.data self._compressor_state = None self._cooling_state = None + self._data = data self._dev_id = dev_id self._extra_state_attributes = None self._get_presets = None @@ -120,9 +120,9 @@ def __init__(self, coordinator, dev_id): self._smile_class = None self._temperature = None - self._active_device = self._cdata["active_device"] - self._heater_id = self._cdata["heater"] - self._sm_thermostat = self._cdata["single_master"] + self._active_device = self._data["active_device"] + self._heater_id = self._data["heater"] + self._sm_thermostat = self._data["single_master"] @property def compressor_state(self): @@ -186,7 +186,7 @@ def extra_state_attributes(self): def update_data(self): """Handle update callbacks.""" - data = self._cdata[self._dev_id] + data = self._data[self._dev_id] # current & target_temps, heater_central data when required s_list = data["sensors"] @@ -197,7 +197,7 @@ def update_data(self): self._setpoint = s_list[idx][ATTR_STATE] self._schedule_temp = data.get("schedule_temperature") if self._active_device: - hc_data = self._cdata[self._heater_id] + hc_data = self._data[self._heater_id] self._compressor_state = hc_data.get("compressor_state") if self._sm_thermostat: self._cooling_state = hc_data.get("cooling_state") From 140d61808a73485994979087ce8f64d6cf6e86f5 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:45:00 +0200 Subject: [PATCH 18/65] Take data from the correct dict --- plugwise/entities.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugwise/entities.py b/plugwise/entities.py index 320aa90a3..013fa6885 100644 --- a/plugwise/entities.py +++ b/plugwise/entities.py @@ -65,7 +65,7 @@ def _icon_selector(arg, state): def update_data(self): """Handle update callbacks.""" - data = self._data[self._dev_id] + data = self._data[1][self._dev_id] for key, _ in data.items(): if key != "binary_sensors": @@ -81,7 +81,7 @@ def update_data(self): if self._binary_sensor != "plugwise_notification": continue - notify = self._data["notifications"] + notify = self._data[0]["notifications"] self._notification = {} for severity in SEVERITIES: self._attributes[f"{severity.upper()}_msg"] = [] @@ -120,9 +120,9 @@ def __init__(self, data, dev_id): self._smile_class = None self._temperature = None - self._active_device = self._data["active_device"] - self._heater_id = self._data["heater"] - self._sm_thermostat = self._data["single_master"] + self._active_device = self._data[0]["active_device"] + self._heater_id = self._data[0]["heater"] + self._sm_thermostat = self._data[0]["single_master"] @property def compressor_state(self): @@ -186,7 +186,7 @@ def extra_state_attributes(self): def update_data(self): """Handle update callbacks.""" - data = self._data[self._dev_id] + data = self._data[1][self._dev_id] # current & target_temps, heater_central data when required s_list = data["sensors"] From cddf35e659961e985edcc10000b999492608b669 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:46:29 +0200 Subject: [PATCH 19/65] Correct varnames --- tests/test_smile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index 9a128bda0..3abd05bcc 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -442,7 +442,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): ) _LOGGER.info(" + Device data: %s", data) if data["class"] in MASTER_THERMOSTATS: - thermostat = pw_entities.GWThermostat(extra, dev_id) + thermostat = pw_entities.GWThermostat(data, dev_id) thermostat.update_data() _LOGGER.info( "%s", @@ -477,7 +477,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): if measure_key == "binary_sensors": b_sensor = None b_sensor = pw_entities.GWBinarySensor( - extra, dev_id, a_item["id"] + data, dev_id, a_item["id"] ) b_sensor.update_data() assert ( From 3b4a665e04d6599fbf894b752525ea837b965ccc Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:53:08 +0200 Subject: [PATCH 20/65] Don't use data double --- tests/test_smile.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index 3abd05bcc..7dcae4bc5 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -433,15 +433,15 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): for dev_id, details in device_list.items(): if testdevice == dev_id: thermostat = None - data = device_list[dev_id] + dev_data = device_list[dev_id] _LOGGER.info( "%s", "- Testing data for device {} ({})".format( details["name"], dev_id ), ) - _LOGGER.info(" + Device data: %s", data) - if data["class"] in MASTER_THERMOSTATS: + _LOGGER.info(" + Device data: %s", dev_data) + if dev_data["class"] in MASTER_THERMOSTATS: thermostat = pw_entities.GWThermostat(data, dev_id) thermostat.update_data() _LOGGER.info( @@ -463,7 +463,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): tests -= 1 for a, a_item in enumerate(measure_assert): tests += 1 - for b, b_item in enumerate(data[measure_key]): + for b, b_item in enumerate(dev_data[measure_key]): if a_item["id"] != b_item["id"]: continue @@ -493,9 +493,9 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): == measure_assert ) else: - if measure_key in data: + if measure_key in dev_data: asserts += 1 - assert data[measure_key] == measure_assert + assert dev_data[measure_key] == measure_assert assert tests == asserts From 83f52fc8bc0ae1501789b09df0242b5cc9f60e49 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 17:54:26 +0200 Subject: [PATCH 21/65] Add missing index --- plugwise/entities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/entities.py b/plugwise/entities.py index 013fa6885..2bb82c093 100644 --- a/plugwise/entities.py +++ b/plugwise/entities.py @@ -197,7 +197,7 @@ def update_data(self): self._setpoint = s_list[idx][ATTR_STATE] self._schedule_temp = data.get("schedule_temperature") if self._active_device: - hc_data = self._data[self._heater_id] + hc_data = self._data[1][self._heater_id] self._compressor_state = hc_data.get("compressor_state") if self._sm_thermostat: self._cooling_state = hc_data.get("cooling_state") From c7d26095f8a43a717d65467db84705089c7c160f Mon Sep 17 00:00:00 2001 From: Bouwe Date: Fri, 27 Aug 2021 20:13:33 +0200 Subject: [PATCH 22/65] Move init to top level --- plugwise/smile.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 1f88c72db..d6edf9fce 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -82,7 +82,7 @@ async def _create_session() -> aiohttp.ClientSession: self._endpoint = f"http://{self._host}:{str(self._port)}" self._timeout = timeout - self.gw_devices = {} + self.gw_data = self.gw_devices = {} async def connect(self): """Connect to Plugwise device and determine its name, type and version.""" @@ -270,9 +270,8 @@ def _append_special(self, data, d_id, bs_list, s_list): def _all_device_data(self): """Helper-function for get_all_devices(). - Collect initial data for each device and add to self.gw_devices. + Collect initial data for each device and add to self.gw_data and self.gw_devices. """ - self.gw_data = {} dev_id_list = [] dev_and_data_list = [] for dev_id, dev_dict in self._devices.items(): From 7557384aa69db8dc58cfda4466780f5bc0fcb2bf Mon Sep 17 00:00:00 2001 From: Bouwe Date: Sat, 28 Aug 2021 19:15:08 +0200 Subject: [PATCH 23/65] Correct function-name, other improvements --- plugwise/smile.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index d6edf9fce..de4f5caf7 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -215,7 +215,7 @@ async def _full_update_device(self): if self.smile_type != "power": await self._update_domain_objects() - async def update(self): + async def async_update(self): """Perform an incremental update for updating the various device states.""" if self.smile_type != "power": await self._update_domain_objects() @@ -226,6 +226,7 @@ async def update(self): if not (self.smile_type == "power" and self._smile_legacy): self._appliances = await self._request(APPLIANCES) + data = [] self.gw_data["notifications"] = self.notifications for dev_id, dev_dict in self.gw_devices.items(): @@ -253,7 +254,8 @@ async def update(self): data, self.gw_devices, dev_dict, dev_id, "switches", key ) - return [self.gw_data, self.gw_devices] + data = [self.gw_data, self.gw_devices] + return data def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). @@ -296,11 +298,11 @@ def _all_device_data(self): self.gw_devices = dict(zip(dev_id_list, dev_and_data_list)) - self.gw_data["gateway"] = self.gateway_id - self.gw_data["heater"] = self._heater_id - self.gw_data["name"] = self.smile_name - self.gw_data["active_device"] = self._active_device_present - self.gw_data["single_master"] = self.single_master_thermostat() + self.gw_data["gateway_id"] = self.gateway_id + self.gw_data["heater_id"] = self._heater_id + self.gw_data["smile_name"] = self.smile_name + self.gw_data["heat_cool_device"] = self._active_device_present + self.gw_data["single_thermostat"] = self.single_master_thermostat() def get_all_devices(self): """Determine the devices present from the obtained XML-data.""" From de201899b9456248fe63956d9a54d465500cda1e Mon Sep 17 00:00:00 2001 From: Bouwe Date: Sat, 28 Aug 2021 19:23:30 +0200 Subject: [PATCH 24/65] Test: adapt to updates --- tests/test_smile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index 7dcae4bc5..117e72db2 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -407,7 +407,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): ] bsw_list = ["binary_sensors", "sensors", "switches"] smile.get_all_devices() - data = await smile.update() + data = await smile.async_update() extra = data[0] device_list = data[1] self._write_json("all_devices", device_list) @@ -415,7 +415,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): location_list = smile._thermo_locs - _LOGGER.info("Gateway id = %s", extra["gateway"]) + _LOGGER.info("Gateway id = %s", extra["gateway_id"]) _LOGGER.info("Hostname = %s", smile.smile_hostname) self.show_setup(location_list, device_list) pp4 = PrettyPrinter(indent=4) From 326185c5780e2bf82df7865b0120fc7bcae764fd Mon Sep 17 00:00:00 2001 From: Bouwe Date: Sat, 28 Aug 2021 19:24:46 +0200 Subject: [PATCH 25/65] Revert return-change --- plugwise/smile.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index de4f5caf7..1eae8a866 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -254,8 +254,7 @@ async def async_update(self): data, self.gw_devices, dev_dict, dev_id, "switches", key ) - data = [self.gw_data, self.gw_devices] - return data + return [self.gw_data, self.gw_devices] def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). From f5b0867f460558391004ad7ed3cebcc6f2709abc Mon Sep 17 00:00:00 2001 From: Bouwe Date: Sat, 28 Aug 2021 19:30:55 +0200 Subject: [PATCH 26/65] Revert more changes --- plugwise/smile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 1eae8a866..a74340223 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -300,8 +300,8 @@ def _all_device_data(self): self.gw_data["gateway_id"] = self.gateway_id self.gw_data["heater_id"] = self._heater_id self.gw_data["smile_name"] = self.smile_name - self.gw_data["heat_cool_device"] = self._active_device_present - self.gw_data["single_thermostat"] = self.single_master_thermostat() + self.gw_data["active_device"] = self._active_device_present + self.gw_data["single_master_thermostat"] = self.single_master_thermostat() def get_all_devices(self): """Determine the devices present from the obtained XML-data.""" From 404e9e0f4486fcc788db49966e5ca26da815a67b Mon Sep 17 00:00:00 2001 From: Bouwe Date: Sat, 28 Aug 2021 19:32:07 +0200 Subject: [PATCH 27/65] Entities.py: adapt to changes --- plugwise/entities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise/entities.py b/plugwise/entities.py index 2bb82c093..5bf14c01c 100644 --- a/plugwise/entities.py +++ b/plugwise/entities.py @@ -121,8 +121,8 @@ def __init__(self, data, dev_id): self._temperature = None self._active_device = self._data[0]["active_device"] - self._heater_id = self._data[0]["heater"] - self._sm_thermostat = self._data[0]["single_master"] + self._heater_id = self._data[0]["heater_id"] + self._sm_thermostat = self._data[0]["single_master_thermostat"] @property def compressor_state(self): From bc197853813b5d56dd62d76c4ebb79ce98dbbfcd Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 13 Sep 2021 15:16:06 +0200 Subject: [PATCH 28/65] Bump to v0.14.5a0 --- plugwise/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 8ad0071e4..d25d4bd73 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -1,6 +1,6 @@ """Plugwise module.""" -__version__ = "0.14.1" +__version__ = "0.14.5a0" from plugwise.smile import Smile from plugwise.stick import Stick From aaf0613f2e39866775757ff4c8e373a20b6e65aa Mon Sep 17 00:00:00 2001 From: autoblack Date: Mon, 13 Sep 2021 13:18:14 +0000 Subject: [PATCH 29/65] fixup: output_to_duc Python code reformatted using Black --- plugwise/smile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index a74340223..bcbcc613c 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -228,7 +228,7 @@ async def async_update(self): data = [] self.gw_data["notifications"] = self.notifications - + for dev_id, dev_dict in self.gw_devices.items(): data = self._get_device_data(dev_id) for key, value in list(data.items()): @@ -254,7 +254,7 @@ async def async_update(self): data, self.gw_devices, dev_dict, dev_id, "switches", key ) - return [self.gw_data, self.gw_devices] + return [self.gw_data, self.gw_devices] def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). From 6e620c294707bf002371fdb5823d04caeeafc9ca Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 13 Sep 2021 16:10:07 +0200 Subject: [PATCH 30/65] Return last_reset attribs, implement state_class = total. --- plugwise/constants.py | 53 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/plugwise/constants.py b/plugwise/constants.py index b695880a4..c90801635 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -2,6 +2,7 @@ # Copied homeassistant.consts ATTR_DEVICE_CLASS = "device_class" +ATTR_LAST_REST = "last_reset" ATTR_NAME = "name" ATTR_STATE = "state" ATTR_STATE_CLASS = "state_class" @@ -572,6 +573,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "battery", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, } @@ -582,6 +584,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -592,6 +595,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: None, ATTR_STATE_CLASS: None, + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: None, } @@ -602,6 +606,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -611,7 +616,8 @@ ATTR_NAME: "Electricity Consumed Interval", ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", - ATTR_STATE_CLASS: "measurement", + ATTR_STATE_CLASS: "total", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -622,6 +628,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total_increasing", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -631,8 +638,9 @@ ATTR_NAME: "Electricity Consumed Off Peak Interval", ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", - ATTR_STATE_CLASS: "measurement", + ATTR_STATE_CLASS: "total", ATTR_ICON: None, + ATTR_LAST_REST: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } EL_CONSUMED_OFF_PEAK_POINT = { @@ -642,6 +650,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -652,6 +661,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total_increasing", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -661,7 +671,8 @@ ATTR_NAME: "Electricity Consumed Peak Interval", ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", - ATTR_STATE_CLASS: "measurement", + ATTR_STATE_CLASS: "total", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -672,6 +683,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -682,6 +694,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -692,6 +705,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -701,7 +715,8 @@ ATTR_NAME: "Electricity Produced Interval", ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", - ATTR_STATE_CLASS: "measurement", + ATTR_STATE_CLASS: "total", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -712,6 +727,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total_increasing", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -721,7 +737,8 @@ ATTR_NAME: "Electricity Produced Off Peak Interval", ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", - ATTR_STATE_CLASS: "measurement", + ATTR_STATE_CLASS: "total", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -732,6 +749,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -742,6 +760,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total_increasing", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -751,7 +770,8 @@ ATTR_NAME: "Electricity Produced Peak Interval", ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", - ATTR_STATE_CLASS: "measurement", + ATTR_STATE_CLASS: "total", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -762,6 +782,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -772,6 +793,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -782,6 +804,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "gas", ATTR_STATE_CLASS: "total_increasing", + ATTR_LAST_REST: None, ATTR_ICON: FLAME_ICON, ATTR_UNIT_OF_MEASUREMENT: VOLUME_CUBIC_METERS, } @@ -791,7 +814,8 @@ ATTR_NAME: "Gas Consumed Interval", ATTR_STATE: None, ATTR_DEVICE_CLASS: "gas", - ATTR_STATE_CLASS: "measurement", + ATTR_STATE_CLASS: "total", + ATTR_LAST_REST: None, ATTR_ICON: FLAME_ICON, ATTR_UNIT_OF_MEASUREMENT: VOLUME_CUBIC_METERS, } @@ -802,6 +826,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "humidity", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, } @@ -812,6 +837,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "illuminance", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: UNIT_LUMEN, } @@ -822,6 +848,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -832,6 +859,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: None, ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: "mdi:percent", ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, } @@ -841,7 +869,8 @@ ATTR_NAME: "Net Electricity Cumulative", ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", - ATTR_STATE_CLASS: "measurement", + ATTR_STATE_CLASS: "total", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -852,6 +881,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -862,6 +892,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -872,6 +903,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -882,6 +914,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -892,6 +925,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: None, ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_KELVIN, } @@ -902,6 +936,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: None, ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: "mdi:valve", ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, } @@ -912,6 +947,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "pressure", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: PRESSURE_BAR, } @@ -922,6 +958,7 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", + ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } From 6c63d64f72f6b96d7422e35a21d45d83df69e3a1 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 13 Sep 2021 16:19:10 +0200 Subject: [PATCH 31/65] Revert removal of last_reset-value --- plugwise/helper.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 766b2ad5e..33f8f66bc 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -1183,9 +1183,14 @@ def _create_lists_from_data(self, data, bs_list, s_list, sw_list): for item in SENSORS: if item[ATTR_ID] == key: data.pop(item[ATTR_ID]) + temp_value = None item[ATTR_STATE] = value - if "interval" in item[ATTR_ID] and isinstance(value, list): - item[ATTR_STATE] = value[0] + if "interval" in item[ATTR_ID]: + if isinstance(value, list): + log_date = value[1] + temp_value = value[0] + item["last_reset"] = log_date + item[ATTR_STATE] = temp_value s_list.append(item) for item in SWITCHES: if item[ATTR_ID] == key: From 8c90db569395ec14ebe431b39f6f67c171de9d61 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 13 Sep 2021 16:20:32 +0200 Subject: [PATCH 32/65] Bump to v0.14.5a1 --- plugwise/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index d25d4bd73..65078dfaa 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -1,6 +1,6 @@ """Plugwise module.""" -__version__ = "0.14.5a0" +__version__ = "0.14.5a1" from plugwise.smile import Smile from plugwise.stick import Stick From 5e5c2f9d800648ac12334b53c360a0c892f78c88 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 13 Sep 2021 16:47:10 +0200 Subject: [PATCH 33/65] Re-add log_date test --- tests/test_smile.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_smile.py b/tests/test_smile.py index 117e72db2..fe931279b 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -472,6 +472,10 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): asserts += 1 assert b_item["state"] == a_item["state"][0] asserts += 1 + assert ( + b_item["last_reset"] == a_item["state"][1] + ) + else: asserts += 1 if measure_key == "binary_sensors": From a4972d3020fc0a80930b0ff7545e997fa912be57 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 13 Sep 2021 19:21:04 +0200 Subject: [PATCH 34/65] Output log_date with UTC-timezone --- plugwise/helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 33f8f66bc..14749adb0 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -736,7 +736,7 @@ def _appliance_measurements(self, appliance, data, measurements): name = f"{measurement}_interval" measure = appliance.find(i_locator).text log_date = parse(appliance.find(i_locator).get("log_date")) - log_date = log_date.astimezone(tz.gettz("UTC")).replace(tzinfo=None) + log_date = log_date.astimezone(pytz.UTC) data[name] = [format_measure(measure, ENERGY_WATT_HOUR), log_date] return data @@ -962,7 +962,7 @@ def _power_data_peak_value(self, loc): loc.net_string = f"net_electricity_{log_found}" val = loc.logs.find(loc.locator).text log_date = parse(loc.logs.find(loc.locator).get("log_date")) - loc.log_date = log_date.astimezone(tz.gettz("UTC")).replace(tzinfo=None) + loc.log_date = log_date.astimezone(pytz.UTC) loc.f_val = power_data_local_format(loc.attrs, loc.key_string, val) return loc From 8a79ef0684fce49e604a2169d9be011b9e4cd608 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 13 Sep 2021 19:24:34 +0200 Subject: [PATCH 35/65] Add tzinfo to datetimes --- tests/test_smile.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index fe931279b..965ee6d79 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -2,6 +2,7 @@ import asyncio import datetime as dt +from dateutil import tz import importlib # Fixture writing @@ -1536,7 +1537,7 @@ async def test_connect_p1v3(self): }, { "id": "electricity_consumed_peak_interval", - "state": [179, dt.datetime(2020, 3, 12, 19, 45)], + "state": [179, dt.datetime(2020, 3, 12, 19, 45, tzinfo=tz.tzutc())], }, {"id": "net_electricity_cumulative", "state": 17965.326}, ] @@ -1782,7 +1783,7 @@ async def test_connect_stretch_v31(self): "sensors": [ { "id": "electricity_consumed_interval", - "state": [0.71, dt.datetime(2020, 9, 6, 12, 00)], + "state": [0.71, dt.datetime(2020, 9, 6, 12, 00, tzinfo=tz.tzutc())], } ] }, @@ -1826,7 +1827,7 @@ async def test_connect_stretch_v23(self): "sensors": [ { "id": "electricity_consumed_interval", - "state": [0.21, dt.datetime(2020, 8, 3, 20, 00)], + "state": [0.21, dt.datetime(2020, 8, 3, 20, 00, tzinfo=tz.tzutc())], } ] }, From 9b1ece94eed688b5b634c76e495ed2803a887d48 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 13 Sep 2021 19:25:18 +0200 Subject: [PATCH 36/65] Bump to v0.14.5a3 --- plugwise/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 65078dfaa..8c0717842 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -1,6 +1,6 @@ """Plugwise module.""" -__version__ = "0.14.5a1" +__version__ = "0.14.5a3" from plugwise.smile import Smile from plugwise.stick import Stick From 07b31b514e8759606dec4136cbdb6cf5fbb304e7 Mon Sep 17 00:00:00 2001 From: autoblack Date: Mon, 13 Sep 2021 17:26:10 +0000 Subject: [PATCH 37/65] fixup: output_to_duc Python code reformatted using Black --- tests/test_smile.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index 965ee6d79..44e87f91b 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -1537,7 +1537,10 @@ async def test_connect_p1v3(self): }, { "id": "electricity_consumed_peak_interval", - "state": [179, dt.datetime(2020, 3, 12, 19, 45, tzinfo=tz.tzutc())], + "state": [ + 179, + dt.datetime(2020, 3, 12, 19, 45, tzinfo=tz.tzutc()), + ], }, {"id": "net_electricity_cumulative", "state": 17965.326}, ] @@ -1783,7 +1786,10 @@ async def test_connect_stretch_v31(self): "sensors": [ { "id": "electricity_consumed_interval", - "state": [0.71, dt.datetime(2020, 9, 6, 12, 00, tzinfo=tz.tzutc())], + "state": [ + 0.71, + dt.datetime(2020, 9, 6, 12, 00, tzinfo=tz.tzutc()), + ], } ] }, @@ -1827,7 +1833,10 @@ async def test_connect_stretch_v23(self): "sensors": [ { "id": "electricity_consumed_interval", - "state": [0.21, dt.datetime(2020, 8, 3, 20, 00, tzinfo=tz.tzutc())], + "state": [ + 0.21, + dt.datetime(2020, 8, 3, 20, 00, tzinfo=tz.tzutc()), + ], } ] }, From 41237f2c31d5b71e89825685d849587e14a7a91f Mon Sep 17 00:00:00 2001 From: Bouwe Date: Mon, 13 Sep 2021 19:29:07 +0200 Subject: [PATCH 38/65] Fix isort error --- tests/test_smile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index 44e87f91b..f306f5e19 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -2,7 +2,6 @@ import asyncio import datetime as dt -from dateutil import tz import importlib # Fixture writing @@ -17,6 +16,7 @@ # Testing import aiohttp +from dateutil import tz import jsonpickle as json import pytest From b90d9988459a9eaccd3b76488516fa17d6f86849 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 10:05:11 +0200 Subject: [PATCH 39/65] Remove all log_date-related code --- plugwise/helper.py | 15 +----------- tests/test_smile.py | 57 +++++++++++---------------------------------- 2 files changed, 14 insertions(+), 58 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 14749adb0..dde1746a3 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -735,9 +735,7 @@ def _appliance_measurements(self, appliance, data, measurements): if appliance.find(i_locator) is not None: name = f"{measurement}_interval" measure = appliance.find(i_locator).text - log_date = parse(appliance.find(i_locator).get("log_date")) - log_date = log_date.astimezone(pytz.UTC) - data[name] = [format_measure(measure, ENERGY_WATT_HOUR), log_date] + data[name] = format_measure(measure, ENERGY_WATT_HOUR) return data @@ -961,8 +959,6 @@ def _power_data_peak_value(self, loc): loc.key_string = f"{loc.measurement}_{log_found}" loc.net_string = f"net_electricity_{log_found}" val = loc.logs.find(loc.locator).text - log_date = parse(loc.logs.find(loc.locator).get("log_date")) - loc.log_date = log_date.astimezone(pytz.UTC) loc.f_val = power_data_local_format(loc.attrs, loc.key_string, val) return loc @@ -1008,8 +1004,6 @@ def _power_data_from_location(self, loc_id): ) direct_data[loc.key_string] = loc.f_val - if "interval" in loc.key_string: - direct_data[loc.key_string] = [loc.f_val, loc.log_date] if direct_data != {}: return direct_data @@ -1183,14 +1177,7 @@ def _create_lists_from_data(self, data, bs_list, s_list, sw_list): for item in SENSORS: if item[ATTR_ID] == key: data.pop(item[ATTR_ID]) - temp_value = None item[ATTR_STATE] = value - if "interval" in item[ATTR_ID]: - if isinstance(value, list): - log_date = value[1] - temp_value = value[0] - item["last_reset"] = log_date - item[ATTR_STATE] = temp_value s_list.append(item) for item in SWITCHES: if item[ATTR_ID] == key: diff --git a/tests/test_smile.py b/tests/test_smile.py index f306f5e19..2e2c67dd8 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -1,7 +1,6 @@ """Test Plugwise Home Assistant module and generate test JSON fixtures.""" import asyncio -import datetime as dt import importlib # Fixture writing @@ -468,29 +467,19 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): if a_item["id"] != b_item["id"]: continue - if isinstance(a_item["state"], list): - tests += 1 - asserts += 1 - assert b_item["state"] == a_item["state"][0] - asserts += 1 + asserts += 1 + if measure_key == "binary_sensors": + b_sensor = None + b_sensor = pw_entities.GWBinarySensor( + data, dev_id, a_item["id"] + ) + b_sensor.update_data() assert ( - b_item["last_reset"] == a_item["state"][1] + self.bs_prop_selector("state", b_sensor) + == a_item["state"] ) - else: - asserts += 1 - if measure_key == "binary_sensors": - b_sensor = None - b_sensor = pw_entities.GWBinarySensor( - data, dev_id, a_item["id"] - ) - b_sensor.update_data() - assert ( - self.bs_prop_selector("state", b_sensor) - == a_item["state"] - ) - else: - assert b_item["state"] == a_item["state"] + assert b_item["state"] == a_item["state"] elif self.th_prop_selector(measure_key, thermostat): asserts += 1 assert ( @@ -1535,13 +1524,7 @@ async def test_connect_p1v3(self): "id": "electricity_consumed_off_peak_cumulative", "state": 10263.159, }, - { - "id": "electricity_consumed_peak_interval", - "state": [ - 179, - dt.datetime(2020, 3, 12, 19, 45, tzinfo=tz.tzutc()), - ], - }, + {"id": "electricity_consumed_peak_interval", "state": 179}, {"id": "net_electricity_cumulative", "state": 17965.326}, ] } @@ -1784,13 +1767,7 @@ async def test_connect_stretch_v31(self): # Vaatwasser "aac7b735042c4832ac9ff33aae4f453b": { "sensors": [ - { - "id": "electricity_consumed_interval", - "state": [ - 0.71, - dt.datetime(2020, 9, 6, 12, 00, tzinfo=tz.tzutc()), - ], - } + {"id": "electricity_consumed_interval", "state": 0.71} ] }, } @@ -1830,15 +1807,7 @@ async def test_connect_stretch_v23(self): }, # Wasdroger 043AECA "fd1b74f59e234a9dae4e23b2b5cf07ed": { - "sensors": [ - { - "id": "electricity_consumed_interval", - "state": [ - 0.21, - dt.datetime(2020, 8, 3, 20, 00, tzinfo=tz.tzutc()), - ], - } - ] + "sensors": [{"id": "electricity_consumed_interval", "state": 0.21}] }, } From b2ec27c1b7dd4506070ca2aa73f6fd770381e150 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 10:06:19 +0200 Subject: [PATCH 40/65] Bump to v0.14.5a4 --- plugwise/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 8c0717842..59da97287 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -1,6 +1,6 @@ """Plugwise module.""" -__version__ = "0.14.5a3" +__version__ = "0.14.5a4" from plugwise.smile import Smile from plugwise.stick import Stick From 662304ce8742417827f9a4c12e094bc9ef145beb Mon Sep 17 00:00:00 2001 From: autoblack Date: Tue, 14 Sep 2021 08:07:07 +0000 Subject: [PATCH 41/65] fixup: output_to_duc Python code reformatted using Black --- tests/test_smile.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index 2e2c67dd8..615497142 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -1766,9 +1766,7 @@ async def test_connect_stretch_v31(self): }, # Vaatwasser "aac7b735042c4832ac9ff33aae4f453b": { - "sensors": [ - {"id": "electricity_consumed_interval", "state": 0.71} - ] + "sensors": [{"id": "electricity_consumed_interval", "state": 0.71}] }, } From fd18e759a01069baf275ac65e030a838560b418b Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 10:11:51 +0200 Subject: [PATCH 42/65] Remove last_reset attribs --- plugwise/constants.py | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/plugwise/constants.py b/plugwise/constants.py index c90801635..8f7651162 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -573,7 +573,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "battery", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, } @@ -584,7 +583,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -595,7 +593,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: None, ATTR_STATE_CLASS: None, - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: None, } @@ -606,7 +603,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -617,7 +613,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -628,7 +623,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total_increasing", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -640,7 +634,6 @@ ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total", ATTR_ICON: None, - ATTR_LAST_REST: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } EL_CONSUMED_OFF_PEAK_POINT = { @@ -650,7 +643,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -661,7 +653,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total_increasing", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -672,7 +663,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -683,7 +673,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -694,7 +683,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -705,7 +693,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -716,7 +703,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -727,7 +713,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total_increasing", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -738,7 +723,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -749,7 +733,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -760,7 +743,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total_increasing", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -771,7 +753,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_WATT_HOUR, } @@ -782,7 +763,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -793,7 +773,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -804,7 +783,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "gas", ATTR_STATE_CLASS: "total_increasing", - ATTR_LAST_REST: None, ATTR_ICON: FLAME_ICON, ATTR_UNIT_OF_MEASUREMENT: VOLUME_CUBIC_METERS, } @@ -815,7 +793,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "gas", ATTR_STATE_CLASS: "total", - ATTR_LAST_REST: None, ATTR_ICON: FLAME_ICON, ATTR_UNIT_OF_MEASUREMENT: VOLUME_CUBIC_METERS, } @@ -826,7 +803,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "humidity", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, } @@ -837,7 +813,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "illuminance", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: UNIT_LUMEN, } @@ -848,7 +823,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -859,7 +833,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: None, ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: "mdi:percent", ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, } @@ -870,7 +843,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "energy", ATTR_STATE_CLASS: "total", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, } @@ -881,7 +853,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "power", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: POWER_WATT, } @@ -892,7 +863,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -903,7 +873,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -914,7 +883,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } @@ -925,7 +893,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: None, ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_KELVIN, } @@ -936,7 +903,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: None, ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: "mdi:valve", ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, } @@ -947,7 +913,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "pressure", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: PRESSURE_BAR, } @@ -958,7 +923,6 @@ ATTR_STATE: None, ATTR_DEVICE_CLASS: "temperature", ATTR_STATE_CLASS: "measurement", - ATTR_LAST_REST: None, ATTR_ICON: None, ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, } From c84fdeb75fa449abafc647fc6fd0b5efc439d62e Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 10:12:57 +0200 Subject: [PATCH 43/65] Bump to a5 --- plugwise/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 59da97287..c7532d2cc 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -1,6 +1,6 @@ """Plugwise module.""" -__version__ = "0.14.5a4" +__version__ = "0.14.5a5" from plugwise.smile import Smile from plugwise.stick import Stick From 5689fe3567a625ea73ad67f536ef3e29c120d216 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 18:04:12 +0200 Subject: [PATCH 44/65] Re-add return for DUC --- plugwise/smile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugwise/smile.py b/plugwise/smile.py index 10460df11..16e3547cd 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -256,6 +256,8 @@ async def async_update(self): data, self.gw_devices, dev_dict, dev_id, "switches", key ) + return [self.gw_data, self.gw_devices] + async def _set_schedule_state_legacy(self, name, state): """Helper-function for set_schedule_state().""" schema_rule_id = None From d9c1ef6c25851bd4742b311a20ee73dd0ea754b3 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 18:17:54 +0200 Subject: [PATCH 45/65] Init self.gw_data --- plugwise/smile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise/smile.py b/plugwise/smile.py index 16e3547cd..7230f8dde 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -470,6 +470,7 @@ def __init__( self.smile_type = None self.smile_version = () + self.gw_data = {} self.gw_devices = {} async def connect(self): From e09e7dd99215a9649638c78ce85ca0282a27100c Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 19:25:18 +0200 Subject: [PATCH 46/65] Remove unused constant --- plugwise/constants.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugwise/constants.py b/plugwise/constants.py index 8f7651162..4d004f120 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -2,7 +2,6 @@ # Copied homeassistant.consts ATTR_DEVICE_CLASS = "device_class" -ATTR_LAST_REST = "last_reset" ATTR_NAME = "name" ATTR_STATE = "state" ATTR_STATE_CLASS = "state_class" From 47f0af3b2897abcbf9bb616d8bda2fccc8fbaf28 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 19:25:47 +0200 Subject: [PATCH 47/65] Bump to release-version --- plugwise/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index c7532d2cc..3d787869a 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -1,6 +1,6 @@ """Plugwise module.""" -__version__ = "0.14.5a5" +__version__ = "0.14.5" from plugwise.smile import Smile from plugwise.stick import Stick From 5e7630edc87d79281b9fd40c7ae82d9958bfae38 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 19:33:18 +0200 Subject: [PATCH 48/65] Fix function-name in descriptions --- plugwise/helper.py | 6 +++--- plugwise/smile.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index b50a2e9e3..cd164c132 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -69,7 +69,7 @@ def device_state_updater(data, devs, d_id, d_dict): - """Helper-function for update(). + """Helper-function for async_update(). Update the Device_State sensor state. """ for idx, item in enumerate(d_dict["sensors"]): @@ -115,7 +115,7 @@ def update_device_state(data, d_dict): def pw_notification_updater(devs, d_id, d_dict, notifs): - """Helper-function for update(). + """Helper-function for async_update(). Update the PW_Notification binary_sensor state. """ for idx, item in enumerate(d_dict["binary_sensors"]): @@ -124,7 +124,7 @@ def pw_notification_updater(devs, d_id, d_dict, notifs): def update_helper(data, devs, d_dict, d_id, e_type, key): - """Helper-function for update().""" + """Helper-function for async_update().""" for dummy in d_dict[e_type]: if key != dummy[ATTR_ID]: continue diff --git a/plugwise/smile.py b/plugwise/smile.py index 7230f8dde..101291b4c 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -195,7 +195,7 @@ async def _full_update_device(self): await self._update_domain_objects() async def _update_domain_objects(self): - """Helper-function for smile.py: full_update_device() and update_gw_devices(). + """Helper-function for smile.py: full_update_device() and async_update(). Request domain_objects data. """ self._domain_objects = await self._request(DOMAIN_OBJECTS) @@ -623,7 +623,7 @@ def _device_data_climate(self, details, device_data): return device_data def _get_device_data(self, dev_id): - """Helper-function for _all_device_data() and update(). + """Helper-function for _all_device_data() and async_update(). Provide device-data, based on Location ID (= dev_id), from APPLIANCES. """ devices = self._devices From 309d5f63ac4b0733920015a22dac017489aebf85 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 19:36:01 +0200 Subject: [PATCH 49/65] Remove unused import --- tests/test_smile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index 615497142..eb436055a 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -15,7 +15,6 @@ # Testing import aiohttp -from dateutil import tz import jsonpickle as json import pytest From 0c3b696cdda43dfd00331393e6356a67f2d2d697 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 19:57:48 +0200 Subject: [PATCH 50/65] Remove double_init --- plugwise/helper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index cd164c132..73038baab 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -322,7 +322,6 @@ class SmileHelper: def __init__(self): self._cp_state = None - self._loc_data = {} def _locations_legacy(self): """Helper-function for _all_locations(). From 94c592c9815b26085ea1c82e52bc4d55eb3ef7c4 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 20:05:14 +0200 Subject: [PATCH 51/65] Move init to local function --- plugwise/helper.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 73038baab..8121c92bb 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -319,10 +319,6 @@ async def close_connection(self): class SmileHelper: """The SmileHelper class.""" - def __init__(self): - - self._cp_state = None - def _locations_legacy(self): """Helper-function for _all_locations(). Create locations for legacy devices. @@ -531,6 +527,7 @@ def _appliance_types_finder(self, appliance, appl): def _all_appliances(self): """Collect all appliances with relevant info.""" self._appl_data = {} + self._cp_state = None self._all_locations() From 989e73fb2b9377777b98f7ca3dff958b52344bce Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 20:12:35 +0200 Subject: [PATCH 52/65] Remove, log_date no longer provided --- plugwise/helper.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugwise/helper.py b/plugwise/helper.py index 8121c92bb..79e550f2e 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -132,8 +132,6 @@ def update_helper(data, devs, d_dict, d_id, e_type, key): if key != item[ATTR_ID]: continue devs[d_id][e_type][idx][ATTR_STATE] = data[key] - if isinstance(data[key], list): - devs[d_id][e_type][idx][ATTR_STATE] = data[key][0] def check_model(name, v_name): From 9e42e1c204b8db6e836de86d5a001178b633c326 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Tue, 14 Sep 2021 20:33:33 +0200 Subject: [PATCH 53/65] Update CHANGELOG for release --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f42f5a542..fe9a46917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## v0.14.5 - Smile: prepare for using the HA Core DataUpdateCoordintor in Plugwise-beta + +- Change the output to enable the use of the HA Core DUC in plugwise-beta. +- Change state_class to "total" for interval- and net_cumulative sensors (following the HA Core sensor platform updates). +- Remove all remnant code related to last_reset (log_date) +- Restructure: introduce additional classes: SmileComm and SmileConnect + ## v0.14.1 - Smile: removing further `last_reset`s - As per https://developers.home-assistant.io/blog/2021/08/16/state_class_total From 5871fb856b899fc20dc6232830462094f566e375 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Wed, 15 Sep 2021 09:58:29 +0200 Subject: [PATCH 54/65] Optimize class-structure further --- plugwise/smile.py | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 101291b4c..3a1f351d0 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -43,7 +43,7 @@ _LOGGER = logging.getLogger(__name__) -class SmileConnect(SmileComm): +class SmileConnect(SmileComm, SmileData): """The Plugwise SmileConnect class.""" # pylint: disable=too-many-instance-attributes, too-many-public-methods @@ -52,9 +52,10 @@ def __init__( self, host, password, - username, - port, - timeout, + username=DEFAULT_USERNAME, + port=DEFAULT_PORT, + timeout=DEFAULT_TIMEOUT, + websession: aiohttp.ClientSession = None, websession, ): """Set the constructor for this class.""" @@ -67,7 +68,7 @@ def __init__( websession, ) - async def _connect(self): + async def connect(self): """Connect to Plugwise device and determine its name, type and version.""" names = [] @@ -429,26 +430,10 @@ async def delete_notification(self): return True -class Smile(SmileConnect, SmileHelper): +class SmileData(SmileHelper): """The Plugwise Smile main class.""" - def __init__( - self, - host, - password, - username=DEFAULT_USERNAME, - port=DEFAULT_PORT, - timeout=DEFAULT_TIMEOUT, - websession: aiohttp.ClientSession = None, - ): - super().__init__( - host, - password, - username, - port, - timeout, - websession, - ) + def __init__(self): self._active_device_present = None self._appl_data = {} @@ -473,11 +458,6 @@ def __init__( self.gw_data = {} self.gw_devices = {} - async def connect(self): - """Connect to Plugwise device and determine its name, type and version.""" - - connected = await self._connect() - return connected def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). From 0b59d878affd521e24f95dbfbdc1c80de7a9c346 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Wed, 15 Sep 2021 10:01:48 +0200 Subject: [PATCH 55/65] Remove line --- plugwise/smile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 3a1f351d0..850295776 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -56,7 +56,6 @@ def __init__( port=DEFAULT_PORT, timeout=DEFAULT_TIMEOUT, websession: aiohttp.ClientSession = None, - websession, ): """Set the constructor for this class.""" super().__init__( From d3c4be511969df71e5077330124d867e60930e2c Mon Sep 17 00:00:00 2001 From: Bouwe Date: Wed, 15 Sep 2021 10:04:18 +0200 Subject: [PATCH 56/65] Move class SmileData up --- plugwise/smile.py | 464 +++++++++++++++++++++++----------------------- 1 file changed, 232 insertions(+), 232 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 850295776..ac4227532 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -43,6 +43,238 @@ _LOGGER = logging.getLogger(__name__) +class SmileData(SmileHelper): + """The Plugwise Smile main class.""" + + def __init__(self): + + self._active_device_present = None + self._appl_data = {} + self._appliances = None + self._domain_objects = None + self._heater_id = None + self._home_location = None + self._locations = None + self._modules = None + self._smile_legacy = False + self._stretch_v2 = False + self._stretch_v3 = False + self._thermo_locs = None + + self.gateway_id = None + self.notifications = {} + self.smile_hostname = None + self.smile_name = None + self.smile_type = None + self.smile_version = () + + self.gw_data = {} + self.gw_devices = {} + + + def _append_special(self, data, d_id, bs_list, s_list): + """Helper-function for smile.py: _all_device_data(). + When conditions are met, the plugwise_notification binary_sensor + and/or the device_state sensor are appended. + """ + if d_id == self.gateway_id: + if self.single_master_thermostat() is not None: + bs_list.append(PW_NOTIFICATION) + if not self._active_device_present and "heating_state" in data: + s_list.append(DEVICE_STATE) + if d_id == self._heater_id and self.single_master_thermostat() is False: + s_list.append(DEVICE_STATE) + + def _all_device_data(self): + """Helper-function for get_all_devices(). + Collect initial data for each device and add to self.gw_data and self.gw_devices. + """ + dev_id_list = [] + dev_and_data_list = [] + for dev_id, dev_dict in self._devices.items(): + dev_and_data = dev_dict + temp_bs_list = [] + temp_s_list = [] + temp_sw_list = [] + data = self._get_device_data(dev_id) + + self._create_lists_from_data(data, temp_bs_list, temp_s_list, temp_sw_list) + self._append_special(data, dev_id, temp_bs_list, temp_s_list) + + dev_and_data.update(data) + if temp_bs_list != []: + dev_and_data["binary_sensors"] = temp_bs_list + if temp_s_list != []: + dev_and_data["sensors"] = temp_s_list + if temp_sw_list != []: + dev_and_data["switches"] = temp_sw_list + dev_id_list.append(dev_id) + dev_and_data_list.append(copy.deepcopy(dev_and_data)) + + self.gw_devices = dict(zip(dev_id_list, dev_and_data_list)) + + self.gw_data["gateway_id"] = self.gateway_id + self.gw_data["heater_id"] = self._heater_id + self.gw_data["smile_name"] = self.smile_name + self.gw_data["active_device"] = self._active_device_present + self.gw_data["single_master_thermostat"] = self.single_master_thermostat() + + def get_all_devices(self): + """Determine the devices present from the obtained XML-data.""" + self._devices = {} + self._scan_thermostats() + + for appliance, details in self._appl_data.items(): + loc_id = details["location"] + if loc_id is None: + details["location"] = self._home_location + + # Override slave thermostat class + if loc_id in self._thermo_locs: + if "slaves" in self._thermo_locs[loc_id]: + if appliance in self._thermo_locs[loc_id]["slaves"]: + details["class"] = "thermo_sensor" + + self._devices[appliance] = details + + group_data = self._group_switches() + if group_data is not None: + self._devices.update(group_data) + + # Collect data for each device via helper function + self._all_device_data() + + def _device_data_switching_group(self, details, device_data): + """Helper-function for _get_device_data(). + Determine switching group device data. + """ + if details["class"] in SWITCH_GROUP_TYPES: + counter = 0 + for member in details["members"]: + appl_data = self._get_appliance_data(member) + if appl_data["relay"]: + counter += 1 + + device_data["relay"] = True + if counter == 0: + device_data["relay"] = False + + return device_data + + def _device_data_anna(self, dev_id, details, device_data): + """Helper-function for _get_device_data(). + Determine Anna and legacy Anna device data. + """ + # Legacy_anna: create Auxiliary heating_state and leave out domestic_hot_water_state + if "boiler_state" in device_data: + device_data["heating_state"] = device_data["intended_boiler_state"] + device_data.pop("boiler_state", None) + device_data.pop("intended_boiler_state", None) + + # Anna specific + if self.smile_name == "Anna": + illuminance = self._object_value("appliance", dev_id, "illuminance") + if illuminance is not None: + device_data["illuminance"] = illuminance + + return device_data + + def _device_data_adam(self, details, device_data): + """Helper-function for _get_device_data(). + Determine Adam device data. + """ + # Adam: indicate heating_state based on valves being open in case of city-provided heating + if self.smile_name == "Adam": + if details["class"] == "gateway": + if ( + not self._active_device_present + and self._heating_valves() is not None + ): + device_data["heating_state"] = True + if self._heating_valves() == 0: + device_data["heating_state"] = False + + return device_data + + def _device_data_climate(self, details, device_data): + """Helper-function for _get_device_data(). + Determine climate-control device data. + """ + device_data["active_preset"] = self._preset(details["location"]) + device_data["presets"] = self._presets(details["location"]) + + avail_schemas, sel_schema, sched_setpoint = self._schemas(details["location"]) + if not self._smile_legacy: + device_data["schedule_temperature"] = sched_setpoint + device_data["available_schedules"] = avail_schemas + device_data["selected_schedule"] = sel_schema + if self._smile_legacy: + device_data["last_used"] = "".join(map(str, avail_schemas)) + else: + device_data["last_used"] = self._last_active_schema(details["location"]) + + return device_data + + def _get_device_data(self, dev_id): + """Helper-function for _all_device_data() and async_update(). + Provide device-data, based on Location ID (= dev_id), from APPLIANCES. + """ + devices = self._devices + details = devices.get(dev_id) + device_data = self._get_appliance_data(dev_id) + + # Generic + if details["class"] == "gateway" or dev_id == self.gateway_id: + # Anna: outdoor_temperature only present in domain_objects + if ( + self.smile_type == "thermostat" + and "outdoor_temperature" not in device_data + ): + outdoor_temperature = self._object_value( + "location", self._home_location, "outdoor_temperature" + ) + if outdoor_temperature is not None: + device_data["outdoor_temperature"] = outdoor_temperature + + # Try to get P1 data and 2nd outdoor_temperature, when present + power_data = self._power_data_from_location(details["location"]) + if power_data is not None: + device_data.update(power_data) + + # Switching groups data + device_data = self._device_data_switching_group(details, device_data) + # Specific, not generic Anna data + device_data = self._device_data_anna(dev_id, details, device_data) + # Specific, not generic Adam data + device_data = self._device_data_adam(details, device_data) + # Unless thermostat based, no need to walk presets + if details["class"] not in THERMOSTAT_CLASSES: + return device_data + + # Climate based data (presets, temperatures etc) + device_data = self._device_data_climate(details, device_data) + + return device_data + + def single_master_thermostat(self): + """Determine if there is a single master thermostat in the setup. + Possible output: None, True, False. + """ + if self.smile_type != "thermostat": + return None + + count = 0 + self._scan_thermostats() + for dummy, data in self._thermo_locs.items(): + if "master_prio" in data: + if data.get("master_prio") > 0: + count += 1 + + if count == 1: + return True + return False + + class SmileConnect(SmileComm, SmileData): """The Plugwise SmileConnect class.""" @@ -427,235 +659,3 @@ async def delete_notification(self): await self._request(uri, method="delete") return True - - -class SmileData(SmileHelper): - """The Plugwise Smile main class.""" - - def __init__(self): - - self._active_device_present = None - self._appl_data = {} - self._appliances = None - self._domain_objects = None - self._heater_id = None - self._home_location = None - self._locations = None - self._modules = None - self._smile_legacy = False - self._stretch_v2 = False - self._stretch_v3 = False - self._thermo_locs = None - - self.gateway_id = None - self.notifications = {} - self.smile_hostname = None - self.smile_name = None - self.smile_type = None - self.smile_version = () - - self.gw_data = {} - self.gw_devices = {} - - - def _append_special(self, data, d_id, bs_list, s_list): - """Helper-function for smile.py: _all_device_data(). - When conditions are met, the plugwise_notification binary_sensor - and/or the device_state sensor are appended. - """ - if d_id == self.gateway_id: - if self.single_master_thermostat() is not None: - bs_list.append(PW_NOTIFICATION) - if not self._active_device_present and "heating_state" in data: - s_list.append(DEVICE_STATE) - if d_id == self._heater_id and self.single_master_thermostat() is False: - s_list.append(DEVICE_STATE) - - def _all_device_data(self): - """Helper-function for get_all_devices(). - Collect initial data for each device and add to self.gw_data and self.gw_devices. - """ - dev_id_list = [] - dev_and_data_list = [] - for dev_id, dev_dict in self._devices.items(): - dev_and_data = dev_dict - temp_bs_list = [] - temp_s_list = [] - temp_sw_list = [] - data = self._get_device_data(dev_id) - - self._create_lists_from_data(data, temp_bs_list, temp_s_list, temp_sw_list) - self._append_special(data, dev_id, temp_bs_list, temp_s_list) - - dev_and_data.update(data) - if temp_bs_list != []: - dev_and_data["binary_sensors"] = temp_bs_list - if temp_s_list != []: - dev_and_data["sensors"] = temp_s_list - if temp_sw_list != []: - dev_and_data["switches"] = temp_sw_list - dev_id_list.append(dev_id) - dev_and_data_list.append(copy.deepcopy(dev_and_data)) - - self.gw_devices = dict(zip(dev_id_list, dev_and_data_list)) - - self.gw_data["gateway_id"] = self.gateway_id - self.gw_data["heater_id"] = self._heater_id - self.gw_data["smile_name"] = self.smile_name - self.gw_data["active_device"] = self._active_device_present - self.gw_data["single_master_thermostat"] = self.single_master_thermostat() - - def get_all_devices(self): - """Determine the devices present from the obtained XML-data.""" - self._devices = {} - self._scan_thermostats() - - for appliance, details in self._appl_data.items(): - loc_id = details["location"] - if loc_id is None: - details["location"] = self._home_location - - # Override slave thermostat class - if loc_id in self._thermo_locs: - if "slaves" in self._thermo_locs[loc_id]: - if appliance in self._thermo_locs[loc_id]["slaves"]: - details["class"] = "thermo_sensor" - - self._devices[appliance] = details - - group_data = self._group_switches() - if group_data is not None: - self._devices.update(group_data) - - # Collect data for each device via helper function - self._all_device_data() - - def _device_data_switching_group(self, details, device_data): - """Helper-function for _get_device_data(). - Determine switching group device data. - """ - if details["class"] in SWITCH_GROUP_TYPES: - counter = 0 - for member in details["members"]: - appl_data = self._get_appliance_data(member) - if appl_data["relay"]: - counter += 1 - - device_data["relay"] = True - if counter == 0: - device_data["relay"] = False - - return device_data - - def _device_data_anna(self, dev_id, details, device_data): - """Helper-function for _get_device_data(). - Determine Anna and legacy Anna device data. - """ - # Legacy_anna: create Auxiliary heating_state and leave out domestic_hot_water_state - if "boiler_state" in device_data: - device_data["heating_state"] = device_data["intended_boiler_state"] - device_data.pop("boiler_state", None) - device_data.pop("intended_boiler_state", None) - - # Anna specific - if self.smile_name == "Anna": - illuminance = self._object_value("appliance", dev_id, "illuminance") - if illuminance is not None: - device_data["illuminance"] = illuminance - - return device_data - - def _device_data_adam(self, details, device_data): - """Helper-function for _get_device_data(). - Determine Adam device data. - """ - # Adam: indicate heating_state based on valves being open in case of city-provided heating - if self.smile_name == "Adam": - if details["class"] == "gateway": - if ( - not self._active_device_present - and self._heating_valves() is not None - ): - device_data["heating_state"] = True - if self._heating_valves() == 0: - device_data["heating_state"] = False - - return device_data - - def _device_data_climate(self, details, device_data): - """Helper-function for _get_device_data(). - Determine climate-control device data. - """ - device_data["active_preset"] = self._preset(details["location"]) - device_data["presets"] = self._presets(details["location"]) - - avail_schemas, sel_schema, sched_setpoint = self._schemas(details["location"]) - if not self._smile_legacy: - device_data["schedule_temperature"] = sched_setpoint - device_data["available_schedules"] = avail_schemas - device_data["selected_schedule"] = sel_schema - if self._smile_legacy: - device_data["last_used"] = "".join(map(str, avail_schemas)) - else: - device_data["last_used"] = self._last_active_schema(details["location"]) - - return device_data - - def _get_device_data(self, dev_id): - """Helper-function for _all_device_data() and async_update(). - Provide device-data, based on Location ID (= dev_id), from APPLIANCES. - """ - devices = self._devices - details = devices.get(dev_id) - device_data = self._get_appliance_data(dev_id) - - # Generic - if details["class"] == "gateway" or dev_id == self.gateway_id: - # Anna: outdoor_temperature only present in domain_objects - if ( - self.smile_type == "thermostat" - and "outdoor_temperature" not in device_data - ): - outdoor_temperature = self._object_value( - "location", self._home_location, "outdoor_temperature" - ) - if outdoor_temperature is not None: - device_data["outdoor_temperature"] = outdoor_temperature - - # Try to get P1 data and 2nd outdoor_temperature, when present - power_data = self._power_data_from_location(details["location"]) - if power_data is not None: - device_data.update(power_data) - - # Switching groups data - device_data = self._device_data_switching_group(details, device_data) - # Specific, not generic Anna data - device_data = self._device_data_anna(dev_id, details, device_data) - # Specific, not generic Adam data - device_data = self._device_data_adam(details, device_data) - # Unless thermostat based, no need to walk presets - if details["class"] not in THERMOSTAT_CLASSES: - return device_data - - # Climate based data (presets, temperatures etc) - device_data = self._device_data_climate(details, device_data) - - return device_data - - def single_master_thermostat(self): - """Determine if there is a single master thermostat in the setup. - Possible output: None, True, False. - """ - if self.smile_type != "thermostat": - return None - - count = 0 - self._scan_thermostats() - for dummy, data in self._thermo_locs.items(): - if "master_prio" in data: - if data.get("master_prio") > 0: - count += 1 - - if count == 1: - return True - return False From a8861754856a2607fcb0b31bd696cd29b82363b7 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Wed, 15 Sep 2021 10:07:18 +0200 Subject: [PATCH 57/65] Keep the Smile classname --- plugwise/smile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index ac4227532..0273f4d7e 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -275,7 +275,7 @@ def single_master_thermostat(self): return False -class SmileConnect(SmileComm, SmileData): +class Smile(SmileComm, SmileData): """The Plugwise SmileConnect class.""" # pylint: disable=too-many-instance-attributes, too-many-public-methods From 604565d19c76beaf9a17f16c739d74f9cd5e7012 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Wed, 15 Sep 2021 10:20:04 +0200 Subject: [PATCH 58/65] Rearrange inits --- plugwise/smile.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 0273f4d7e..4866b8a55 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -48,29 +48,11 @@ class SmileData(SmileHelper): def __init__(self): - self._active_device_present = None self._appl_data = {} - self._appliances = None - self._domain_objects = None - self._heater_id = None self._home_location = None - self._locations = None - self._modules = None - self._smile_legacy = False - self._stretch_v2 = False - self._stretch_v3 = False self._thermo_locs = None self.gateway_id = None - self.notifications = {} - self.smile_hostname = None - self.smile_name = None - self.smile_type = None - self.smile_version = () - - self.gw_data = {} - self.gw_devices = {} - def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). @@ -299,6 +281,24 @@ def __init__( websession, ) + self._active_device_present = None + self._appliances = None + self._domain_objects = None + self._heater_id = None + self._locations = None + self._modules = None + self._smile_legacy = False + self._stretch_v2 = False + self._stretch_v3 = False + + self.gw_data = {} + self.gw_devices = {} + self.notifications = {} + self.smile_hostname = None + self.smile_name = None + self.smile_type = None + self.smile_version = () + async def connect(self): """Connect to Plugwise device and determine its name, type and version.""" names = [] From 794af206d72236864f18012da1182dfc2d976194 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Wed, 15 Sep 2021 10:37:00 +0200 Subject: [PATCH 59/65] Bump to v0.14.5a6 for extra testing --- plugwise/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 3d787869a..7863a8d2a 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -1,6 +1,6 @@ """Plugwise module.""" -__version__ = "0.14.5" +__version__ = "0.14.5a6" from plugwise.smile import Smile from plugwise.stick import Stick From 2bdff9d3434c2596fbb9e62d0142e7597bcbff3b Mon Sep 17 00:00:00 2001 From: Bouwe Date: Wed, 15 Sep 2021 10:48:19 +0200 Subject: [PATCH 60/65] Rearrange more inits --- plugwise/smile.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 4866b8a55..08b618662 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -46,14 +46,6 @@ class SmileData(SmileHelper): """The Plugwise Smile main class.""" - def __init__(self): - - self._appl_data = {} - self._home_location = None - self._thermo_locs = None - - self.gateway_id = None - def _append_special(self, data, d_id, bs_list, s_list): """Helper-function for smile.py: _all_device_data(). When conditions are met, the plugwise_notification binary_sensor @@ -283,14 +275,18 @@ def __init__( self._active_device_present = None self._appliances = None + self._appl_data = {} self._domain_objects = None self._heater_id = None + self._home_location = None self._locations = None self._modules = None self._smile_legacy = False self._stretch_v2 = False self._stretch_v3 = False + self._thermo_locs = None + self.gateway_id = None self.gw_data = {} self.gw_devices = {} self.notifications = {} @@ -299,6 +295,7 @@ def __init__( self.smile_type = None self.smile_version = () + async def connect(self): """Connect to Plugwise device and determine its name, type and version.""" names = [] From 6a96fddb7ea84c131b6662763460944b76fc044e Mon Sep 17 00:00:00 2001 From: autoblack Date: Wed, 15 Sep 2021 08:49:19 +0000 Subject: [PATCH 61/65] fixup: output_to_duc Python code reformatted using Black --- plugwise/smile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugwise/smile.py b/plugwise/smile.py index 08b618662..f024993fa 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -295,7 +295,6 @@ def __init__( self.smile_type = None self.smile_version = () - async def connect(self): """Connect to Plugwise device and determine its name, type and version.""" names = [] From 6c9b8d63e38fdabea0d099eeb8502d078df6b1a9 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Thu, 16 Sep 2021 11:20:54 +0200 Subject: [PATCH 62/65] Correct order --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a49fdd7ac..d53641306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,5 @@ # Changelog -## v0.14.2 - Smile: fix P1 legacy location handling error - ## v0.14.5 - Smile: prepare for using the HA Core DataUpdateCoordintor in Plugwise-beta - Change the output to enable the use of the HA Core DUC in plugwise-beta. @@ -9,6 +7,8 @@ - Remove all remnant code related to last_reset (log_date) - Restructure: introduce additional classes: SmileComm and SmileConnect +## v0.14.2 - Smile: fix P1 legacy location handling error + ## v0.14.1 - Smile: removing further `last_reset`s - As per https://developers.home-assistant.io/blog/2021/08/16/state_class_total From 6d810e5cfcde502c37b7417abd0fa1f888ac3db1 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Thu, 16 Sep 2021 11:21:22 +0200 Subject: [PATCH 63/65] Bump to v0.14.5a7 --- plugwise/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 7863a8d2a..90c3f8eef 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -1,6 +1,6 @@ """Plugwise module.""" -__version__ = "0.14.5a6" +__version__ = "0.14.5a7" from plugwise.smile import Smile from plugwise.stick import Stick From 9128ed428baf5a5d1413cfe156cefc0bb3ee8b61 Mon Sep 17 00:00:00 2001 From: Bouwe Date: Thu, 16 Sep 2021 12:04:29 +0200 Subject: [PATCH 64/65] Update fixtures-content for DUC-use --- tests/test_smile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_smile.py b/tests/test_smile.py index c49768f30..ed47f7812 100644 --- a/tests/test_smile.py +++ b/tests/test_smile.py @@ -409,7 +409,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None): data = await smile.async_update() extra = data[0] device_list = data[1] - self._write_json("all_devices", device_list) + self._write_json("all_data", data) self._write_json("notifications", extra["notifications"]) location_list = smile._thermo_locs From 6253d31948655c8b29abcbe7118e78120da9157e Mon Sep 17 00:00:00 2001 From: Bouwe Date: Thu, 16 Sep 2021 18:10:26 +0200 Subject: [PATCH 65/65] Bump back to release-version --- plugwise/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 90c3f8eef..3d787869a 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -1,6 +1,6 @@ """Plugwise module.""" -__version__ = "0.14.5a7" +__version__ = "0.14.5" from plugwise.smile import Smile from plugwise.stick import Stick