first commit
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
"""Implement the Yandex Smart Home custom capabilities."""
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
import itertools
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.const import STATE_OFF
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.helpers.service import async_call_from_config
|
||||
|
||||
from . import const
|
||||
from .capability import AbstractCapability
|
||||
from .capability_mode import ModeCapability
|
||||
from .capability_range import RangeCapability
|
||||
from .capability_toggle import ToggleCapability
|
||||
from .const import (
|
||||
CONF_ENTITY_MODE_MAP,
|
||||
CONF_ENTITY_RANGE,
|
||||
CONF_ENTITY_RANGE_MAX,
|
||||
CONF_ENTITY_RANGE_MIN,
|
||||
CONF_ENTITY_RANGE_PRECISION,
|
||||
ERR_DEVICE_UNREACHABLE,
|
||||
ERR_NOT_SUPPORTED_IN_CURRENT_MODE,
|
||||
)
|
||||
from .error import SmartHomeError
|
||||
from .helpers import Config, RequestData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CustomCapability(AbstractCapability, ABC):
|
||||
def __init__(self, hass: HomeAssistant, config: Config, state: State,
|
||||
instance: str, capability_config: dict[str, Any]):
|
||||
super().__init__(hass, config, state)
|
||||
self.instance = instance
|
||||
self.capability_config = capability_config
|
||||
self.state_entity_id = self.capability_config.get(const.CONF_ENTITY_CUSTOM_CAPABILITY_STATE_ENTITY_ID)
|
||||
self.retrievable = bool(self.state_entity_id or self.state_value_attribute)
|
||||
|
||||
@property
|
||||
def state_value_attribute(self) -> str | None:
|
||||
"""Return HA attribute for state of this entity."""
|
||||
return self.capability_config.get(const.CONF_ENTITY_CUSTOM_CAPABILITY_STATE_ATTRIBUTE)
|
||||
|
||||
def get_value(self) -> float | str | None:
|
||||
"""Return the state value of this capability for this entity."""
|
||||
if not self.retrievable:
|
||||
return None
|
||||
|
||||
entity_state = self.state
|
||||
|
||||
if self.state_entity_id:
|
||||
entity_state = self.hass.states.get(self.state_entity_id)
|
||||
if not entity_state:
|
||||
raise SmartHomeError(
|
||||
ERR_DEVICE_UNREACHABLE,
|
||||
f'Entity {self.state_entity_id} not found for {self.instance} instance of {self.state.entity_id}'
|
||||
)
|
||||
|
||||
if self.state_value_attribute:
|
||||
value = entity_state.attributes.get(self.state_value_attribute)
|
||||
else:
|
||||
value = entity_state.state
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class CustomModeCapability(CustomCapability, ModeCapability):
|
||||
def __init__(self, hass: HomeAssistant, config: Config, state: State,
|
||||
instance: str, capability_config: dict[str, Any]):
|
||||
super().__init__(hass, config, state, instance, capability_config)
|
||||
|
||||
self.set_mode_config = self.capability_config[const.CONF_ENTITY_CUSTOM_MODE_SET_MODE]
|
||||
|
||||
@property
|
||||
def supported_ha_modes(self) -> list[str]:
|
||||
"""Returns list of supported HA modes for this entity."""
|
||||
modes = self.entity_config.get(CONF_ENTITY_MODE_MAP, {}).get(self.instance, {})
|
||||
rv = list(itertools.chain(*modes.values()))
|
||||
return rv
|
||||
|
||||
@property
|
||||
def modes_list_attribute(self) -> str | None:
|
||||
"""Return HA attribute contains modes list for this entity."""
|
||||
return None
|
||||
|
||||
def get_value(self) -> str | None:
|
||||
"""Return the state value of this capability for this entity."""
|
||||
if not self.retrievable:
|
||||
return None
|
||||
|
||||
return self.get_yandex_mode_by_ha_mode(super().get_value())
|
||||
|
||||
async def set_state(self, data: RequestData, state: dict[str, Any]):
|
||||
"""Set device state."""
|
||||
await async_call_from_config(
|
||||
self.hass,
|
||||
self.set_mode_config,
|
||||
validate_config=False,
|
||||
variables={'mode': self.get_ha_mode_by_yandex_mode(state['value'])},
|
||||
blocking=True,
|
||||
context=data.context
|
||||
)
|
||||
|
||||
|
||||
class CustomToggleCapability(CustomCapability, ToggleCapability):
|
||||
def __init__(self, hass: HomeAssistant, config: Config, state: State,
|
||||
instance: str, capability_config: dict[str, Any]):
|
||||
super().__init__(hass, config, state, instance, capability_config)
|
||||
|
||||
self.turn_on_config = self.capability_config[const.CONF_ENTITY_CUSTOM_TOGGLE_TURN_ON]
|
||||
self.turn_off_config = self.capability_config[const.CONF_ENTITY_CUSTOM_TOGGLE_TURN_OFF]
|
||||
|
||||
def supported(self) -> bool:
|
||||
"""Test if capability is supported."""
|
||||
return True
|
||||
|
||||
def get_value(self) -> bool | None:
|
||||
"""Return the state value of this capability for this entity."""
|
||||
if not self.retrievable:
|
||||
return None
|
||||
|
||||
return not super().get_value() in [STATE_OFF, False]
|
||||
|
||||
async def set_state(self, data: RequestData, state: dict[str, Any]):
|
||||
"""Set device state."""
|
||||
await async_call_from_config(
|
||||
self.hass,
|
||||
self.turn_on_config if state['value'] else self.turn_off_config,
|
||||
validate_config=False,
|
||||
blocking=True,
|
||||
context=data.context
|
||||
)
|
||||
|
||||
|
||||
class CustomRangeCapability(CustomCapability, RangeCapability):
|
||||
def __init__(self, hass: HomeAssistant, config: Config, state: State,
|
||||
instance: str, capability_config: dict[str, Any]):
|
||||
self.capability_config = capability_config
|
||||
|
||||
self.set_value = self.capability_config.get(const.CONF_ENTITY_CUSTOM_RANGE_SET_VALUE)
|
||||
self.increase_value = self.capability_config.get(const.CONF_ENTITY_CUSTOM_RANGE_INCREASE_VALUE)
|
||||
self.decrease_value = self.capability_config.get(const.CONF_ENTITY_CUSTOM_RANGE_DECREASE_VALUE)
|
||||
|
||||
super().__init__(hass, config, state, instance, capability_config)
|
||||
|
||||
self.default_range = (
|
||||
self.capability_config.get(CONF_ENTITY_RANGE, {}).get(CONF_ENTITY_RANGE_MIN, self.default_range[0]),
|
||||
self.capability_config.get(CONF_ENTITY_RANGE, {}).get(CONF_ENTITY_RANGE_MAX, self.default_range[1]),
|
||||
self.capability_config.get(CONF_ENTITY_RANGE, {}).get(CONF_ENTITY_RANGE_PRECISION, self.default_range[2])
|
||||
)
|
||||
|
||||
def supported(self) -> bool:
|
||||
"""Test if capability is supported."""
|
||||
return True
|
||||
|
||||
@property
|
||||
def support_random_access(self) -> bool:
|
||||
"""Test if capability supports random access."""
|
||||
for key in [CONF_ENTITY_RANGE_MIN, CONF_ENTITY_RANGE_MAX]:
|
||||
if key not in self.capability_config.get(CONF_ENTITY_RANGE, {}):
|
||||
return False
|
||||
|
||||
return self.set_value is not None
|
||||
|
||||
def get_value(self) -> float | None:
|
||||
"""Return the state value of this capability for this entity."""
|
||||
if not self.retrievable:
|
||||
return None
|
||||
|
||||
return self.float_value(super().get_value())
|
||||
|
||||
async def set_state(self, data: RequestData, state: dict[str, Any]):
|
||||
"""Set device state."""
|
||||
value = state['value']
|
||||
service = self.set_value
|
||||
|
||||
if state.get('relative'):
|
||||
if self.increase_value or self.decrease_value:
|
||||
if value >= 0:
|
||||
service = self.increase_value
|
||||
else:
|
||||
service = self.decrease_value
|
||||
else:
|
||||
if not self.retrievable:
|
||||
raise SmartHomeError(
|
||||
ERR_NOT_SUPPORTED_IN_CURRENT_MODE,
|
||||
f'Failed to set relative value for {self.instance} instance of {self.state.entity_id}. '
|
||||
f'No state or service found.'
|
||||
)
|
||||
|
||||
value = self.get_absolute_value(state['value'])
|
||||
|
||||
await async_call_from_config(
|
||||
self.hass,
|
||||
service,
|
||||
validate_config=False,
|
||||
variables={'value': value},
|
||||
blocking=True,
|
||||
context=data.context
|
||||
)
|
||||
Reference in New Issue
Block a user