parent
bbd1a85b09
commit
6eb3307734
|
@ -2,13 +2,13 @@
|
|||
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_ARM_VACATION,
|
||||
SUPPORT_ALARM_TRIGGER,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
|
@ -22,6 +22,8 @@ from homeassistant.const import (
|
|||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_ARMED_VACATION,
|
||||
STATE_ALARM_ARMING,
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
)
|
||||
|
@ -36,28 +38,43 @@ from .const import (
|
|||
|
||||
_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,
|
||||
HK_ALARM_STAY_ARMED = 0
|
||||
HK_ALARM_AWAY_ARMED = 1
|
||||
HK_ALARM_NIGHT_ARMED = 2
|
||||
HK_ALARM_DISARMED = 3
|
||||
HK_ALARM_TRIGGERED = 4
|
||||
|
||||
HASS_TO_HOMEKIT_CURRENT = {
|
||||
STATE_ALARM_ARMED_HOME: HK_ALARM_STAY_ARMED,
|
||||
STATE_ALARM_ARMED_VACATION: HK_ALARM_AWAY_ARMED,
|
||||
STATE_ALARM_ARMED_AWAY: HK_ALARM_AWAY_ARMED,
|
||||
STATE_ALARM_ARMED_NIGHT: HK_ALARM_NIGHT_ARMED,
|
||||
STATE_ALARM_ARMING: HK_ALARM_DISARMED,
|
||||
STATE_ALARM_DISARMED: HK_ALARM_DISARMED,
|
||||
STATE_ALARM_TRIGGERED: HK_ALARM_TRIGGERED,
|
||||
}
|
||||
|
||||
HASS_TO_HOMEKIT_TARGET = {
|
||||
STATE_ALARM_ARMED_HOME: HK_ALARM_STAY_ARMED,
|
||||
STATE_ALARM_ARMED_VACATION: HK_ALARM_AWAY_ARMED,
|
||||
STATE_ALARM_ARMED_AWAY: HK_ALARM_AWAY_ARMED,
|
||||
STATE_ALARM_ARMED_NIGHT: HK_ALARM_NIGHT_ARMED,
|
||||
STATE_ALARM_ARMING: HK_ALARM_AWAY_ARMED,
|
||||
STATE_ALARM_DISARMED: HK_ALARM_DISARMED,
|
||||
}
|
||||
|
||||
HASS_TO_HOMEKIT_SERVICES = {
|
||||
SERVICE_ALARM_ARM_HOME: 0,
|
||||
SERVICE_ALARM_ARM_AWAY: 1,
|
||||
SERVICE_ALARM_ARM_NIGHT: 2,
|
||||
SERVICE_ALARM_DISARM: 3,
|
||||
SERVICE_ALARM_ARM_HOME: HK_ALARM_STAY_ARMED,
|
||||
SERVICE_ALARM_ARM_AWAY: HK_ALARM_AWAY_ARMED,
|
||||
SERVICE_ALARM_ARM_NIGHT: HK_ALARM_NIGHT_ARMED,
|
||||
SERVICE_ALARM_DISARM: HK_ALARM_DISARMED,
|
||||
}
|
||||
|
||||
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,
|
||||
HK_TO_SERVICE = {
|
||||
HK_ALARM_AWAY_ARMED: SERVICE_ALARM_ARM_AWAY,
|
||||
HK_ALARM_STAY_ARMED: SERVICE_ALARM_ARM_HOME,
|
||||
HK_ALARM_NIGHT_ARMED: SERVICE_ALARM_ARM_NIGHT,
|
||||
HK_ALARM_DISARMED: SERVICE_ALARM_DISARM,
|
||||
}
|
||||
|
||||
|
||||
|
@ -75,65 +92,51 @@ class SecuritySystem(HomeAccessory):
|
|||
ATTR_SUPPORTED_FEATURES,
|
||||
(
|
||||
SUPPORT_ALARM_ARM_HOME
|
||||
| SUPPORT_ALARM_ARM_VACATION
|
||||
| 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")
|
||||
serv_alarm = self.add_preload_service(SERV_SECURITY_SYSTEM)
|
||||
current_char = serv_alarm.get_characteristic(CHAR_CURRENT_SECURITY_STATE)
|
||||
target_char = serv_alarm.get_characteristic(CHAR_TARGET_SECURITY_STATE)
|
||||
default_current_states = current_char.properties.get("ValidValues")
|
||||
default_target_services = target_char.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]]
|
||||
current_supported_states = [HK_ALARM_DISARMED, HK_ALARM_TRIGGERED]
|
||||
target_supported_services = [HK_ALARM_DISARMED]
|
||||
|
||||
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]
|
||||
)
|
||||
current_supported_states.append(HK_ALARM_STAY_ARMED)
|
||||
target_supported_services.append(HK_ALARM_STAY_ARMED)
|
||||
|
||||
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_AWAY | SUPPORT_ALARM_ARM_VACATION):
|
||||
current_supported_states.append(HK_ALARM_AWAY_ARMED)
|
||||
target_supported_services.append(HK_ALARM_AWAY_ARMED)
|
||||
|
||||
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]
|
||||
)
|
||||
current_supported_states.append(HK_ALARM_NIGHT_ARMED)
|
||||
target_supported_services.append(HK_ALARM_NIGHT_ARMED)
|
||||
|
||||
new_current_states = {
|
||||
self.char_current_state = serv_alarm.configure_char(
|
||||
CHAR_CURRENT_SECURITY_STATE,
|
||||
value=HASS_TO_HOMEKIT_CURRENT[STATE_ALARM_DISARMED],
|
||||
valid_values={
|
||||
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,
|
||||
valid_values={
|
||||
key: val
|
||||
for key, val in default_target_services.items()
|
||||
if val in target_supported_services
|
||||
},
|
||||
setter_callback=self.set_security_state,
|
||||
)
|
||||
|
||||
|
@ -144,9 +147,7 @@ class SecuritySystem(HomeAccessory):
|
|||
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]
|
||||
|
||||
service = HK_TO_SERVICE[value]
|
||||
params = {ATTR_ENTITY_ID: self.entity_id}
|
||||
if self._alarm_code:
|
||||
params[ATTR_CODE] = self._alarm_code
|
||||
|
@ -156,20 +157,16 @@ class SecuritySystem(HomeAccessory):
|
|||
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)
|
||||
if (current_state := HASS_TO_HOMEKIT_CURRENT.get(hass_state)) is not None:
|
||||
if self.char_current_state.value != current_state:
|
||||
self.char_current_state.set_value(current_state)
|
||||
_LOGGER.debug(
|
||||
"%s: Updated current state to %s (%d)",
|
||||
self.entity_id,
|
||||
hass_state,
|
||||
current_security_state,
|
||||
current_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)
|
||||
if (target_state := HASS_TO_HOMEKIT_TARGET.get(hass_state)) is not None:
|
||||
if self.char_target_state.value != target_state:
|
||||
self.char_target_state.set_value(target_state)
|
||||
|
|
|
@ -17,6 +17,8 @@ from homeassistant.const import (
|
|||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_ARMED_VACATION,
|
||||
STATE_ALARM_ARMING,
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
STATE_UNKNOWN,
|
||||
|
@ -79,7 +81,7 @@ async def test_switch_set_state(hass, hk_driver, events):
|
|||
call_arm_night = async_mock_service(hass, DOMAIN, "alarm_arm_night")
|
||||
call_disarm = async_mock_service(hass, DOMAIN, "alarm_disarm")
|
||||
|
||||
await hass.async_add_executor_job(acc.char_target_state.client_update_value, 0)
|
||||
acc.char_target_state.client_update_value(0)
|
||||
await hass.async_block_till_done()
|
||||
assert call_arm_home
|
||||
assert call_arm_home[0].data[ATTR_ENTITY_ID] == entity_id
|
||||
|
@ -88,7 +90,7 @@ async def test_switch_set_state(hass, hk_driver, events):
|
|||
assert len(events) == 1
|
||||
assert events[-1].data[ATTR_VALUE] is None
|
||||
|
||||
await hass.async_add_executor_job(acc.char_target_state.client_update_value, 1)
|
||||
acc.char_target_state.client_update_value(1)
|
||||
await hass.async_block_till_done()
|
||||
assert call_arm_away
|
||||
assert call_arm_away[0].data[ATTR_ENTITY_ID] == entity_id
|
||||
|
@ -97,7 +99,7 @@ async def test_switch_set_state(hass, hk_driver, events):
|
|||
assert len(events) == 2
|
||||
assert events[-1].data[ATTR_VALUE] is None
|
||||
|
||||
await hass.async_add_executor_job(acc.char_target_state.client_update_value, 2)
|
||||
acc.char_target_state.client_update_value(2)
|
||||
await hass.async_block_till_done()
|
||||
assert call_arm_night
|
||||
assert call_arm_night[0].data[ATTR_ENTITY_ID] == entity_id
|
||||
|
@ -106,7 +108,7 @@ async def test_switch_set_state(hass, hk_driver, events):
|
|||
assert len(events) == 3
|
||||
assert events[-1].data[ATTR_VALUE] is None
|
||||
|
||||
await hass.async_add_executor_job(acc.char_target_state.client_update_value, 3)
|
||||
acc.char_target_state.client_update_value(3)
|
||||
await hass.async_block_till_done()
|
||||
assert call_disarm
|
||||
assert call_disarm[0].data[ATTR_ENTITY_ID] == entity_id
|
||||
|
@ -128,7 +130,7 @@ async def test_no_alarm_code(hass, hk_driver, config, events):
|
|||
# Set from HomeKit
|
||||
call_arm_home = async_mock_service(hass, DOMAIN, "alarm_arm_home")
|
||||
|
||||
await hass.async_add_executor_job(acc.char_target_state.client_update_value, 0)
|
||||
acc.char_target_state.client_update_value(0)
|
||||
await hass.async_block_till_done()
|
||||
assert call_arm_home
|
||||
assert call_arm_home[0].data[ATTR_ENTITY_ID] == entity_id
|
||||
|
@ -138,6 +140,57 @@ async def test_no_alarm_code(hass, hk_driver, config, events):
|
|||
assert events[-1].data[ATTR_VALUE] is None
|
||||
|
||||
|
||||
async def test_arming(hass, hk_driver, events):
|
||||
"""Test to make sure arming sets the right state."""
|
||||
entity_id = "alarm_control_panel.test"
|
||||
|
||||
hass.states.async_set(entity_id, None)
|
||||
|
||||
acc = SecuritySystem(hass, hk_driver, "SecuritySystem", entity_id, 2, {})
|
||||
await acc.run()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
hass.states.async_set(entity_id, STATE_ALARM_ARMED_AWAY)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_target_state.value == 1
|
||||
assert acc.char_current_state.value == 1
|
||||
|
||||
hass.states.async_set(entity_id, STATE_ALARM_ARMED_HOME)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_target_state.value == 0
|
||||
assert acc.char_current_state.value == 0
|
||||
|
||||
hass.states.async_set(entity_id, STATE_ALARM_ARMED_VACATION)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_target_state.value == 1
|
||||
assert acc.char_current_state.value == 1
|
||||
|
||||
hass.states.async_set(entity_id, STATE_ALARM_ARMED_NIGHT)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_target_state.value == 2
|
||||
assert acc.char_current_state.value == 2
|
||||
|
||||
hass.states.async_set(entity_id, STATE_ALARM_ARMING)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_target_state.value == 1
|
||||
assert acc.char_current_state.value == 3
|
||||
|
||||
hass.states.async_set(entity_id, STATE_ALARM_DISARMED)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_target_state.value == 3
|
||||
assert acc.char_current_state.value == 3
|
||||
|
||||
hass.states.async_set(entity_id, STATE_ALARM_ARMED_AWAY)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_target_state.value == 1
|
||||
assert acc.char_current_state.value == 1
|
||||
|
||||
hass.states.async_set(entity_id, STATE_ALARM_TRIGGERED)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_target_state.value == 1
|
||||
assert acc.char_current_state.value == 4
|
||||
|
||||
|
||||
async def test_supported_states(hass, hk_driver, events):
|
||||
"""Test different supported states."""
|
||||
code = "1234"
|
||||
|
|
Loading…
Reference in New Issue