Skip to content

Commit 4ba0003

Browse files
authored
Merge pull request #261 from plugwise/more_typing
More typing improvements
2 parents 71574dd + f181d40 commit 4ba0003

File tree

5 files changed

+47
-45
lines changed

5 files changed

+47
-45
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changelog
22

3+
# v0.27.3 More typing improvements
4+
35
# v0.27.2: Strict-typing
46

57
# v0.27.1: More cooling-related updates, based on additional info from Plugwise

plugwise/constants.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ class ModelData(TypedDict):
650650
hardware_version: str | None
651651
firmware_version: str | None
652652
zigbee_mac_address: str | None
653-
available: bool | None
653+
reachable: bool | None
654654

655655

656656
class SmileBinarySensors(TypedDict, total=False):
@@ -751,10 +751,15 @@ class ActuatorData(TypedDict, total=False):
751751
upper_bound: float
752752

753753

754-
class DeviceDataPoints(
755-
SmileBinarySensors, SmileSensors, SmileSwitches, TypedDict, total=False
754+
class DeviceData(
755+
ApplianceData,
756+
SmileBinarySensors,
757+
SmileSensors,
758+
SmileSwitches,
759+
TypedDict,
760+
total=False,
756761
):
757-
"""The class covering all possible collected data points."""
762+
"""The Device Data class, covering the collected and ordered output-data per device."""
758763

759764
# Loria
760765
dhw_mode: str
@@ -778,16 +783,12 @@ class DeviceDataPoints(
778783
control_state: str | bool
779784

780785
# For temporary use
781-
c_heating_state: str
786+
c_heating_state: bool
782787
modified: str
783788

784789
# Device availability
785790
available: bool | None
786791

787-
788-
class DeviceData(ApplianceData, DeviceDataPoints, TypedDict, total=False):
789-
"""The Device Data class, covering the collected and ordere output-data per device."""
790-
791792
binary_sensors: SmileBinarySensors
792793
domestic_hot_water_setpoint: ActuatorData | float
793794
maximum_boiler_temperature: ActuatorData | float

plugwise/helper.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import asyncio
88
import datetime as dt
9-
from typing import Any, cast
109

1110
# This way of importing aiohttp is because of patch/mocking in testing (aiohttp timeouts)
1211
from aiohttp import BasicAuth, ClientError, ClientResponse, ClientSession, ClientTimeout
@@ -49,9 +48,9 @@
4948
THERMOSTAT_CLASSES,
5049
TOGGLES,
5150
UOM,
51+
ActuatorData,
5252
ApplianceData,
5353
DeviceData,
54-
DeviceDataPoints,
5554
GatewayData,
5655
ModelData,
5756
SmileBinarySensors,
@@ -72,9 +71,11 @@
7271
version_to_model,
7372
)
7473

74+
# from typing import cast
75+
7576

7677
def update_helper(
77-
data: DeviceDataPoints,
78+
data: DeviceData,
7879
devices: dict[str, DeviceData],
7980
device_dict: DeviceData,
8081
device_id: str,
@@ -102,20 +103,20 @@ def check_model(name: str | None, vendor_name: str | None) -> str | None:
102103
return name
103104

104105

105-
def _get_actuator_functionalities(xml: etree, data: dict[str, Any]) -> None:
106+
def _get_actuator_functionalities(xml: etree, data: DeviceData) -> None:
106107
"""Helper-function for _get_appliance_data()."""
107108
for item in ACTIVE_ACTUATORS:
108-
temp_dict: dict[str, float] = {}
109+
temp_dict: ActuatorData = {}
109110
for key in LIMITS:
110111
locator = f'.//actuator_functionalities/thermostat_functionality[type="{item}"]/{key}'
111112
if (function := xml.find(locator)) is not None:
112113
if function.text == "nil":
113114
break
114115

115-
temp_dict.update({key: format_measure(function.text, TEMP_CELSIUS)})
116+
temp_dict[key] = format_measure(function.text, TEMP_CELSIUS) # type: ignore [literal-required]
116117

117118
if temp_dict:
118-
data[item] = temp_dict
119+
data[item] = temp_dict # type: ignore [literal-required]
119120

120121

121122
def schedules_temps(
@@ -443,12 +444,12 @@ def _get_module_data(
443444
"""
444445
model_data: ModelData = {
445446
"contents": False,
447+
"firmware_version": None,
448+
"hardware_version": None,
449+
"reachable": None,
446450
"vendor_name": None,
447451
"vendor_model": None,
448-
"hardware_version": None,
449-
"firmware_version": None,
450452
"zigbee_mac_address": None,
451-
"available": None,
452453
}
453454
if (appl_search := appliance.find(locator)) is not None:
454455
link_id = appl_search.attrib["id"]
@@ -466,7 +467,7 @@ def _get_module_data(
466467
# Adam
467468
if found := module.find("./protocols/zig_bee_node"):
468469
model_data["zigbee_mac_address"] = found.find("mac_address").text
469-
model_data["available"] = found.find("reachable").text == "true"
470+
model_data["reachable"] = found.find("reachable").text == "true"
470471
# Stretches
471472
if found := module.find("./protocols/network_router"):
472473
model_data["zigbee_mac_address"] = found.find("mac_address").text
@@ -873,7 +874,7 @@ def _rule_ids_by_tag(self, tag: str, loc_id: str) -> dict[str, str]:
873874
def _appliance_measurements(
874875
self,
875876
appliance: etree,
876-
data: dict[str, Any],
877+
data: DeviceData,
877878
measurements: dict[str, DATA | UOM],
878879
) -> None:
879880
"""Helper-function for _get_appliance_data() - collect appliance measurement data."""
@@ -894,28 +895,28 @@ def _appliance_measurements(
894895
if new_name := getattr(attrs, ATTR_NAME, None):
895896
measurement = new_name
896897

897-
data[measurement] = appl_p_loc.text
898+
data[measurement] = appl_p_loc.text # type: ignore [literal-required]
898899
# measurements with states "on" or "off" that need to be passed directly
899900
if measurement not in ("dhw_mode"):
900-
data[measurement] = format_measure(
901+
data[measurement] = format_measure( # type: ignore [literal-required]
901902
appl_p_loc.text, getattr(attrs, ATTR_UNIT_OF_MEASUREMENT)
902903
)
903904

904905
# Anna: save cooling-related measurements for later use
905906
# Use the local outdoor temperature as reference for turning cooling on/off
906907
if measurement == "cooling_activation_outdoor_temperature":
907-
self._cooling_activation_outdoor_temp = data[measurement]
908+
self._cooling_activation_outdoor_temp = data[measurement] # type: ignore [literal-required]
908909
if measurement == "cooling_deactivation_threshold":
909-
self._cooling_deactivation_threshold = data[measurement]
910+
self._cooling_deactivation_threshold = data[measurement] # type: ignore [literal-required]
910911
if measurement == "outdoor_air_temperature":
911-
self._outdoor_temp = data[measurement]
912+
self._outdoor_temp = data[measurement] # type: ignore [literal-required]
912913

913914
i_locator = f'.//logs/interval_log[type="{measurement}"]/period/measurement'
914915
if (appl_i_loc := appliance.find(i_locator)) is not None:
915916
name = f"{measurement}_interval"
916-
data[name] = format_measure(appl_i_loc.text, ENERGY_WATT_HOUR)
917+
data[name] = format_measure(appl_i_loc.text, ENERGY_WATT_HOUR) # type: ignore [literal-required]
917918

918-
def _wireless_availablity(self, appliance: etree, data: dict[str, Any]) -> None:
919+
def _wireless_availablity(self, appliance: etree, data: DeviceData) -> None:
919920
"""
920921
Helper-function for _get_appliance_data().
921922
@@ -926,16 +927,16 @@ def _wireless_availablity(self, appliance: etree, data: dict[str, Any]) -> None:
926927
locator = "./logs/interval_log/electricity_interval_meter"
927928
mod_type = "electricity_interval_meter"
928929
module_data = self._get_module_data(appliance, locator, mod_type)
929-
if module_data["available"] is None:
930+
if module_data["reachable"] is None:
930931
# Collect for wireless thermostats
931932
locator = "./logs/point_log[type='thermostat']/thermostat"
932933
mod_type = "thermostat"
933934
module_data = self._get_module_data(appliance, locator, mod_type)
934935

935-
if module_data["available"] is not None:
936-
data["available"] = module_data["available"]
936+
if module_data["reachable"] is not None:
937+
data["available"] = module_data["reachable"]
937938

938-
def _get_regulation_mode(self, appliance: etree, data: dict[str, Any]) -> None:
939+
def _get_regulation_mode(self, appliance: etree, data: DeviceData) -> None:
939940
"""
940941
Helper-function for _get_appliance_data().
941942
@@ -946,7 +947,7 @@ def _get_regulation_mode(self, appliance: etree, data: dict[str, Any]) -> None:
946947
data["regulation_mode"] = search.find("mode").text
947948
self._cooling_enabled = search.find("mode").text == "cooling"
948949

949-
def _cleanup_data(self, data: dict[str, Any]) -> None:
950+
def _cleanup_data(self, data: DeviceData) -> None:
950951
"""
951952
Helper-function for _get_appliance_data().
952953
@@ -960,12 +961,12 @@ def _cleanup_data(self, data: dict[str, Any]) -> None:
960961
if not self._cooling_present:
961962
for item in ("cooling_state", "cooling_ena_switch"):
962963
if item in data:
963-
data.pop(item)
964+
data.pop(item) # type: ignore [misc]
964965
# Keep cooling_enabled for Elga
965966
if not self._elga and "cooling_enabled" in data:
966967
data.pop("cooling_enabled") # pragma: no cover
967968

968-
def _process_c_heating_state(self, data: dict[str, Any]) -> None:
969+
def _process_c_heating_state(self, data: DeviceData) -> None:
969970
"""
970971
Helper-function for _get_appliance_data().
971972
@@ -992,10 +993,10 @@ def _get_appliance_data(self, d_id: str) -> DeviceData:
992993
Collect the appliance-data based on device id.
993994
Determined from APPLIANCES, for legacy from DOMAIN_OBJECTS.
994995
"""
995-
data: dict[str, Any] = {}
996+
data: DeviceData = {}
996997
# P1 legacy has no APPLIANCES, also not present in DOMAIN_OBJECTS
997998
if self._smile_legacy and self.smile_type == "power":
998-
return cast(DeviceData, data)
999+
return data
9991000

10001001
measurements = DEVICE_MEASUREMENTS
10011002
if d_id == self._heater_id:
@@ -1055,7 +1056,7 @@ def _get_appliance_data(self, d_id: str) -> DeviceData:
10551056

10561057
self._cleanup_data(data)
10571058

1058-
return cast(DeviceData, data)
1059+
return data
10591060

10601061
def _rank_thermostat(
10611062
self,
@@ -1439,7 +1440,7 @@ def _object_value(self, obj_id: str, measurement: str) -> float | int | None:
14391440

14401441
return val
14411442

1442-
def _get_lock_state(self, xml: etree, data: dict[str, Any]) -> None:
1443+
def _get_lock_state(self, xml: etree, data: DeviceData) -> None:
14431444
"""
14441445
Helper-function for _get_appliance_data().
14451446
@@ -1456,7 +1457,7 @@ def _get_lock_state(self, xml: etree, data: dict[str, Any]) -> None:
14561457
data["lock"] = found.text == "true"
14571458

14581459
def _get_toggle_state(
1459-
self, xml: etree, toggle: str, name: str, data: dict[str, Any]
1460+
self, xml: etree, toggle: str, name: str, data: DeviceData
14601461
) -> None:
14611462
"""
14621463
Helper-function for _get_appliance_data().
@@ -1469,7 +1470,7 @@ def _get_toggle_state(
14691470
for item in found:
14701471
if (toggle_type := item.find("type")) is not None:
14711472
if toggle_type.text == toggle:
1472-
data.update({name: item.find("state").text == "on"})
1473+
data[name] = item.find("state").text == "on" # type: ignore [literal-required]
14731474
# Remove the cooling_enabled key when the corresponding toggle is present
14741475
# Except for Elga
14751476
if toggle == "cooling_enabled" and not self._elga:

plugwise/smile.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
"""
55
from __future__ import annotations
66

7-
from typing import cast
8-
97
import aiohttp
108
from defusedxml import ElementTree as etree
119

@@ -560,7 +558,7 @@ async def async_update(self) -> tuple[GatewayData, dict[str, DeviceData]]:
560558
# Update for cooling
561559
self.update_for_cooling(dev_dict)
562560

563-
return (cast(GatewayData, self.gw_data), self.gw_devices)
561+
return (self.gw_data, self.gw_devices)
564562

565563
async def _set_schedule_state_legacy(
566564
self, loc_id: str, name: str, status: str

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "plugwise"
7-
version = "0.27.2"
7+
version = "0.27.3"
88
license = {file = "LICENSE"}
99
description = "Plugwise Smile (Adam/Anna/P1), Stretch and USB (Stick) module for Python 3."
1010
readme = "README.md"

0 commit comments

Comments
 (0)