Remove unsupported states from security systems in HomeKit (#40060)

This commit is contained in:
Niccolo Zapponi 2020-09-16 16:00:32 +01:00 committed by GitHub
parent d25192d8c5
commit 2b7e735e3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 195 additions and 2 deletions

View file

@ -2,8 +2,15 @@
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,
@ -36,6 +43,13 @@ HASS_TO_HOMEKIT = {
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 = {
@ -56,13 +70,72 @@ class SecuritySystem(HomeAccessory):
state = self.hass.states.get(self.entity_id)
self._alarm_code = self.config.get(ATTR_CODE)
supported_states = state.attributes.get(
"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=3
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=3, setter_callback=self.set_security_state
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)

View file

@ -1,7 +1,14 @@
"""Test different accessory types: Security Systems."""
from pyhap.loader import get_loader
import pytest
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.components.homekit.const import ATTR_VALUE
from homeassistant.components.homekit.type_security_systems import SecuritySystem
from homeassistant.const import (
@ -129,3 +136,116 @@ async def test_no_alarm_code(hass, hk_driver, config, events):
assert acc.char_target_state.value == 0
assert len(events) == 1
assert events[-1].data[ATTR_VALUE] is None
async def test_supported_states(hass, hk_driver, events):
"""Test different supported states."""
code = "1234"
config = {ATTR_CODE: code}
entity_id = "alarm_control_panel.test"
loader = get_loader()
default_current_states = loader.get_char(
"SecuritySystemCurrentState"
).properties.get("ValidValues")
default_target_services = loader.get_char(
"SecuritySystemTargetState"
).properties.get("ValidValues")
# Set up a number of test configuration
test_configs = [
{
"features": SUPPORT_ALARM_ARM_HOME,
"current_values": [
default_current_states["Disarmed"],
default_current_states["AlarmTriggered"],
default_current_states["StayArm"],
],
"target_values": [
default_target_services["Disarm"],
default_target_services["StayArm"],
],
},
{
"features": SUPPORT_ALARM_ARM_AWAY,
"current_values": [
default_current_states["Disarmed"],
default_current_states["AlarmTriggered"],
default_current_states["AwayArm"],
],
"target_values": [
default_target_services["Disarm"],
default_target_services["AwayArm"],
],
},
{
"features": SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY,
"current_values": [
default_current_states["Disarmed"],
default_current_states["AlarmTriggered"],
default_current_states["StayArm"],
default_current_states["AwayArm"],
],
"target_values": [
default_target_services["Disarm"],
default_target_services["StayArm"],
default_target_services["AwayArm"],
],
},
{
"features": SUPPORT_ALARM_ARM_HOME
| SUPPORT_ALARM_ARM_AWAY
| SUPPORT_ALARM_ARM_NIGHT,
"current_values": [
default_current_states["Disarmed"],
default_current_states["AlarmTriggered"],
default_current_states["StayArm"],
default_current_states["AwayArm"],
default_current_states["NightArm"],
],
"target_values": [
default_target_services["Disarm"],
default_target_services["StayArm"],
default_target_services["AwayArm"],
default_target_services["NightArm"],
],
},
{
"features": SUPPORT_ALARM_ARM_HOME
| SUPPORT_ALARM_ARM_AWAY
| SUPPORT_ALARM_ARM_NIGHT
| SUPPORT_ALARM_TRIGGER,
"current_values": [
default_current_states["Disarmed"],
default_current_states["AlarmTriggered"],
default_current_states["StayArm"],
default_current_states["AwayArm"],
default_current_states["NightArm"],
],
"target_values": [
default_target_services["Disarm"],
default_target_services["StayArm"],
default_target_services["AwayArm"],
default_target_services["NightArm"],
],
},
]
for test_config in test_configs:
attrs = {"supported_features": test_config.get("features")}
hass.states.async_set(entity_id, None, attributes=attrs)
await hass.async_block_till_done()
acc = SecuritySystem(hass, hk_driver, "SecuritySystem", entity_id, 2, config)
await acc.run_handler()
await hass.async_block_till_done()
valid_current_values = acc.char_current_state.properties.get("ValidValues")
valid_target_values = acc.char_target_state.properties.get("ValidValues")
for val in valid_current_values.values():
assert val in test_config.get("current_values")
for val in valid_target_values.values():
assert val in test_config.get("target_values")