diff --git a/CHANGELOG.md b/CHANGELOG.md index 47d6847b7..2f97c0f90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v1.11.0 + +- Extend feature: support pumping group, add group sensors + ## v1.10.0 - New feature: implement setting Adam zone profile via PR [#814](https://github.com/plugwise/python-plugwise/pull/814) diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..00ec4d1e9 --- /dev/null +++ b/biome.json @@ -0,0 +1,5 @@ +{ + "files": { + "maxSize": 5000000 + } +} diff --git a/fixtures/adam_multiple_devices_per_zone/data.json b/fixtures/adam_multiple_devices_per_zone/data.json index ab49c5a65..364d71e59 100644 --- a/fixtures/adam_multiple_devices_per_zone/data.json +++ b/fixtures/adam_multiple_devices_per_zone/data.json @@ -512,6 +512,20 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A06" }, + "e117db6848394c8cb70d9c28e63d92d2": { + "dev_class": "pumping", + "members": [ + "78d1126fc4c743db81b61c20e88342a7", + "b59bcebaf94b499ea7d46e4a66fb62d8" + ], + "model": "Group", + "name": "Vloerverwarming Woonkamer", + "sensors": { + "electricity_consumed": 35.6, + "temperature": 20.9 + }, + "vendor": "Plugwise" + }, "e7693eb9582644e5b865dba8d4447cf1": { "available": true, "binary_sensors": { @@ -546,8 +560,12 @@ "02cf28bfec924855854c544690a609ef", "4a810418d5394b3f82727340b91ba740" ], - "model": "Switchgroup", + "model": "Group", "name": "Test", + "sensors": { + "electricity_consumed": 14.8, + "electricity_produced": 0.0 + }, "switches": { "relay": true }, diff --git a/fixtures/adam_plus_anna_new/data.json b/fixtures/adam_plus_anna_new/data.json index ee4ecd743..afa38abd8 100644 --- a/fixtures/adam_plus_anna_new/data.json +++ b/fixtures/adam_plus_anna_new/data.json @@ -181,6 +181,21 @@ }, "vendor": "Plugwise" }, + "c9293d1d68ee48fc8843c6f0dee2b6be": { + "dev_class": "pumping", + "members": [ + "854f8a9b0e7e425db97f1f110e1ce4b3", + "ad4838d7d35c4d6ea796ee12ae5aedf8" + ], + "model": "Group", + "name": "Vloerverwarming", + "sensors": { + "electricity_consumed": 45.0, + "electricity_produced": 0.0, + "temperature": 20.1 + }, + "vendor": "Plugwise" + }, "da224107914542988a88561b4452b0f6": { "binary_sensors": { "plugwise_notification": false @@ -257,8 +272,12 @@ "2568cc4b9c1e401495d4741a5f89bee1", "29542b2b6a6a4169acecc15c72a599b8" ], - "model": "Switchgroup", + "model": "Group", "name": "Test", + "sensors": { + "electricity_consumed": 16.5, + "electricity_produced": 0.0 + }, "switches": { "relay": true }, diff --git a/fixtures/adam_plus_anna_new_regulation_off/data.json b/fixtures/adam_plus_anna_new_regulation_off/data.json index 8d07fdc92..0a859e23a 100644 --- a/fixtures/adam_plus_anna_new_regulation_off/data.json +++ b/fixtures/adam_plus_anna_new_regulation_off/data.json @@ -155,6 +155,21 @@ }, "vendor": "Plugwise" }, + "c9293d1d68ee48fc8843c6f0dee2b6be": { + "dev_class": "pumping", + "members": [ + "854f8a9b0e7e425db97f1f110e1ce4b3", + "ad4838d7d35c4d6ea796ee12ae5aedf8" + ], + "model": "Group", + "name": "Vloerverwarming", + "sensors": { + "electricity_consumed": 43.8, + "electricity_produced": 0.0, + "temperature": 18.4 + }, + "vendor": "Plugwise" + }, "da224107914542988a88561b4452b0f6": { "binary_sensors": { "plugwise_notification": false @@ -210,8 +225,12 @@ "2568cc4b9c1e401495d4741a5f89bee1", "29542b2b6a6a4169acecc15c72a599b8" ], - "model": "Switchgroup", + "model": "Group", "name": "Test", + "sensors": { + "electricity_consumed": 14.8, + "electricity_produced": 0.0 + }, "switches": { "relay": true }, diff --git a/fixtures/adam_zone_per_device/data.json b/fixtures/adam_zone_per_device/data.json index 31b8b4456..c40afc0ba 100644 --- a/fixtures/adam_zone_per_device/data.json +++ b/fixtures/adam_zone_per_device/data.json @@ -509,6 +509,20 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A06" }, + "e117db6848394c8cb70d9c28e63d92d2": { + "dev_class": "pumping", + "members": [ + "78d1126fc4c743db81b61c20e88342a7", + "b59bcebaf94b499ea7d46e4a66fb62d8" + ], + "model": "Group", + "name": "Vloerverwarming Woonkamer", + "sensors": { + "electricity_consumed": 35.8, + "temperature": 21.1 + }, + "vendor": "Plugwise" + }, "e7693eb9582644e5b865dba8d4447cf1": { "available": true, "binary_sensors": { diff --git a/fixtures/m_adam_cooling/data.json b/fixtures/m_adam_cooling/data.json index fdd52b1ef..9b95475eb 100644 --- a/fixtures/m_adam_cooling/data.json +++ b/fixtures/m_adam_cooling/data.json @@ -91,6 +91,21 @@ }, "vendor": "Plugwise" }, + "c9293d1d68ee48fc8843c6f0dee2b6be": { + "dev_class": "pumping", + "members": [ + "854f8a9b0e7e425db97f1f110e1ce4b3", + "ad4838d7d35c4d6ea796ee12ae5aedf8" + ], + "model": "Group", + "name": "Vloerverwarming", + "sensors": { + "electricity_consumed": 45.0, + "electricity_produced": 0.0, + "temperature": 20.1 + }, + "vendor": "Plugwise" + }, "da224107914542988a88561b4452b0f6": { "binary_sensors": { "plugwise_notification": false @@ -173,8 +188,12 @@ "2568cc4b9c1e401495d4741a5f89bee1", "29542b2b6a6a4169acecc15c72a599b8" ], - "model": "Switchgroup", + "model": "Group", "name": "Test", + "sensors": { + "electricity_consumed": 16.5, + "electricity_produced": 0.0 + }, "switches": { "relay": true }, diff --git a/fixtures/m_adam_heating/data.json b/fixtures/m_adam_heating/data.json index 2f3951c0c..47a8fdeb2 100644 --- a/fixtures/m_adam_heating/data.json +++ b/fixtures/m_adam_heating/data.json @@ -96,6 +96,21 @@ }, "vendor": "Plugwise" }, + "c9293d1d68ee48fc8843c6f0dee2b6be": { + "dev_class": "pumping", + "members": [ + "854f8a9b0e7e425db97f1f110e1ce4b3", + "ad4838d7d35c4d6ea796ee12ae5aedf8" + ], + "model": "Group", + "name": "Vloerverwarming", + "sensors": { + "electricity_consumed": 45.0, + "electricity_produced": 0.0, + "temperature": 20.1 + }, + "vendor": "Plugwise" + }, "da224107914542988a88561b4452b0f6": { "binary_sensors": { "plugwise_notification": false @@ -172,8 +187,12 @@ "2568cc4b9c1e401495d4741a5f89bee1", "29542b2b6a6a4169acecc15c72a599b8" ], - "model": "Switchgroup", + "model": "Group", "name": "Test", + "sensors": { + "electricity_consumed": 16.5, + "electricity_produced": 0.0 + }, "switches": { "relay": true }, diff --git a/fixtures/m_adam_multiple_devices_per_zone/data.json b/fixtures/m_adam_multiple_devices_per_zone/data.json index f1880ba69..126031795 100644 --- a/fixtures/m_adam_multiple_devices_per_zone/data.json +++ b/fixtures/m_adam_multiple_devices_per_zone/data.json @@ -505,6 +505,20 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A06" }, + "e117db6848394c8cb70d9c28e63d92d2": { + "dev_class": "pumping", + "members": [ + "78d1126fc4c743db81b61c20e88342a7", + "b59bcebaf94b499ea7d46e4a66fb62d8" + ], + "model": "Group", + "name": "Vloerverwarming Woonkamer", + "sensors": { + "electricity_consumed": 35.6, + "temperature": 20.9 + }, + "vendor": "Plugwise" + }, "e7693eb9582644e5b865dba8d4447cf1": { "available": true, "binary_sensors": { @@ -539,8 +553,12 @@ "02cf28bfec924855854c544690a609ef", "4a810418d5394b3f82727340b91ba740" ], - "model": "Switchgroup", + "model": "Group", "name": "Test", + "sensors": { + "electricity_consumed": 14.8, + "electricity_produced": 0.0 + }, "switches": { "relay": true }, diff --git a/fixtures/stretch_v23/data.json b/fixtures/stretch_v23/data.json index 0516e1436..5ca04fcd8 100644 --- a/fixtures/stretch_v23/data.json +++ b/fixtures/stretch_v23/data.json @@ -305,7 +305,7 @@ "f7b145c8492f4dd7a4de760456fdef3e": { "dev_class": "switching", "members": ["407aa1c1099d463c9137a3a9eda787fd"], - "model": "Switchgroup", + "model": "Group", "name": "Test", "switches": { "relay": false diff --git a/fixtures/stretch_v31/data.json b/fixtures/stretch_v31/data.json index 250839d08..9927b5c13 100644 --- a/fixtures/stretch_v31/data.json +++ b/fixtures/stretch_v31/data.json @@ -91,7 +91,7 @@ "059e4d03c7a34d278add5c7a4a781d19", "cfe95cf3de1948c0b8955125bf754614" ], - "model": "Switchgroup", + "model": "Group", "name": "Schakel", "switches": { "relay": true @@ -107,7 +107,7 @@ "cfe95cf3de1948c0b8955125bf754614", "e1c884e7dede431dadee09506ec4f859" ], - "model": "Switchgroup", + "model": "Group", "name": "Stroomvreters", "switches": { "relay": true diff --git a/plugwise/common.py b/plugwise/common.py index 47a456266..9722975e6 100644 --- a/plugwise/common.py +++ b/plugwise/common.py @@ -9,6 +9,7 @@ from plugwise.constants import ( ANNA, + GROUP_TYPES, NONE, PRIORITY_DEVICE_CLASSES, SPECIAL_PLUG_TYPES, @@ -187,15 +188,15 @@ def _entity_switching_group(self, entity: GwEntityData, data: GwEntityData) -> N data["switches"]["relay"] = counter != 0 self._count += 1 - def _get_group_switches(self) -> dict[str, GwEntityData]: + def _get_groups(self) -> dict[str, GwEntityData]: """Helper-function for smile.py: get_all_gateway_entities(). - Collect switching- or pump-group info. + Collect switching-, pumping- or report-group info. """ - switch_groups: dict[str, GwEntityData] = {} - # P1 and Anna don't have switchgroups + groups: dict[str, GwEntityData] = {} + # P1 and Anna don't have groups if self.smile.type == "power" or self.check_name(ANNA): - return switch_groups + return groups for group in self._domain_objects.findall("./group"): members: list[str] = [] @@ -208,17 +209,17 @@ def _get_group_switches(self) -> dict[str, GwEntityData]: if item.attrib["id"] in self.gw_entities: members.append(item.attrib["id"]) - if group_type in SWITCH_GROUP_TYPES and members: - switch_groups[group_id] = { + if group_type in GROUP_TYPES and members: + groups[group_id] = { "dev_class": group_type, - "model": "Switchgroup", + "model": "Group", "name": group_name, "members": members, "vendor": "Plugwise", } self._count += 5 - return switch_groups + return groups def _get_lock_state( self, xml: etree.Element, data: GwEntityData, stretch_v2: bool = False diff --git a/plugwise/constants.py b/plugwise/constants.py index d68842d5f..1ddd37c5f 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -106,6 +106,11 @@ UOM = namedtuple("UOM", "unit_of_measurement") DATA = namedtuple("DATA", "name unit_of_measurement") +GROUP_MEASUREMENTS: Final[dict[str, UOM]] = { + "electricity_consumed": UOM(POWER_WATT), + "electricity_produced": UOM(POWER_WATT), + "temperature": UOM(TEMP_CELSIUS), +} # P1 related measurements: P1_MEASUREMENTS: Final[dict[str, UOM]] = { "electricity_consumed": UOM(POWER_WATT), @@ -299,6 +304,8 @@ ] BINARY_SENSORS: Final[tuple[str, ...]] = get_args(BinarySensorType) +GROUP_TYPES: Final[tuple[str, ...]] = ("pumping", "report", "switching") + SensorType = Literal[ "battery", "cooling_activation_outdoor_temperature", diff --git a/plugwise/helper.py b/plugwise/helper.py index 4f1de9258..475eabd32 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -21,6 +21,7 @@ DHW_SETPOINT, DOMAIN_OBJECTS, ENERGY_WATT_HOUR, + GROUP_MEASUREMENTS, HEATER_CENTRAL_MEASUREMENTS, LOCATIONS, LOGGER, @@ -326,8 +327,9 @@ def _get_measurement_data(self, entity_id: str) -> GwEntityData: Collect the appliance-data based on entity_id. """ data: GwEntityData = {"binary_sensors": {}, "sensors": {}, "switches": {}} - # Get P1 smartmeter data from LOCATIONS entity = self.gw_entities[entity_id] + + # Get P1 smartmeter data from LOCATIONS smile_is_power = self.smile.type == "power" if (smile_is_power or self.smile.anna_p1) and entity.get( "dev_class" @@ -337,6 +339,10 @@ def _get_measurement_data(self, entity_id: str) -> GwEntityData: if smile_is_power and not self.smile.anna_p1: return data + # Get group data + if "members" in entity: + self._collect_group_sensors(data, entity_id, GROUP_MEASUREMENTS) + # Get non-P1 data from APPLIANCES measurements = DEVICE_MEASUREMENTS if self._is_thermostat and entity_id == self.heater_id: @@ -368,6 +374,24 @@ def _get_measurement_data(self, entity_id: str) -> GwEntityData: return data + def _collect_group_sensors( + self, + data: GwEntityData, + entity_id: str, + measurements: dict[str, UOM], + ) -> None: + """Collect group sensors.""" + if ( + group := self._domain_objects.find(f'./group[@id="{entity_id}"]') + ) is not None: + for measurement, attrs in measurements.items(): + locator = f'.//logs/point_log[type="{measurement}"]/period/measurement' + if (group_meas_loc := group.find(locator)) is None: + continue + + common_match_cases(measurement, attrs, group_meas_loc, data) + self._count += 1 + def _collect_appliance_data( self, data: GwEntityData, diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 61f219521..54a21c15d 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -82,7 +82,7 @@ def get_all_gateway_entities(self) -> None: Finally, collect the data and states for each entity. """ self._all_appliances() - if group_data := self._get_group_switches(): + if group_data := self._get_groups(): self.gw_entities.update(group_data) self._all_entity_data() diff --git a/plugwise/smile.py b/plugwise/smile.py index ad6a93544..22010af5c 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -115,7 +115,7 @@ def get_all_gateway_entities(self) -> None: if self.check_name(ADAM): self._scan_thermostats() - if group_data := self._get_group_switches(): + if group_data := self._get_groups(): self.gw_entities.update(group_data) self._all_entity_data() diff --git a/pyproject.toml b/pyproject.toml index bb78312fa..e3df36e18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "1.10.0" +version = "1.11.0" license = "MIT" description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" diff --git a/tests/data/adam/adam_multiple_devices_per_zone.json b/tests/data/adam/adam_multiple_devices_per_zone.json index ab49c5a65..364d71e59 100644 --- a/tests/data/adam/adam_multiple_devices_per_zone.json +++ b/tests/data/adam/adam_multiple_devices_per_zone.json @@ -512,6 +512,20 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A06" }, + "e117db6848394c8cb70d9c28e63d92d2": { + "dev_class": "pumping", + "members": [ + "78d1126fc4c743db81b61c20e88342a7", + "b59bcebaf94b499ea7d46e4a66fb62d8" + ], + "model": "Group", + "name": "Vloerverwarming Woonkamer", + "sensors": { + "electricity_consumed": 35.6, + "temperature": 20.9 + }, + "vendor": "Plugwise" + }, "e7693eb9582644e5b865dba8d4447cf1": { "available": true, "binary_sensors": { @@ -546,8 +560,12 @@ "02cf28bfec924855854c544690a609ef", "4a810418d5394b3f82727340b91ba740" ], - "model": "Switchgroup", + "model": "Group", "name": "Test", + "sensors": { + "electricity_consumed": 14.8, + "electricity_produced": 0.0 + }, "switches": { "relay": true }, diff --git a/tests/data/adam/adam_plus_anna_new.json b/tests/data/adam/adam_plus_anna_new.json index ee4ecd743..afa38abd8 100644 --- a/tests/data/adam/adam_plus_anna_new.json +++ b/tests/data/adam/adam_plus_anna_new.json @@ -181,6 +181,21 @@ }, "vendor": "Plugwise" }, + "c9293d1d68ee48fc8843c6f0dee2b6be": { + "dev_class": "pumping", + "members": [ + "854f8a9b0e7e425db97f1f110e1ce4b3", + "ad4838d7d35c4d6ea796ee12ae5aedf8" + ], + "model": "Group", + "name": "Vloerverwarming", + "sensors": { + "electricity_consumed": 45.0, + "electricity_produced": 0.0, + "temperature": 20.1 + }, + "vendor": "Plugwise" + }, "da224107914542988a88561b4452b0f6": { "binary_sensors": { "plugwise_notification": false @@ -257,8 +272,12 @@ "2568cc4b9c1e401495d4741a5f89bee1", "29542b2b6a6a4169acecc15c72a599b8" ], - "model": "Switchgroup", + "model": "Group", "name": "Test", + "sensors": { + "electricity_consumed": 16.5, + "electricity_produced": 0.0 + }, "switches": { "relay": true }, diff --git a/tests/data/adam/adam_plus_anna_new_regulation_off.json b/tests/data/adam/adam_plus_anna_new_regulation_off.json index 8d07fdc92..10cd1add3 100644 --- a/tests/data/adam/adam_plus_anna_new_regulation_off.json +++ b/tests/data/adam/adam_plus_anna_new_regulation_off.json @@ -178,6 +178,16 @@ "vendor": "Plugwise", "zigbee_mac_address": "000D6F000D5A168D" }, + "c9293d1d68ee48fc8843c6f0dee2b6be": { + "dev_class": "pumping", + "members": [ + "854f8a9b0e7e425db97f1f110e1ce4b3", + "ad4838d7d35c4d6ea796ee12ae5aedf8" + ], + "model": "Group", + "name": "Vloerverwarming", + "vendor": "Plugwise" + }, "e2f4322d57924fa090fbbc48b3a140dc": { "available": true, "binary_sensors": { @@ -210,7 +220,7 @@ "2568cc4b9c1e401495d4741a5f89bee1", "29542b2b6a6a4169acecc15c72a599b8" ], - "model": "Switchgroup", + "model": "Group", "name": "Test", "switches": { "relay": true diff --git a/tests/data/adam/adam_zone_per_device.json b/tests/data/adam/adam_zone_per_device.json index 31b8b4456..c40afc0ba 100644 --- a/tests/data/adam/adam_zone_per_device.json +++ b/tests/data/adam/adam_zone_per_device.json @@ -509,6 +509,20 @@ "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A06" }, + "e117db6848394c8cb70d9c28e63d92d2": { + "dev_class": "pumping", + "members": [ + "78d1126fc4c743db81b61c20e88342a7", + "b59bcebaf94b499ea7d46e4a66fb62d8" + ], + "model": "Group", + "name": "Vloerverwarming Woonkamer", + "sensors": { + "electricity_consumed": 35.8, + "temperature": 21.1 + }, + "vendor": "Plugwise" + }, "e7693eb9582644e5b865dba8d4447cf1": { "available": true, "binary_sensors": { diff --git a/tests/data/stretch/stretch_v23.json b/tests/data/stretch/stretch_v23.json index 0516e1436..5ca04fcd8 100644 --- a/tests/data/stretch/stretch_v23.json +++ b/tests/data/stretch/stretch_v23.json @@ -305,7 +305,7 @@ "f7b145c8492f4dd7a4de760456fdef3e": { "dev_class": "switching", "members": ["407aa1c1099d463c9137a3a9eda787fd"], - "model": "Switchgroup", + "model": "Group", "name": "Test", "switches": { "relay": false diff --git a/tests/data/stretch/stretch_v31.json b/tests/data/stretch/stretch_v31.json index 250839d08..9927b5c13 100644 --- a/tests/data/stretch/stretch_v31.json +++ b/tests/data/stretch/stretch_v31.json @@ -91,7 +91,7 @@ "059e4d03c7a34d278add5c7a4a781d19", "cfe95cf3de1948c0b8955125bf754614" ], - "model": "Switchgroup", + "model": "Group", "name": "Schakel", "switches": { "relay": true @@ -107,7 +107,7 @@ "cfe95cf3de1948c0b8955125bf754614", "e1c884e7dede431dadee09506ec4f859" ], - "model": "Switchgroup", + "model": "Group", "name": "Stroomvreters", "switches": { "relay": true diff --git a/tests/test_adam.py b/tests/test_adam.py index c45f5a354..8484d56d7 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -34,7 +34,7 @@ async def test_connect_adam_plus_anna_new(self): test_items = await self.device_test(api, "2025-10-12 00:00:01", testdata) assert api.gateway_id == "da224107914542988a88561b4452b0f6" - assert self.entity_items == 220 + assert self.entity_items == 230 assert test_items == self.entity_items assert self.entity_list == [ "da224107914542988a88561b4452b0f6", @@ -49,6 +49,7 @@ async def test_connect_adam_plus_anna_new(self): "ad4838d7d35c4d6ea796ee12ae5aedf8", "14df5c4dc8cb4ba69f9d1ac0eaf7c5c6", "da575e9e09b947e281fb6e3ebce3b174", + "c9293d1d68ee48fc8843c6f0dee2b6be", "e8ef2a01ed3b4139a53bf749204fe6b4", "f2bf9048bef64cc5b6d5110154e33c81", "f871b8c4d63549319221e294e4f88074", @@ -227,7 +228,7 @@ async def test_connect_adam_zone_per_device(self): test_items = await self.device_test(api, "2022-05-16 00:00:01", testdata) assert api.gateway_id == "fe799307f1624099878210aa0b9f1475" - assert self.entity_items == 379 + assert self.entity_items == 386 assert test_items == self.entity_items assert "af82e4ccf9c548528166d38e560662a4" in self.notifications @@ -301,7 +302,7 @@ async def test_connect_adam_multiple_devices_per_zone(self): ) test_items = await self.device_test(api, "2022-05-16 00:00:01", testdata) - assert self.entity_items == 385 + assert self.entity_items == 394 assert test_items == self.entity_items assert "af82e4ccf9c548528166d38e560662a4" in self.notifications