From c959a0a484e085b4fbb9c30a822a309e58dba00a Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 3 Aug 2021 22:50:14 +0200 Subject: [PATCH] Limit zwave_js meter sensor last reset (#53921) --- homeassistant/components/zwave_js/sensor.py | 20 ++++++++------ tests/components/zwave_js/common.py | 3 ++- tests/components/zwave_js/test_sensor.py | 29 ++++++++++++++++----- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py index 304a80f7940..7b491661e68 100644 --- a/homeassistant/components/zwave_js/sensor.py +++ b/homeassistant/components/zwave_js/sensor.py @@ -248,20 +248,20 @@ class ZWaveMeterSensor(ZWaveNumericSensor, RestoreEntity): # Entity class attributes self._attr_state_class = STATE_CLASS_MEASUREMENT - self._attr_last_reset = dt.utc_from_timestamp(0) + if self.device_class == DEVICE_CLASS_ENERGY: + self._attr_last_reset = dt.utc_from_timestamp(0) @callback def async_update_last_reset( self, node: ZwaveNode, endpoint: int, meter_type: int | None ) -> None: """Update last reset.""" - # If the signal is not for this node or is for a different endpoint, ignore it - if self.info.node != node or self.info.primary_value.endpoint != endpoint: - return - # If a meter type was specified and doesn't match this entity's meter type, - # ignore it + # If the signal is not for this node or is for a different endpoint, + # or a meter type was specified and doesn't match this entity's meter type: if ( - meter_type is not None + self.info.node != node + or self.info.primary_value.endpoint != endpoint + or meter_type is not None and self.info.primary_value.metadata.cc_specific.get("meterType") != meter_type ): @@ -274,6 +274,10 @@ class ZWaveMeterSensor(ZWaveNumericSensor, RestoreEntity): """Call when entity is added.""" await super().async_added_to_hass() + # If the meter is not an accumulating meter type, do not reset. + if self.device_class != DEVICE_CLASS_ENERGY: + return + # Restore the last reset time from stored state restored_state = await self.async_get_last_state() if restored_state and ATTR_LAST_RESET in restored_state.attributes: @@ -310,7 +314,7 @@ class ZWaveMeterSensor(ZWaveNumericSensor, RestoreEntity): primary_value.endpoint, options, ) - self._attr_last_reset = dt.utcnow() + # Notify meters that may have been reset async_dispatcher_send( self.hass, diff --git a/tests/components/zwave_js/common.py b/tests/components/zwave_js/common.py index 8c8a3f2e576..44943fed9fb 100644 --- a/tests/components/zwave_js/common.py +++ b/tests/components/zwave_js/common.py @@ -33,7 +33,8 @@ ID_LOCK_CONFIG_PARAMETER_SENSOR = ( "sensor.z_wave_module_for_id_lock_150_and_101_config_parameter_door_lock_mode" ) ZEN_31_ENTITY = "light.kitchen_under_cabinet_lights" -METER_SENSOR = "sensor.smart_switch_6_electric_consumed_v" +METER_ENERGY_SENSOR = "sensor.smart_switch_6_electric_consumed_kwh" +METER_VOLTAGE_SENSOR = "sensor.smart_switch_6_electric_consumed_v" DATETIME_ZERO = datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc) DATETIME_LAST_RESET = datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc) diff --git a/tests/components/zwave_js/test_sensor.py b/tests/components/zwave_js/test_sensor.py index 9fa4152ad6b..04583559421 100644 --- a/tests/components/zwave_js/test_sensor.py +++ b/tests/components/zwave_js/test_sensor.py @@ -36,7 +36,8 @@ from .common import ( HUMIDITY_SENSOR, ID_LOCK_CONFIG_PARAMETER_SENSOR, INDICATOR_SENSOR, - METER_SENSOR, + METER_ENERGY_SENSOR, + METER_VOLTAGE_SENSOR, NOTIFICATION_MOTION_SENSOR, POWER_SENSOR, VOLTAGE_SENSOR, @@ -202,9 +203,13 @@ async def test_reset_meter( client.async_send_command.return_value = {} client.async_send_command_no_wait.return_value = {} + # Validate that non accumulating meter does not have a last reset attribute + + assert ATTR_LAST_RESET not in hass.states.get(METER_VOLTAGE_SENSOR).attributes + # Validate that the sensor last reset is starting from nothing assert ( - hass.states.get(METER_SENSOR).attributes[ATTR_LAST_RESET] + hass.states.get(METER_ENERGY_SENSOR).attributes[ATTR_LAST_RESET] == DATETIME_ZERO.isoformat() ) @@ -215,13 +220,13 @@ async def test_reset_meter( DOMAIN, SERVICE_RESET_METER, { - ATTR_ENTITY_ID: METER_SENSOR, + ATTR_ENTITY_ID: METER_ENERGY_SENSOR, }, blocking=True, ) assert ( - hass.states.get(METER_SENSOR).attributes[ATTR_LAST_RESET] + hass.states.get(METER_ENERGY_SENSOR).attributes[ATTR_LAST_RESET] == DATETIME_LAST_RESET.isoformat() ) @@ -232,6 +237,10 @@ async def test_reset_meter( assert args["endpoint"] == 0 assert args["args"] == [] + # Validate that non accumulating meter does not have a last reset attribute + + assert ATTR_LAST_RESET not in hass.states.get(METER_VOLTAGE_SENSOR).attributes + client.async_send_command_no_wait.reset_mock() # Test successful meter reset call with options @@ -239,7 +248,7 @@ async def test_reset_meter( DOMAIN, SERVICE_RESET_METER, { - ATTR_ENTITY_ID: METER_SENSOR, + ATTR_ENTITY_ID: METER_ENERGY_SENSOR, ATTR_METER_TYPE: 1, ATTR_VALUE: 2, }, @@ -253,6 +262,10 @@ async def test_reset_meter( assert args["endpoint"] == 0 assert args["args"] == [{"type": 1, "targetValue": 2}] + # Validate that non accumulating meter does not have a last reset attribute + + assert ATTR_LAST_RESET not in hass.states.get(METER_VOLTAGE_SENSOR).attributes + client.async_send_command_no_wait.reset_mock() @@ -265,6 +278,10 @@ async def test_restore_last_reset( ): """Test restoring last_reset on setup.""" assert ( - hass.states.get(METER_SENSOR).attributes[ATTR_LAST_RESET] + hass.states.get(METER_ENERGY_SENSOR).attributes[ATTR_LAST_RESET] == DATETIME_LAST_RESET.isoformat() ) + + # Validate that non accumulating meter does not have a last reset attribute + + assert ATTR_LAST_RESET not in hass.states.get(METER_VOLTAGE_SENSOR).attributes