Skip to content

Commit

Permalink
Limit Whirlpool timestamp changes to +/- 60 seconds (home-assistant#8…
Browse files Browse the repository at this point in the history
…5368)

* Limit timestamp changes to +/- 60 seconds

* Add timestamp callback tests
  • Loading branch information
mkmer authored Jan 8, 2023
1 parent fc00c6d commit 45eb1ef
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 4 deletions.
9 changes: 7 additions & 2 deletions homeassistant/components/whirlpool/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,13 @@ def update_from_latest_data(self) -> None:

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()
4 changes: 4 additions & 0 deletions tests/components/whirlpool/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -114,13 +115,16 @@ 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):
"""Get a mock of a sensor."""
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
Expand Down
102 changes: 100 additions & 2 deletions tests/components/whirlpool/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

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

Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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"
Expand All @@ -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)
Expand Down Expand Up @@ -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()

0 comments on commit 45eb1ef

Please sign in to comment.