1
mirror of https://github.com/home-assistant/core synced 2024-08-31 05:57:13 +02:00
ha-core/homeassistant/components/homekit/type_security_systems.py
J. Nick Koston b775a0d796
Run homekit service calls in async since the server is now async (#45859)
* Simplify homekit runs and service calls

Now that the homekit server is async, call_service
and run are running in the Home Assistant event loop

* remove comment

* remove another comment
2021-02-19 21:34:52 -10:00

176 lines
5.9 KiB
Python

"""Class to hold all alarm control panel accessories."""
import logging
from pyhap.const import CATEGORY_ALARM_SYSTEM
from pyhap.loader import get_loader
from homeassistant.components.alarm_control_panel import DOMAIN
from homeassistant.components.alarm_control_panel.const import (
SUPPORT_ALARM_ARM_AWAY,
SUPPORT_ALARM_ARM_HOME,
SUPPORT_ALARM_ARM_NIGHT,
SUPPORT_ALARM_TRIGGER,
)
from homeassistant.const import (
ATTR_CODE,
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
SERVICE_ALARM_ARM_AWAY,
SERVICE_ALARM_ARM_HOME,
SERVICE_ALARM_ARM_NIGHT,
SERVICE_ALARM_DISARM,
STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED,
)
from homeassistant.core import callback
from .accessories import TYPES, HomeAccessory
from .const import (
CHAR_CURRENT_SECURITY_STATE,
CHAR_TARGET_SECURITY_STATE,
SERV_SECURITY_SYSTEM,
)
_LOGGER = logging.getLogger(__name__)
HASS_TO_HOMEKIT = {
STATE_ALARM_ARMED_HOME: 0,
STATE_ALARM_ARMED_AWAY: 1,
STATE_ALARM_ARMED_NIGHT: 2,
STATE_ALARM_DISARMED: 3,
STATE_ALARM_TRIGGERED: 4,
}
HASS_TO_HOMEKIT_SERVICES = {
SERVICE_ALARM_ARM_HOME: 0,
SERVICE_ALARM_ARM_AWAY: 1,
SERVICE_ALARM_ARM_NIGHT: 2,
SERVICE_ALARM_DISARM: 3,
}
HOMEKIT_TO_HASS = {c: s for s, c in HASS_TO_HOMEKIT.items()}
STATE_TO_SERVICE = {
STATE_ALARM_ARMED_AWAY: SERVICE_ALARM_ARM_AWAY,
STATE_ALARM_ARMED_HOME: SERVICE_ALARM_ARM_HOME,
STATE_ALARM_ARMED_NIGHT: SERVICE_ALARM_ARM_NIGHT,
STATE_ALARM_DISARMED: SERVICE_ALARM_DISARM,
}
@TYPES.register("SecuritySystem")
class SecuritySystem(HomeAccessory):
"""Generate an SecuritySystem accessory for an alarm control panel."""
def __init__(self, *args):
"""Initialize a SecuritySystem accessory object."""
super().__init__(*args, category=CATEGORY_ALARM_SYSTEM)
state = self.hass.states.get(self.entity_id)
self._alarm_code = self.config.get(ATTR_CODE)
supported_states = state.attributes.get(
ATTR_SUPPORTED_FEATURES,
(
SUPPORT_ALARM_ARM_HOME
| SUPPORT_ALARM_ARM_AWAY
| SUPPORT_ALARM_ARM_NIGHT
| SUPPORT_ALARM_TRIGGER
),
)
loader = get_loader()
default_current_states = loader.get_char(
"SecuritySystemCurrentState"
).properties.get("ValidValues")
default_target_services = loader.get_char(
"SecuritySystemTargetState"
).properties.get("ValidValues")
current_supported_states = [
HASS_TO_HOMEKIT[STATE_ALARM_DISARMED],
HASS_TO_HOMEKIT[STATE_ALARM_TRIGGERED],
]
target_supported_services = [HASS_TO_HOMEKIT_SERVICES[SERVICE_ALARM_DISARM]]
if supported_states & SUPPORT_ALARM_ARM_HOME:
current_supported_states.append(HASS_TO_HOMEKIT[STATE_ALARM_ARMED_HOME])
target_supported_services.append(
HASS_TO_HOMEKIT_SERVICES[SERVICE_ALARM_ARM_HOME]
)
if supported_states & SUPPORT_ALARM_ARM_AWAY:
current_supported_states.append(HASS_TO_HOMEKIT[STATE_ALARM_ARMED_AWAY])
target_supported_services.append(
HASS_TO_HOMEKIT_SERVICES[SERVICE_ALARM_ARM_AWAY]
)
if supported_states & SUPPORT_ALARM_ARM_NIGHT:
current_supported_states.append(HASS_TO_HOMEKIT[STATE_ALARM_ARMED_NIGHT])
target_supported_services.append(
HASS_TO_HOMEKIT_SERVICES[SERVICE_ALARM_ARM_NIGHT]
)
new_current_states = {
key: val
for key, val in default_current_states.items()
if val in current_supported_states
}
new_target_services = {
key: val
for key, val in default_target_services.items()
if val in target_supported_services
}
serv_alarm = self.add_preload_service(SERV_SECURITY_SYSTEM)
self.char_current_state = serv_alarm.configure_char(
CHAR_CURRENT_SECURITY_STATE,
value=HASS_TO_HOMEKIT[STATE_ALARM_DISARMED],
valid_values=new_current_states,
)
self.char_target_state = serv_alarm.configure_char(
CHAR_TARGET_SECURITY_STATE,
value=HASS_TO_HOMEKIT_SERVICES[SERVICE_ALARM_DISARM],
valid_values=new_target_services,
setter_callback=self.set_security_state,
)
# Set the state so it is in sync on initial
# GET to avoid an event storm after homekit startup
self.async_update_state(state)
def set_security_state(self, value):
"""Move security state to value if call came from HomeKit."""
_LOGGER.debug("%s: Set security state to %d", self.entity_id, value)
hass_value = HOMEKIT_TO_HASS[value]
service = STATE_TO_SERVICE[hass_value]
params = {ATTR_ENTITY_ID: self.entity_id}
if self._alarm_code:
params[ATTR_CODE] = self._alarm_code
self.async_call_service(DOMAIN, service, params)
@callback
def async_update_state(self, new_state):
"""Update security state after state changed."""
hass_state = new_state.state
if hass_state in HASS_TO_HOMEKIT:
current_security_state = HASS_TO_HOMEKIT[hass_state]
if self.char_current_state.value != current_security_state:
self.char_current_state.set_value(current_security_state)
_LOGGER.debug(
"%s: Updated current state to %s (%d)",
self.entity_id,
hass_state,
current_security_state,
)
# SecuritySystemTargetState does not support triggered
if (
hass_state != STATE_ALARM_TRIGGERED
and self.char_target_state.value != current_security_state
):
self.char_target_state.set_value(current_security_state)