2019-04-07 19:03:26 +02:00
|
|
|
import enum
|
2021-06-18 17:57:02 +02:00
|
|
|
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Type, TypeVar
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
import attr
|
|
|
|
|
2021-06-18 17:57:02 +02:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from .api_pb2 import HomeassistantServiceMap # type: ignore
|
|
|
|
|
2019-10-24 13:59:06 +02:00
|
|
|
# All fields in here should have defaults set
|
|
|
|
# Home Assistant depends on these fields being constructible
|
|
|
|
# with args from a previous version of Home Assistant.
|
|
|
|
# The default value should *always* be the Protobuf default value
|
|
|
|
# for a field (False, 0, empty string, enum with value 0, ...)
|
|
|
|
|
2021-06-18 17:57:02 +02:00
|
|
|
_T = TypeVar("_T", bound="APIIntEnum")
|
2019-04-07 19:03:26 +02:00
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
|
|
|
|
class APIIntEnum(enum.IntEnum):
|
|
|
|
"""Base class for int enum values in API model."""
|
2021-06-18 17:57:02 +02:00
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
@classmethod
|
|
|
|
def convert(cls: Type[_T], value: int) -> Optional[_T]:
|
|
|
|
try:
|
|
|
|
return cls(value)
|
|
|
|
except ValueError:
|
|
|
|
return None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def convert_list(cls: Type[_T], value: List[int]) -> List[_T]:
|
|
|
|
ret = []
|
|
|
|
for x in value:
|
|
|
|
try:
|
|
|
|
ret.append(cls(x))
|
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
2020-01-30 07:03:58 +01:00
|
|
|
@attr.s
|
2019-04-07 19:03:26 +02:00
|
|
|
class APIVersion:
|
2019-10-24 13:59:06 +02:00
|
|
|
major = attr.ib(type=int, default=0)
|
|
|
|
minor = attr.ib(type=int, default=0)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class DeviceInfo:
|
2019-10-24 13:59:06 +02:00
|
|
|
uses_password = attr.ib(type=bool, default=False)
|
2021-06-18 17:57:02 +02:00
|
|
|
name = attr.ib(type=str, default="")
|
|
|
|
mac_address = attr.ib(type=str, default="")
|
|
|
|
compilation_time = attr.ib(type=str, default="")
|
|
|
|
model = attr.ib(type=str, default="")
|
2019-10-24 13:59:06 +02:00
|
|
|
has_deep_sleep = attr.ib(type=bool, default=False)
|
2021-06-18 17:57:02 +02:00
|
|
|
esphome_version = attr.ib(type=str, default="")
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class EntityInfo:
|
2021-06-18 17:57:02 +02:00
|
|
|
object_id = attr.ib(type=str, default="")
|
2019-10-24 13:59:06 +02:00
|
|
|
key = attr.ib(type=int, default=0)
|
2021-06-18 17:57:02 +02:00
|
|
|
name = attr.ib(type=str, default="")
|
|
|
|
unique_id = attr.ib(type=str, default="")
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class EntityState:
|
2019-10-24 13:59:06 +02:00
|
|
|
key = attr.ib(type=int, default=0)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
# ==================== BINARY SENSOR ====================
|
|
|
|
@attr.s
|
|
|
|
class BinarySensorInfo(EntityInfo):
|
2021-06-18 17:57:02 +02:00
|
|
|
device_class = attr.ib(type=str, default="")
|
2019-10-24 13:59:06 +02:00
|
|
|
is_status_binary_sensor = attr.ib(type=bool, default=False)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class BinarySensorState(EntityState):
|
2019-10-24 13:59:06 +02:00
|
|
|
state = attr.ib(type=bool, default=False)
|
2019-11-12 14:55:35 +01:00
|
|
|
missing_state = attr.ib(type=bool, default=False)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
# ==================== COVER ====================
|
|
|
|
@attr.s
|
|
|
|
class CoverInfo(EntityInfo):
|
2019-04-19 16:22:50 +02:00
|
|
|
assumed_state = attr.ib(type=bool, default=False)
|
|
|
|
supports_position = attr.ib(type=bool, default=False)
|
|
|
|
supports_tilt = attr.ib(type=bool, default=False)
|
2021-06-18 17:57:02 +02:00
|
|
|
device_class = attr.ib(type=str, default="")
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
class LegacyCoverState(APIIntEnum):
|
2019-04-07 19:03:26 +02:00
|
|
|
OPEN = 0
|
|
|
|
CLOSED = 1
|
|
|
|
|
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
class LegacyCoverCommand(APIIntEnum):
|
2019-04-07 19:03:26 +02:00
|
|
|
OPEN = 0
|
|
|
|
CLOSE = 1
|
|
|
|
STOP = 2
|
|
|
|
|
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
class CoverOperation(APIIntEnum):
|
2019-04-07 19:03:26 +02:00
|
|
|
IDLE = 0
|
|
|
|
IS_OPENING = 1
|
|
|
|
IS_CLOSING = 2
|
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class CoverState(EntityState):
|
2021-06-18 17:57:02 +02:00
|
|
|
legacy_state = attr.ib(
|
|
|
|
type=LegacyCoverState,
|
|
|
|
converter=LegacyCoverState.convert, # type: ignore
|
|
|
|
default=LegacyCoverState.OPEN,
|
|
|
|
)
|
2019-10-24 13:59:06 +02:00
|
|
|
position = attr.ib(type=float, default=0.0)
|
|
|
|
tilt = attr.ib(type=float, default=0.0)
|
2021-06-18 17:57:02 +02:00
|
|
|
current_operation = attr.ib(
|
|
|
|
type=CoverOperation,
|
|
|
|
converter=CoverOperation.convert, # type: ignore
|
|
|
|
default=CoverOperation.IDLE,
|
|
|
|
)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
2021-06-18 17:57:02 +02:00
|
|
|
def is_closed(self, api_version: APIVersion) -> bool:
|
2019-04-07 19:03:26 +02:00
|
|
|
if api_version >= APIVersion(1, 1):
|
|
|
|
return self.position == 0.0
|
|
|
|
return self.legacy_state == LegacyCoverState.CLOSED
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== FAN ====================
|
|
|
|
@attr.s
|
|
|
|
class FanInfo(EntityInfo):
|
2019-10-24 13:59:06 +02:00
|
|
|
supports_oscillation = attr.ib(type=bool, default=False)
|
|
|
|
supports_speed = attr.ib(type=bool, default=False)
|
2020-12-14 04:16:37 +01:00
|
|
|
supports_direction = attr.ib(type=bool, default=False)
|
2021-06-18 16:16:20 +02:00
|
|
|
supported_speed_levels = attr.ib(type=int, default=0)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
class FanSpeed(APIIntEnum):
|
2019-04-07 19:03:26 +02:00
|
|
|
LOW = 0
|
|
|
|
MEDIUM = 1
|
|
|
|
HIGH = 2
|
|
|
|
|
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
class FanDirection(APIIntEnum):
|
2020-12-14 04:16:37 +01:00
|
|
|
FORWARD = 0
|
|
|
|
REVERSE = 1
|
|
|
|
|
|
|
|
|
2019-04-07 19:03:26 +02:00
|
|
|
@attr.s
|
|
|
|
class FanState(EntityState):
|
2019-10-24 13:59:06 +02:00
|
|
|
state = attr.ib(type=bool, default=False)
|
|
|
|
oscillating = attr.ib(type=bool, default=False)
|
2021-06-18 17:57:02 +02:00
|
|
|
speed = attr.ib(
|
|
|
|
type=Optional[FanSpeed],
|
|
|
|
converter=FanSpeed.convert, # type: ignore
|
|
|
|
default=FanSpeed.LOW,
|
|
|
|
)
|
2021-03-14 01:21:09 +01:00
|
|
|
speed_level = attr.ib(type=int, default=0)
|
2021-06-18 17:57:02 +02:00
|
|
|
direction = attr.ib(
|
|
|
|
type=FanDirection,
|
|
|
|
converter=FanDirection.convert, # type: ignore
|
|
|
|
default=FanDirection.FORWARD,
|
|
|
|
)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
# ==================== LIGHT ====================
|
|
|
|
@attr.s
|
|
|
|
class LightInfo(EntityInfo):
|
2019-10-24 13:59:06 +02:00
|
|
|
supports_brightness = attr.ib(type=bool, default=False)
|
|
|
|
supports_rgb = attr.ib(type=bool, default=False)
|
|
|
|
supports_white_value = attr.ib(type=bool, default=False)
|
|
|
|
supports_color_temperature = attr.ib(type=bool, default=False)
|
|
|
|
min_mireds = attr.ib(type=float, default=0.0)
|
|
|
|
max_mireds = attr.ib(type=float, default=0.0)
|
|
|
|
effects = attr.ib(type=List[str], converter=list, factory=list)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class LightState(EntityState):
|
2019-10-24 13:59:06 +02:00
|
|
|
state = attr.ib(type=bool, default=False)
|
|
|
|
brightness = attr.ib(type=float, default=0.0)
|
|
|
|
red = attr.ib(type=float, default=0.0)
|
|
|
|
green = attr.ib(type=float, default=0.0)
|
|
|
|
blue = attr.ib(type=float, default=0.0)
|
|
|
|
white = attr.ib(type=float, default=0.0)
|
|
|
|
color_temperature = attr.ib(type=float, default=0.0)
|
2021-06-18 17:57:02 +02:00
|
|
|
effect = attr.ib(type=str, default="")
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
# ==================== SENSOR ====================
|
2021-06-18 16:57:07 +02:00
|
|
|
class SensorStateClass(APIIntEnum):
|
2021-05-25 23:39:01 +02:00
|
|
|
NONE = 0
|
|
|
|
MEASUREMENT = 1
|
|
|
|
|
2021-06-18 17:57:02 +02:00
|
|
|
|
2019-04-07 19:03:26 +02:00
|
|
|
@attr.s
|
|
|
|
class SensorInfo(EntityInfo):
|
2021-06-18 17:57:02 +02:00
|
|
|
icon = attr.ib(type=str, default="")
|
|
|
|
device_class = attr.ib(type=str, default="")
|
|
|
|
unit_of_measurement = attr.ib(type=str, default="")
|
2019-10-24 13:59:06 +02:00
|
|
|
accuracy_decimals = attr.ib(type=int, default=0)
|
|
|
|
force_update = attr.ib(type=bool, default=False)
|
2021-06-18 17:57:02 +02:00
|
|
|
state_class = attr.ib(
|
|
|
|
type=SensorStateClass,
|
|
|
|
converter=SensorStateClass.convert, # type: ignore
|
|
|
|
default=SensorStateClass.NONE,
|
|
|
|
)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class SensorState(EntityState):
|
2019-10-24 13:59:06 +02:00
|
|
|
state = attr.ib(type=float, default=0.0)
|
2019-11-12 14:55:35 +01:00
|
|
|
missing_state = attr.ib(type=bool, default=False)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
# ==================== SWITCH ====================
|
|
|
|
@attr.s
|
|
|
|
class SwitchInfo(EntityInfo):
|
2021-06-18 17:57:02 +02:00
|
|
|
icon = attr.ib(type=str, default="")
|
2019-04-19 16:22:50 +02:00
|
|
|
assumed_state = attr.ib(type=bool, default=False)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class SwitchState(EntityState):
|
2019-10-24 13:59:06 +02:00
|
|
|
state = attr.ib(type=bool, default=False)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
# ==================== TEXT SENSOR ====================
|
|
|
|
@attr.s
|
|
|
|
class TextSensorInfo(EntityInfo):
|
2021-06-18 17:57:02 +02:00
|
|
|
icon = attr.ib(type=str, default="")
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class TextSensorState(EntityState):
|
2021-06-18 17:57:02 +02:00
|
|
|
state = attr.ib(type=str, default="")
|
2019-11-12 14:55:35 +01:00
|
|
|
missing_state = attr.ib(type=bool, default=False)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
# ==================== CAMERA ====================
|
|
|
|
@attr.s
|
|
|
|
class CameraInfo(EntityInfo):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class CameraState(EntityState):
|
2019-10-24 13:59:06 +02:00
|
|
|
image = attr.ib(type=bytes, factory=bytes)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
# ==================== CLIMATE ====================
|
2021-06-18 16:57:07 +02:00
|
|
|
class ClimateMode(APIIntEnum):
|
2019-04-07 19:03:26 +02:00
|
|
|
OFF = 0
|
2021-06-23 23:40:41 +02:00
|
|
|
HEAT_COOL = 1
|
2019-04-07 19:03:26 +02:00
|
|
|
COOL = 2
|
|
|
|
HEAT = 3
|
2019-11-16 16:34:14 +01:00
|
|
|
FAN_ONLY = 4
|
|
|
|
DRY = 5
|
2021-06-23 23:40:41 +02:00
|
|
|
AUTO = 6
|
2019-11-16 16:34:14 +01:00
|
|
|
|
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
class ClimateFanMode(APIIntEnum):
|
2019-11-16 16:34:14 +01:00
|
|
|
ON = 0
|
|
|
|
OFF = 1
|
|
|
|
AUTO = 2
|
|
|
|
LOW = 3
|
|
|
|
MEDIUM = 4
|
|
|
|
HIGH = 5
|
|
|
|
MIDDLE = 6
|
|
|
|
FOCUS = 7
|
|
|
|
DIFFUSE = 8
|
|
|
|
|
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
class ClimateSwingMode(APIIntEnum):
|
2019-11-16 16:34:14 +01:00
|
|
|
OFF = 0
|
|
|
|
BOTH = 1
|
|
|
|
VERTICAL = 2
|
|
|
|
HORIZONTAL = 3
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
class ClimateAction(APIIntEnum):
|
2019-10-17 21:25:54 +02:00
|
|
|
OFF = 0
|
|
|
|
COOLING = 2
|
|
|
|
HEATING = 3
|
2019-11-23 20:36:12 +01:00
|
|
|
IDLE = 4
|
|
|
|
DRYING = 5
|
|
|
|
FAN = 6
|
2019-10-17 21:25:54 +02:00
|
|
|
|
|
|
|
|
2021-06-23 23:40:41 +02:00
|
|
|
class ClimatePreset(APIIntEnum):
|
|
|
|
NONE = 0
|
|
|
|
HOME = 1
|
|
|
|
AWAY = 2
|
|
|
|
BOOST = 3
|
|
|
|
COMFORT = 4
|
|
|
|
ECO = 5
|
|
|
|
SLEEP = 6
|
|
|
|
ACTIVITY = 7
|
|
|
|
|
|
|
|
|
2019-04-07 19:03:26 +02:00
|
|
|
@attr.s
|
|
|
|
class ClimateInfo(EntityInfo):
|
2019-10-24 13:59:06 +02:00
|
|
|
supports_current_temperature = attr.ib(type=bool, default=False)
|
|
|
|
supports_two_point_target_temperature = attr.ib(type=bool, default=False)
|
2021-06-18 17:57:02 +02:00
|
|
|
supported_modes = attr.ib(
|
|
|
|
type=List[ClimateMode],
|
|
|
|
converter=ClimateMode.convert_list, # type: ignore
|
|
|
|
factory=list,
|
|
|
|
)
|
2019-10-24 13:59:06 +02:00
|
|
|
visual_min_temperature = attr.ib(type=float, default=0.0)
|
|
|
|
visual_max_temperature = attr.ib(type=float, default=0.0)
|
|
|
|
visual_temperature_step = attr.ib(type=float, default=0.0)
|
2021-06-23 23:40:41 +02:00
|
|
|
legacy_supports_away = attr.ib(type=bool, default=False)
|
2019-10-24 13:59:06 +02:00
|
|
|
supports_action = attr.ib(type=bool, default=False)
|
2019-11-16 16:34:14 +01:00
|
|
|
supported_fan_modes = attr.ib(
|
2021-06-18 17:57:02 +02:00
|
|
|
type=List[ClimateFanMode],
|
|
|
|
converter=ClimateFanMode.convert_list, # type: ignore
|
|
|
|
factory=list,
|
2019-11-16 16:34:14 +01:00
|
|
|
)
|
|
|
|
supported_swing_modes = attr.ib(
|
2021-06-18 17:57:02 +02:00
|
|
|
type=List[ClimateSwingMode],
|
|
|
|
converter=ClimateSwingMode.convert_list, # type: ignore
|
|
|
|
factory=list,
|
2019-11-16 16:34:14 +01:00
|
|
|
)
|
2021-06-23 23:40:41 +02:00
|
|
|
supported_custom_fan_modes = attr.ib(type=List[str], converter=list, factory=list)
|
|
|
|
supported_presets = attr.ib(
|
|
|
|
type=List[ClimatePreset], converter=ClimatePreset.convert_list, factory=list # type: ignore
|
|
|
|
)
|
|
|
|
supported_custom_presets = attr.ib(type=List[str], converter=list, factory=list)
|
|
|
|
|
|
|
|
def supported_presets_compat(self, api_version: APIVersion) -> List[ClimatePreset]:
|
|
|
|
if api_version < APIVersion(1, 5):
|
|
|
|
return (
|
|
|
|
[ClimatePreset.HOME, ClimatePreset.AWAY]
|
|
|
|
if self.legacy_supports_away
|
|
|
|
else []
|
|
|
|
)
|
|
|
|
return self.supported_presets
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class ClimateState(EntityState):
|
2021-06-18 17:57:02 +02:00
|
|
|
mode = attr.ib(
|
|
|
|
type=ClimateMode,
|
|
|
|
converter=ClimateMode.convert, # type: ignore
|
|
|
|
default=ClimateMode.OFF,
|
|
|
|
)
|
|
|
|
action = attr.ib(
|
|
|
|
type=ClimateAction,
|
|
|
|
converter=ClimateAction.convert, # type: ignore
|
|
|
|
default=ClimateAction.OFF,
|
|
|
|
)
|
2019-10-24 13:59:06 +02:00
|
|
|
current_temperature = attr.ib(type=float, default=0.0)
|
|
|
|
target_temperature = attr.ib(type=float, default=0.0)
|
|
|
|
target_temperature_low = attr.ib(type=float, default=0.0)
|
|
|
|
target_temperature_high = attr.ib(type=float, default=0.0)
|
2021-06-23 23:40:41 +02:00
|
|
|
legacy_away = attr.ib(type=bool, default=False)
|
2019-11-16 16:34:14 +01:00
|
|
|
fan_mode = attr.ib(
|
2021-06-18 17:57:02 +02:00
|
|
|
type=Optional[ClimateFanMode],
|
|
|
|
converter=ClimateFanMode.convert, # type: ignore
|
|
|
|
default=ClimateFanMode.ON,
|
2019-11-16 16:34:14 +01:00
|
|
|
)
|
|
|
|
swing_mode = attr.ib(
|
2021-06-18 17:57:02 +02:00
|
|
|
type=Optional[ClimateSwingMode],
|
|
|
|
converter=ClimateSwingMode.convert, # type: ignore
|
|
|
|
default=ClimateSwingMode.OFF,
|
2019-11-16 16:34:14 +01:00
|
|
|
)
|
2021-06-23 23:40:41 +02:00
|
|
|
custom_fan_mode = attr.ib(type=str, default="")
|
|
|
|
preset = attr.ib(
|
|
|
|
type=Optional[ClimatePreset],
|
|
|
|
converter=ClimatePreset.convert, # type: ignore
|
|
|
|
default=ClimatePreset.HOME,
|
|
|
|
)
|
|
|
|
custom_preset = attr.ib(type=str, default="")
|
|
|
|
|
|
|
|
def preset_compat(self, api_version: APIVersion) -> Optional[ClimatePreset]:
|
|
|
|
if api_version < APIVersion(1, 5):
|
|
|
|
return ClimatePreset.AWAY if self.legacy_away else ClimatePreset.HOME
|
|
|
|
return self.preset
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
2021-06-29 12:42:38 +02:00
|
|
|
# ==================== NUMBER ====================
|
|
|
|
@attr.s
|
|
|
|
class NumberInfo(EntityInfo):
|
|
|
|
icon = attr.ib(type=str, default="")
|
|
|
|
min_value = attr.ib(type=float, default=0.0)
|
|
|
|
max_value = attr.ib(type=float, default=0.0)
|
|
|
|
step = attr.ib(type=float, default=0.0)
|
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class NumberState(EntityState):
|
|
|
|
state = attr.ib(type=float, default=0.0)
|
|
|
|
missing_state = attr.ib(type=bool, default=False)
|
|
|
|
|
|
|
|
|
2019-04-07 19:03:26 +02:00
|
|
|
COMPONENT_TYPE_TO_INFO = {
|
2021-06-18 17:57:02 +02:00
|
|
|
"binary_sensor": BinarySensorInfo,
|
|
|
|
"cover": CoverInfo,
|
|
|
|
"fan": FanInfo,
|
|
|
|
"light": LightInfo,
|
|
|
|
"sensor": SensorInfo,
|
|
|
|
"switch": SwitchInfo,
|
|
|
|
"text_sensor": TextSensorInfo,
|
|
|
|
"camera": CameraInfo,
|
|
|
|
"climate": ClimateInfo,
|
2021-06-29 12:42:38 +02:00
|
|
|
"number": NumberInfo,
|
2019-04-07 19:03:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== USER-DEFINED SERVICES ====================
|
2021-06-18 17:57:02 +02:00
|
|
|
def _convert_homeassistant_service_map(
|
|
|
|
value: Iterable["HomeassistantServiceMap"],
|
|
|
|
) -> Dict[str, str]:
|
2019-06-18 11:10:32 +02:00
|
|
|
return {v.key: v.value for v in value}
|
|
|
|
|
|
|
|
|
2019-04-07 19:03:26 +02:00
|
|
|
@attr.s
|
2019-06-18 11:10:32 +02:00
|
|
|
class HomeassistantServiceCall:
|
2021-06-18 17:57:02 +02:00
|
|
|
service = attr.ib(type=str, default="")
|
2019-10-24 13:59:06 +02:00
|
|
|
is_event = attr.ib(type=bool, default=False)
|
2021-06-18 17:57:02 +02:00
|
|
|
data = attr.ib(
|
|
|
|
type=Dict[str, str], converter=_convert_homeassistant_service_map, factory=dict
|
|
|
|
)
|
|
|
|
data_template = attr.ib(
|
|
|
|
type=Dict[str, str], converter=_convert_homeassistant_service_map, factory=dict
|
|
|
|
)
|
|
|
|
variables = attr.ib(
|
|
|
|
type=Dict[str, str], converter=_convert_homeassistant_service_map, factory=dict
|
|
|
|
)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
2021-06-18 16:57:07 +02:00
|
|
|
class UserServiceArgType(APIIntEnum):
|
2019-04-07 19:03:26 +02:00
|
|
|
BOOL = 0
|
|
|
|
INT = 1
|
|
|
|
FLOAT = 2
|
|
|
|
STRING = 3
|
2019-06-17 23:40:23 +02:00
|
|
|
BOOL_ARRAY = 4
|
|
|
|
INT_ARRAY = 5
|
|
|
|
FLOAT_ARRAY = 6
|
|
|
|
STRING_ARRAY = 7
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
2021-06-18 17:57:02 +02:00
|
|
|
_K = TypeVar("_K")
|
|
|
|
|
|
|
|
|
|
|
|
def _attr_obj_from_dict(cls: Type[_K], **kwargs: Any) -> _K:
|
|
|
|
return cls(**{key: kwargs[key] for key in attr.fields_dict(cls)}) # type: ignore
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class UserServiceArg:
|
2021-06-18 17:57:02 +02:00
|
|
|
name = attr.ib(type=str, default="")
|
|
|
|
type_ = attr.ib(
|
|
|
|
type=UserServiceArgType,
|
|
|
|
converter=UserServiceArgType.convert, # type: ignore
|
|
|
|
default=UserServiceArgType.BOOL,
|
|
|
|
)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
class UserService:
|
2021-06-18 17:57:02 +02:00
|
|
|
name = attr.ib(type=str, default="")
|
2019-10-24 13:59:06 +02:00
|
|
|
key = attr.ib(type=int, default=0)
|
|
|
|
args = attr.ib(type=List[UserServiceArg], converter=list, factory=list)
|
2019-04-07 19:03:26 +02:00
|
|
|
|
2021-06-18 17:57:02 +02:00
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, dict_: Dict[str, Any]) -> "UserService":
|
2019-04-07 19:03:26 +02:00
|
|
|
args = []
|
2021-06-18 17:57:02 +02:00
|
|
|
for arg in dict_.get("args", []):
|
2019-04-07 19:03:26 +02:00
|
|
|
args.append(_attr_obj_from_dict(UserServiceArg, **arg))
|
2021-06-18 17:57:02 +02:00
|
|
|
return cls(
|
|
|
|
name=dict_.get("name", ""),
|
|
|
|
key=dict_.get("key", 0),
|
|
|
|
args=args, # type: ignore
|
2019-04-07 19:03:26 +02:00
|
|
|
)
|
|
|
|
|
2021-06-18 17:57:02 +02:00
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
2019-04-07 19:03:26 +02:00
|
|
|
return {
|
2021-06-18 17:57:02 +02:00
|
|
|
"name": self.name,
|
|
|
|
"key": self.key,
|
|
|
|
"args": [attr.asdict(arg) for arg in self.args],
|
2019-04-07 19:03:26 +02:00
|
|
|
}
|