diff --git a/homeassistant/components/whirlpool/sensor.py b/homeassistant/components/whirlpool/sensor.py index 41337aea9bd..8a8df82eb55 100644 --- a/homeassistant/components/whirlpool/sensor.py +++ b/homeassistant/components/whirlpool/sensor.py @@ -280,8 +280,13 @@ class WasherDryerTimeClass(RestoreSensor): if machine_state is MachineState.RunningMainCycle: self._running = True - self._attr_native_value = now + timedelta( + new_timestamp = now + timedelta( seconds=int(self._wd.get_attribute("Cavity_TimeStatusEstTimeRemaining")) ) - self._async_write_ha_state() + if isinstance(self._attr_native_value, datetime) and abs( + new_timestamp - self._attr_native_value + ) > timedelta(seconds=60): + + self._attr_native_value = new_timestamp + self._async_write_ha_state() diff --git a/tests/components/whirlpool/conftest.py b/tests/components/whirlpool/conftest.py index e411cfb8c2d..2fad5913749 100644 --- a/tests/components/whirlpool/conftest.py +++ b/tests/components/whirlpool/conftest.py @@ -63,6 +63,7 @@ def get_aircon_mock(said): mock_aircon = mock.Mock(said=said) mock_aircon.connect = AsyncMock() mock_aircon.disconnect = AsyncMock() + mock_aircon.register_attr_callback = AsyncMock() mock_aircon.get_online.return_value = True mock_aircon.get_power_on.return_value = True mock_aircon.get_mode.return_value = whirlpool.aircon.Mode.Cool @@ -114,6 +115,8 @@ def side_effect_function(*args, **kwargs): return "0" if args[0] == "WashCavity_OpStatusBulkDispense1Level": return "3" + if args[0] == "Cavity_TimeStatusEstTimeRemaining": + return "4000" def get_sensor_mock(said): @@ -121,6 +124,7 @@ def get_sensor_mock(said): mock_sensor = mock.Mock(said=said) mock_sensor.connect = AsyncMock() mock_sensor.disconnect = AsyncMock() + mock_sensor.register_attr_callback = AsyncMock() mock_sensor.get_online.return_value = True mock_sensor.get_machine_state.return_value = ( whirlpool.washerdryer.MachineState.Standby diff --git a/tests/components/whirlpool/test_sensor.py b/tests/components/whirlpool/test_sensor.py index 2da53521a05..658613b48c1 100644 --- a/tests/components/whirlpool/test_sensor.py +++ b/tests/components/whirlpool/test_sensor.py @@ -6,6 +6,7 @@ from whirlpool.washerdryer import MachineState from homeassistant.core import CoreState, HomeAssistant, State from homeassistant.helpers import entity_registry +from homeassistant.util.dt import as_timestamp, utc_from_timestamp from . import init_integration @@ -45,6 +46,25 @@ async def test_dryer_sensor_values( mock_sensor2_api: MagicMock, ): """Test the sensor value callbacks.""" + hass.state = CoreState.not_running + thetimestamp: datetime = datetime(2022, 11, 29, 00, 00, 00, 00, timezone.utc) + mock_restore_cache_with_extra_data( + hass, + ( + ( + State( + "sensor.washer_end_time", + "1", + ), + {"native_value": thetimestamp, "native_unit_of_measurement": None}, + ), + ( + State("sensor.dryer_end_time", "1"), + {"native_value": thetimestamp, "native_unit_of_measurement": None}, + ), + ), + ) + await init_integration(hass) entity_id = "sensor.dryer_state" @@ -60,7 +80,7 @@ async def test_dryer_sensor_values( assert state is not None state_id = f"{entity_id.split('_')[0]}_end_time" state = hass.states.get(state_id) - assert state is not None + assert state.state == thetimestamp.isoformat() mock_instance.get_machine_state.return_value = MachineState.RunningMainCycle mock_instance.get_cycle_status_filling.return_value = False @@ -90,6 +110,25 @@ async def test_washer_sensor_values( mock_sensor1_api: MagicMock, ): """Test the sensor value callbacks.""" + hass.state = CoreState.not_running + thetimestamp: datetime = datetime(2022, 11, 29, 00, 00, 00, 00, timezone.utc) + mock_restore_cache_with_extra_data( + hass, + ( + ( + State( + "sensor.washer_end_time", + "1", + ), + {"native_value": thetimestamp, "native_unit_of_measurement": None}, + ), + ( + State("sensor.dryer_end_time", "1"), + {"native_value": thetimestamp, "native_unit_of_measurement": None}, + ), + ), + ) + await init_integration(hass) entity_id = "sensor.washer_state" @@ -105,7 +144,7 @@ async def test_washer_sensor_values( assert state is not None state_id = f"{entity_id.split('_')[0]}_end_time" state = hass.states.get(state_id) - assert state is not None + assert state.state == thetimestamp.isoformat() state_id = f"{entity_id.split('_')[0]}_detergent_level" state = hass.states.get(state_id) @@ -243,3 +282,62 @@ async def test_restore_state( assert state.state == thetimestamp.isoformat() state = hass.states.get("sensor.dryer_end_time") assert state.state == thetimestamp.isoformat() + + +async def test_callback( + hass: HomeAssistant, + mock_sensor_api_instances: MagicMock, + mock_sensor1_api: MagicMock, +): + """Test callback timestamp callback function.""" + hass.state = CoreState.not_running + thetimestamp: datetime = datetime(2022, 11, 29, 00, 00, 00, 00, timezone.utc) + mock_restore_cache_with_extra_data( + hass, + ( + ( + State( + "sensor.washer_end_time", + "1", + ), + {"native_value": thetimestamp, "native_unit_of_measurement": None}, + ), + ( + State("sensor.dryer_end_time", "1"), + {"native_value": thetimestamp, "native_unit_of_measurement": None}, + ), + ), + ) + + # create and add entry + await init_integration(hass) + # restore from cache + state = hass.states.get("sensor.washer_end_time") + assert state.state == thetimestamp.isoformat() + callback = mock_sensor1_api.register_attr_callback.call_args_list[2][0][0] + callback() + # await hass.async_block_till_done() + state = hass.states.get("sensor.washer_end_time") + assert state.state == thetimestamp.isoformat() + mock_sensor1_api.get_machine_state.return_value = MachineState.RunningMainCycle + mock_sensor1_api.get_attribute.side_effect = None + mock_sensor1_api.get_attribute.return_value = "60" + callback() + + # Test new timestamp when machine starts a cycle. + state = hass.states.get("sensor.washer_end_time") + time = state.state + assert state.state != thetimestamp.isoformat() + + # Test no timestamp change for < 60 seconds time change. + mock_sensor1_api.get_attribute.return_value = "65" + callback() + state = hass.states.get("sensor.washer_end_time") + assert state.state == time + + # Test timestamp change for > 60 seconds. + mock_sensor1_api.get_attribute.return_value = "120" + callback() + state = hass.states.get("sensor.washer_end_time") + newtime = utc_from_timestamp(as_timestamp(time) + 60) + assert state.state == newtime.isoformat()