plugwise 1.7.6__tar.gz → 1.7.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {plugwise-1.7.6 → plugwise-1.7.8}/PKG-INFO +2 -1
  2. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/__init__.py +43 -51
  3. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/common.py +15 -5
  4. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/constants.py +2 -2
  5. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/data.py +15 -13
  6. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/helper.py +28 -42
  7. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/legacy/data.py +3 -1
  8. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/legacy/helper.py +15 -30
  9. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/legacy/smile.py +3 -18
  10. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/smile.py +27 -34
  11. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise.egg-info/PKG-INFO +2 -1
  12. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise.egg-info/requires.txt +1 -0
  13. {plugwise-1.7.6 → plugwise-1.7.8}/pyproject.toml +2 -1
  14. {plugwise-1.7.6 → plugwise-1.7.8}/tests/test_adam.py +133 -141
  15. {plugwise-1.7.6 → plugwise-1.7.8}/tests/test_anna.py +127 -138
  16. {plugwise-1.7.6 → plugwise-1.7.8}/tests/test_generic.py +2 -2
  17. {plugwise-1.7.6 → plugwise-1.7.8}/tests/test_init.py +133 -196
  18. plugwise-1.7.8/tests/test_legacy_anna.py +77 -0
  19. {plugwise-1.7.6 → plugwise-1.7.8}/tests/test_legacy_generic.py +1 -1
  20. {plugwise-1.7.6 → plugwise-1.7.8}/tests/test_legacy_p1.py +15 -15
  21. {plugwise-1.7.6 → plugwise-1.7.8}/tests/test_legacy_stretch.py +26 -26
  22. {plugwise-1.7.6 → plugwise-1.7.8}/tests/test_p1.py +18 -18
  23. plugwise-1.7.6/tests/test_legacy_anna.py +0 -79
  24. {plugwise-1.7.6 → plugwise-1.7.8}/LICENSE +0 -0
  25. {plugwise-1.7.6 → plugwise-1.7.8}/README.md +0 -0
  26. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/exceptions.py +0 -0
  27. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/py.typed +0 -0
  28. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/smilecomm.py +0 -0
  29. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise/util.py +0 -0
  30. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise.egg-info/SOURCES.txt +0 -0
  31. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise.egg-info/dependency_links.txt +0 -0
  32. {plugwise-1.7.6 → plugwise-1.7.8}/plugwise.egg-info/top_level.txt +0 -0
  33. {plugwise-1.7.6 → plugwise-1.7.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugwise
3
- Version: 1.7.6
3
+ Version: 1.7.8
4
4
  Summary: Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3.
5
5
  Author: Plugwise device owners
6
6
  Maintainer: bouwew, CoMPaTech
@@ -16,6 +16,7 @@ Classifier: Topic :: Home Automation
16
16
  Requires-Python: >=3.13
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
+ Requires-Dist: aiofiles
19
20
  Requires-Dist: aiohttp
20
21
  Requires-Dist: defusedxml
21
22
  Requires-Dist: munch
@@ -5,6 +5,8 @@ Plugwise backend module for Home Assistant Core.
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ from typing import cast
9
+
8
10
  from plugwise.constants import (
9
11
  DEFAULT_LEGACY_TIMEOUT,
10
12
  DEFAULT_PORT,
@@ -36,6 +38,7 @@ from plugwise.smilecomm import SmileComm
36
38
 
37
39
  import aiohttp
38
40
  from defusedxml import ElementTree as etree
41
+ from munch import Munch
39
42
  from packaging.version import Version, parse
40
43
 
41
44
 
@@ -72,16 +75,17 @@ class Smile(SmileComm):
72
75
  self._smile_api: SmileAPI | SmileLegacyAPI
73
76
  self._stretch_v2 = False
74
77
  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
85
89
 
86
90
  @property
87
91
  def cooling_present(self) -> bool:
@@ -109,7 +113,7 @@ class Smile(SmileComm):
109
113
 
110
114
  All non-legacy devices support gateway-rebooting.
111
115
  """
112
- return not self.smile_legacy
116
+ return not self.smile.legacy
113
117
 
114
118
  async def connect(self) -> Version:
115
119
  """Connect to the Plugwise Gateway and determine its name, type, version, and other data."""
@@ -158,16 +162,9 @@ class Smile(SmileComm):
158
162
  self._opentherm_device,
159
163
  self._request,
160
164
  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,
169
166
  )
170
- if not self.smile_legacy
167
+ if not self.smile.legacy
171
168
  else SmileLegacyAPI(
172
169
  self._is_thermostat,
173
170
  self._loc_data,
@@ -176,21 +173,14 @@ class Smile(SmileComm):
176
173
  self._request,
177
174
  self._stretch_v2,
178
175
  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,
187
177
  )
188
178
  )
189
179
 
190
180
  # Update all endpoints on first connect
191
181
  await self._smile_api.full_xml_update()
192
182
 
193
- return self.smile_version
183
+ return cast(Version, self.smile.version)
194
184
 
195
185
  async def _smile_detect(
196
186
  self, result: etree.Element, dsmrmain: etree.Element
@@ -203,15 +193,17 @@ class Smile(SmileComm):
203
193
  if (gateway := result.find("./gateway")) is not None:
204
194
  if (v_model := gateway.find("vendor_model")) is not None:
205
195
  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
211
201
  else:
212
202
  model = await self._smile_detect_legacy(result, dsmrmain, model)
213
203
 
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
215
207
  # Corner case check
216
208
  LOGGER.error(
217
209
  "Unable to find model or version information, please create"
@@ -219,7 +211,7 @@ class Smile(SmileComm):
219
211
  )
220
212
  raise UnsupportedDeviceError
221
213
 
222
- version_major = str(self.smile_version.major)
214
+ version_major = str(self.smile.version.major)
223
215
  self._target_smile = f"{model}_v{version_major}"
224
216
  LOGGER.debug("Plugwise identified as %s", self._target_smile)
225
217
  if self._target_smile not in SMILES:
@@ -230,7 +222,7 @@ class Smile(SmileComm):
230
222
  )
231
223
  raise UnsupportedDeviceError
232
224
 
233
- if not self.smile_legacy:
225
+ if not self.smile.legacy:
234
226
  self._timeout = DEFAULT_TIMEOUT
235
227
 
236
228
  if self._target_smile in ("smile_open_therm_v2", "smile_thermo_v3"):
@@ -240,14 +232,14 @@ class Smile(SmileComm):
240
232
  ) # pragma: no cover
241
233
  raise UnsupportedDeviceError # pragma: no cover
242
234
 
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
246
238
 
247
- if self.smile_type == "stretch":
239
+ if self.smile.type == "stretch":
248
240
  self._stretch_v2 = int(version_major) == 2
249
241
 
250
- if self.smile_type == "thermostat":
242
+ if self.smile.type == "thermostat":
251
243
  self._is_thermostat = True
252
244
  # For Adam, Anna, determine the system capabilities:
253
245
  # Find the connected heating/cooling device (heater_central),
@@ -275,13 +267,13 @@ class Smile(SmileComm):
275
267
  return_model = model
276
268
  # Stretch: find the MAC of the zigbee master_controller (= Stick)
277
269
  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
279
271
  # Find the active MAC in case there is an orphaned Stick
280
272
  if zb_networks := result.findall("./network"):
281
273
  for zb_network in zb_networks:
282
274
  if zb_network.find("./nodes/network_router") is not None:
283
275
  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
285
277
 
286
278
  # Legacy Anna or Stretch:
287
279
  if (
@@ -289,22 +281,22 @@ class Smile(SmileComm):
289
281
  or network is not None
290
282
  ):
291
283
  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)
293
285
  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
295
287
  # If wlan0 contains data it's active, eth0 should be checked last as is preferred
296
288
  for network in ("wlan0", "eth0"):
297
289
  locator = f"./{network}/mac"
298
290
  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
300
292
 
301
293
  # P1 legacy:
302
294
  elif dsmrmain is not None:
303
295
  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)
305
297
  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
308
300
  else: # pragma: no cover
309
301
  # No cornercase, just end of the line
310
302
  LOGGER.error(
@@ -313,7 +305,7 @@ class Smile(SmileComm):
313
305
  )
314
306
  raise ResponseError
315
307
 
316
- self.smile_legacy = True
308
+ self.smile.legacy = True
317
309
  return return_model
318
310
 
319
311
  async def async_update(self) -> dict[str, GwEntityData]:
@@ -10,6 +10,7 @@ from typing import cast
10
10
  from plugwise.constants import (
11
11
  ANNA,
12
12
  NONE,
13
+ PRIORITY_DEVICE_CLASSES,
13
14
  SPECIAL_PLUG_TYPES,
14
15
  SWITCH_GROUP_TYPES,
15
16
  ApplianceType,
@@ -55,17 +56,16 @@ class SmileCommon:
55
56
  self._heater_id: str = NONE
56
57
  self._on_off_device: bool
57
58
  self.gw_entities: dict[str, GwEntityData] = {}
58
- self.smile_name: str
59
- self.smile_type: str
59
+ self.smile: Munch
60
60
 
61
61
  @property
62
62
  def heater_id(self) -> str:
63
63
  """Return the heater-id."""
64
64
  return self._heater_id
65
65
 
66
- def smile(self, name: str) -> bool:
66
+ def check_name(self, name: str) -> bool:
67
67
  """Helper-function checking the smile-name."""
68
- return self.smile_name == name
68
+ return bool(self.smile.name == name)
69
69
 
70
70
  def _appl_heater_central_info(
71
71
  self,
@@ -153,6 +153,16 @@ class SmileCommon:
153
153
  self.gw_entities[appl.entity_id][appl_key] = value
154
154
  self._count += 1
155
155
 
156
+ def _reorder_devices(self) -> None:
157
+ """Place the gateway and optional heater_central devices as 1st and 2nd."""
158
+ reordered = {}
159
+ for dev_class in PRIORITY_DEVICE_CLASSES:
160
+ for entity_id, entity in dict(self.gw_entities).items():
161
+ if entity["dev_class"] == dev_class:
162
+ reordered[entity_id] = self.gw_entities.pop(entity_id)
163
+ break
164
+ self.gw_entities = {**reordered, **self.gw_entities}
165
+
156
166
  def _entity_switching_group(self, entity: GwEntityData, data: GwEntityData) -> None:
157
167
  """Helper-function for _get_device_zone_data().
158
168
 
@@ -173,7 +183,7 @@ class SmileCommon:
173
183
  """
174
184
  switch_groups: dict[str, GwEntityData] = {}
175
185
  # P1 and Anna don't have switchgroups
176
- if self.smile_type == "power" or self.smile(ANNA):
186
+ if self.smile.type == "power" or self.check_name(ANNA):
177
187
  return switch_groups
178
188
 
179
189
  for group in self._domain_objects.findall("./group"):
@@ -86,7 +86,7 @@ MIN_SETPOINT: Final[float] = 4.0
86
86
  MODULE_LOCATOR: Final = "./logs/point_log/*[@id]"
87
87
  NONE: Final = "None"
88
88
  OFF: Final = "off"
89
- PRIORITY_DEVICE_CLASSES = ("heater_central", "gateway")
89
+ PRIORITY_DEVICE_CLASSES = ("gateway", "heater_central")
90
90
 
91
91
  # XML data paths
92
92
  APPLIANCES: Final = "/core/appliances"
@@ -553,7 +553,7 @@ class GwEntityData(TypedDict, total=False):
553
553
  preset_modes: list[str] | None
554
554
  # Schedules:
555
555
  available_schedules: list[str]
556
- select_schedule: str
556
+ select_schedule: str | None
557
557
 
558
558
  climate_mode: str
559
559
  # Extra for Adam Master Thermostats
@@ -35,7 +35,7 @@ class SmileData(SmileHelper):
35
35
  Collect data for each entity and add to self.gw_entities.
36
36
  """
37
37
  self._update_gw_entities()
38
- if self.smile(ADAM):
38
+ if self.check_name(ADAM):
39
39
  self._update_zones()
40
40
  self.gw_entities.update(self._zones)
41
41
 
@@ -86,7 +86,7 @@ class SmileData(SmileHelper):
86
86
  mac_pattern = re.compile(r"(?:[0-9A-F]{2}){8}")
87
87
  matches = ["Battery", "below"]
88
88
  if self._notifications:
89
- for msg_id, notification in list(self._notifications.items()):
89
+ for msg_id, notification in self._notifications.copy().items():
90
90
  mac_address: str | None = None
91
91
  message: str | None = notification.get("message")
92
92
  warning: str | None = notification.get("warning")
@@ -111,7 +111,7 @@ class SmileData(SmileHelper):
111
111
  """Helper-function adding or updating the Plugwise notifications."""
112
112
  if (
113
113
  entity_id == self._gateway_id
114
- and (self._is_thermostat or self.smile_type == "power")
114
+ and (self._is_thermostat or self.smile.type == "power")
115
115
  ) or (
116
116
  "binary_sensors" in entity
117
117
  and "plugwise_notification" in entity["binary_sensors"]
@@ -124,7 +124,7 @@ class SmileData(SmileHelper):
124
124
  """Helper-function for adding/updating various cooling-related values."""
125
125
  # For Anna and heating + cooling, replace setpoint with setpoint_high/_low
126
126
  if (
127
- self.smile(ANNA)
127
+ self.check_name(ANNA)
128
128
  and self._cooling_present
129
129
  and entity["dev_class"] == "thermostat"
130
130
  ):
@@ -194,11 +194,11 @@ class SmileData(SmileHelper):
194
194
  # Switching groups data
195
195
  self._entity_switching_group(entity, data)
196
196
  # Adam data
197
- if self.smile(ADAM):
197
+ if self.check_name(ADAM):
198
198
  self._get_adam_data(entity, data)
199
199
 
200
200
  # Thermostat data for Anna (presets, temperatures etc)
201
- if self.smile(ANNA) and entity["dev_class"] == "thermostat":
201
+ if self.check_name(ANNA) and entity["dev_class"] == "thermostat":
202
202
  self._climate_data(entity_id, entity, data)
203
203
  self._get_anna_control_state(data)
204
204
 
@@ -232,12 +232,12 @@ class SmileData(SmileHelper):
232
232
  if self._on_off_device and isinstance(self._heating_valves(), int):
233
233
  data["binary_sensors"]["heating_state"] = self._heating_valves() != 0
234
234
  # Add cooling_enabled binary_sensor
235
- if "binary_sensors" in data:
236
- if (
237
- "cooling_enabled" not in data["binary_sensors"]
238
- and self._cooling_present
239
- ):
240
- data["binary_sensors"]["cooling_enabled"] = self._cooling_enabled
235
+ if (
236
+ "binary_sensors" in data
237
+ and "cooling_enabled" not in data["binary_sensors"]
238
+ and self._cooling_present
239
+ ):
240
+ data["binary_sensors"]["cooling_enabled"] = self._cooling_enabled
241
241
 
242
242
  # Show the allowed regulation_modes and gateway_modes
243
243
  if entity["dev_class"] == "gateway":
@@ -268,11 +268,13 @@ class SmileData(SmileHelper):
268
268
  data["active_preset"] = self._preset(loc_id)
269
269
 
270
270
  # Schedule
271
+ data["available_schedules"] = []
272
+ data["select_schedule"] = None
273
+ self._count += 2
271
274
  avail_schedules, sel_schedule = self._schedules(loc_id)
272
275
  if avail_schedules != [NONE]:
273
276
  data["available_schedules"] = avail_schedules
274
277
  data["select_schedule"] = sel_schedule
275
- self._count += 2
276
278
 
277
279
  # Set HA climate HVACMode: auto, heat, heat_cool, cool and off
278
280
  data["climate_mode"] = "auto"
@@ -28,7 +28,6 @@ from plugwise.constants import (
28
28
  NONE,
29
29
  OFF,
30
30
  P1_MEASUREMENTS,
31
- PRIORITY_DEVICE_CLASSES,
32
31
  TEMP_CELSIUS,
33
32
  THERMOSTAT_CLASSES,
34
33
  TOGGLES,
@@ -85,11 +84,7 @@ class SmileHelper(SmileCommon):
85
84
  self._gateway_id: str = NONE
86
85
  self._zones: dict[str, GwEntityData]
87
86
  self.gw_entities: dict[str, GwEntityData]
88
- self.smile_hw_version: str | None
89
- self.smile_mac_address: str | None
90
- self.smile_model: str
91
- self.smile_model_id: str | None
92
- self.smile_version: version.Version
87
+ self.smile: Munch = Munch()
93
88
 
94
89
  @property
95
90
  def gateway_id(self) -> str:
@@ -160,11 +155,11 @@ class SmileHelper(SmileCommon):
160
155
 
161
156
  self._create_gw_entities(appl)
162
157
 
163
- if self.smile_type == "power":
158
+ if self.smile.type == "power":
164
159
  self._get_p1_smartmeter_info()
165
160
 
166
161
  # Sort the gw_entities
167
- self._sort_gw_entities()
162
+ self._reorder_devices()
168
163
 
169
164
  def _get_p1_smartmeter_info(self) -> None:
170
165
  """For P1 collect the connected SmartMeter info from the Home/building location.
@@ -197,18 +192,6 @@ class SmileHelper(SmileCommon):
197
192
 
198
193
  self._create_gw_entities(appl)
199
194
 
200
- def _sort_gw_entities(self) -> None:
201
- """Place the gateway and optional heater_central entities as 1st and 2nd."""
202
- for dev_class in PRIORITY_DEVICE_CLASSES:
203
- for entity_id, entity in dict(self.gw_entities).items():
204
- if entity["dev_class"] == dev_class:
205
- priority_entity = entity
206
- self.gw_entities.pop(entity_id)
207
- other_entities = self.gw_entities
208
- priority_entities = {entity_id: priority_entity}
209
- self.gw_entities = {**priority_entities, **other_entities}
210
- break
211
-
212
195
  def _all_locations(self) -> None:
213
196
  """Collect all locations."""
214
197
  loc = Munch()
@@ -268,16 +251,16 @@ class SmileHelper(SmileCommon):
268
251
  def _appl_gateway_info(self, appl: Munch, appliance: etree.Element) -> Munch:
269
252
  """Helper-function for _appliance_info_finder()."""
270
253
  self._gateway_id = appliance.attrib["id"]
271
- appl.firmware = str(self.smile_version)
272
- appl.hardware = self.smile_hw_version
273
- appl.mac = self.smile_mac_address
274
- appl.model = self.smile_model
275
- appl.model_id = self.smile_model_id
276
- appl.name = self.smile_name
254
+ appl.firmware = str(self.smile.version)
255
+ appl.hardware = self.smile.hw_version
256
+ appl.mac = self.smile.mac_address
257
+ appl.model = self.smile.model
258
+ appl.model_id = self.smile.model_id
259
+ appl.name = self.smile.name
277
260
  appl.vendor_name = "Plugwise"
278
261
 
279
262
  # Adam: collect the ZigBee MAC address of the Smile
280
- if self.smile(ADAM):
263
+ if self.check_name(ADAM):
281
264
  if (
282
265
  found := self._domain_objects.find(".//protocols/zig_bee_coordinator")
283
266
  ) is not None:
@@ -346,7 +329,7 @@ class SmileHelper(SmileCommon):
346
329
  # Get P1 smartmeter data from LOCATIONS
347
330
  entity = self.gw_entities[entity_id]
348
331
  # !! DON'T CHANGE below two if-lines, will break stuff !!
349
- if self.smile_type == "power":
332
+ if self.smile.type == "power":
350
333
  if entity["dev_class"] == "smartmeter":
351
334
  data.update(self._power_data_from_location())
352
335
 
@@ -383,7 +366,7 @@ class SmileHelper(SmileCommon):
383
366
  data.pop("c_heating_state")
384
367
  self._count -= 1
385
368
 
386
- if self._is_thermostat and self.smile(ANNA):
369
+ if self._is_thermostat and self.check_name(ANNA):
387
370
  self._update_anna_cooling(entity_id, data)
388
371
 
389
372
  self._cleanup_data(data)
@@ -428,7 +411,8 @@ class SmileHelper(SmileCommon):
428
411
  case "elga_status_code":
429
412
  data["elga_status_code"] = int(appl_p_loc.text)
430
413
  case "select_dhw_mode":
431
- data["select_dhw_mode"] = appl_p_loc.text
414
+ if self._dhw_allowed_modes:
415
+ data["select_dhw_mode"] = appl_p_loc.text
432
416
 
433
417
  common_match_cases(measurement, attrs, appl_p_loc, data)
434
418
 
@@ -484,7 +468,7 @@ class SmileHelper(SmileCommon):
484
468
  item == "thermostat"
485
469
  and (
486
470
  entity["dev_class"] != "climate"
487
- if self.smile(ADAM)
471
+ if self.check_name(ADAM)
488
472
  else entity["dev_class"] != "thermostat"
489
473
  )
490
474
  ):
@@ -539,7 +523,7 @@ class SmileHelper(SmileCommon):
539
523
 
540
524
  Collect the requested gateway mode.
541
525
  """
542
- if not (self.smile(ADAM) and entity_id == self._gateway_id):
526
+ if not (entity_id == self._gateway_id and self.check_name(ADAM)):
543
527
  return None
544
528
 
545
529
  if (search := search_actuator_functionalities(appliance, key)) is not None:
@@ -552,29 +536,31 @@ class SmileHelper(SmileCommon):
552
536
  ) -> None:
553
537
  """Helper-function for _get_measurement_data().
554
538
 
555
- Adam: collect the gateway regulation_mode.
539
+ Adam gateway: collect the gateway regulation_mode.
556
540
  """
557
541
  if (
558
542
  mode := self._get_actuator_mode(
559
543
  appliance, entity_id, "regulation_mode_control_functionality"
560
544
  )
561
545
  ) is not None:
562
- data["select_regulation_mode"] = mode
563
- self._count += 1
546
+ # Below line needs to be here to set the boolean for both older and recent Adam firmware versions
564
547
  self._cooling_enabled = mode == "cooling"
548
+ if self._reg_allowed_modes:
549
+ data["select_regulation_mode"] = mode
550
+ self._count += 1
565
551
 
566
552
  def _get_gateway_mode(
567
553
  self, appliance: etree.Element, entity_id: str, data: GwEntityData
568
554
  ) -> None:
569
555
  """Helper-function for _get_measurement_data().
570
556
 
571
- Adam: collect the gateway mode.
557
+ Adam gateway: collect the gateway mode.
572
558
  """
573
559
  if (
574
560
  mode := self._get_actuator_mode(
575
561
  appliance, entity_id, "gateway_mode_control_functionality"
576
562
  )
577
- ) is not None:
563
+ ) is not None and self._gw_allowed_modes:
578
564
  data["select_gateway_mode"] = mode
579
565
  self._count += 1
580
566
 
@@ -605,10 +591,10 @@ class SmileHelper(SmileCommon):
605
591
 
606
592
  Solution for Core issue #81839.
607
593
  """
608
- if self.smile(ANNA):
594
+ if self.check_name(ANNA):
609
595
  data["binary_sensors"]["heating_state"] = data["c_heating_state"]
610
596
 
611
- if self.smile(ADAM):
597
+ if self.check_name(ADAM):
612
598
  # First count when not present, then create and init to False.
613
599
  # When present init to False
614
600
  if "heating_state" not in data["binary_sensors"]:
@@ -723,7 +709,7 @@ class SmileHelper(SmileCommon):
723
709
  for entity_id, entity in self.gw_entities.items():
724
710
  self._rank_thermostat(thermo_matching, loc_id, entity_id, entity)
725
711
 
726
- for loc_id, loc_data in list(self._thermo_locs.items()):
712
+ for loc_id, loc_data in self._thermo_locs.items():
727
713
  if loc_data["primary_prio"] != 0:
728
714
  self._zones[loc_id] = {
729
715
  "dev_class": "climate",
@@ -799,8 +785,8 @@ class SmileHelper(SmileCommon):
799
785
 
800
786
  # Handle missing control_state in regulation_mode off for firmware >= 3.2.0 (issue #776)
801
787
  # In newer firmware versions, default to "off" when control_state is not present
802
- if self.smile_version != version.Version("0.0.0"):
803
- if self.smile_version >= version.parse("3.2.0"):
788
+ if self.smile.version != version.Version("0.0.0"):
789
+ if self.smile.version >= version.parse("3.2.0"):
804
790
  return "off"
805
791
 
806
792
  # Older Adam firmware does not have the control_state xml-key
@@ -67,11 +67,13 @@ class SmileLegacyData(SmileLegacyHelper):
67
67
  data["active_preset"] = self._preset()
68
68
 
69
69
  # Schedule
70
+ data["available_schedules"] = []
71
+ data["select_schedule"] = None
72
+ self._count += 2
70
73
  avail_schedules, sel_schedule = self._schedules()
71
74
  if avail_schedules != [NONE]:
72
75
  data["available_schedules"] = avail_schedules
73
76
  data["select_schedule"] = sel_schedule
74
- self._count += 2
75
77
 
76
78
  # Set HA climate HVACMode: auto, heat
77
79
  data["climate_mode"] = "auto"