55
66from __future__ import annotations
77
8+ from typing import cast
9+
810from plugwise .constants import (
911 DEFAULT_LEGACY_TIMEOUT ,
1012 DEFAULT_PORT ,
3638
3739import aiohttp
3840from defusedxml import ElementTree as etree
41+ from munch import Munch
3942from packaging .version import Version , parse
4043
4144
@@ -72,16 +75,17 @@ def __init__(
7275 self ._smile_api : SmileAPI | SmileLegacyAPI
7376 self ._stretch_v2 = False
7477 self ._target_smile : str = NONE
75- self .smile_hostname : str = NONE
76- self .smile_hw_version : str | None = None
77- self .smile_legacy = False
78- self .smile_mac_address : str | None = None
79- self .smile_model : str = NONE
80- self .smile_model_id : str | None = None
81- self .smile_name : str = NONE
82- self .smile_type : str = NONE
83- self .smile_version : Version = Version ("0.0.0" )
84- self .smile_zigbee_mac_address : str | None = None
78+ self .smile : Munch = Munch ()
79+ self .smile .hostname = NONE
80+ self .smile .hw_version = None
81+ self .smile .legacy = False
82+ self .smile .mac_address = None
83+ self .smile .model = NONE
84+ self .smile .model_id = None
85+ self .smile .name = NONE
86+ self .smile .type = NONE
87+ self .smile .version = Version ("0.0.0" )
88+ self .smile .zigbee_mac_address = None
8589
8690 @property
8791 def cooling_present (self ) -> bool :
@@ -109,7 +113,7 @@ def reboot(self) -> bool:
109113
110114 All non-legacy devices support gateway-rebooting.
111115 """
112- return not self .smile_legacy
116+ return not self .smile . legacy
113117
114118 async def connect (self ) -> Version :
115119 """Connect to the Plugwise Gateway and determine its name, type, version, and other data."""
@@ -158,16 +162,9 @@ async def connect(self) -> Version:
158162 self ._opentherm_device ,
159163 self ._request ,
160164 self ._schedule_old_states ,
161- self .smile_hostname ,
162- self .smile_hw_version ,
163- self .smile_mac_address ,
164- self .smile_model ,
165- self .smile_model_id ,
166- self .smile_name ,
167- self .smile_type ,
168- self .smile_version ,
165+ self .smile ,
169166 )
170- if not self .smile_legacy
167+ if not self .smile . legacy
171168 else SmileLegacyAPI (
172169 self ._is_thermostat ,
173170 self ._loc_data ,
@@ -176,21 +173,14 @@ async def connect(self) -> Version:
176173 self ._request ,
177174 self ._stretch_v2 ,
178175 self ._target_smile ,
179- self .smile_hostname ,
180- self .smile_hw_version ,
181- self .smile_mac_address ,
182- self .smile_model ,
183- self .smile_name ,
184- self .smile_type ,
185- self .smile_version ,
186- self .smile_zigbee_mac_address ,
176+ self .smile ,
187177 )
188178 )
189179
190180 # Update all endpoints on first connect
191181 await self ._smile_api .full_xml_update ()
192182
193- return self .smile_version
183+ return cast ( Version , self .smile . version )
194184
195185 async def _smile_detect (
196186 self , result : etree .Element , dsmrmain : etree .Element
@@ -203,23 +193,25 @@ async def _smile_detect(
203193 if (gateway := result .find ("./gateway" )) is not None :
204194 if (v_model := gateway .find ("vendor_model" )) is not None :
205195 model = v_model .text
206- self .smile_version = parse (gateway .find ("firmware_version" ).text )
207- self .smile_hw_version = gateway .find ("hardware_version" ).text
208- self .smile_hostname = gateway .find ("hostname" ).text
209- self .smile_mac_address = gateway .find ("mac_address" ).text
210- self .smile_model_id = gateway .find ("vendor_model" ).text
196+ self .smile . version = parse (gateway .find ("firmware_version" ).text )
197+ self .smile . hw_version = gateway .find ("hardware_version" ).text
198+ self .smile . hostname = gateway .find ("hostname" ).text
199+ self .smile . mac_address = gateway .find ("mac_address" ).text
200+ self .smile . model_id = gateway .find ("vendor_model" ).text
211201 else :
212202 model = await self ._smile_detect_legacy (result , dsmrmain , model )
213203
214- if model == "Unknown" or self .smile_version is None : # pragma: no cover
204+ if model == "Unknown" or self .smile .version == Version (
205+ "0.0.0"
206+ ): # pragma: no cover
215207 # Corner case check
216208 LOGGER .error (
217209 "Unable to find model or version information, please create"
218210 " an issue on http://github.com/plugwise/python-plugwise"
219211 )
220212 raise UnsupportedDeviceError
221213
222- version_major = str (self .smile_version .major )
214+ version_major = str (self .smile . version .major )
223215 self ._target_smile = f"{ model } _v{ version_major } "
224216 LOGGER .debug ("Plugwise identified as %s" , self ._target_smile )
225217 if self ._target_smile not in SMILES :
@@ -230,7 +222,7 @@ async def _smile_detect(
230222 )
231223 raise UnsupportedDeviceError
232224
233- if not self .smile_legacy :
225+ if not self .smile . legacy :
234226 self ._timeout = DEFAULT_TIMEOUT
235227
236228 if self ._target_smile in ("smile_open_therm_v2" , "smile_thermo_v3" ):
@@ -240,14 +232,14 @@ async def _smile_detect(
240232 ) # pragma: no cover
241233 raise UnsupportedDeviceError # pragma: no cover
242234
243- self .smile_model = "Gateway"
244- self .smile_name = SMILES [self ._target_smile ].smile_name
245- self .smile_type = SMILES [self ._target_smile ].smile_type
235+ self .smile . model = "Gateway"
236+ self .smile . name = SMILES [self ._target_smile ].smile_name
237+ self .smile . type = SMILES [self ._target_smile ].smile_type
246238
247- if self .smile_type == "stretch" :
239+ if self .smile . type == "stretch" :
248240 self ._stretch_v2 = int (version_major ) == 2
249241
250- if self .smile_type == "thermostat" :
242+ if self .smile . type == "thermostat" :
251243 self ._is_thermostat = True
252244 # For Adam, Anna, determine the system capabilities:
253245 # Find the connected heating/cooling device (heater_central),
@@ -275,36 +267,36 @@ async def _smile_detect_legacy(
275267 return_model = model
276268 # Stretch: find the MAC of the zigbee master_controller (= Stick)
277269 if (network := result .find ("./module/protocols/master_controller" )) is not None :
278- self .smile_zigbee_mac_address = network .find ("mac_address" ).text
270+ self .smile . zigbee_mac_address = network .find ("mac_address" ).text
279271 # Find the active MAC in case there is an orphaned Stick
280272 if zb_networks := result .findall ("./network" ):
281273 for zb_network in zb_networks :
282274 if zb_network .find ("./nodes/network_router" ) is not None :
283275 network = zb_network .find ("./master_controller" )
284- self .smile_zigbee_mac_address = network .find ("mac_address" ).text
276+ self .smile . zigbee_mac_address = network .find ("mac_address" ).text
285277
286278 # Legacy Anna or Stretch:
287279 if (
288280 result .find ('./appliance[type="thermostat"]' ) is not None
289281 or network is not None
290282 ):
291283 system = await self ._request (SYSTEM )
292- self .smile_version = parse (system .find ("./gateway/firmware" ).text )
284+ self .smile . version = parse (system .find ("./gateway/firmware" ).text )
293285 return_model = str (system .find ("./gateway/product" ).text )
294- self .smile_hostname = system .find ("./gateway/hostname" ).text
286+ self .smile . hostname = system .find ("./gateway/hostname" ).text
295287 # If wlan0 contains data it's active, eth0 should be checked last as is preferred
296288 for network in ("wlan0" , "eth0" ):
297289 locator = f"./{ network } /mac"
298290 if (net_locator := system .find (locator )) is not None :
299- self .smile_mac_address = net_locator .text
291+ self .smile . mac_address = net_locator .text
300292
301293 # P1 legacy:
302294 elif dsmrmain is not None :
303295 status = await self ._request (STATUS )
304- self .smile_version = parse (status .find ("./system/version" ).text )
296+ self .smile . version = parse (status .find ("./system/version" ).text )
305297 return_model = str (status .find ("./system/product" ).text )
306- self .smile_hostname = status .find ("./network/hostname" ).text
307- self .smile_mac_address = status .find ("./network/mac_address" ).text
298+ self .smile . hostname = status .find ("./network/hostname" ).text
299+ self .smile . mac_address = status .find ("./network/mac_address" ).text
308300 else : # pragma: no cover
309301 # No cornercase, just end of the line
310302 LOGGER .error (
@@ -313,7 +305,7 @@ async def _smile_detect_legacy(
313305 )
314306 raise ResponseError
315307
316- self .smile_legacy = True
308+ self .smile . legacy = True
317309 return return_model
318310
319311 async def async_update (self ) -> dict [str , GwEntityData ]:
0 commit comments