first commit
This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
"""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'))
|
||||
Reference in New Issue
Block a user