Move soma base entity to separate module (#126177)

This commit is contained in:
epenet 2024-09-18 11:05:21 +02:00 committed by GitHub
parent 0deb152bb2
commit 6325a332bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 119 additions and 111 deletions

View File

@ -2,12 +2,7 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable, Coroutine
import logging
from typing import Any
from api.soma_api import SomaApi from api.soma_api import SomaApi
from requests import RequestException
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
@ -15,16 +10,9 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT, Platform from homeassistant.const import CONF_HOST, CONF_PORT, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from .const import API, DOMAIN, HOST, PORT from .const import API, DEVICES, DOMAIN, HOST, PORT
from .utils import is_api_response_success
_LOGGER = logging.getLogger(__name__)
DEVICES = "devices"
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
vol.All( vol.All(
@ -72,98 +60,3 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
def soma_api_call[_SomaEntityT: SomaEntity](
api_call: Callable[[_SomaEntityT], Coroutine[Any, Any, dict]],
) -> Callable[[_SomaEntityT], Coroutine[Any, Any, dict]]:
"""Soma api call decorator."""
async def inner(self: _SomaEntityT) -> dict:
response = {}
try:
response_from_api = await api_call(self)
except RequestException:
if self.api_is_available:
_LOGGER.warning("Connection to SOMA Connect failed")
self.api_is_available = False
else:
if not self.api_is_available:
self.api_is_available = True
_LOGGER.info("Connection to SOMA Connect succeeded")
if not is_api_response_success(response_from_api):
if self.is_available:
self.is_available = False
_LOGGER.warning(
(
"Device is unreachable (%s). Error while fetching the"
" state: %s"
),
self.name,
response_from_api["msg"],
)
else:
if not self.is_available:
self.is_available = True
_LOGGER.info("Device %s is now reachable", self.name)
response = response_from_api
return response
return inner
class SomaEntity(Entity):
"""Representation of a generic Soma device."""
_attr_has_entity_name = True
def __init__(self, device, api):
"""Initialize the Soma device."""
self.device = device
self.api = api
self.current_position = 50
self.battery_state = 0
self.is_available = True
self.api_is_available = True
@property
def available(self):
"""Return true if the last API commands returned successfully."""
return self.is_available
@property
def unique_id(self):
"""Return the unique id base on the id returned by pysoma API."""
return self.device["mac"]
@property
def device_info(self) -> DeviceInfo:
"""Return device specific attributes.
Implemented by platform classes.
"""
return DeviceInfo(
identifiers={(DOMAIN, self.unique_id)},
manufacturer="Wazombi Labs",
name=self.device["name"],
)
def set_position(self, position: int) -> None:
"""Set the current device position."""
self.current_position = position
self.schedule_update_ha_state()
@soma_api_call
async def get_shade_state_from_api(self) -> dict:
"""Return the shade state from the api."""
return await self.hass.async_add_executor_job(
self.api.get_shade_state, self.device["mac"]
)
@soma_api_call
async def get_battery_level_from_api(self) -> dict:
"""Return the battery level from the api."""
return await self.hass.async_add_executor_job(
self.api.get_battery_level, self.device["mac"]
)

View File

@ -4,3 +4,5 @@ DOMAIN = "soma"
HOST = "host" HOST = "host"
PORT = "port" PORT = "port"
API = "api" API = "api"
DEVICES = "devices"

View File

@ -16,7 +16,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import API, DEVICES, DOMAIN, SomaEntity from .const import API, DEVICES, DOMAIN
from .entity import SomaEntity
from .utils import is_api_response_success from .utils import is_api_response_success

View File

@ -0,0 +1,112 @@
"""Support for Soma Smartshades."""
from __future__ import annotations
from collections.abc import Callable, Coroutine
import logging
from typing import Any
from requests import RequestException
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
from .const import DOMAIN
from .utils import is_api_response_success
_LOGGER = logging.getLogger(__name__)
def soma_api_call[_SomaEntityT: SomaEntity](
api_call: Callable[[_SomaEntityT], Coroutine[Any, Any, dict]],
) -> Callable[[_SomaEntityT], Coroutine[Any, Any, dict]]:
"""Soma api call decorator."""
async def inner(self: _SomaEntityT) -> dict:
response = {}
try:
response_from_api = await api_call(self)
except RequestException:
if self.api_is_available:
_LOGGER.warning("Connection to SOMA Connect failed")
self.api_is_available = False
else:
if not self.api_is_available:
self.api_is_available = True
_LOGGER.info("Connection to SOMA Connect succeeded")
if not is_api_response_success(response_from_api):
if self.is_available:
self.is_available = False
_LOGGER.warning(
(
"Device is unreachable (%s). Error while fetching the"
" state: %s"
),
self.name,
response_from_api["msg"],
)
else:
if not self.is_available:
self.is_available = True
_LOGGER.info("Device %s is now reachable", self.name)
response = response_from_api
return response
return inner
class SomaEntity(Entity):
"""Representation of a generic Soma device."""
_attr_has_entity_name = True
def __init__(self, device, api):
"""Initialize the Soma device."""
self.device = device
self.api = api
self.current_position = 50
self.battery_state = 0
self.is_available = True
self.api_is_available = True
@property
def available(self):
"""Return true if the last API commands returned successfully."""
return self.is_available
@property
def unique_id(self):
"""Return the unique id base on the id returned by pysoma API."""
return self.device["mac"]
@property
def device_info(self) -> DeviceInfo:
"""Return device specific attributes.
Implemented by platform classes.
"""
return DeviceInfo(
identifiers={(DOMAIN, self.unique_id)},
manufacturer="Wazombi Labs",
name=self.device["name"],
)
def set_position(self, position: int) -> None:
"""Set the current device position."""
self.current_position = position
self.schedule_update_ha_state()
@soma_api_call
async def get_shade_state_from_api(self) -> dict:
"""Return the shade state from the api."""
return await self.hass.async_add_executor_job(
self.api.get_shade_state, self.device["mac"]
)
@soma_api_call
async def get_battery_level_from_api(self) -> dict:
"""Return the battery level from the api."""
return await self.hass.async_add_executor_job(
self.api.get_battery_level, self.device["mac"]
)

View File

@ -9,8 +9,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import Throttle from homeassistant.util import Throttle
from . import DEVICES, SomaEntity from .const import API, DEVICES, DOMAIN
from .const import API, DOMAIN from .entity import SomaEntity
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)