Skip to content

Commit 19618fa

Browse files
committed
Import Logger, start adding typing hints
1 parent 4810a9c commit 19618fa

File tree

2 files changed

+72
-76
lines changed

2 files changed

+72
-76
lines changed

plugwise/helper.py

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44
import asyncio
55
import datetime as dt
6+
from typing import Any
67

78
# This way of importing aiohttp is because of patch/mocking in testing (aiohttp timeouts)
89
from aiohttp import BasicAuth, ClientSession, ClientTimeout, ServerTimeoutError
@@ -271,36 +272,36 @@ class SmileHelper:
271272

272273
def __init__(self):
273274
"""Set the constructor for this class."""
274-
self._appl_data = None
275-
self._appliances = None
276-
self._cooling_present = None
277-
self._cp_state = None
278-
self._devices = None
279-
self._domain_objects = None
280-
self._heater_id = None
281-
self._home_location = None
282-
self._last_active = {}
283-
self._loc_data = None
284-
self._locations = None
285-
self._modules = None
286-
self._on_off_device = None
287-
self._ot_device = None
288-
self._outdoor_temp = None
289-
self._sm_thermostat = None
290-
self._thermo_locs = None
291-
292-
self._smile_legacy = None
293-
self._stretch_v2 = None
294-
self._stretch_v3 = None
295-
296-
self.cooling_active = None
297-
self.gateway_id = None
298-
self.gw_data = {}
299-
self.gw_devices = {}
300-
301-
self.smile_name = None
302-
self.smile_type = None
303-
self.smile_version = []
275+
self._appl_data: dict[str, Any] = {}
276+
self._appliances: etree = None
277+
self._cooling_present: bool = False
278+
self._devices: dict[str, str] = {}
279+
self._domain_objects: etree = None
280+
self._heater_id: str = None
281+
self._home_location: str = None
282+
self._last_active: dict[str, str] = {}
283+
self._loc_data: dict[str, Any] = {}
284+
self._locations: etree = None
285+
self._modules: etree = None
286+
self._on_off_device: bool = False
287+
self._opentherm_device: bool = False
288+
self._outdoor_temp: float = None
289+
self._is_thermostat: bool = False
290+
self._multi_thermostats: bool = False
291+
self._thermo_locs: dict[str, Any] = {}
292+
293+
self._smile_legacy: bool = False
294+
self._stretch_v2: bool = False
295+
self._stretch_v3: bool = False
296+
297+
self.cooling_active: bool = False
298+
self.gateway_id: str = None
299+
self.gw_data: dict[str, Any] = {}
300+
self.gw_devices: dict[str, Any] = {}
301+
302+
self.smile_name: str = None
303+
self.smile_type: str = None
304+
self.smile_version: list[str] = []
304305

305306
def _locations_legacy(self):
306307
"""Helper-function for _all_locations().
@@ -348,7 +349,6 @@ def _locations_specials(self, loc, location):
348349

349350
def _all_locations(self):
350351
"""Collect all locations."""
351-
self._loc_data = {}
352352
loc = Munch()
353353

354354
# Legacy Anna without outdoor_temp and Stretches have no locations, create one containing all appliances
@@ -476,7 +476,7 @@ def _appliance_info_finder(self, appliance, appl):
476476
return appl
477477

478478
# Remove heater_central when no active device present
479-
if not self._ot_device and not self._on_off_device:
479+
if not self._opentherm_device and not self._on_off_device:
480480
return None
481481

482482
self._heater_id = appliance.attrib["id"]
@@ -531,9 +531,6 @@ def _appliance_types_finder(self, appliance, appl):
531531

532532
def _all_appliances(self):
533533
"""Collect all appliances with relevant info."""
534-
self._appl_data = {}
535-
self._cp_state = None
536-
537534
self._all_locations()
538535

539536
# Create a gateway for legacy Anna, P1 and Stretches
@@ -572,7 +569,7 @@ def _all_appliances(self):
572569
ot_fault_code = self._appliances.find(
573570
".//logs/point_log[type='open_therm_oem_fault_code']"
574571
)
575-
self._ot_device = ch_state is not None and ot_fault_code is not None
572+
self._opentherm_device = ch_state is not None and ot_fault_code is not None
576573
self._on_off_device = ch_state is not None and ot_fault_code is None
577574

578575
for appliance in self._appliances.findall("./appliance"):
@@ -767,7 +764,7 @@ def _get_appliance_data(self, d_id):
767764

768765
appliance = self._appliances.find(f'.//appliance[@id="{d_id}"]')
769766
measurements = DEVICE_MEASUREMENTS.items()
770-
if self._ot_device or self._on_off_device:
767+
if self._opentherm_device or self._on_off_device:
771768
measurements = {
772769
**DEVICE_MEASUREMENTS,
773770
**HEATER_CENTRAL_MEASUREMENTS,
@@ -1203,7 +1200,7 @@ def _create_dicts_from_data(self, data, bs_dict, s_dict, sw_dict):
12031200
for item in BINARY_SENSORS:
12041201
if list(item.keys())[0] == key:
12051202
data.pop(key)
1206-
if self._ot_device or self._on_off_device:
1203+
if self._opentherm_device or self._on_off_device:
12071204
bs_dict[key] = value
12081205
for item in SENSORS:
12091206
if list(item.keys())[0] == key:

plugwise/smile.py

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
"""Use of this source code is governed by the MIT license found in the LICENSE file.
22
Plugwise backend module for Home Assistant Core.
33
"""
4-
import logging
5-
64
import aiohttp
5+
from defusedxml import ElementTree as etree
76

87
# Dict as class
98
from munch import Munch
@@ -18,6 +17,7 @@
1817
DEFAULT_USERNAME,
1918
DOMAIN_OBJECTS,
2019
LOCATIONS,
20+
LOGGER,
2121
MODULES,
2222
NOTIFICATIONS,
2323
PW_NOTIFICATION,
@@ -31,8 +31,6 @@
3131
from .exceptions import ConnectionFailedError, InvalidXMLError, UnsupportedDeviceError
3232
from .helper import SmileComm, SmileHelper, pw_notification_updater, update_helper
3333

34-
_LOGGER = logging.getLogger(__name__)
35-
3634

3735
class SmileData(SmileHelper):
3836
"""The Plugwise Smile main class."""
@@ -42,7 +40,7 @@ def _append_special(self, data, d_id, bs_dict, s_dict):
4240
When conditions are met, the plugwise_notification binary_sensor is appended.
4341
"""
4442
if d_id == self.gateway_id:
45-
if self._sm_thermostat is not None:
43+
if self._is_thermostat:
4644
bs_dict.update(PW_NOTIFICATION)
4745

4846
def _all_device_data(self):
@@ -69,11 +67,13 @@ def _all_device_data(self):
6967

7068
self.gw_devices[dev_id] = dev_and_data
7169

72-
self.gw_data["active_device"] = self._ot_device or self._on_off_device
70+
self.gw_data["active_device"] = self._opentherm_device or self._on_off_device
7371
self.gw_data["cooling_present"] = self._cooling_present
7472
self.gw_data["gateway_id"] = self.gateway_id
7573
self.gw_data["heater_id"] = self._heater_id
76-
self.gw_data["single_master_thermostat"] = self._sm_thermostat
74+
self.gw_data["single_master_thermostat"] = (
75+
self._is_thermostat and not self._multi_thermostats
76+
)
7777
self.gw_data["smile_name"] = self.smile_name
7878

7979
def get_all_devices(self):
@@ -221,20 +221,19 @@ def _get_device_data(self, dev_id):
221221
return device_data
222222

223223
def single_master_thermostat(self):
224-
"""Determine if there is a single master thermostat in the setup.
225-
Possible output: None, True, False.
226-
"""
224+
"""Determine if there is a single master thermostat in the setup."""
227225
if self.smile_type == "thermostat":
226+
self._is_thermostat = True
228227
count = 0
229228
for dummy, data in self._thermo_locs.items():
230229
if "master_prio" in data:
231230
if data.get("master_prio") > 0:
232231
count += 1
233232

234233
if count == 1:
235-
self._sm_thermostat = True
234+
self._multi_thermostats = False
236235
if count > 1:
237-
self._sm_thermostat = False
236+
self._multi_thermostats = True
238237

239238

240239
class Smile(SmileComm, SmileData):
@@ -244,13 +243,13 @@ class Smile(SmileComm, SmileData):
244243

245244
def __init__(
246245
self,
247-
host,
248-
password,
249-
username=DEFAULT_USERNAME,
250-
port=DEFAULT_PORT,
251-
timeout=DEFAULT_TIMEOUT,
246+
host: str,
247+
password: str,
248+
username: str = DEFAULT_USERNAME,
249+
port: str = DEFAULT_PORT,
250+
timeout: str = DEFAULT_TIMEOUT,
252251
websession: aiohttp.ClientSession = None,
253-
):
252+
) -> None:
254253
"""Set the constructor for this class."""
255254
super().__init__(
256255
host,
@@ -262,17 +261,17 @@ def __init__(
262261
)
263262
SmileData.__init__(self)
264263

265-
self._notifications = {}
266-
self.smile_hostname = None
264+
self._notifications: dict[str, str] = {}
265+
self.smile_hostname: str = "Unknown"
267266

268-
async def connect(self):
267+
async def connect(self) -> bool:
269268
"""Connect to Plugwise device and determine its name, type and version."""
270-
names = []
269+
names: list[str] = []
271270

272-
result = await self._request(DOMAIN_OBJECTS)
273-
dsmrmain = result.find(".//module/protocols/dsmrmain")
271+
result: etree = await self._request(DOMAIN_OBJECTS)
272+
dsmrmain: etree | None = result.find(".//module/protocols/dsmrmain")
274273

275-
vendor_names = result.findall(".//module/vendor_name")
274+
vendor_names: list[etree] = result.findall(".//module/vendor_name")
276275
if not vendor_names:
277276
# Work-around for Stretch fv 2.7.18
278277
result = await self._request(MODULES)
@@ -283,7 +282,7 @@ async def connect(self):
283282

284283
if "Plugwise" not in names:
285284
if dsmrmain is None: # pragma: no cover
286-
_LOGGER.error(
285+
LOGGER.error(
287286
"Connected but expected text not returned, \
288287
we got %s",
289288
result,
@@ -298,23 +297,23 @@ async def connect(self):
298297

299298
return True
300299

301-
async def _smile_detect_legacy(self, result, dsmrmain):
300+
async def _smile_detect_legacy(self, result, dsmrmain) -> None:
302301
"""Helper-function for _smile_detect()."""
303-
network = result.find(".//module/protocols/master_controller")
302+
network: etree = result.find(".//module/protocols/master_controller")
304303

305304
# Assume legacy
306305
self._smile_legacy = True
307306
# Try if it is an Anna, assuming appliance thermostat
308-
anna = result.find('.//appliance[type="thermostat"]')
307+
anna: etree = result.find('.//appliance[type="thermostat"]')
309308
# Fake insert version assuming Anna
310309
# couldn't find another way to identify as legacy Anna
311-
version = "1.8.0"
312-
model = "smile_thermo"
310+
version: str = "1.8.0"
311+
model: str = "smile_thermo"
313312
if anna is None:
314313
# P1 legacy:
315314
if dsmrmain is not None:
316315
try:
317-
status = await self._request(STATUS)
316+
status: etree = await self._request(STATUS)
318317
version = status.find(".//system/version").text
319318
model = status.find(".//system/product").text
320319
self.smile_hostname = status.find(".//network/hostname").text
@@ -334,7 +333,7 @@ async def _smile_detect_legacy(self, result, dsmrmain):
334333
raise ConnectionFailedError
335334
else: # pragma: no cover
336335
# No cornercase, just end of the line
337-
_LOGGER.error("Connected but no gateway device information found")
336+
LOGGER.error("Connected but no gateway device information found")
338337
raise ConnectionFailedError
339338
return model, version
340339

@@ -353,14 +352,14 @@ async def _smile_detect(self, result, dsmrmain):
353352

354353
if model is None or version is None: # pragma: no cover
355354
# Corner case check
356-
_LOGGER.error("Unable to find model or version information")
355+
LOGGER.error("Unable to find model or version information")
357356
raise UnsupportedDeviceError
358357

359358
ver = semver.VersionInfo.parse(version)
360359
target_smile = f"{model}_v{ver.major}"
361-
_LOGGER.debug("Plugwise identified as %s", target_smile)
360+
LOGGER.debug("Plugwise identified as %s", target_smile)
362361
if target_smile not in SMILES:
363-
_LOGGER.error(
362+
LOGGER.error(
364363
'Your version Smile identified as "%s" seems\
365364
unsupported by our plugin, please create an issue\
366365
on http://github.com/plugwise/python-plugwise!',
@@ -407,9 +406,9 @@ async def _update_domain_objects(self):
407406
msg_type = notification.find("type").text
408407
msg = notification.find("message").text
409408
self._notifications.update({msg_id: {msg_type: msg}})
410-
_LOGGER.debug("Plugwise notifications: %s", self._notifications)
409+
LOGGER.debug("Plugwise notifications: %s", self._notifications)
411410
except AttributeError: # pragma: no cover
412-
_LOGGER.info(
411+
LOGGER.info(
413412
"Plugwise notification present but unable to process, manually investigate: %s",
414413
f"{self._endpoint}{DOMAIN_OBJECTS}",
415414
)

0 commit comments

Comments
 (0)