diff --git a/homeassistant/components/wemo/wemo_device.py b/homeassistant/components/wemo/wemo_device.py index 9423d0b8d1c..1690d30e082 100644 --- a/homeassistant/components/wemo/wemo_device.py +++ b/homeassistant/components/wemo/wemo_device.py @@ -3,7 +3,7 @@ import asyncio from datetime import timedelta import logging -from pywemo import WeMoDevice +from pywemo import Insight, WeMoDevice from pywemo.exceptions import ActionException from pywemo.subscribe import EVENT_TYPE_LONG_PRESS @@ -81,11 +81,26 @@ class DeviceCoordinator(DataUpdateCoordinator): else: self.async_set_updated_data(None) + @property + def should_poll(self) -> bool: + """Return True if polling is needed to update the state for the device. + + The alternative, when this returns False, is to rely on the subscription + "push updates" to update the device state in Home Assistant. + """ + if isinstance(self.wemo, Insight) and self.wemo.get_state() == 0: + # The WeMo Insight device does not send subscription updates for the + # insight_params values when the device is off. Polling is required in + # this case so the Sensor entities are properly populated. + return True + + registry = self.hass.data[DOMAIN]["registry"] + return not (registry.is_subscribed(self.wemo) and self.last_update_success) + async def _async_update_data(self) -> None: """Update WeMo state.""" # No need to poll if the device will push updates. - registry = self.hass.data[DOMAIN]["registry"] - if registry.is_subscribed(self.wemo) and self.last_update_success: + if not self.should_poll: return # If an update is in progress, we don't do anything. diff --git a/tests/components/wemo/test_wemo_device.py b/tests/components/wemo/test_wemo_device.py index 6f3cc12a81a..e756e816a47 100644 --- a/tests/components/wemo/test_wemo_device.py +++ b/tests/components/wemo/test_wemo_device.py @@ -1,6 +1,7 @@ """Tests for wemo_device.py.""" import asyncio -from unittest.mock import patch +from datetime import timedelta +from unittest.mock import call, patch import async_timeout import pytest @@ -14,9 +15,12 @@ from homeassistant.core import callback from homeassistant.helpers import device_registry from homeassistant.helpers.update_coordinator import UpdateFailed from homeassistant.setup import async_setup_component +from homeassistant.util.dt import utcnow from .conftest import MOCK_HOST +from tests.common import async_fire_time_changed + asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(True)) @@ -148,3 +152,53 @@ async def test_async_update_data_subscribed( pywemo_device.get_state.reset_mock() await device._async_update_data() pywemo_device.get_state.assert_not_called() + + +class TestInsight: + """Tests specific to the WeMo Insight device.""" + + @pytest.fixture + def pywemo_model(self): + """Pywemo Dimmer models use the light platform (WemoDimmer class).""" + return "Insight" + + @pytest.fixture(name="pywemo_device") + def pywemo_device_fixture(self, pywemo_device): + """Fixture for WeMoDevice instances.""" + pywemo_device.insight_params = { + "currentpower": 1.0, + "todaymw": 200000000.0, + "state": 0, + "onfor": 0, + "ontoday": 0, + "ontotal": 0, + "powerthreshold": 0, + } + yield pywemo_device + + @pytest.mark.parametrize( + "subscribed,state,expected_calls", + [ + (False, 0, [call(), call(True), call(), call()]), + (False, 1, [call(), call(True), call(), call()]), + (True, 0, [call(), call(True), call(), call()]), + (True, 1, [call(), call(), call()]), + ], + ) + async def test_should_poll( + self, + hass, + subscribed, + state, + expected_calls, + wemo_entity, + pywemo_device, + pywemo_registry, + ): + """Validate the should_poll returns the correct value.""" + pywemo_registry.is_subscribed.return_value = subscribed + pywemo_device.get_state.reset_mock() + pywemo_device.get_state.return_value = state + async_fire_time_changed(hass, utcnow() + timedelta(seconds=31)) + await hass.async_block_till_done() + pywemo_device.get_state.assert_has_calls(expected_calls)