Remove unsupported states from security systems in HomeKit (#40060)
This commit is contained in:
parent
d25192d8c5
commit
2b7e735e3d
2 changed files with 195 additions and 2 deletions
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Add table
Reference in a new issue