Centralize wemo exception handling (#46705)
This commit is contained in:
parent
b2df9aaaf1
commit
8bee3cda37
5 changed files with 48 additions and 92 deletions
|
@ -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
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue