plugwise 1.7.8a1__tar.gz → 1.8.0__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.7.8a1 → plugwise-1.8.0}/PKG-INFO +9 -8
  2. {plugwise-1.7.8a1 → plugwise-1.8.0}/README.md +7 -7
  3. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/common.py +2 -2
  4. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/constants.py +2 -1
  5. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/data.py +3 -1
  6. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/helper.py +11 -8
  7. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/legacy/data.py +4 -1
  8. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise.egg-info/PKG-INFO +9 -8
  9. {plugwise-1.7.8a1 → plugwise-1.8.0}/pyproject.toml +5 -4
  10. {plugwise-1.7.8a1 → plugwise-1.8.0}/tests/test_adam.py +29 -7
  11. {plugwise-1.7.8a1 → plugwise-1.8.0}/tests/test_legacy_anna.py +2 -2
  12. {plugwise-1.7.8a1 → plugwise-1.8.0}/tests/test_legacy_stretch.py +2 -2
  13. {plugwise-1.7.8a1 → plugwise-1.8.0}/LICENSE +0 -0
  14. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/__init__.py +0 -0
  15. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/exceptions.py +0 -0
  16. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/legacy/helper.py +0 -0
  17. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/legacy/smile.py +0 -0
  18. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/py.typed +0 -0
  19. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/smile.py +0 -0
  20. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/smilecomm.py +0 -0
  21. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise/util.py +0 -0
  22. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise.egg-info/SOURCES.txt +0 -0
  23. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise.egg-info/dependency_links.txt +0 -0
  24. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise.egg-info/requires.txt +0 -0
  25. {plugwise-1.7.8a1 → plugwise-1.8.0}/plugwise.egg-info/top_level.txt +0 -0
  26. {plugwise-1.7.8a1 → plugwise-1.8.0}/setup.cfg +0 -0
  27. {plugwise-1.7.8a1 → plugwise-1.8.0}/tests/test_anna.py +0 -0
  28. {plugwise-1.7.8a1 → plugwise-1.8.0}/tests/test_generic.py +0 -0
  29. {plugwise-1.7.8a1 → plugwise-1.8.0}/tests/test_init.py +0 -0
  30. {plugwise-1.7.8a1 → plugwise-1.8.0}/tests/test_legacy_generic.py +0 -0
  31. {plugwise-1.7.8a1 → plugwise-1.8.0}/tests/test_legacy_p1.py +0 -0
  32. {plugwise-1.7.8a1 → plugwise-1.8.0}/tests/test_p1.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugwise
3
- Version: 1.7.8a1
3
+ Version: 1.8.0
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
@@ -11,6 +11,7 @@ Keywords: home,automation,plugwise,module
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3.14
14
15
  Classifier: Programming Language :: Python :: 3.13
15
16
  Classifier: Topic :: Home Automation
16
17
  Requires-Python: >=3.13
@@ -87,17 +88,17 @@ Module providing interfacing with the Plugwise devices:
87
88
  ### Smile
88
89
 
89
90
  - [x] Adam
90
- - [x] Lisa
91
+ - [x] Emma (only tested as ZigBee device connected to Adam)
91
92
  - [x] Jip
92
- - [x] Floor
93
- - [x] Tom
93
+ - [x] Lisa
94
+ - [x] Tom/Floor
94
95
  - [x] Koen (a Koen always comes with a Plug, the Plug is the active part)
95
96
  - [x] Plug
96
97
  - [x] Aqara Plug
97
- - [x] Anna
98
- - [x] Smile P1
99
- - [x] Stretch
100
- - [ ] Some of the equipment mentioned in USB when in use via Stretch or Adam
98
+ - [x] Anna (v1.8 and later firmware versions)
99
+ - [ ] Anna P1
100
+ - [x] Smile P1 (v2.0 and later firmware versions)
101
+ - [x] Stretch (only with Circles, please help out with other devices)
101
102
 
102
103
  - [x] [Home-Assistant](https://home-assistant.io) via
103
104
  - [x] Native supporting networked Plugwise products
@@ -62,17 +62,17 @@ Module providing interfacing with the Plugwise devices:
62
62
  ### Smile
63
63
 
64
64
  - [x] Adam
65
- - [x] Lisa
65
+ - [x] Emma (only tested as ZigBee device connected to Adam)
66
66
  - [x] Jip
67
- - [x] Floor
68
- - [x] Tom
67
+ - [x] Lisa
68
+ - [x] Tom/Floor
69
69
  - [x] Koen (a Koen always comes with a Plug, the Plug is the active part)
70
70
  - [x] Plug
71
71
  - [x] Aqara Plug
72
- - [x] Anna
73
- - [x] Smile P1
74
- - [x] Stretch
75
- - [ ] Some of the equipment mentioned in USB when in use via Stretch or Adam
72
+ - [x] Anna (v1.8 and later firmware versions)
73
+ - [ ] Anna P1
74
+ - [x] Smile P1 (v2.0 and later firmware versions)
75
+ - [x] Stretch (only with Circles, please help out with other devices)
76
76
 
77
77
  - [x] [Home-Assistant](https://home-assistant.io) via
78
78
  - [x] Native supporting networked Plugwise products
@@ -145,8 +145,8 @@ class SmileCommon:
145
145
  "model": appl.model,
146
146
  "model_id": appl.model_id,
147
147
  "name": appl.name,
148
- "zigbee_mac_address": appl.zigbee_mac,
149
148
  "vendor": appl.vendor_name,
149
+ "zigbee_mac_address": appl.zigbee_mac,
150
150
  }.items():
151
151
  if value is not None or key == "location":
152
152
  appl_key = cast(ApplianceType, key)
@@ -205,7 +205,7 @@ class SmileCommon:
205
205
  "members": members,
206
206
  "vendor": "Plugwise",
207
207
  }
208
- self._count += 4
208
+ self._count += 5
209
209
 
210
210
  return switch_groups
211
211
 
@@ -50,6 +50,7 @@ HW_MODELS: Final[dict[str, str]] = {
50
50
  "158-01": "Lisa",
51
51
  "160-01": "Plug",
52
52
  "168-01": "Jip",
53
+ "170-01": "Emma",
53
54
  "038500": "Stick",
54
55
  "070085": "Stick",
55
56
  "120002": "Stick Legrand",
@@ -553,7 +554,7 @@ class GwEntityData(TypedDict, total=False):
553
554
  preset_modes: list[str] | None
554
555
  # Schedules:
555
556
  available_schedules: list[str]
556
- select_schedule: str
557
+ select_schedule: str | None
557
558
 
558
559
  climate_mode: str
559
560
  # Extra for Adam Master Thermostats
@@ -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"
@@ -411,7 +411,8 @@ class SmileHelper(SmileCommon):
411
411
  case "elga_status_code":
412
412
  data["elga_status_code"] = int(appl_p_loc.text)
413
413
  case "select_dhw_mode":
414
- data["select_dhw_mode"] = appl_p_loc.text
414
+ if self._dhw_allowed_modes:
415
+ data["select_dhw_mode"] = appl_p_loc.text
415
416
 
416
417
  common_match_cases(measurement, attrs, appl_p_loc, data)
417
418
 
@@ -522,7 +523,7 @@ class SmileHelper(SmileCommon):
522
523
 
523
524
  Collect the requested gateway mode.
524
525
  """
525
- if not (self.check_name(ADAM) and entity_id == self._gateway_id):
526
+ if not (entity_id == self._gateway_id and self.check_name(ADAM)):
526
527
  return None
527
528
 
528
529
  if (search := search_actuator_functionalities(appliance, key)) is not None:
@@ -535,29 +536,31 @@ class SmileHelper(SmileCommon):
535
536
  ) -> None:
536
537
  """Helper-function for _get_measurement_data().
537
538
 
538
- Adam: collect the gateway regulation_mode.
539
+ Adam gateway: collect the gateway regulation_mode.
539
540
  """
540
541
  if (
541
542
  mode := self._get_actuator_mode(
542
543
  appliance, entity_id, "regulation_mode_control_functionality"
543
544
  )
544
545
  ) is not None:
545
- data["select_regulation_mode"] = mode
546
- self._count += 1
546
+ # Below line needs to be here to set the boolean for both older and recent Adam firmware versions
547
547
  self._cooling_enabled = mode == "cooling"
548
+ if self._reg_allowed_modes:
549
+ data["select_regulation_mode"] = mode
550
+ self._count += 1
548
551
 
549
552
  def _get_gateway_mode(
550
553
  self, appliance: etree.Element, entity_id: str, data: GwEntityData
551
554
  ) -> None:
552
555
  """Helper-function for _get_measurement_data().
553
556
 
554
- Adam: collect the gateway mode.
557
+ Adam gateway: collect the gateway mode.
555
558
  """
556
559
  if (
557
560
  mode := self._get_actuator_mode(
558
561
  appliance, entity_id, "gateway_mode_control_functionality"
559
562
  )
560
- ) is not None:
563
+ ) is not None and self._gw_allowed_modes:
561
564
  data["select_gateway_mode"] = mode
562
565
  self._count += 1
563
566
 
@@ -718,7 +721,7 @@ class SmileHelper(SmileCommon):
718
721
  },
719
722
  "vendor": "Plugwise",
720
723
  }
721
- self._count += 3
724
+ self._count += 5
722
725
 
723
726
  def _match_locations(self) -> dict[str, ThermoLoc]:
724
727
  """Helper-function for _scan_thermostats().
@@ -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"
@@ -82,6 +84,7 @@ class SmileLegacyData(SmileLegacyHelper):
82
84
  def _get_anna_control_state(self, data: GwEntityData) -> None:
83
85
  """Set the thermostat control_state based on the opentherm/onoff device state."""
84
86
  data["control_state"] = "idle"
87
+ self._count += 1
85
88
  for entity in self.gw_entities.values():
86
89
  if entity["dev_class"] != "heater_central":
87
90
  continue
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugwise
3
- Version: 1.7.8a1
3
+ Version: 1.8.0
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
@@ -11,6 +11,7 @@ Keywords: home,automation,plugwise,module
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3.14
14
15
  Classifier: Programming Language :: Python :: 3.13
15
16
  Classifier: Topic :: Home Automation
16
17
  Requires-Python: >=3.13
@@ -87,17 +88,17 @@ Module providing interfacing with the Plugwise devices:
87
88
  ### Smile
88
89
 
89
90
  - [x] Adam
90
- - [x] Lisa
91
+ - [x] Emma (only tested as ZigBee device connected to Adam)
91
92
  - [x] Jip
92
- - [x] Floor
93
- - [x] Tom
93
+ - [x] Lisa
94
+ - [x] Tom/Floor
94
95
  - [x] Koen (a Koen always comes with a Plug, the Plug is the active part)
95
96
  - [x] Plug
96
97
  - [x] Aqara Plug
97
- - [x] Anna
98
- - [x] Smile P1
99
- - [x] Stretch
100
- - [ ] Some of the equipment mentioned in USB when in use via Stretch or Adam
98
+ - [x] Anna (v1.8 and later firmware versions)
99
+ - [ ] Anna P1
100
+ - [x] Smile P1 (v2.0 and later firmware versions)
101
+ - [x] Stretch (only with Circles, please help out with other devices)
101
102
 
102
103
  - [x] [Home-Assistant](https://home-assistant.io) via
103
104
  - [x] Native supporting networked Plugwise products
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "plugwise"
7
- version = "1.7.8a1"
7
+ version = "1.8.0"
8
8
  license = "MIT"
9
9
  description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
10
10
  readme = "README.md"
@@ -13,6 +13,7 @@ classifiers = [
13
13
  "Development Status :: 5 - Production/Stable",
14
14
  "Intended Audience :: Developers",
15
15
  "Operating System :: OS Independent",
16
+ "Programming Language :: Python :: 3.14",
16
17
  "Programming Language :: Python :: 3.13",
17
18
  "Topic :: Home Automation",
18
19
  ]
@@ -56,7 +57,7 @@ include = ["plugwise*"]
56
57
  ##
57
58
 
58
59
  [tool.pylint.MAIN]
59
- py-version = "3.13"
60
+ py-version = "3.14"
60
61
  # Use a conservative default here; 2 should speed up most setups and not hurt
61
62
  # any too bad. Override on command line as appropriate.
62
63
  jobs = 2
@@ -391,7 +392,7 @@ max-line-length-suggestions = 72
391
392
 
392
393
 
393
394
  [tool.mypy]
394
- python_version = "3.13"
395
+ python_version = "3.14"
395
396
  show_error_codes = true
396
397
  follow_imports = "silent"
397
398
  ignore_missing_imports = true
@@ -421,7 +422,7 @@ omit= [
421
422
  ]
422
423
 
423
424
  [tool.ruff]
424
- target-version = "py313"
425
+ target-version = "py314"
425
426
 
426
427
  lint.select = [
427
428
  "B002", # Python does not support the unary prefix increment
@@ -36,7 +36,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
36
36
  assert api.gateway_id == "da224107914542988a88561b4452b0f6"
37
37
  assert api._last_active["f2bf9048bef64cc5b6d5110154e33c81"] == "Weekschema"
38
38
  assert api._last_active["f871b8c4d63549319221e294e4f88074"] == "Badkamer"
39
- assert self.entity_items == 178
39
+ assert self.entity_items == 183
40
40
  assert self.entity_list == [
41
41
  "da224107914542988a88561b4452b0f6",
42
42
  "056ee145a816487eaa69243c3280f8bf",
@@ -181,6 +181,28 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
181
181
  await api.close_connection()
182
182
  await self.disconnect(server, client)
183
183
 
184
+ @pytest.mark.asyncio
185
+ async def test_connect_adam_anna_new_2(self):
186
+ """Test extended Adam (firmware 3.9) with Emma setup."""
187
+ self.smile_setup = "adam_anna_new_2"
188
+
189
+ testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup)
190
+ server, api, client = await self.connect_wrapper()
191
+ assert api.smile.hostname == "smile000000"
192
+
193
+ self.validate_test_basics(
194
+ _LOGGER,
195
+ api,
196
+ smile_type=None,
197
+ smile_version="3.9.0",
198
+ )
199
+
200
+ await self.device_test(api, "2025-10-12 00:00:01", testdata)
201
+ assert self.entity_items == 195
202
+
203
+ await api.close_connection()
204
+ await self.disconnect(server, client)
205
+
184
206
  @pytest.mark.asyncio
185
207
  async def test_connect_adam_plus_anna_new_regulation_off(self):
186
208
  """Test regultaion_mode off with control_state key missing for Adam."""
@@ -217,7 +239,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
217
239
  assert api._last_active["82fa13f017d240daa0d0ea1775420f24"] == CV_JESSIE
218
240
  assert api._last_active["08963fec7c53423ca5680aa4cb502c63"] == BADKAMER_SCHEMA
219
241
  assert api._last_active["446ac08dd04d4eff8ac57489757b7314"] == BADKAMER_SCHEMA
220
- assert self.entity_items == 370
242
+ assert self.entity_items == 379
221
243
 
222
244
  assert "af82e4ccf9c548528166d38e560662a4" in self.notifications
223
245
  await api.delete_notification()
@@ -295,7 +317,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
295
317
  assert api._last_active["82fa13f017d240daa0d0ea1775420f24"] == CV_JESSIE
296
318
  assert api._last_active["08963fec7c53423ca5680aa4cb502c63"] == BADKAMER_SCHEMA
297
319
  assert api._last_active["446ac08dd04d4eff8ac57489757b7314"] == BADKAMER_SCHEMA
298
- assert self.entity_items == 375
320
+ assert self.entity_items == 385
299
321
 
300
322
  assert "af82e4ccf9c548528166d38e560662a4" in self.notifications
301
323
 
@@ -341,7 +363,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
341
363
  assert api._last_active["a562019b0b1f47a4bde8ebe3dbe3e8a9"] == WERKDAG_SCHEMA
342
364
  assert api._last_active["8cf650a4c10c44819e426bed406aec34"] == WERKDAG_SCHEMA
343
365
  assert api._last_active["5cc21042f87f4b4c94ccb5537c47a53f"] == WERKDAG_SCHEMA
344
- assert self.entity_items == 498
366
+ assert self.entity_items == 518
345
367
  assert self.cooling_present
346
368
  assert self._cooling_enabled
347
369
 
@@ -364,7 +386,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
364
386
  )
365
387
 
366
388
  await self.device_test(api, "2022-01-02 00:00:01", testdata)
367
- assert self.entity_items == 65
389
+ assert self.entity_items == 67
368
390
  assert self.cooling_present
369
391
  # assert self._cooling_enabled - no cooling_enabled indication present
370
392
 
@@ -389,7 +411,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
389
411
  await self.device_test(api, "2020-03-22 00:00:01", testdata)
390
412
  assert api.gateway_id == "b128b4bbbd1f47e9bf4d756e8fb5ee94"
391
413
  assert api._last_active["009490cc2f674ce6b576863fbb64f867"] == "Weekschema"
392
- assert self.entity_items == 81
414
+ assert self.entity_items == 82
393
415
  assert "6fb89e35caeb4b1cb275184895202d84" in self.notifications
394
416
 
395
417
  result = await self.tinker_thermostat(
@@ -433,7 +455,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
433
455
  assert api._last_active["06aecb3d00354375924f50c47af36bd2"] is None
434
456
  assert api._last_active["d27aede973b54be484f6842d1b2802ad"] is None
435
457
  assert api._last_active["13228dab8ce04617af318a2888b3c548"] is None
436
- assert self.entity_items == 245
458
+ assert self.entity_items == 261
437
459
 
438
460
  # Negative test
439
461
  result = await self.tinker_thermostat(
@@ -30,7 +30,7 @@ class TestPlugwiseAnna(TestPlugwise): # pylint: disable=attribute-defined-outsi
30
30
 
31
31
  await self.device_test(api, "2020-03-22 00:00:01", testdata)
32
32
  assert api.gateway_id == "0000aaaa0000aaaa0000aaaa0000aa00"
33
- assert self.entity_items == 41
33
+ assert self.entity_items == 44
34
34
  assert not api.reboot
35
35
 
36
36
  result = await self.tinker_legacy_thermostat(api, schedule_on=False)
@@ -65,7 +65,7 @@ class TestPlugwiseAnna(TestPlugwise): # pylint: disable=attribute-defined-outsi
65
65
  await self.device_test(api, "2020-05-03 00:00:01", testdata)
66
66
 
67
67
  assert api.gateway_id == "be81e3f8275b4129852c4d8d550ae2eb"
68
- assert self.entity_items == 43
68
+ assert self.entity_items == 44
69
69
 
70
70
  result = await self.tinker_legacy_thermostat(api)
71
71
  assert result
@@ -29,7 +29,7 @@ class TestPlugwiseStretch(TestPlugwise): # pylint: disable=attribute-defined-ou
29
29
 
30
30
  await self.device_test(api, "2022-05-16 00:00:01", testdata)
31
31
  assert api.gateway_id == "0000aaaa0000aaaa0000aaaa0000aa00"
32
- assert self.entity_items == 83
32
+ assert self.entity_items == 85
33
33
 
34
34
  switch_change = await self.tinker_switch(
35
35
  api,
@@ -68,7 +68,7 @@ class TestPlugwiseStretch(TestPlugwise): # pylint: disable=attribute-defined-ou
68
68
  )
69
69
 
70
70
  await self.device_test(api, "2022-05-16 00:00:01", testdata)
71
- assert self.entity_items == 243
71
+ assert self.entity_items == 244
72
72
 
73
73
  switch_change = await self.tinker_switch(
74
74
  api, "2587a7fcdd7e482dab03fda256076b4b"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes