plugwise 1.6.1__tar.gz → 1.6.2__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 (32) hide show
  1. {plugwise-1.6.1 → plugwise-1.6.2}/PKG-INFO +1 -1
  2. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/__init__.py +1 -0
  3. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/constants.py +2 -1
  4. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/data.py +10 -3
  5. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/helper.py +18 -4
  6. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/smile.py +2 -0
  7. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise.egg-info/PKG-INFO +1 -1
  8. {plugwise-1.6.1 → plugwise-1.6.2}/pyproject.toml +1 -1
  9. {plugwise-1.6.1 → plugwise-1.6.2}/tests/test_adam.py +18 -4
  10. {plugwise-1.6.1 → plugwise-1.6.2}/LICENSE +0 -0
  11. {plugwise-1.6.1 → plugwise-1.6.2}/README.md +0 -0
  12. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/common.py +0 -0
  13. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/exceptions.py +0 -0
  14. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/legacy/data.py +0 -0
  15. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/legacy/helper.py +0 -0
  16. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/legacy/smile.py +0 -0
  17. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/py.typed +0 -0
  18. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise/util.py +0 -0
  19. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise.egg-info/SOURCES.txt +0 -0
  20. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise.egg-info/dependency_links.txt +0 -0
  21. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise.egg-info/requires.txt +0 -0
  22. {plugwise-1.6.1 → plugwise-1.6.2}/plugwise.egg-info/top_level.txt +0 -0
  23. {plugwise-1.6.1 → plugwise-1.6.2}/setup.cfg +0 -0
  24. {plugwise-1.6.1 → plugwise-1.6.2}/setup.py +0 -0
  25. {plugwise-1.6.1 → plugwise-1.6.2}/tests/test_anna.py +0 -0
  26. {plugwise-1.6.1 → plugwise-1.6.2}/tests/test_generic.py +0 -0
  27. {plugwise-1.6.1 → plugwise-1.6.2}/tests/test_init.py +0 -0
  28. {plugwise-1.6.1 → plugwise-1.6.2}/tests/test_legacy_anna.py +0 -0
  29. {plugwise-1.6.1 → plugwise-1.6.2}/tests/test_legacy_generic.py +0 -0
  30. {plugwise-1.6.1 → plugwise-1.6.2}/tests/test_legacy_p1.py +0 -0
  31. {plugwise-1.6.1 → plugwise-1.6.2}/tests/test_legacy_stretch.py +0 -0
  32. {plugwise-1.6.1 → plugwise-1.6.2}/tests/test_p1.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: plugwise
3
- Version: 1.6.1
3
+ Version: 1.6.2
4
4
  Summary: Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3.
5
5
  Home-page: https://github.com/plugwise/python-plugwise
6
6
  Author: Plugwise device owners
@@ -147,6 +147,7 @@ class Smile(SmileComm):
147
147
  self.smile_model_id,
148
148
  self.smile_name,
149
149
  self.smile_type,
150
+ self.smile_version,
150
151
  self._port,
151
152
  self._username,
152
153
  ) if not self.smile_legacy else SmileLegacyAPI(
@@ -196,6 +196,7 @@ ZONE_MEASUREMENTS: Final[dict[str, DATA | UOM]] = {
196
196
  "electricity_produced": UOM(POWER_WATT),
197
197
  "relay": UOM(NONE),
198
198
  "temperature": UOM(TEMP_CELSIUS), # HA Core thermostat current_temperature
199
+ "thermostat": DATA("setpoint", TEMP_CELSIUS), # HA Core thermostat setpoint
199
200
  }
200
201
 
201
202
  # Literals
@@ -566,7 +567,7 @@ class GwEntityData(TypedDict, total=False):
566
567
 
567
568
  climate_mode: str
568
569
  # Extra for Adam Master Thermostats
569
- control_state: str | bool
570
+ control_state: str
570
571
 
571
572
  # Dict-types
572
573
  binary_sensors: SmileBinarySensors
@@ -160,9 +160,16 @@ class SmileData(SmileHelper):
160
160
  """
161
161
  zone = self._zones[loc_id]
162
162
  data = self._get_zone_data(loc_id)
163
- if ctrl_state := self._control_state(loc_id):
164
- data["control_state"] = ctrl_state
165
- self._count += 1
163
+ if ctrl_state := self._control_state(data, loc_id):
164
+ if str(ctrl_state) in ("cooling", "heating", "preheating"):
165
+ data["control_state"] = str(ctrl_state)
166
+ self._count += 1
167
+ if str(ctrl_state) == "off":
168
+ data["control_state"] = "idle"
169
+ self._count += 1
170
+
171
+ data["sensors"].pop("setpoint") # remove, only used in _control_state()
172
+ self._count -= 1
166
173
 
167
174
  # Thermostat data (presets, temperatures etc)
168
175
  self._climate_data(loc_id, zone, data)
@@ -64,7 +64,7 @@ from dateutil import tz
64
64
  from dateutil.parser import parse
65
65
  from defusedxml import ElementTree as etree
66
66
  from munch import Munch
67
- from packaging.version import Version
67
+ from packaging import version
68
68
 
69
69
 
70
70
  class SmileComm:
@@ -252,13 +252,14 @@ class SmileHelper(SmileCommon):
252
252
  self.gateway_id: str
253
253
  self.gw_data: GatewayData = {}
254
254
  self.gw_entities: dict[str, GwEntityData] = {}
255
- self.smile_fw_version: Version | None
255
+ self.smile_fw_version: version.Version | None
256
256
  self.smile_hw_version: str | None
257
257
  self.smile_mac_address: str | None
258
258
  self.smile_model: str
259
259
  self.smile_model_id: str | None
260
260
  self.smile_name: str
261
261
  self.smile_type: str
262
+ self.smile_version: version.Version | None
262
263
  self.smile_zigbee_mac_address: str | None
263
264
  self.therms_with_offset_func: list[str] = []
264
265
  self._zones: dict[str, GwEntityData] = {}
@@ -917,7 +918,7 @@ class SmileHelper(SmileCommon):
917
918
  else:
918
919
  thermo_loc["secondary"].append(appliance_id)
919
920
 
920
- def _control_state(self, loc_id: str) -> str | bool:
921
+ def _control_state(self, data: GwEntityData, loc_id: str) -> str | bool:
921
922
  """Helper-function for _get_adam_data().
922
923
 
923
924
  Adam: find the thermostat control_state of a location, from DOMAIN_OBJECTS.
@@ -930,7 +931,20 @@ class SmileHelper(SmileCommon):
930
931
  if (ctrl_state := location.find(locator)) is not None:
931
932
  return str(ctrl_state.text)
932
933
 
933
- return False
934
+ # Handle missing control_state in regulation_mode off for firmware >= 3.2.0 (issue #776)
935
+ # In newer firmware versions, default to "off" when control_state is not present
936
+ if self.smile_version is not None:
937
+ if self.smile_version >= version.parse("3.2.0"):
938
+ return "off"
939
+
940
+ # Older Adam firmware does not have the control_state xml-key
941
+ # Work around this by comparing the reported temperature and setpoint for a location
942
+ setpoint = data["sensors"]["setpoint"]
943
+ temperature = data["sensors"]["temperature"]
944
+ # No cooling available in older firmware
945
+ return "heating" if temperature < setpoint else "off"
946
+
947
+ return False # pragma: no cover
934
948
 
935
949
  def _heating_valves(self) -> int | bool:
936
950
  """Helper-function for smile.py: _get_adam_data().
@@ -66,6 +66,7 @@ class SmileAPI(SmileData):
66
66
  smile_model_id: str | None,
67
67
  smile_name: str,
68
68
  smile_type: str,
69
+ smile_version: Version | None,
69
70
  port: int = DEFAULT_PORT,
70
71
  username: str = DEFAULT_USERNAME,
71
72
  ) -> None:
@@ -90,6 +91,7 @@ class SmileAPI(SmileData):
90
91
  self.smile_model_id = smile_model_id
91
92
  self.smile_name = smile_name
92
93
  self.smile_type = smile_type
94
+ self.smile_version = smile_version
93
95
  SmileData.__init__(self)
94
96
 
95
97
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: plugwise
3
- Version: 1.6.1
3
+ Version: 1.6.2
4
4
  Summary: Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3.
5
5
  Home-page: https://github.com/plugwise/python-plugwise
6
6
  Author: Plugwise device owners
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "plugwise"
7
- version = "1.6.1"
7
+ version = "1.6.2"
8
8
  license = {file = "LICENSE"}
9
9
  description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
10
10
  readme = "README.md"
@@ -18,7 +18,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
18
18
 
19
19
  @pytest.mark.asyncio
20
20
  async def test_connect_adam_plus_anna_new(self):
21
- """Test extended Adam (firmware 3.8) with Anna and a switch-group setup."""
21
+ """Test extended Adam (firmware 3.7) with Anna and a switch-group setup."""
22
22
  self.smile_setup = "adam_plus_anna_new"
23
23
 
24
24
  testdata = self.load_testdata(SMILE_TYPE, self.smile_setup)
@@ -167,6 +167,20 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
167
167
  await smile.close_connection()
168
168
  await self.disconnect(server, client)
169
169
 
170
+ @pytest.mark.asyncio
171
+ async def test_connect_adam_plus_anna_new_regulation_off(self):
172
+ """Test regultaion_mode off with control_state key missing for Adam."""
173
+ self.smile_setup = "adam_plus_anna_new_regulation_off"
174
+
175
+ testdata = self.load_testdata(SMILE_TYPE, self.smile_setup)
176
+ server, smile, client = await self.connect_wrapper()
177
+ assert smile.smile_hostname == "smile000000"
178
+
179
+ await self.device_test(smile, "2023-12-17 00:00:01", testdata)
180
+
181
+ await smile.close_connection()
182
+ await self.disconnect(server, client)
183
+
170
184
  @pytest.mark.asyncio
171
185
  async def test_connect_adam_zone_per_device(self):
172
186
  """Test an extensive setup of Adam with a zone per device."""
@@ -189,7 +203,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
189
203
  assert smile._last_active["82fa13f017d240daa0d0ea1775420f24"] == CV_JESSIE
190
204
  assert smile._last_active["08963fec7c53423ca5680aa4cb502c63"] == BADKAMER_SCHEMA
191
205
  assert smile._last_active["446ac08dd04d4eff8ac57489757b7314"] == BADKAMER_SCHEMA
192
- assert self.entity_items == 364
206
+ assert self.entity_items == 369
193
207
 
194
208
  assert "af82e4ccf9c548528166d38e560662a4" in self.notifications
195
209
  await smile.delete_notification()
@@ -267,7 +281,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
267
281
  assert smile._last_active["82fa13f017d240daa0d0ea1775420f24"] == CV_JESSIE
268
282
  assert smile._last_active["08963fec7c53423ca5680aa4cb502c63"] == BADKAMER_SCHEMA
269
283
  assert smile._last_active["446ac08dd04d4eff8ac57489757b7314"] == BADKAMER_SCHEMA
270
- assert self.entity_items == 364
284
+ assert self.entity_items == 369
271
285
 
272
286
  assert "af82e4ccf9c548528166d38e560662a4" in self.notifications
273
287
 
@@ -351,7 +365,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
351
365
  await self.device_test(smile, "2020-03-22 00:00:01", testdata)
352
366
  assert smile.gateway_id == "b128b4bbbd1f47e9bf4d756e8fb5ee94"
353
367
  assert smile._last_active["009490cc2f674ce6b576863fbb64f867"] == "Weekschema"
354
- assert self.entity_items == 79
368
+ assert self.entity_items == 80
355
369
  assert "6fb89e35caeb4b1cb275184895202d84" in self.notifications
356
370
 
357
371
  result = await self.tinker_thermostat(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes