From c21add195a780a98e50c5e5f00d9c05da148d033 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Tue, 4 May 2021 19:10:28 +0300 Subject: [PATCH] Catch Shelly set state exceptions when device is inaccessible (#50064) --- homeassistant/components/shelly/cover.py | 8 ++--- homeassistant/components/shelly/entity.py | 20 +++++++++++- homeassistant/components/shelly/light.py | 37 ++++++++++++++++++++--- homeassistant/components/shelly/switch.py | 4 +-- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/shelly/cover.py b/homeassistant/components/shelly/cover.py index 0438e5fe6b7..18f13479c30 100644 --- a/homeassistant/components/shelly/cover.py +++ b/homeassistant/components/shelly/cover.py @@ -83,24 +83,24 @@ class ShellyCover(ShellyBlockEntity, CoverEntity): async def async_close_cover(self, **kwargs): """Close cover.""" - self.control_result = await self.block.set_state(go="close") + self.control_result = await self.set_state(go="close") self.async_write_ha_state() async def async_open_cover(self, **kwargs): """Open cover.""" - self.control_result = await self.block.set_state(go="open") + self.control_result = await self.set_state(go="open") self.async_write_ha_state() async def async_set_cover_position(self, **kwargs): """Move the cover to a specific position.""" - self.control_result = await self.block.set_state( + self.control_result = await self.set_state( go="to_pos", roller_pos=kwargs[ATTR_POSITION] ) self.async_write_ha_state() async def async_stop_cover(self, **_kwargs): """Stop the cover.""" - self.control_result = await self.block.set_state(go="stop") + self.control_result = await self.set_state(go="stop") self.async_write_ha_state() @callback diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index 44ef41b82b6..675eead2155 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -1,11 +1,13 @@ """Shelly entity helper.""" from __future__ import annotations +import asyncio from dataclasses import dataclass import logging from typing import Any, Callable import aioshelly +import async_timeout from homeassistant.core import callback from homeassistant.helpers import ( @@ -17,7 +19,7 @@ from homeassistant.helpers import ( from homeassistant.helpers.restore_state import RestoreEntity from . import ShellyDeviceRestWrapper, ShellyDeviceWrapper -from .const import COAP, DATA_CONFIG_ENTRY, DOMAIN, REST +from .const import AIOSHELLY_DEVICE_TIMEOUT_SEC, COAP, DATA_CONFIG_ENTRY, DOMAIN, REST from .utils import async_remove_shelly_entity, get_entity_name _LOGGER = logging.getLogger(__name__) @@ -218,6 +220,22 @@ class ShellyBlockEntity(entity.Entity): """Handle device update.""" self.async_write_ha_state() + async def set_state(self, **kwargs): + """Set block state (HTTP request).""" + _LOGGER.debug("Setting state for entity %s, state: %s", self.name, kwargs) + try: + async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): + return await self.block.set_state(**kwargs) + except (asyncio.TimeoutError, OSError) as err: + _LOGGER.error( + "Setting state for entity %s failed, state: %s, error: %s", + self.name, + kwargs, + repr(err), + ) + self.wrapper.last_update_success = False + return None + class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity): """Helper class to represent a block attribute.""" diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index f3e80ee87b0..adca498c3f9 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -1,7 +1,11 @@ """Light for Shelly.""" from __future__ import annotations +import asyncio +import logging + from aioshelly import Block +import async_timeout from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -24,6 +28,7 @@ from homeassistant.util.color import ( from . import ShellyDeviceWrapper from .const import ( + AIOSHELLY_DEVICE_TIMEOUT_SEC, COAP, DATA_CONFIG_ENTRY, DOMAIN, @@ -34,6 +39,8 @@ from .const import ( from .entity import ShellyBlockEntity from .utils import async_remove_shelly_entity +_LOGGER = logging.getLogger(__name__) + async def async_setup_entry(hass, config_entry, async_add_entities): """Set up lights for device.""" @@ -199,7 +206,7 @@ class ShellyLight(ShellyBlockEntity, LightEntity): async def async_turn_on(self, **kwargs) -> None: """Turn on light.""" if self.block.type == "relay": - self.control_result = await self.block.set_state(turn="on") + self.control_result = await self.set_state(turn="on") self.async_write_ha_state() return @@ -233,17 +240,37 @@ class ShellyLight(ShellyBlockEntity, LightEntity): ATTR_RGBW_COLOR ] - if set_mode and self.mode != set_mode: - self.mode_result = await self.wrapper.device.switch_light_mode(set_mode) + if await self.set_light_mode(set_mode): + self.control_result = await self.set_state(**params) - self.control_result = await self.block.set_state(**params) self.async_write_ha_state() async def async_turn_off(self, **kwargs) -> None: """Turn off light.""" - self.control_result = await self.block.set_state(turn="off") + self.control_result = await self.set_state(turn="off") self.async_write_ha_state() + async def set_light_mode(self, set_mode): + """Change device mode color/white if mode has changed.""" + if set_mode is None or self.mode == set_mode: + return True + + _LOGGER.debug("Setting light mode for entity %s, mode: %s", self.name, set_mode) + try: + async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): + self.mode_result = await self.wrapper.device.switch_light_mode(set_mode) + except (asyncio.TimeoutError, OSError) as err: + _LOGGER.error( + "Setting light mode for entity %s failed, state: %s, error: %s", + self.name, + set_mode, + repr(err), + ) + self.wrapper.last_update_success = False + return False + + return True + @callback def _update_callback(self): """When device updates, clear control & mode result that overrides state.""" diff --git a/homeassistant/components/shelly/switch.py b/homeassistant/components/shelly/switch.py index c86487072c6..6f3dd0b0136 100644 --- a/homeassistant/components/shelly/switch.py +++ b/homeassistant/components/shelly/switch.py @@ -62,12 +62,12 @@ class RelaySwitch(ShellyBlockEntity, SwitchEntity): async def async_turn_on(self, **kwargs): """Turn on relay.""" - self.control_result = await self.block.set_state(turn="on") + self.control_result = await self.set_state(turn="on") self.async_write_ha_state() async def async_turn_off(self, **kwargs): """Turn off relay.""" - self.control_result = await self.block.set_state(turn="off") + self.control_result = await self.set_state(turn="off") self.async_write_ha_state() @callback