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
|
import logging
|
||||||
|
|
||||||
from pyhap.const import CATEGORY_ALARM_SYSTEM
|
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 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 (
|
from homeassistant.const import (
|
||||||
ATTR_CODE,
|
ATTR_CODE,
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
|
@ -36,6 +43,13 @@ HASS_TO_HOMEKIT = {
|
||||||
STATE_ALARM_TRIGGERED: 4,
|
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()}
|
HOMEKIT_TO_HASS = {c: s for s, c in HASS_TO_HOMEKIT.items()}
|
||||||
|
|
||||||
STATE_TO_SERVICE = {
|
STATE_TO_SERVICE = {
|
||||||
|
@ -56,13 +70,72 @@ class SecuritySystem(HomeAccessory):
|
||||||
state = self.hass.states.get(self.entity_id)
|
state = self.hass.states.get(self.entity_id)
|
||||||
self._alarm_code = self.config.get(ATTR_CODE)
|
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)
|
serv_alarm = self.add_preload_service(SERV_SECURITY_SYSTEM)
|
||||||
self.char_current_state = serv_alarm.configure_char(
|
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(
|
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
|
# Set the state so it is in sync on initial
|
||||||
# GET to avoid an event storm after homekit startup
|
# GET to avoid an event storm after homekit startup
|
||||||
self.async_update_state(state)
|
self.async_update_state(state)
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
"""Test different accessory types: Security Systems."""
|
"""Test different accessory types: Security Systems."""
|
||||||
|
from pyhap.loader import get_loader
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.alarm_control_panel import DOMAIN
|
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.const import ATTR_VALUE
|
||||||
from homeassistant.components.homekit.type_security_systems import SecuritySystem
|
from homeassistant.components.homekit.type_security_systems import SecuritySystem
|
||||||
from homeassistant.const import (
|
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 acc.char_target_state.value == 0
|
||||||
assert len(events) == 1
|
assert len(events) == 1
|
||||||
assert events[-1].data[ATTR_VALUE] is None
|
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
Add a link
Reference in a new issue