Skip to content
31 changes: 25 additions & 6 deletions homeassistant/components/lock/zwave.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
POLYCONTROL = 0x10E
DANALOCK_V2_BTZE = 0x2
POLYCONTROL_DANALOCK_V2_BTZE_LOCK = (POLYCONTROL, DANALOCK_V2_BTZE)
WORKAROUND_V2BTZE = 'v2btze'
WORKAROUND_DEVICE_STATE = 'state'
WORKAROUND_V2BTZE = 1
WORKAROUND_DEVICE_STATE = 2
WORKAROUND_ALARM_TYPE = 4

DEVICE_MAPPINGS = {
POLYCONTROL_DANALOCK_V2_BTZE_LOCK: WORKAROUND_V2BTZE,
Expand All @@ -41,7 +42,7 @@
(0x0129, 0xAA00): WORKAROUND_DEVICE_STATE,
(0x0129, 0x0000): WORKAROUND_DEVICE_STATE,
# Yale YRD220 (as reported by adrum in PR #17386)
(0x0109, 0x0000): WORKAROUND_DEVICE_STATE,
(0x0109, 0x0000): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
# Schlage BE469
(0x003B, 0x5044): WORKAROUND_DEVICE_STATE,
# Schlage FE599NX
Expand Down Expand Up @@ -229,6 +230,7 @@ def __init__(self, values):
self._lock_status = None
self._v2btze = None
self._state_workaround = False
self._alarm_type_workaround = False

# Enable appropriate workaround flags for our device
# Make sure that we have values for the key before converting to int
Expand All @@ -237,26 +239,32 @@ def __init__(self, values):
specific_sensor_key = (int(self.node.manufacturer_id, 16),
int(self.node.product_id, 16))
if specific_sensor_key in DEVICE_MAPPINGS:
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_V2BTZE:
workaround = DEVICE_MAPPINGS[specific_sensor_key]
if workaround & WORKAROUND_V2BTZE:
self._v2btze = 1
_LOGGER.debug("Polycontrol Danalock v2 BTZE "
"workaround enabled")
if DEVICE_MAPPINGS[specific_sensor_key] == \
WORKAROUND_DEVICE_STATE:
if workaround & WORKAROUND_DEVICE_STATE:
self._state_workaround = True
_LOGGER.debug(
"Notification device state workaround enabled")
if workaround & WORKAROUND_ALARM_TYPE:
self._alarm_type_workaround = True
_LOGGER.debug(
"Alarm Type device state workaround enabled")
self.update_properties()

def update_properties(self):
"""Handle data changes for node values."""
did_use_state_workaround = False
self._state = self.values.primary.data
_LOGGER.debug("Lock state set from Bool value and is %s", self._state)
if self.values.access_control:
notification_data = self.values.access_control.data
self._notification = LOCK_NOTIFICATION.get(str(notification_data))
if self._state_workaround:
self._state = LOCK_STATUS.get(str(notification_data))
did_use_state_workaround = True
if self._v2btze:
if self.values.v2btze_advanced and \
self.values.v2btze_advanced.data == CONFIG_ADVANCED:
Expand All @@ -278,6 +286,17 @@ def update_properties(self):

if not alarm_type:
return

# If has only alarm_type_workaround, then allow
# If has both state_workaround and alarm_type_workaround,
# only allow if state_workaround failed
if self._alarm_type_workaround and \
(self._state_workaround == False or \
Comment thread
adrum marked this conversation as resolved.
Outdated
did_use_state_workaround == False):
Comment thread
adrum marked this conversation as resolved.
Outdated
self._state = LOCK_STATUS.get(str(alarm_type))
_LOGGER.debug("workaround: lock state set to %s from alarm type: %s", \
Comment thread
adrum marked this conversation as resolved.
Outdated
self._state, str(alarm_type))
Comment thread
adrum marked this conversation as resolved.
Outdated

if alarm_type == 21:
self._lock_status = '{}{}'.format(
LOCK_ALARM_TYPE.get(str(alarm_type)),
Expand Down
39 changes: 39 additions & 0 deletions tests/components/lock/test_zwave.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,45 @@ def test_v2btze_value_changed(mock_openzwave):

assert device.is_locked

def test_alarm_type_workaround(mock_openzwave):
Comment thread
adrum marked this conversation as resolved.
"""Test value changed for Z-Wave lock using alarm type."""
node = MockNode(manufacturer_id='0109', product_id='0000')
values = MockEntityValues(
primary=MockValue(data=True, node=node),
access_control=None,
alarm_type=MockValue(data=16, node=node),
alarm_level=None,
)
device = zwave.get_device(node=node, values=values)
assert not device.is_locked

values.alarm_type.data = 18
value_changed(values.alarm_type)
assert device.is_locked

values.alarm_type.data = 19
value_changed(values.alarm_type)
assert not device.is_locked

values.alarm_type.data = 21
value_changed(values.alarm_type)
assert device.is_locked

values.alarm_type.data = 22
value_changed(values.alarm_type)
assert not device.is_locked

values.alarm_type.data = 24
value_changed(values.alarm_type)
assert device.is_locked

values.alarm_type.data = 25
value_changed(values.alarm_type)
assert not device.is_locked

values.alarm_type.data = 27
value_changed(values.alarm_type)
assert device.is_locked

def test_lock_access_control(mock_openzwave):
"""Test access control for Z-Wave lock."""
Expand Down