Centralize wemo exception handling (#46705)

This commit is contained in:
Eric Severance 2021-02-17 14:36:39 -08:00 committed by GitHub
parent b2df9aaaf1
commit 8bee3cda37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 92 deletions

View file

@ -2,8 +2,6 @@
import asyncio import asyncio
import logging import logging
from pywemo.ouimeaux_device.api.service import ActionException
from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -35,12 +33,5 @@ class WemoBinarySensor(WemoSubscriptionEntity, BinarySensorEntity):
def _update(self, force_update=True): def _update(self, force_update=True):
"""Update the sensor state.""" """Update the sensor state."""
try: with self._wemo_exception_handler("update status"):
self._state = self.wemo.get_state(force_update) self._state = self.wemo.get_state(force_update)
if not self._available:
_LOGGER.info("Reconnected to %s", self.name)
self._available = True
except ActionException as err:
_LOGGER.warning("Could not update status for %s (%s)", self.name, err)
self._available = False

View file

@ -1,10 +1,12 @@
"""Classes shared among Wemo entities.""" """Classes shared among Wemo entities."""
import asyncio import asyncio
import contextlib
import logging import logging
from typing import Any, Dict, Optional from typing import Any, Dict, Generator, Optional
import async_timeout import async_timeout
from pywemo import WeMoDevice from pywemo import WeMoDevice
from pywemo.ouimeaux_device.api.service import ActionException
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -13,6 +15,18 @@ from .const import DOMAIN as WEMO_DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
class ExceptionHandlerStatus:
"""Exit status from the _wemo_exception_handler context manager."""
# An exception if one was raised in the _wemo_exception_handler.
exception: Optional[Exception] = None
@property
def success(self) -> bool:
"""Return True if the handler completed with no exception."""
return self.exception is None
class WemoEntity(Entity): class WemoEntity(Entity):
"""Common methods for Wemo entities. """Common methods for Wemo entities.
@ -36,6 +50,23 @@ class WemoEntity(Entity):
"""Return true if switch is available.""" """Return true if switch is available."""
return self._available return self._available
@contextlib.contextmanager
def _wemo_exception_handler(
self, message: str
) -> Generator[ExceptionHandlerStatus, None, None]:
"""Wrap device calls to set `_available` when wemo exceptions happen."""
status = ExceptionHandlerStatus()
try:
yield status
except ActionException as err:
status.exception = err
_LOGGER.warning("Could not %s for %s (%s)", message, self.name, err)
self._available = False
else:
if not self._available:
_LOGGER.info("Reconnected to %s", self.name)
self._available = True
def _update(self, force_update: Optional[bool] = True): def _update(self, force_update: Optional[bool] = True):
"""Update the device state.""" """Update the device state."""
raise NotImplementedError() raise NotImplementedError()

View file

@ -4,7 +4,6 @@ from datetime import timedelta
import logging import logging
import math import math
from pywemo.ouimeaux_device.api.service import ActionException
import voluptuous as vol import voluptuous as vol
from homeassistant.components.fan import SUPPORT_SET_SPEED, FanEntity from homeassistant.components.fan import SUPPORT_SET_SPEED, FanEntity
@ -138,7 +137,7 @@ class WemoHumidifier(WemoSubscriptionEntity, FanEntity):
def _update(self, force_update=True): def _update(self, force_update=True):
"""Update the device state.""" """Update the device state."""
try: with self._wemo_exception_handler("update status"):
self._state = self.wemo.get_state(force_update) self._state = self.wemo.get_state(force_update)
self._fan_mode = self.wemo.fan_mode self._fan_mode = self.wemo.fan_mode
@ -152,13 +151,6 @@ class WemoHumidifier(WemoSubscriptionEntity, FanEntity):
if self.wemo.fan_mode != WEMO_FAN_OFF: if self.wemo.fan_mode != WEMO_FAN_OFF:
self._last_fan_on_mode = self.wemo.fan_mode self._last_fan_on_mode = self.wemo.fan_mode
if not self._available:
_LOGGER.info("Reconnected to %s", self.name)
self._available = True
except ActionException as err:
_LOGGER.warning("Could not update status for %s (%s)", self.name, err)
self._available = False
def turn_on( def turn_on(
self, self,
speed: str = None, speed: str = None,
@ -171,11 +163,8 @@ class WemoHumidifier(WemoSubscriptionEntity, FanEntity):
def turn_off(self, **kwargs) -> None: def turn_off(self, **kwargs) -> None:
"""Turn the switch off.""" """Turn the switch off."""
try: with self._wemo_exception_handler("turn off"):
self.wemo.set_state(WEMO_FAN_OFF) self.wemo.set_state(WEMO_FAN_OFF)
except ActionException as err:
_LOGGER.warning("Error while turning off device %s (%s)", self.name, err)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()
@ -188,13 +177,8 @@ class WemoHumidifier(WemoSubscriptionEntity, FanEntity):
else: else:
named_speed = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage)) named_speed = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage))
try: with self._wemo_exception_handler("set speed"):
self.wemo.set_state(named_speed) self.wemo.set_state(named_speed)
except ActionException as err:
_LOGGER.warning(
"Error while setting speed of device %s (%s)", self.name, err
)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()
@ -211,24 +195,14 @@ class WemoHumidifier(WemoSubscriptionEntity, FanEntity):
elif target_humidity >= 100: elif target_humidity >= 100:
pywemo_humidity = WEMO_HUMIDITY_100 pywemo_humidity = WEMO_HUMIDITY_100
try: with self._wemo_exception_handler("set humidity"):
self.wemo.set_humidity(pywemo_humidity) self.wemo.set_humidity(pywemo_humidity)
except ActionException as err:
_LOGGER.warning(
"Error while setting humidity of device: %s (%s)", self.name, err
)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()
def reset_filter_life(self) -> None: def reset_filter_life(self) -> None:
"""Reset the filter life to 100%.""" """Reset the filter life to 100%."""
try: with self._wemo_exception_handler("reset filter life"):
self.wemo.reset_filter_life() self.wemo.reset_filter_life()
except ActionException as err:
_LOGGER.warning(
"Error while resetting filter life on device: %s (%s)", self.name, err
)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()

View file

@ -3,8 +3,6 @@ import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
from pywemo.ouimeaux_device.api.service import ActionException
from homeassistant import util from homeassistant import util
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
@ -158,7 +156,7 @@ class WemoLight(WemoEntity, LightEntity):
"force_update": False, "force_update": False,
} }
try: with self._wemo_exception_handler("turn on"):
if xy_color is not None: if xy_color is not None:
self.wemo.set_color(xy_color, transition=transition_time) self.wemo.set_color(xy_color, transition=transition_time)
@ -167,9 +165,6 @@ class WemoLight(WemoEntity, LightEntity):
if self.wemo.turn_on(**turn_on_kwargs): if self.wemo.turn_on(**turn_on_kwargs):
self._state["onoff"] = WEMO_ON self._state["onoff"] = WEMO_ON
except ActionException as err:
_LOGGER.warning("Error while turning on device %s (%s)", self.name, err)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()
@ -177,28 +172,21 @@ class WemoLight(WemoEntity, LightEntity):
"""Turn the light off.""" """Turn the light off."""
transition_time = int(kwargs.get(ATTR_TRANSITION, 0)) transition_time = int(kwargs.get(ATTR_TRANSITION, 0))
try: with self._wemo_exception_handler("turn off"):
if self.wemo.turn_off(transition=transition_time): if self.wemo.turn_off(transition=transition_time):
self._state["onoff"] = WEMO_OFF self._state["onoff"] = WEMO_OFF
except ActionException as err:
_LOGGER.warning("Error while turning off device %s (%s)", self.name, err)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()
def _update(self, force_update=True): def _update(self, force_update=True):
"""Synchronize state with bridge.""" """Synchronize state with bridge."""
try: with self._wemo_exception_handler("update status") as handler:
self._update_lights(no_throttle=force_update) self._update_lights(no_throttle=force_update)
self._state = self.wemo.state self._state = self.wemo.state
except ActionException as err: if handler.success:
_LOGGER.warning("Could not update status for %s (%s)", self.name, err)
self._available = False
else:
self._is_on = self._state.get("onoff") != WEMO_OFF self._is_on = self._state.get("onoff") != WEMO_OFF
self._brightness = self._state.get("level", 255) self._brightness = self._state.get("level", 255)
self._color_temp = self._state.get("temperature_mireds") self._color_temp = self._state.get("temperature_mireds")
self._available = True
xy_color = self._state.get("color_xy") xy_color = self._state.get("color_xy")
@ -228,19 +216,12 @@ class WemoDimmer(WemoSubscriptionEntity, LightEntity):
def _update(self, force_update=True): def _update(self, force_update=True):
"""Update the device state.""" """Update the device state."""
try: with self._wemo_exception_handler("update status"):
self._state = self.wemo.get_state(force_update) self._state = self.wemo.get_state(force_update)
wemobrightness = int(self.wemo.get_brightness(force_update)) wemobrightness = int(self.wemo.get_brightness(force_update))
self._brightness = int((wemobrightness * 255) / 100) self._brightness = int((wemobrightness * 255) / 100)
if not self._available:
_LOGGER.info("Reconnected to %s", self.name)
self._available = True
except ActionException as err:
_LOGGER.warning("Could not update status for %s (%s)", self.name, err)
self._available = False
def turn_on(self, **kwargs): def turn_on(self, **kwargs):
"""Turn the dimmer on.""" """Turn the dimmer on."""
# Wemo dimmer switches use a range of [0, 100] to control # Wemo dimmer switches use a range of [0, 100] to control
@ -251,24 +232,18 @@ class WemoDimmer(WemoSubscriptionEntity, LightEntity):
else: else:
brightness = 255 brightness = 255
try: with self._wemo_exception_handler("turn on"):
if self.wemo.on(): if self.wemo.on():
self._state = WEMO_ON self._state = WEMO_ON
self.wemo.set_brightness(brightness) self.wemo.set_brightness(brightness)
except ActionException as err:
_LOGGER.warning("Error while turning on device %s (%s)", self.name, err)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the dimmer off.""" """Turn the dimmer off."""
try: with self._wemo_exception_handler("turn off"):
if self.wemo.off(): if self.wemo.off():
self._state = WEMO_OFF self._state = WEMO_OFF
except ActionException as err:
_LOGGER.warning("Error while turning on device %s (%s)", self.name, err)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()

View file

@ -3,8 +3,6 @@ import asyncio
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging import logging
from pywemo.ouimeaux_device.api.service import ActionException
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.const import STATE_OFF, STATE_ON, STATE_STANDBY, STATE_UNKNOWN from homeassistant.const import STATE_OFF, STATE_ON, STATE_STANDBY, STATE_UNKNOWN
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -140,29 +138,23 @@ class WemoSwitch(WemoSubscriptionEntity, SwitchEntity):
def turn_on(self, **kwargs): def turn_on(self, **kwargs):
"""Turn the switch on.""" """Turn the switch on."""
try: with self._wemo_exception_handler("turn on"):
if self.wemo.on(): if self.wemo.on():
self._state = WEMO_ON self._state = WEMO_ON
except ActionException as err:
_LOGGER.warning("Error while turning on device %s (%s)", self.name, err)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the switch off.""" """Turn the switch off."""
try: with self._wemo_exception_handler("turn off"):
if self.wemo.off(): if self.wemo.off():
self._state = WEMO_OFF self._state = WEMO_OFF
except ActionException as err:
_LOGGER.warning("Error while turning off device %s (%s)", self.name, err)
self._available = False
self.schedule_update_ha_state() self.schedule_update_ha_state()
def _update(self, force_update=True): def _update(self, force_update=True):
"""Update the device state.""" """Update the device state."""
try: with self._wemo_exception_handler("update status"):
self._state = self.wemo.get_state(force_update) self._state = self.wemo.get_state(force_update)
if self.wemo.model_name == "Insight": if self.wemo.model_name == "Insight":
@ -173,10 +165,3 @@ class WemoSwitch(WemoSubscriptionEntity, SwitchEntity):
elif self.wemo.model_name == "CoffeeMaker": elif self.wemo.model_name == "CoffeeMaker":
self.coffeemaker_mode = self.wemo.mode self.coffeemaker_mode = self.wemo.mode
self._mode_string = self.wemo.mode_string self._mode_string = self.wemo.mode_string
if not self._available:
_LOGGER.info("Reconnected to %s", self.name)
self._available = True
except ActionException as err:
_LOGGER.warning("Could not update status for %s (%s)", self.name, err)
self._available = False