From c3e27f68123511fe28a6898ef49b1a60aeabe64c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 16 Jan 2023 16:33:18 +0100 Subject: [PATCH] Add tier summation delivered for Lixee Zlinky TIC (#82602) * Add tier summation delivered for zlinky * Improve name case * Add other tiers and register tier * Fix smartenergy sensor update * Account for new reporting configuration in unit tests * Use cluster ID attributes instead of hardcoding the values * Use tier names instead of the numeric constants for formatter * Revert active register tier delivered * Fix tests Co-authored-by: puddly <32534428+puddly@users.noreply.github.com> --- .../zha/core/channels/smartenergy.py | 18 +++ homeassistant/components/zha/sensor.py | 83 +++++++++++- tests/components/zha/test_channels.py | 124 ++++++++++++------ tests/components/zha/test_sensor.py | 4 +- 4 files changed, 186 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/zha/core/channels/smartenergy.py b/homeassistant/components/zha/core/channels/smartenergy.py index 66d3e3d6810..03d11356f0a 100644 --- a/homeassistant/components/zha/core/channels/smartenergy.py +++ b/homeassistant/components/zha/core/channels/smartenergy.py @@ -68,6 +68,24 @@ class Metering(ZigbeeChannel): REPORT_CONFIG = ( AttrReportConfig(attr="instantaneous_demand", config=REPORT_CONFIG_OP), AttrReportConfig(attr="current_summ_delivered", config=REPORT_CONFIG_DEFAULT), + AttrReportConfig( + attr="current_tier1_summ_delivered", config=REPORT_CONFIG_DEFAULT + ), + AttrReportConfig( + attr="current_tier2_summ_delivered", config=REPORT_CONFIG_DEFAULT + ), + AttrReportConfig( + attr="current_tier3_summ_delivered", config=REPORT_CONFIG_DEFAULT + ), + AttrReportConfig( + attr="current_tier4_summ_delivered", config=REPORT_CONFIG_DEFAULT + ), + AttrReportConfig( + attr="current_tier5_summ_delivered", config=REPORT_CONFIG_DEFAULT + ), + AttrReportConfig( + attr="current_tier6_summ_delivered", config=REPORT_CONFIG_DEFAULT + ), AttrReportConfig(attr="status", config=REPORT_CONFIG_ASAP), ) ZCL_INIT_ATTRS = { diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index 644c78d5f77..4e1c8a54a9f 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -1,6 +1,7 @@ """Sensors on Zigbee Home Automation networks.""" from __future__ import annotations +import enum import functools import numbers from typing import TYPE_CHECKING, Any, TypeVar @@ -174,7 +175,7 @@ class Sensor(ZhaEntity, SensorEntity): """Handle state update from channel.""" self.async_write_ha_state() - def formatter(self, value: int) -> int | float | None: + def formatter(self, value: int | enum.IntEnum) -> int | float | str | None: """Numeric pass-through formatter.""" if self._decimals > 0: return round( @@ -495,7 +496,7 @@ class SmartEnergySummation(SmartEnergyMetering, id_suffix="summation_delivered") @MULTI_MATCH( channel_names=CHANNEL_SMARTENERGY_METERING, - models={"TS011F"}, + models={"TS011F", "ZLinky_TIC"}, stop_on_match_group=CHANNEL_SMARTENERGY_METERING, ) class PolledSmartEnergySummation(SmartEnergySummation): @@ -510,6 +511,84 @@ class PolledSmartEnergySummation(SmartEnergySummation): await self._channel.async_force_update() +@MULTI_MATCH( + channel_names=CHANNEL_SMARTENERGY_METERING, + models={"ZLinky_TIC"}, +) +class Tier1SmartEnergySummation( + SmartEnergySummation, id_suffix="tier1_summation_delivered" +): + """Tier 1 Smart Energy Metering summation sensor.""" + + SENSOR_ATTR: int | str = "current_tier1_summ_delivered" + _attr_name: str = "Tier 1 summation delivered" + + +@MULTI_MATCH( + channel_names=CHANNEL_SMARTENERGY_METERING, + models={"ZLinky_TIC"}, +) +class Tier2SmartEnergySummation( + SmartEnergySummation, id_suffix="tier2_summation_delivered" +): + """Tier 2 Smart Energy Metering summation sensor.""" + + SENSOR_ATTR: int | str = "current_tier2_summ_delivered" + _attr_name: str = "Tier 2 summation delivered" + + +@MULTI_MATCH( + channel_names=CHANNEL_SMARTENERGY_METERING, + models={"ZLinky_TIC"}, +) +class Tier3SmartEnergySummation( + SmartEnergySummation, id_suffix="tier3_summation_delivered" +): + """Tier 3 Smart Energy Metering summation sensor.""" + + SENSOR_ATTR: int | str = "current_tier3_summ_delivered" + _attr_name: str = "Tier 3 summation delivered" + + +@MULTI_MATCH( + channel_names=CHANNEL_SMARTENERGY_METERING, + models={"ZLinky_TIC"}, +) +class Tier4SmartEnergySummation( + SmartEnergySummation, id_suffix="tier4_summation_delivered" +): + """Tier 4 Smart Energy Metering summation sensor.""" + + SENSOR_ATTR: int | str = "current_tier4_summ_delivered" + _attr_name: str = "Tier 4 summation delivered" + + +@MULTI_MATCH( + channel_names=CHANNEL_SMARTENERGY_METERING, + models={"ZLinky_TIC"}, +) +class Tier5SmartEnergySummation( + SmartEnergySummation, id_suffix="tier5_summation_delivered" +): + """Tier 5 Smart Energy Metering summation sensor.""" + + SENSOR_ATTR: int | str = "current_tier5_summ_delivered" + _attr_name: str = "Tier 5 summation delivered" + + +@MULTI_MATCH( + channel_names=CHANNEL_SMARTENERGY_METERING, + models={"ZLinky_TIC"}, +) +class Tier6SmartEnergySummation( + SmartEnergySummation, id_suffix="tier6_summation_delivered" +): + """Tier 6 Smart Energy Metering summation sensor.""" + + SENSOR_ATTR: int | str = "current_tier6_summ_delivered" + _attr_name: str = "Tier 6 summation delivered" + + @MULTI_MATCH(channel_names=CHANNEL_PRESSURE) class Pressure(Sensor): """Pressure sensor.""" diff --git a/tests/components/zha/test_channels.py b/tests/components/zha/test_channels.py index 6aba5500a2a..0ab905692c2 100644 --- a/tests/components/zha/test_channels.py +++ b/tests/components/zha/test_channels.py @@ -106,35 +106,43 @@ async def poll_control_device(zha_device_restored, zigpy_device_mock): @pytest.mark.parametrize( "cluster_id, bind_count, attrs", [ - (0x0000, 0, {}), - (0x0001, 1, {"battery_voltage", "battery_percentage_remaining"}), - (0x0002, 1, {"current_temperature"}), - (0x0003, 0, {}), - (0x0004, 0, {}), - (0x0005, 1, {}), - (0x0006, 1, {"on_off"}), - (0x0007, 1, {}), - (0x0008, 1, {"current_level"}), - (0x0009, 1, {}), - (0x000C, 1, {"present_value"}), - (0x000D, 1, {"present_value"}), - (0x000E, 1, {"present_value"}), - (0x000D, 1, {"present_value"}), - (0x0010, 1, {"present_value"}), - (0x0011, 1, {"present_value"}), - (0x0012, 1, {"present_value"}), - (0x0013, 1, {"present_value"}), - (0x0014, 1, {"present_value"}), - (0x0015, 1, {}), - (0x0016, 1, {}), - (0x0019, 0, {}), - (0x001A, 1, {}), - (0x001B, 1, {}), - (0x0020, 1, {}), - (0x0021, 0, {}), - (0x0101, 1, {"lock_state"}), + (zigpy.zcl.clusters.general.Basic.cluster_id, 0, {}), ( - 0x0201, + zigpy.zcl.clusters.general.PowerConfiguration.cluster_id, + 1, + {"battery_voltage", "battery_percentage_remaining"}, + ), + ( + zigpy.zcl.clusters.general.DeviceTemperature.cluster_id, + 1, + {"current_temperature"}, + ), + (zigpy.zcl.clusters.general.Identify.cluster_id, 0, {}), + (zigpy.zcl.clusters.general.Groups.cluster_id, 0, {}), + (zigpy.zcl.clusters.general.Scenes.cluster_id, 1, {}), + (zigpy.zcl.clusters.general.OnOff.cluster_id, 1, {"on_off"}), + (zigpy.zcl.clusters.general.OnOffConfiguration.cluster_id, 1, {}), + (zigpy.zcl.clusters.general.LevelControl.cluster_id, 1, {"current_level"}), + (zigpy.zcl.clusters.general.Alarms.cluster_id, 1, {}), + (zigpy.zcl.clusters.general.AnalogInput.cluster_id, 1, {"present_value"}), + (zigpy.zcl.clusters.general.AnalogOutput.cluster_id, 1, {"present_value"}), + (zigpy.zcl.clusters.general.AnalogValue.cluster_id, 1, {"present_value"}), + (zigpy.zcl.clusters.general.AnalogOutput.cluster_id, 1, {"present_value"}), + (zigpy.zcl.clusters.general.BinaryOutput.cluster_id, 1, {"present_value"}), + (zigpy.zcl.clusters.general.BinaryValue.cluster_id, 1, {"present_value"}), + (zigpy.zcl.clusters.general.MultistateInput.cluster_id, 1, {"present_value"}), + (zigpy.zcl.clusters.general.MultistateOutput.cluster_id, 1, {"present_value"}), + (zigpy.zcl.clusters.general.MultistateValue.cluster_id, 1, {"present_value"}), + (zigpy.zcl.clusters.general.Commissioning.cluster_id, 1, {}), + (zigpy.zcl.clusters.general.Partition.cluster_id, 1, {}), + (zigpy.zcl.clusters.general.Ota.cluster_id, 0, {}), + (zigpy.zcl.clusters.general.PowerProfile.cluster_id, 1, {}), + (zigpy.zcl.clusters.general.ApplianceControl.cluster_id, 1, {}), + (zigpy.zcl.clusters.general.PollControl.cluster_id, 1, {}), + (zigpy.zcl.clusters.general.GreenPowerProxy.cluster_id, 0, {}), + (zigpy.zcl.clusters.closures.DoorLock.cluster_id, 1, {"lock_state"}), + ( + zigpy.zcl.clusters.hvac.Thermostat.cluster_id, 1, { "local_temperature", @@ -150,9 +158,9 @@ async def poll_control_device(zha_device_restored, zigpy_device_mock): "pi_heating_demand", }, ), - (0x0202, 1, {"fan_mode"}), + (zigpy.zcl.clusters.hvac.Fan.cluster_id, 1, {"fan_mode"}), ( - 0x0300, + zigpy.zcl.clusters.lighting.Color.cluster_id, 1, { "current_x", @@ -163,16 +171,54 @@ async def poll_control_device(zha_device_restored, zigpy_device_mock): "current_saturation", }, ), - (0x0400, 1, {"measured_value"}), - (0x0401, 1, {"level_status"}), - (0x0402, 1, {"measured_value"}), - (0x0403, 1, {"measured_value"}), - (0x0404, 1, {"measured_value"}), - (0x0405, 1, {"measured_value"}), - (0x0406, 1, {"occupancy"}), - (0x0702, 1, {"instantaneous_demand"}), ( - 0x0B04, + zigpy.zcl.clusters.measurement.IlluminanceMeasurement.cluster_id, + 1, + {"measured_value"}, + ), + ( + zigpy.zcl.clusters.measurement.IlluminanceLevelSensing.cluster_id, + 1, + {"level_status"}, + ), + ( + zigpy.zcl.clusters.measurement.TemperatureMeasurement.cluster_id, + 1, + {"measured_value"}, + ), + ( + zigpy.zcl.clusters.measurement.PressureMeasurement.cluster_id, + 1, + {"measured_value"}, + ), + ( + zigpy.zcl.clusters.measurement.FlowMeasurement.cluster_id, + 1, + {"measured_value"}, + ), + ( + zigpy.zcl.clusters.measurement.RelativeHumidity.cluster_id, + 1, + {"measured_value"}, + ), + (zigpy.zcl.clusters.measurement.OccupancySensing.cluster_id, 1, {"occupancy"}), + ( + zigpy.zcl.clusters.smartenergy.Metering.cluster_id, + 1, + { + "instantaneous_demand", + "current_summ_delivered", + "current_tier1_summ_delivered", + "current_tier2_summ_delivered", + "current_tier3_summ_delivered", + "current_tier4_summ_delivered", + "current_tier5_summ_delivered", + "current_tier6_summ_delivered", + "status", + }, + ), + ( + zigpy.zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id, 1, { "active_power", diff --git a/tests/components/zha/test_sensor.py b/tests/components/zha/test_sensor.py index 373fce517eb..b8373e4bcae 100644 --- a/tests/components/zha/test_sensor.py +++ b/tests/components/zha/test_sensor.py @@ -309,7 +309,7 @@ async def async_test_device_temperature(hass, cluster, entity_id): smartenergy.Metering.cluster_id, "instantaneous_demand", async_test_metering, - 1, + 9, { "demand_formatting": 0xF9, "divisor": 1, @@ -323,7 +323,7 @@ async def async_test_device_temperature(hass, cluster, entity_id): smartenergy.Metering.cluster_id, "summation_delivered", async_test_smart_energy_summation, - 1, + 9, { "demand_formatting": 0xF9, "divisor": 1000,