Perform wemo state update quickly after a timeout (#46702)
* Perform state update quickly after a timeout * with async_timeout.timeout(...) -> async with async_timeout.timeout(...)
This commit is contained in:
parent
8bee3cda37
commit
2ac075bb37
3 changed files with 35 additions and 15 deletions
|
@ -89,16 +89,28 @@ class WemoEntity(Entity):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(self.platform.scan_interval.seconds - 0.1):
|
async with async_timeout.timeout(
|
||||||
await asyncio.shield(self._async_locked_update(True))
|
self.platform.scan_interval.seconds - 0.1
|
||||||
|
) as timeout:
|
||||||
|
await asyncio.shield(self._async_locked_update(True, timeout))
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
_LOGGER.warning("Lost connection to %s", self.name)
|
_LOGGER.warning("Lost connection to %s", self.name)
|
||||||
self._available = False
|
self._available = False
|
||||||
|
|
||||||
async def _async_locked_update(self, force_update: bool) -> None:
|
async def _async_locked_update(
|
||||||
|
self, force_update: bool, timeout: Optional[async_timeout.timeout] = None
|
||||||
|
) -> None:
|
||||||
"""Try updating within an async lock."""
|
"""Try updating within an async lock."""
|
||||||
async with self._update_lock:
|
async with self._update_lock:
|
||||||
await self.hass.async_add_executor_job(self._update, force_update)
|
await self.hass.async_add_executor_job(self._update, force_update)
|
||||||
|
# When the timeout expires HomeAssistant is no longer waiting for an
|
||||||
|
# update from the device. Instead, the state needs to be updated
|
||||||
|
# asynchronously. This also handles the case where an update came
|
||||||
|
# directly from the device (device push). In that case no polling
|
||||||
|
# update was involved and the state also needs to be updated
|
||||||
|
# asynchronously.
|
||||||
|
if not timeout or timeout.expired:
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
class WemoSubscriptionEntity(WemoEntity):
|
class WemoSubscriptionEntity(WemoEntity):
|
||||||
|
@ -152,4 +164,3 @@ class WemoSubscriptionEntity(WemoEntity):
|
||||||
return
|
return
|
||||||
|
|
||||||
await self._async_locked_update(force_update)
|
await self._async_locked_update(force_update)
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import asyncio
|
||||||
import threading
|
import threading
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import async_timeout
|
||||||
from pywemo.ouimeaux_device.api.service import ActionException
|
from pywemo.ouimeaux_device.api.service import ActionException
|
||||||
|
|
||||||
from homeassistant.components.homeassistant import (
|
from homeassistant.components.homeassistant import (
|
||||||
|
@ -146,7 +147,19 @@ async def test_async_update_with_timeout_and_recovery(hass, wemo_entity, pywemo_
|
||||||
assert hass.states.get(wemo_entity.entity_id).state == STATE_OFF
|
assert hass.states.get(wemo_entity.entity_id).state == STATE_OFF
|
||||||
await async_setup_component(hass, HA_DOMAIN, {})
|
await async_setup_component(hass, HA_DOMAIN, {})
|
||||||
|
|
||||||
with patch("async_timeout.timeout", side_effect=asyncio.TimeoutError):
|
event = threading.Event()
|
||||||
|
|
||||||
|
def get_state(*args):
|
||||||
|
event.wait()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if hasattr(pywemo_device, "bridge_update"):
|
||||||
|
pywemo_device.bridge_update.side_effect = get_state
|
||||||
|
else:
|
||||||
|
pywemo_device.get_state.side_effect = get_state
|
||||||
|
timeout = async_timeout.timeout(0)
|
||||||
|
|
||||||
|
with patch("async_timeout.timeout", return_value=timeout):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
HA_DOMAIN,
|
HA_DOMAIN,
|
||||||
SERVICE_UPDATE_ENTITY,
|
SERVICE_UPDATE_ENTITY,
|
||||||
|
@ -157,11 +170,6 @@ async def test_async_update_with_timeout_and_recovery(hass, wemo_entity, pywemo_
|
||||||
assert hass.states.get(wemo_entity.entity_id).state == STATE_UNAVAILABLE
|
assert hass.states.get(wemo_entity.entity_id).state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
# Check that the entity recovers and is available after the update succeeds.
|
# Check that the entity recovers and is available after the update succeeds.
|
||||||
await hass.services.async_call(
|
event.set()
|
||||||
HA_DOMAIN,
|
await hass.async_block_till_done()
|
||||||
SERVICE_UPDATE_ENTITY,
|
|
||||||
{ATTR_ENTITY_ID: [wemo_entity.entity_id]},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert hass.states.get(wemo_entity.entity_id).state == STATE_OFF
|
assert hass.states.get(wemo_entity.entity_id).state == STATE_OFF
|
||||||
|
|
|
@ -69,9 +69,10 @@ async def test_async_update_with_timeout_and_recovery(
|
||||||
hass, pywemo_bridge_light, wemo_entity, pywemo_device
|
hass, pywemo_bridge_light, wemo_entity, pywemo_device
|
||||||
):
|
):
|
||||||
"""Test that the entity becomes unavailable after a timeout, and that it recovers."""
|
"""Test that the entity becomes unavailable after a timeout, and that it recovers."""
|
||||||
await entity_test_helpers.test_async_update_with_timeout_and_recovery(
|
with _bypass_throttling():
|
||||||
hass, wemo_entity, pywemo_device
|
await entity_test_helpers.test_async_update_with_timeout_and_recovery(
|
||||||
)
|
hass, wemo_entity, pywemo_device
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_async_locked_update_with_exception(
|
async def test_async_locked_update_with_exception(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue