Add reconnect logic and proper reporting to MotionMount integration (#125670)

* Add reconnect logic and proper reporting

* Use snake_case

* Log on warning, not on info

* Reduce line length

* Refactor non-raising code out of try blocks

* Remove `_ensure_connected()` from action functions
This commit is contained in:
RJPoelstra 2024-09-16 11:56:13 +02:00 committed by GitHub
parent 18e2c2f6dd
commit e6b86b662a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 59 additions and 9 deletions

View file

@ -1,5 +1,7 @@
"""Support for MotionMount sensors."""
import logging
import socket
from typing import TYPE_CHECKING
import motionmount
@ -12,6 +14,8 @@ from homeassistant.helpers.entity import Entity
from .const import DOMAIN, EMPTY_MAC
_LOGGER = logging.getLogger(__name__)
class MotionMountEntity(Entity):
"""Representation of a MotionMount entity."""
@ -70,3 +74,23 @@ class MotionMountEntity(Entity):
self.mm.remove_listener(self.async_write_ha_state)
self.mm.remove_listener(self.update_name)
await super().async_will_remove_from_hass()
async def _ensure_connected(self) -> bool:
"""Make sure there is a connection with the MotionMount.
Returns false if the connection failed to be ensured.
"""
if self.mm.is_connected:
return True
try:
await self.mm.connect()
except (ConnectionError, TimeoutError, socket.gaierror):
# We're not interested in exceptions here. In case of a failed connection
# the try/except from the caller will report it.
# The purpose of `_ensure_connected()` is only to make sure we try to
# reconnect, where failures should not be logged each time
return False
else:
_LOGGER.warning("Successfully reconnected to MotionMount")
return True

View file

@ -1,11 +1,14 @@
"""Support for MotionMount numeric control."""
import socket
import motionmount
from homeassistant.components.number import NumberEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
@ -46,7 +49,10 @@ class MotionMountExtension(MotionMountEntity, NumberEntity):
async def async_set_native_value(self, value: float) -> None:
"""Set the new value for extension."""
try:
await self.mm.set_extension(int(value))
except (TimeoutError, socket.gaierror) as ex:
raise HomeAssistantError("Failed to communicate with MotionMount") from ex
class MotionMountTurn(MotionMountEntity, NumberEntity):
@ -69,4 +75,7 @@ class MotionMountTurn(MotionMountEntity, NumberEntity):
async def async_set_native_value(self, value: float) -> None:
"""Set the new value for turn."""
try:
await self.mm.set_turn(int(value * -1))
except (TimeoutError, socket.gaierror) as ex:
raise HomeAssistantError("Failed to communicate with MotionMount") from ex

View file

@ -1,15 +1,23 @@
"""Support for MotionMount numeric control."""
from datetime import timedelta
import logging
import socket
import motionmount
from homeassistant.components.select import SelectEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN, WALL_PRESET_NAME
from .entity import MotionMountEntity
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=60)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
@ -23,6 +31,7 @@ async def async_setup_entry(
class MotionMountPresets(MotionMountEntity, SelectEntity):
"""The presets of a MotionMount."""
_attr_should_poll = True
_attr_translation_key = "motionmount_preset"
def __init__(
@ -44,7 +53,14 @@ class MotionMountPresets(MotionMountEntity, SelectEntity):
async def async_update(self) -> None:
"""Get latest state from MotionMount."""
if not await self._ensure_connected():
return
try:
self._presets = await self.mm.get_presets()
except (TimeoutError, socket.gaierror) as ex:
_LOGGER.warning("Failed to communicate with MotionMount: %s", ex)
else:
self._update_options(self._presets)
@property
@ -72,8 +88,9 @@ class MotionMountPresets(MotionMountEntity, SelectEntity):
async def async_select_option(self, option: str) -> None:
"""Set the new option."""
index = int(option[:1])
try:
await self.mm.go_to_preset(index)
except (TimeoutError, socket.gaierror) as ex:
raise HomeAssistantError("Failed to communicate with MotionMount") from ex
else:
self._attr_current_option = option
# Perform an update so we detect changes to the presets (changes are not pushed)
self.async_schedule_update_ha_state(True)