Add ZHA metering summation received sensor (#107576)

* Add sensor for exposing Summation Received from Metering cluster

* Ruff format

* Test updates for new sensor

* Update test_sensor.py to support summation_received

* Correct report_count for smart meterning and some pylint warning fixes
This commit is contained in:
Jack 2024-01-24 12:56:49 +00:00 committed by GitHub
parent 8fa93f6fe5
commit af1ba4b22f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 134 additions and 29 deletions

View File

@ -92,6 +92,7 @@ class Metering(ClusterHandler):
AttrReportConfig(
attr="current_tier6_summ_delivered", config=REPORT_CONFIG_DEFAULT
),
AttrReportConfig(attr="current_summ_received", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="status", config=REPORT_CONFIG_ASAP),
)
ZCL_INIT_ATTRS = {

View File

@ -802,6 +802,19 @@ class Tier6SmartEnergySummation(PolledSmartEnergySummation):
_attr_translation_key: str = "tier6_summation_delivered"
@MULTI_MATCH(
cluster_handler_names=CLUSTER_HANDLER_SMARTENERGY_METERING,
)
# pylint: disable-next=hass-invalid-inheritance # needs fixing
class SmartEnergySummationReceived(PolledSmartEnergySummation):
"""Smart Energy Metering summation received sensor."""
_use_custom_polling = False # Poll indirectly by PolledSmartEnergySummation
_attribute_name = "current_summ_received"
_unique_id_suffix = "summation_received"
_attr_translation_key: str = "summation_received"
@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_PRESSURE)
# pylint: disable-next=hass-invalid-inheritance # needs fixing
class Pressure(Sensor):

View File

@ -837,6 +837,9 @@
"tier6_summation_delivered": {
"name": "Tier 6 summation delivered"
},
"summation_received": {
"name": "Summation received"
},
"device_temperature": {
"name": "Device temperature"
},

View File

@ -235,6 +235,7 @@ async def poll_control_device(zha_device_restored, zigpy_device_mock):
"current_tier4_summ_delivered",
"current_tier5_summ_delivered",
"current_tier6_summ_delivered",
"current_summ_received",
"status",
},
),

View File

@ -5,10 +5,7 @@ from unittest.mock import MagicMock, patch
import pytest
import zigpy.profiles.zha
import zigpy.zcl.clusters.general as general
import zigpy.zcl.clusters.homeautomation as homeautomation
import zigpy.zcl.clusters.measurement as measurement
import zigpy.zcl.clusters.smartenergy as smartenergy
from zigpy.zcl.clusters import general, homeautomation, measurement, smartenergy
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.components.zha.core.const import ZHA_CLUSTER_HANDLER_READS_PER_REQ
@ -70,7 +67,7 @@ def sensor_platform_only():
@pytest.fixture
async def elec_measurement_zigpy_dev(hass, zigpy_device_mock):
async def elec_measurement_zigpy_dev(hass: HomeAssistant, zigpy_device_mock):
"""Electric Measurement zigpy device."""
zigpy_device = zigpy_device_mock(
@ -110,19 +107,19 @@ async def elec_measurement_zha_dev(elec_measurement_zigpy_dev, zha_device_joined
return zha_dev
async def async_test_humidity(hass, cluster, entity_id):
async def async_test_humidity(hass: HomeAssistant, cluster, entity_id):
"""Test humidity sensor."""
await send_attributes_report(hass, cluster, {1: 1, 0: 1000, 2: 100})
assert_state(hass, entity_id, "10.0", PERCENTAGE)
async def async_test_temperature(hass, cluster, entity_id):
async def async_test_temperature(hass: HomeAssistant, cluster, entity_id):
"""Test temperature sensor."""
await send_attributes_report(hass, cluster, {1: 1, 0: 2900, 2: 100})
assert_state(hass, entity_id, "29.0", UnitOfTemperature.CELSIUS)
async def async_test_pressure(hass, cluster, entity_id):
async def async_test_pressure(hass: HomeAssistant, cluster, entity_id):
"""Test pressure sensor."""
await send_attributes_report(hass, cluster, {1: 1, 0: 1000, 2: 10000})
assert_state(hass, entity_id, "1000", UnitOfPressure.HPA)
@ -131,7 +128,7 @@ async def async_test_pressure(hass, cluster, entity_id):
assert_state(hass, entity_id, "1000", UnitOfPressure.HPA)
async def async_test_illuminance(hass, cluster, entity_id):
async def async_test_illuminance(hass: HomeAssistant, cluster, entity_id):
"""Test illuminance sensor."""
await send_attributes_report(hass, cluster, {1: 1, 0: 10, 2: 20})
assert_state(hass, entity_id, "1", LIGHT_LUX)
@ -143,7 +140,7 @@ async def async_test_illuminance(hass, cluster, entity_id):
assert_state(hass, entity_id, "unknown", LIGHT_LUX)
async def async_test_metering(hass, cluster, entity_id):
async def async_test_metering(hass: HomeAssistant, cluster, entity_id):
"""Test Smart Energy metering sensor."""
await send_attributes_report(hass, cluster, {1025: 1, 1024: 12345, 1026: 100})
assert_state(hass, entity_id, "12345.0", None)
@ -164,8 +161,10 @@ async def async_test_metering(hass, cluster, entity_id):
assert hass.states.get(entity_id).attributes["status"] in ("<bitmap8.32: 32>", "32")
async def async_test_smart_energy_summation(hass, cluster, entity_id):
"""Test SmartEnergy Summation delivered sensro."""
async def async_test_smart_energy_summation_delivered(
hass: HomeAssistant, cluster, entity_id
):
"""Test SmartEnergy Summation delivered sensor."""
await send_attributes_report(
hass, cluster, {1025: 1, "current_summ_delivered": 12321, 1026: 100}
@ -179,7 +178,24 @@ async def async_test_smart_energy_summation(hass, cluster, entity_id):
)
async def async_test_electrical_measurement(hass, cluster, entity_id):
async def async_test_smart_energy_summation_received(
hass: HomeAssistant, cluster, entity_id
):
"""Test SmartEnergy Summation received sensor."""
await send_attributes_report(
hass, cluster, {1025: 1, "current_summ_received": 12321, 1026: 100}
)
assert_state(hass, entity_id, "12.321", UnitOfEnergy.KILO_WATT_HOUR)
assert hass.states.get(entity_id).attributes["status"] == "NO_ALARMS"
assert hass.states.get(entity_id).attributes["device_type"] == "Electric Metering"
assert (
hass.states.get(entity_id).attributes[ATTR_DEVICE_CLASS]
== SensorDeviceClass.ENERGY
)
async def async_test_electrical_measurement(hass: HomeAssistant, cluster, entity_id):
"""Test electrical measurement sensor."""
# update divisor cached value
await send_attributes_report(hass, cluster, {"ac_power_divisor": 1})
@ -201,7 +217,7 @@ async def async_test_electrical_measurement(hass, cluster, entity_id):
assert hass.states.get(entity_id).attributes["active_power_max"] == "8.8"
async def async_test_em_apparent_power(hass, cluster, entity_id):
async def async_test_em_apparent_power(hass: HomeAssistant, cluster, entity_id):
"""Test electrical measurement Apparent Power sensor."""
# update divisor cached value
await send_attributes_report(hass, cluster, {"ac_power_divisor": 1})
@ -219,7 +235,7 @@ async def async_test_em_apparent_power(hass, cluster, entity_id):
assert_state(hass, entity_id, "9.9", UnitOfApparentPower.VOLT_AMPERE)
async def async_test_em_rms_current(hass, cluster, entity_id):
async def async_test_em_rms_current(hass: HomeAssistant, cluster, entity_id):
"""Test electrical measurement RMS Current sensor."""
await send_attributes_report(hass, cluster, {0: 1, 0x0508: 1234, 10: 1000})
@ -237,7 +253,7 @@ async def async_test_em_rms_current(hass, cluster, entity_id):
assert hass.states.get(entity_id).attributes["rms_current_max"] == "8.8"
async def async_test_em_rms_voltage(hass, cluster, entity_id):
async def async_test_em_rms_voltage(hass: HomeAssistant, cluster, entity_id):
"""Test electrical measurement RMS Voltage sensor."""
await send_attributes_report(hass, cluster, {0: 1, 0x0505: 1234, 10: 1000})
@ -255,7 +271,7 @@ async def async_test_em_rms_voltage(hass, cluster, entity_id):
assert hass.states.get(entity_id).attributes["rms_voltage_max"] == "8.9"
async def async_test_powerconfiguration(hass, cluster, entity_id):
async def async_test_powerconfiguration(hass: HomeAssistant, cluster, entity_id):
"""Test powerconfiguration/battery sensor."""
await send_attributes_report(hass, cluster, {33: 98})
assert_state(hass, entity_id, "49", "%")
@ -266,7 +282,7 @@ async def async_test_powerconfiguration(hass, cluster, entity_id):
assert hass.states.get(entity_id).attributes["battery_voltage"] == 2.0
async def async_test_powerconfiguration2(hass, cluster, entity_id):
async def async_test_powerconfiguration2(hass: HomeAssistant, cluster, entity_id):
"""Test powerconfiguration/battery sensor."""
await send_attributes_report(hass, cluster, {33: -1})
assert_state(hass, entity_id, STATE_UNKNOWN, "%")
@ -278,7 +294,7 @@ async def async_test_powerconfiguration2(hass, cluster, entity_id):
assert_state(hass, entity_id, "49", "%")
async def async_test_device_temperature(hass, cluster, entity_id):
async def async_test_device_temperature(hass: HomeAssistant, cluster, entity_id):
"""Test temperature sensor."""
await send_attributes_report(hass, cluster, {0: 2900})
assert_state(hass, entity_id, "29.0", UnitOfTemperature.CELSIUS)
@ -330,7 +346,7 @@ async def async_test_device_temperature(hass, cluster, entity_id):
smartenergy.Metering.cluster_id,
"instantaneous_demand",
async_test_metering,
9,
10,
{
"demand_formatting": 0xF9,
"divisor": 1,
@ -338,13 +354,13 @@ async def async_test_device_temperature(hass, cluster, entity_id):
"multiplier": 1,
"status": 0x00,
},
{"current_summ_delivered"},
{"current_summ_delivered", "current_summ_received"},
),
(
smartenergy.Metering.cluster_id,
"summation_delivered",
async_test_smart_energy_summation,
9,
async_test_smart_energy_summation_delivered,
10,
{
"demand_formatting": 0xF9,
"divisor": 1000,
@ -354,7 +370,23 @@ async def async_test_device_temperature(hass, cluster, entity_id):
"summation_formatting": 0b1_0111_010,
"unit_of_measure": 0x00,
},
{"instaneneous_demand"},
{"instaneneous_demand", "current_summ_received"},
),
(
smartenergy.Metering.cluster_id,
"summation_received",
async_test_smart_energy_summation_received,
10,
{
"demand_formatting": 0xF9,
"divisor": 1000,
"metering_device_type": 0x00,
"multiplier": 1,
"status": 0x00,
"summation_formatting": 0b1_0111_010,
"unit_of_measure": 0x00,
},
{"instaneneous_demand", "current_summ_delivered"},
),
(
homeautomation.ElectricalMeasurement.cluster_id,
@ -476,7 +508,7 @@ async def test_sensor(
await async_test_rejoin(hass, zigpy_device, [cluster], (report_count,))
def assert_state(hass, entity_id, state, unit_of_measurement):
def assert_state(hass: HomeAssistant, entity_id, state, unit_of_measurement):
"""Check that the state is what is expected.
This is used to ensure that the logic in each sensor class handled the
@ -488,7 +520,7 @@ def assert_state(hass, entity_id, state, unit_of_measurement):
@pytest.fixture
def hass_ms(hass):
def hass_ms(hass: HomeAssistant):
"""Hass instance with measurement system."""
async def _hass_ms(meas_sys):
@ -710,6 +742,7 @@ async def test_electrical_measurement_init(
},
{
"summation_delivered",
"summation_received",
},
{
"instantaneous_demand",
@ -717,19 +750,21 @@ async def test_electrical_measurement_init(
),
(
smartenergy.Metering.cluster_id,
{"instantaneous_demand", "current_summ_delivered"},
{"instantaneous_demand", "current_summ_delivered", "current_summ_received"},
{},
{
"summation_delivered",
"instantaneous_demand",
"summation_delivered",
"summation_received",
},
),
(
smartenergy.Metering.cluster_id,
{},
{
"summation_delivered",
"instantaneous_demand",
"summation_delivered",
"summation_received",
},
{},
),

View File

@ -233,6 +233,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3210_l_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3210_l_summation_received",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
@ -571,6 +576,13 @@ DEVICES = [
"sensor.climaxtechnology_psmp5_00_00_02_02tc_summation_delivered"
),
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: (
"sensor.climaxtechnology_psmp5_00_00_02_02tc_summation_received"
),
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
@ -1510,6 +1522,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45852_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45852_summation_received",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
@ -1565,6 +1582,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45856_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45856_summation_received",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
@ -1620,6 +1642,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45857_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45857_summation_received",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
@ -2183,6 +2210,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_relay_c2acn01_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_relay_c2acn01_summation_received",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergyMetering",
@ -4199,6 +4231,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.sercomm_corp_sz_esw01_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.sercomm_corp_sz_esw01_summation_received",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
@ -4955,6 +4992,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e11_g13_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e11_g13_summation_received",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
@ -5003,6 +5045,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e12_n14_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e12_n14_summation_received",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
@ -5051,6 +5098,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.sengled_z01_a19nae26_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.sengled_z01_a19nae26_summation_received",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",