312 lines
11 KiB
Python
312 lines
11 KiB
Python
"""Implement the Yandex Smart Home event properties."""
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC
|
|
import logging
|
|
from typing import Any
|
|
|
|
from homeassistant.components import binary_sensor, sensor
|
|
from homeassistant.const import ATTR_DEVICE_CLASS, STATE_ON, STATE_OPEN, STATE_UNAVAILABLE, STATE_UNKNOWN
|
|
from homeassistant.core import HomeAssistant, State
|
|
|
|
from . import const
|
|
from .const import ERR_NOT_SUPPORTED_IN_CURRENT_MODE, STATE_EMPTY, STATE_NONE, STATE_NONE_UI
|
|
from .error import SmartHomeError
|
|
from .helpers import Config
|
|
from .prop import PREFIX_PROPERTIES, AbstractProperty, register_property
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
PROPERTY_EVENT = PREFIX_PROPERTIES + 'event'
|
|
PROPERTY_EVENT_VALUES = {
|
|
const.EVENT_INSTANCE_VIBRATION: [
|
|
const.EVENT_VIBRATION_VIBRATION,
|
|
const.EVENT_VIBRATION_TILT,
|
|
const.EVENT_VIBRATION_FALL,
|
|
],
|
|
const.EVENT_INSTANCE_OPEN: [
|
|
const.EVENT_OPEN_OPENED,
|
|
const.EVENT_OPEN_CLOSED,
|
|
],
|
|
const.EVENT_INSTANCE_BUTTON: [
|
|
const.EVENT_BUTTON_CLICK,
|
|
const.EVENT_BUTTON_DOUBLE_CLICK,
|
|
const.EVENT_BUTTON_LONG_PRESS,
|
|
],
|
|
const.EVENT_INSTANCE_MOTION: [
|
|
const.EVENT_MOTION_DETECTED,
|
|
const.EVENT_MOTION_NOT_DETECTED,
|
|
],
|
|
const.EVENT_INSTANCE_SMOKE: [
|
|
const.EVENT_SMOKE_DETECTED,
|
|
const.EVENT_SMOKE_NOT_DETECTED,
|
|
const.EVENT_SMOKE_HIGH,
|
|
],
|
|
const.EVENT_INSTANCE_GAS: [
|
|
const.EVENT_GAS_DETECTED,
|
|
const.EVENT_GAS_NOT_DETECTED,
|
|
const.EVENT_GAS_HIGH,
|
|
],
|
|
const.EVENT_INSTANCE_BATTERY_LEVEL: [
|
|
const.EVENT_BATTERY_LEVEL_LOW,
|
|
const.EVENT_BATTERY_LEVEL_NORMAL,
|
|
],
|
|
const.EVENT_INSTANCE_WATER_LEVEL: [
|
|
const.EVENT_WATER_LEVEL_LOW,
|
|
const.EVENT_WATER_LEVEL_NORMAL,
|
|
],
|
|
const.EVENT_INSTANCE_WATER_LEAK: [
|
|
const.EVENT_WATER_LEAK_LEAK,
|
|
const.EVENT_WATER_LEAK_DRY,
|
|
],
|
|
}
|
|
|
|
|
|
class EventProperty(AbstractProperty, ABC):
|
|
type = PROPERTY_EVENT
|
|
report_immediately = True
|
|
|
|
def __init__(self, hass: HomeAssistant, config: Config, state: State):
|
|
super().__init__(hass, config, state)
|
|
self.values = PROPERTY_EVENT_VALUES.get(self.instance)
|
|
|
|
def parameters(self) -> dict[str, Any]:
|
|
return {
|
|
'instance': self.instance,
|
|
'events': [
|
|
{'value': v}
|
|
for v in self.values
|
|
]
|
|
} if self.values else {}
|
|
|
|
@staticmethod
|
|
def bool_value(value) -> bool:
|
|
"""Return the bool value according to any type of value."""
|
|
return value in [1, STATE_ON, STATE_OPEN, 'high', True]
|
|
|
|
def event_value(self, value) -> str | None:
|
|
if str(value).lower() in (STATE_UNAVAILABLE, STATE_UNKNOWN, STATE_NONE, STATE_NONE_UI, STATE_EMPTY):
|
|
return None
|
|
|
|
if self.instance == const.EVENT_INSTANCE_OPEN:
|
|
return const.EVENT_OPEN_OPENED if self.bool_value(value) else const.EVENT_OPEN_CLOSED
|
|
|
|
elif self.instance == const.EVENT_INSTANCE_MOTION:
|
|
return const.EVENT_MOTION_DETECTED if self.bool_value(value) else const.EVENT_MOTION_NOT_DETECTED
|
|
|
|
elif self.instance == const.EVENT_INSTANCE_SMOKE:
|
|
if value == 'high':
|
|
return const.EVENT_SMOKE_HIGH
|
|
|
|
return const.EVENT_SMOKE_DETECTED if self.bool_value(value) else const.EVENT_SMOKE_NOT_DETECTED
|
|
|
|
elif self.instance == const.EVENT_INSTANCE_GAS:
|
|
if value == 'high':
|
|
return const.EVENT_GAS_HIGH
|
|
|
|
return const.EVENT_GAS_DETECTED if self.bool_value(value) else const.EVENT_GAS_NOT_DETECTED
|
|
|
|
elif self.instance in const.EVENT_INSTANCE_BATTERY_LEVEL:
|
|
return const.EVENT_BATTERY_LEVEL_LOW if self.bool_value(value) else const.EVENT_BATTERY_LEVEL_NORMAL
|
|
|
|
elif self.instance in const.EVENT_INSTANCE_WATER_LEVEL:
|
|
return const.EVENT_WATER_LEVEL_LOW if self.bool_value(value) else const.EVENT_WATER_LEVEL_NORMAL
|
|
|
|
elif self.instance == const.EVENT_INSTANCE_WATER_LEAK:
|
|
return const.EVENT_WATER_LEAK_LEAK if self.bool_value(value) else const.EVENT_WATER_LEAK_DRY
|
|
|
|
elif self.instance in const.EVENT_INSTANCE_BUTTON:
|
|
if value in ['single', 'click']:
|
|
return const.EVENT_BUTTON_CLICK
|
|
elif value in ['double', 'double_click']:
|
|
return const.EVENT_BUTTON_DOUBLE_CLICK
|
|
elif value in ['long', 'long_click', 'long_click_press', 'hold']:
|
|
return const.EVENT_BUTTON_LONG_PRESS
|
|
|
|
elif self.instance in const.EVENT_INSTANCE_VIBRATION:
|
|
if value in ['vibrate', 'vibration', 'actively', 'move', 'tap_twice', 'shake_air', 'swing']:
|
|
return const.EVENT_VIBRATION_VIBRATION
|
|
elif self.bool_value(value):
|
|
return const.EVENT_VIBRATION_VIBRATION
|
|
elif value in ['tilt', 'flip90', 'flip180', 'rotate']:
|
|
return const.EVENT_VIBRATION_TILT
|
|
elif value in ['free_fall', 'drop']:
|
|
return const.EVENT_VIBRATION_FALL
|
|
|
|
def get_value(self) -> str | None:
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
return self.event_value(self.state.state)
|
|
|
|
raise SmartHomeError(
|
|
ERR_NOT_SUPPORTED_IN_CURRENT_MODE,
|
|
f'Failed to get value for instance {self.instance} of {self.state.entity_id}'
|
|
)
|
|
|
|
|
|
@register_property
|
|
class ContactProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_OPEN
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
return self.state.attributes.get(ATTR_DEVICE_CLASS) in (
|
|
binary_sensor.DEVICE_CLASS_DOOR,
|
|
binary_sensor.DEVICE_CLASS_GARAGE_DOOR,
|
|
binary_sensor.DEVICE_CLASS_WINDOW,
|
|
binary_sensor.DEVICE_CLASS_OPENING
|
|
)
|
|
|
|
return False
|
|
|
|
|
|
@register_property
|
|
class MotionProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_MOTION
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
return self.state.attributes.get(ATTR_DEVICE_CLASS) in (
|
|
binary_sensor.DEVICE_CLASS_MOTION,
|
|
binary_sensor.DEVICE_CLASS_OCCUPANCY,
|
|
binary_sensor.DEVICE_CLASS_PRESENCE
|
|
)
|
|
|
|
return False
|
|
|
|
|
|
@register_property
|
|
class GasProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_GAS
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
return self.state.attributes.get(ATTR_DEVICE_CLASS) == binary_sensor.DEVICE_CLASS_GAS
|
|
|
|
return False
|
|
|
|
|
|
@register_property
|
|
class SmokeProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_SMOKE
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
return self.state.attributes.get(ATTR_DEVICE_CLASS) == binary_sensor.DEVICE_CLASS_SMOKE
|
|
|
|
return False
|
|
|
|
|
|
@register_property
|
|
class BatteryLevelLowProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_BATTERY_LEVEL
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
return self.state.attributes.get(ATTR_DEVICE_CLASS) == binary_sensor.DEVICE_CLASS_BATTERY
|
|
|
|
return False
|
|
|
|
|
|
@register_property
|
|
class WaterLevelLowProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_WATER_LEVEL
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
return self.state.attributes.get(ATTR_DEVICE_CLASS) == 'water_level'
|
|
|
|
return False
|
|
|
|
|
|
@register_property
|
|
class WaterLeakProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_WATER_LEAK
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
return self.state.attributes.get(ATTR_DEVICE_CLASS) == binary_sensor.DEVICE_CLASS_MOISTURE
|
|
|
|
return False
|
|
|
|
|
|
@register_property
|
|
class ButtonProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_BUTTON
|
|
retrievable = False
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.attributes.get(ATTR_DEVICE_CLASS) == const.DEVICE_CLASS_BUTTON:
|
|
return True
|
|
|
|
if self.entity_config.get(const.CONF_DEVICE_CLASS) == const.DEVICE_CLASS_BUTTON:
|
|
return True
|
|
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
return self.state.attributes.get('last_action') in [
|
|
'single', 'click', 'double', 'double_click',
|
|
'long', 'long_click', 'long_click_press',
|
|
'long_click_release', 'hold', 'release',
|
|
'triple', 'quadruple', 'many'
|
|
]
|
|
|
|
if self.state.domain == sensor.DOMAIN:
|
|
return self.state.attributes.get('action') in [
|
|
'single', 'click', 'double', 'double_click',
|
|
'long', 'long_click', 'long_click_press',
|
|
'long_click_release', 'hold', 'release',
|
|
'triple', 'quadruple', 'many'
|
|
]
|
|
|
|
return False
|
|
|
|
def get_value(self) -> str | None:
|
|
for value in [self.state.attributes.get('last_action'), self.state.attributes.get('action'), self.state.state]:
|
|
event_value = self.event_value(value)
|
|
|
|
if event_value:
|
|
return event_value
|
|
|
|
|
|
@register_property
|
|
class VibrationBinarySensorProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_VIBRATION
|
|
retrievable = False
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain == binary_sensor.DOMAIN:
|
|
if self.state.attributes.get(ATTR_DEVICE_CLASS) == binary_sensor.DEVICE_CLASS_VIBRATION:
|
|
return True
|
|
|
|
return self.state.attributes.get('last_action') in [
|
|
'vibrate', 'tilt', 'free_fall', 'actively',
|
|
'move', 'tap_twice', 'shake_air', 'swing',
|
|
'flip90', 'flip180', 'rotate', 'drop'
|
|
]
|
|
|
|
return False
|
|
|
|
def get_value(self) -> str | None:
|
|
if self.state.attributes.get(ATTR_DEVICE_CLASS) == binary_sensor.DEVICE_CLASS_VIBRATION:
|
|
return self.event_value(self.state.state)
|
|
|
|
return self.event_value(self.state.attributes.get('last_action'))
|
|
|
|
|
|
@register_property
|
|
class VibrationSensorProperty(EventProperty):
|
|
instance = const.EVENT_INSTANCE_VIBRATION
|
|
retrievable = False
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain == sensor.DOMAIN:
|
|
return self.state.attributes.get('action') in [
|
|
'vibrate', 'tilt', 'free_fall', 'actively',
|
|
'move', 'tap_twice', 'shake_air', 'swing',
|
|
'flip90', 'flip180', 'rotate', 'drop'
|
|
]
|
|
|
|
return False
|
|
|
|
def get_value(self) -> str | None:
|
|
return self.event_value(self.state.attributes.get('action'))
|