Update homekit lock to support locking, unlocking, jammed (#52819)

This commit is contained in:
J. Nick Koston 2021-07-20 18:55:19 -10:00 committed by GitHub
parent 564a505486
commit fe89603ee7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 33 deletions

View File

@ -3,7 +3,14 @@ import logging
from pyhap.const import CATEGORY_DOOR_LOCK from pyhap.const import CATEGORY_DOOR_LOCK
from homeassistant.components.lock import DOMAIN, STATE_LOCKED, STATE_UNLOCKED from homeassistant.components.lock import (
DOMAIN,
STATE_JAMMED,
STATE_LOCKED,
STATE_LOCKING,
STATE_UNLOCKED,
STATE_UNLOCKING,
)
from homeassistant.const import ATTR_CODE, ATTR_ENTITY_ID, STATE_UNKNOWN from homeassistant.const import ATTR_CODE, ATTR_ENTITY_ID, STATE_UNKNOWN
from homeassistant.core import callback from homeassistant.core import callback
@ -12,16 +19,37 @@ from .const import CHAR_LOCK_CURRENT_STATE, CHAR_LOCK_TARGET_STATE, SERV_LOCK
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
HASS_TO_HOMEKIT = { HASS_TO_HOMEKIT_CURRENT = {
STATE_UNLOCKED: 0, STATE_UNLOCKED: 0,
STATE_UNLOCKING: 1,
STATE_LOCKING: 0,
STATE_LOCKED: 1, STATE_LOCKED: 1,
# Value 2 is Jammed which hass doesn't have a state for STATE_JAMMED: 2,
STATE_UNKNOWN: 3, STATE_UNKNOWN: 3,
} }
HOMEKIT_TO_HASS = {c: s for s, c in HASS_TO_HOMEKIT.items()} HASS_TO_HOMEKIT_TARGET = {
STATE_UNLOCKED: 0,
STATE_UNLOCKING: 0,
STATE_LOCKING: 1,
STATE_LOCKED: 1,
}
STATE_TO_SERVICE = {STATE_LOCKED: "lock", STATE_UNLOCKED: "unlock"} VALID_TARGET_STATES = {STATE_LOCKING, STATE_UNLOCKING, STATE_LOCKED, STATE_UNLOCKED}
HOMEKIT_TO_HASS = {
0: STATE_UNLOCKED,
1: STATE_LOCKED,
2: STATE_JAMMED,
3: STATE_UNKNOWN,
}
STATE_TO_SERVICE = {
STATE_LOCKING: "unlock",
STATE_LOCKED: "lock",
STATE_UNLOCKING: "lock",
STATE_UNLOCKED: "unlock",
}
@TYPES.register("Lock") @TYPES.register("Lock")
@ -39,11 +67,11 @@ class Lock(HomeAccessory):
serv_lock_mechanism = self.add_preload_service(SERV_LOCK) serv_lock_mechanism = self.add_preload_service(SERV_LOCK)
self.char_current_state = serv_lock_mechanism.configure_char( self.char_current_state = serv_lock_mechanism.configure_char(
CHAR_LOCK_CURRENT_STATE, value=HASS_TO_HOMEKIT[STATE_UNKNOWN] CHAR_LOCK_CURRENT_STATE, value=HASS_TO_HOMEKIT_CURRENT[STATE_UNKNOWN]
) )
self.char_target_state = serv_lock_mechanism.configure_char( self.char_target_state = serv_lock_mechanism.configure_char(
CHAR_LOCK_TARGET_STATE, CHAR_LOCK_TARGET_STATE,
value=HASS_TO_HOMEKIT[STATE_LOCKED], value=HASS_TO_HOMEKIT_CURRENT[STATE_LOCKED],
setter_callback=self.set_state, setter_callback=self.set_state,
) )
self.async_update_state(state) self.async_update_state(state)
@ -52,12 +80,9 @@ class Lock(HomeAccessory):
"""Set lock state to value if call came from HomeKit.""" """Set lock state to value if call came from HomeKit."""
_LOGGER.debug("%s: Set state to %d", self.entity_id, value) _LOGGER.debug("%s: Set state to %d", self.entity_id, value)
hass_value = HOMEKIT_TO_HASS.get(value) hass_value = HOMEKIT_TO_HASS[value]
service = STATE_TO_SERVICE[hass_value] service = STATE_TO_SERVICE[hass_value]
if self.char_current_state.value != value:
self.char_current_state.set_value(value)
params = {ATTR_ENTITY_ID: self.entity_id} params = {ATTR_ENTITY_ID: self.entity_id}
if self._code: if self._code:
params[ATTR_CODE] = self._code params[ATTR_CODE] = self._code
@ -67,25 +92,28 @@ class Lock(HomeAccessory):
def async_update_state(self, new_state): def async_update_state(self, new_state):
"""Update lock after state changed.""" """Update lock after state changed."""
hass_state = new_state.state hass_state = new_state.state
if hass_state in HASS_TO_HOMEKIT: current_lock_state = HASS_TO_HOMEKIT_CURRENT.get(
current_lock_state = HASS_TO_HOMEKIT[hass_state] hass_state, HASS_TO_HOMEKIT_CURRENT[STATE_UNKNOWN]
_LOGGER.debug( )
"%s: Updated current state to %s (%d)", target_lock_state = HASS_TO_HOMEKIT_TARGET.get(hass_state)
self.entity_id, _LOGGER.debug(
hass_state, "%s: Updated current state to %s (current=%d) (target=%s)",
current_lock_state, self.entity_id,
) hass_state,
# LockTargetState only supports locked and unlocked current_lock_state,
# Must set lock target state before current state target_lock_state,
# or there will be no notification )
if ( # LockTargetState only supports locked and unlocked
hass_state in (STATE_LOCKED, STATE_UNLOCKED) # Must set lock target state before current state
and self.char_target_state.value != current_lock_state # or there will be no notification
): if (
self.char_target_state.set_value(current_lock_state) target_lock_state is not None
and self.char_target_state.value != target_lock_state
):
self.char_target_state.set_value(target_lock_state)
# Set lock current state ONLY after ensuring that # Set lock current state ONLY after ensuring that
# target state is correct or there will be no # target state is correct or there will be no
# notification # notification
if self.char_current_state.value != current_lock_state: if self.char_current_state.value != current_lock_state:
self.char_current_state.set_value(current_lock_state) self.char_current_state.set_value(current_lock_state)

View File

@ -3,7 +3,12 @@ import pytest
from homeassistant.components.homekit.const import ATTR_VALUE from homeassistant.components.homekit.const import ATTR_VALUE
from homeassistant.components.homekit.type_locks import Lock from homeassistant.components.homekit.type_locks import Lock
from homeassistant.components.lock import DOMAIN from homeassistant.components.lock import (
DOMAIN,
STATE_JAMMED,
STATE_LOCKING,
STATE_UNLOCKING,
)
from homeassistant.const import ( from homeassistant.const import (
ATTR_CODE, ATTR_CODE,
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
@ -37,11 +42,26 @@ async def test_lock_unlock(hass, hk_driver, events):
assert acc.char_current_state.value == 1 assert acc.char_current_state.value == 1
assert acc.char_target_state.value == 1 assert acc.char_target_state.value == 1
hass.states.async_set(entity_id, STATE_LOCKING)
await hass.async_block_till_done()
assert acc.char_current_state.value == 0
assert acc.char_target_state.value == 1
hass.states.async_set(entity_id, STATE_UNLOCKED) hass.states.async_set(entity_id, STATE_UNLOCKED)
await hass.async_block_till_done() await hass.async_block_till_done()
assert acc.char_current_state.value == 0 assert acc.char_current_state.value == 0
assert acc.char_target_state.value == 0 assert acc.char_target_state.value == 0
hass.states.async_set(entity_id, STATE_UNLOCKING)
await hass.async_block_till_done()
assert acc.char_current_state.value == 1
assert acc.char_target_state.value == 0
hass.states.async_set(entity_id, STATE_JAMMED)
await hass.async_block_till_done()
assert acc.char_current_state.value == 2
assert acc.char_target_state.value == 0
hass.states.async_set(entity_id, STATE_UNKNOWN) hass.states.async_set(entity_id, STATE_UNKNOWN)
await hass.async_block_till_done() await hass.async_block_till_done()
assert acc.char_current_state.value == 3 assert acc.char_current_state.value == 3