2019-02-13 20:21:14 +00:00
|
|
|
"""Support for Z-Wave door locks."""
|
2016-12-27 21:08:35 +00:00
|
|
|
import logging
|
2017-01-27 05:45:04 +00:00
|
|
|
|
|
|
|
import voluptuous as vol
|
2016-12-27 21:08:35 +00:00
|
|
|
|
2018-11-20 13:59:34 +00:00
|
|
|
from homeassistant.core import callback
|
2016-05-03 02:38:48 +00:00
|
|
|
from homeassistant.components.lock import DOMAIN, LockDevice
|
|
|
|
from homeassistant.components import zwave
|
2018-11-20 13:59:34 +00:00
|
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
2017-02-09 03:59:47 +00:00
|
|
|
import homeassistant.helpers.config_validation as cv
|
2016-05-03 02:38:48 +00:00
|
|
|
|
2016-12-27 21:08:35 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
ATTR_NOTIFICATION = 'notification'
|
2017-01-20 21:01:36 +00:00
|
|
|
ATTR_LOCK_STATUS = 'lock_status'
|
2017-01-27 05:45:04 +00:00
|
|
|
ATTR_CODE_SLOT = 'code_slot'
|
|
|
|
ATTR_USERCODE = 'usercode'
|
2017-02-02 04:57:57 +00:00
|
|
|
CONFIG_ADVANCED = 'Advanced'
|
2017-01-27 05:45:04 +00:00
|
|
|
|
|
|
|
SERVICE_SET_USERCODE = 'set_usercode'
|
|
|
|
SERVICE_GET_USERCODE = 'get_usercode'
|
|
|
|
SERVICE_CLEAR_USERCODE = 'clear_usercode'
|
|
|
|
|
2017-02-02 04:57:57 +00:00
|
|
|
POLYCONTROL = 0x10E
|
|
|
|
DANALOCK_V2_BTZE = 0x2
|
|
|
|
POLYCONTROL_DANALOCK_V2_BTZE_LOCK = (POLYCONTROL, DANALOCK_V2_BTZE)
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
WORKAROUND_V2BTZE = 1
|
|
|
|
WORKAROUND_DEVICE_STATE = 2
|
|
|
|
WORKAROUND_TRACK_MESSAGE = 4
|
2019-01-05 08:59:43 +00:00
|
|
|
WORKAROUND_ALARM_TYPE = 8
|
2017-02-02 04:57:57 +00:00
|
|
|
|
|
|
|
DEVICE_MAPPINGS = {
|
2018-11-06 10:00:48 +00:00
|
|
|
POLYCONTROL_DANALOCK_V2_BTZE_LOCK: WORKAROUND_V2BTZE,
|
|
|
|
# Kwikset 914TRL ZW500
|
|
|
|
(0x0090, 0x440): WORKAROUND_DEVICE_STATE,
|
2019-01-05 08:48:40 +00:00
|
|
|
(0x0090, 0x446): WORKAROUND_DEVICE_STATE,
|
2019-01-26 10:01:04 +00:00
|
|
|
# Yale YRD210, Yale YRD240
|
|
|
|
(0x0129, 0x0209): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
2018-11-06 10:00:48 +00:00
|
|
|
(0x0129, 0xAA00): WORKAROUND_DEVICE_STATE,
|
2019-02-10 15:00:03 +00:00
|
|
|
# Yale YRL220/YRD220
|
|
|
|
(0x0129, 0x0000): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
|
|
|
# Yale YRD120
|
|
|
|
(0x0129, 0x0800): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
2018-11-06 10:00:48 +00:00
|
|
|
# Yale YRD220 (as reported by adrum in PR #17386)
|
2019-01-05 08:59:43 +00:00
|
|
|
(0x0109, 0x0000): WORKAROUND_DEVICE_STATE | WORKAROUND_ALARM_TYPE,
|
2018-11-06 10:00:48 +00:00
|
|
|
# Schlage BE469
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
(0x003B, 0x5044): WORKAROUND_DEVICE_STATE | WORKAROUND_TRACK_MESSAGE,
|
2018-11-06 10:00:48 +00:00
|
|
|
# Schlage FE599NX
|
|
|
|
(0x003B, 0x504C): WORKAROUND_DEVICE_STATE,
|
2017-02-02 04:57:57 +00:00
|
|
|
}
|
|
|
|
|
2016-12-27 21:08:35 +00:00
|
|
|
LOCK_NOTIFICATION = {
|
2017-02-09 12:40:35 +00:00
|
|
|
'1': 'Manual Lock',
|
|
|
|
'2': 'Manual Unlock',
|
|
|
|
'5': 'Keypad Lock',
|
|
|
|
'6': 'Keypad Unlock',
|
|
|
|
'11': 'Lock Jammed',
|
|
|
|
'254': 'Unknown Event'
|
2016-12-27 21:08:35 +00:00
|
|
|
}
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
NOTIFICATION_RF_LOCK = '3'
|
|
|
|
NOTIFICATION_RF_UNLOCK = '4'
|
|
|
|
LOCK_NOTIFICATION[NOTIFICATION_RF_LOCK] = 'RF Lock'
|
|
|
|
LOCK_NOTIFICATION[NOTIFICATION_RF_UNLOCK] = 'RF Unlock'
|
2016-12-27 21:08:35 +00:00
|
|
|
|
2017-01-20 21:01:36 +00:00
|
|
|
LOCK_ALARM_TYPE = {
|
2017-02-09 12:40:35 +00:00
|
|
|
'9': 'Deadbolt Jammed',
|
2018-02-21 22:03:10 +00:00
|
|
|
'16': 'Unlocked by Bluetooth ',
|
2017-02-09 12:40:35 +00:00
|
|
|
'18': 'Locked with Keypad by user ',
|
|
|
|
'19': 'Unlocked with Keypad by user ',
|
2017-03-24 06:53:59 +00:00
|
|
|
'21': 'Manually Locked ',
|
|
|
|
'22': 'Manually Unlocked ',
|
2017-02-09 12:40:35 +00:00
|
|
|
'27': 'Auto re-lock',
|
|
|
|
'33': 'User deleted: ',
|
|
|
|
'112': 'Master code changed or User added: ',
|
|
|
|
'113': 'Duplicate Pin-code: ',
|
|
|
|
'130': 'RF module, power restored',
|
2018-02-21 22:03:10 +00:00
|
|
|
'144': 'Unlocked by NFC Tag or Card by user ',
|
2017-02-09 12:40:35 +00:00
|
|
|
'161': 'Tamper Alarm: ',
|
|
|
|
'167': 'Low Battery',
|
|
|
|
'168': 'Critical Battery Level',
|
|
|
|
'169': 'Battery too low to operate'
|
2017-01-20 21:01:36 +00:00
|
|
|
}
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
ALARM_RF_LOCK = '24'
|
|
|
|
ALARM_RF_UNLOCK = '25'
|
|
|
|
LOCK_ALARM_TYPE[ALARM_RF_LOCK] = 'Locked by RF'
|
|
|
|
LOCK_ALARM_TYPE[ALARM_RF_UNLOCK] = 'Unlocked by RF'
|
2017-01-20 21:01:36 +00:00
|
|
|
|
|
|
|
MANUAL_LOCK_ALARM_LEVEL = {
|
2017-03-24 06:53:59 +00:00
|
|
|
'1': 'by Key Cylinder or Inside thumb turn',
|
|
|
|
'2': 'by Touch function (lock and leave)'
|
2017-01-20 21:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TAMPER_ALARM_LEVEL = {
|
2017-02-09 12:40:35 +00:00
|
|
|
'1': 'Too many keypresses',
|
|
|
|
'2': 'Cover removed'
|
2017-01-20 21:01:36 +00:00
|
|
|
}
|
|
|
|
|
2016-12-27 21:08:35 +00:00
|
|
|
LOCK_STATUS = {
|
2017-02-09 12:40:35 +00:00
|
|
|
'1': True,
|
|
|
|
'2': False,
|
|
|
|
'3': True,
|
|
|
|
'4': False,
|
|
|
|
'5': True,
|
|
|
|
'6': False,
|
|
|
|
'9': False,
|
|
|
|
'18': True,
|
|
|
|
'19': False,
|
|
|
|
'21': True,
|
|
|
|
'22': False,
|
|
|
|
'24': True,
|
|
|
|
'25': False,
|
|
|
|
'27': True
|
2016-12-27 21:08:35 +00:00
|
|
|
}
|
|
|
|
|
2017-01-20 21:01:36 +00:00
|
|
|
ALARM_TYPE_STD = [
|
2017-02-09 12:40:35 +00:00
|
|
|
'18',
|
|
|
|
'19',
|
|
|
|
'33',
|
|
|
|
'112',
|
2018-02-21 22:03:10 +00:00
|
|
|
'113',
|
|
|
|
'144'
|
2017-01-20 21:01:36 +00:00
|
|
|
]
|
|
|
|
|
2017-01-27 05:45:04 +00:00
|
|
|
SET_USERCODE_SCHEMA = vol.Schema({
|
|
|
|
vol.Required(zwave.const.ATTR_NODE_ID): vol.Coerce(int),
|
|
|
|
vol.Required(ATTR_CODE_SLOT): vol.Coerce(int),
|
2017-02-09 03:59:47 +00:00
|
|
|
vol.Required(ATTR_USERCODE): cv.string,
|
2017-01-27 05:45:04 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
GET_USERCODE_SCHEMA = vol.Schema({
|
|
|
|
vol.Required(zwave.const.ATTR_NODE_ID): vol.Coerce(int),
|
|
|
|
vol.Required(ATTR_CODE_SLOT): vol.Coerce(int),
|
|
|
|
})
|
|
|
|
|
|
|
|
CLEAR_USERCODE_SCHEMA = vol.Schema({
|
|
|
|
vol.Required(zwave.const.ATTR_NODE_ID): vol.Coerce(int),
|
|
|
|
vol.Required(ATTR_CODE_SLOT): vol.Coerce(int),
|
|
|
|
})
|
|
|
|
|
2016-05-03 02:38:48 +00:00
|
|
|
|
2018-10-01 06:56:50 +00:00
|
|
|
async def async_setup_platform(hass, config, async_add_entities,
|
|
|
|
discovery_info=None):
|
2018-11-20 13:59:34 +00:00
|
|
|
"""Old method of setting up Z-Wave locks."""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
|
|
|
"""Set up Z-Wave Lock from Config Entry."""
|
|
|
|
@callback
|
|
|
|
def async_add_lock(lock):
|
|
|
|
"""Add Z-Wave Lock."""
|
|
|
|
async_add_entities([lock])
|
|
|
|
|
|
|
|
async_dispatcher_connect(hass, 'zwave_new_lock', async_add_lock)
|
2017-03-21 15:55:21 +00:00
|
|
|
|
2017-05-26 04:11:02 +00:00
|
|
|
network = hass.data[zwave.const.DATA_NETWORK]
|
2017-01-27 05:45:04 +00:00
|
|
|
|
|
|
|
def set_usercode(service):
|
|
|
|
"""Set the usercode to index X on the lock."""
|
|
|
|
node_id = service.data.get(zwave.const.ATTR_NODE_ID)
|
2017-04-12 17:09:29 +00:00
|
|
|
lock_node = network.nodes[node_id]
|
2017-01-27 05:45:04 +00:00
|
|
|
code_slot = service.data.get(ATTR_CODE_SLOT)
|
|
|
|
usercode = service.data.get(ATTR_USERCODE)
|
|
|
|
|
|
|
|
for value in lock_node.get_values(
|
|
|
|
class_id=zwave.const.COMMAND_CLASS_USER_CODE).values():
|
|
|
|
if value.index != code_slot:
|
|
|
|
continue
|
2017-05-19 11:40:26 +00:00
|
|
|
if len(str(usercode)) < 4:
|
2017-05-02 16:18:47 +00:00
|
|
|
_LOGGER.error("Invalid code provided: (%s) "
|
2017-05-19 11:40:26 +00:00
|
|
|
"usercode must be atleast 4 and at most"
|
|
|
|
" %s digits",
|
2017-01-27 05:45:04 +00:00
|
|
|
usercode, len(value.data))
|
2017-03-21 15:55:21 +00:00
|
|
|
break
|
2017-01-27 05:45:04 +00:00
|
|
|
value.data = str(usercode)
|
|
|
|
break
|
|
|
|
|
|
|
|
def get_usercode(service):
|
|
|
|
"""Get a usercode at index X on the lock."""
|
|
|
|
node_id = service.data.get(zwave.const.ATTR_NODE_ID)
|
2017-04-12 17:09:29 +00:00
|
|
|
lock_node = network.nodes[node_id]
|
2017-01-27 05:45:04 +00:00
|
|
|
code_slot = service.data.get(ATTR_CODE_SLOT)
|
|
|
|
|
|
|
|
for value in lock_node.get_values(
|
|
|
|
class_id=zwave.const.COMMAND_CLASS_USER_CODE).values():
|
|
|
|
if value.index != code_slot:
|
|
|
|
continue
|
2017-05-02 16:18:47 +00:00
|
|
|
_LOGGER.info("Usercode at slot %s is: %s", value.index, value.data)
|
2017-01-27 05:45:04 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
def clear_usercode(service):
|
|
|
|
"""Set usercode to slot X on the lock."""
|
|
|
|
node_id = service.data.get(zwave.const.ATTR_NODE_ID)
|
2017-04-12 17:09:29 +00:00
|
|
|
lock_node = network.nodes[node_id]
|
2017-01-27 05:45:04 +00:00
|
|
|
code_slot = service.data.get(ATTR_CODE_SLOT)
|
|
|
|
data = ''
|
|
|
|
|
|
|
|
for value in lock_node.get_values(
|
|
|
|
class_id=zwave.const.COMMAND_CLASS_USER_CODE).values():
|
|
|
|
if value.index != code_slot:
|
|
|
|
continue
|
|
|
|
for i in range(len(value.data)):
|
|
|
|
data += '\0'
|
|
|
|
i += 1
|
|
|
|
_LOGGER.debug('Data to clear lock: %s', data)
|
|
|
|
value.data = data
|
2017-05-02 16:18:47 +00:00
|
|
|
_LOGGER.info("Usercode at slot %s is cleared", value.index)
|
2017-01-27 05:45:04 +00:00
|
|
|
break
|
|
|
|
|
2017-05-02 16:18:47 +00:00
|
|
|
hass.services.async_register(
|
|
|
|
DOMAIN, SERVICE_SET_USERCODE, set_usercode,
|
2018-01-07 22:54:16 +00:00
|
|
|
schema=SET_USERCODE_SCHEMA)
|
2017-05-02 16:18:47 +00:00
|
|
|
hass.services.async_register(
|
|
|
|
DOMAIN, SERVICE_GET_USERCODE, get_usercode,
|
2018-01-07 22:54:16 +00:00
|
|
|
schema=GET_USERCODE_SCHEMA)
|
2017-05-02 16:18:47 +00:00
|
|
|
hass.services.async_register(
|
|
|
|
DOMAIN, SERVICE_CLEAR_USERCODE, clear_usercode,
|
2018-01-07 22:54:16 +00:00
|
|
|
schema=CLEAR_USERCODE_SCHEMA)
|
2017-03-21 15:55:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_device(node, values, **kwargs):
|
2017-05-02 16:18:47 +00:00
|
|
|
"""Create Z-Wave entity device."""
|
2017-03-14 23:55:33 +00:00
|
|
|
return ZwaveLock(values)
|
2016-05-03 02:38:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice):
|
2017-02-02 04:57:57 +00:00
|
|
|
"""Representation of a Z-Wave Lock."""
|
2016-05-03 02:38:48 +00:00
|
|
|
|
2017-03-14 23:55:33 +00:00
|
|
|
def __init__(self, values):
|
2017-02-02 04:57:57 +00:00
|
|
|
"""Initialize the Z-Wave lock device."""
|
2017-03-14 23:55:33 +00:00
|
|
|
zwave.ZWaveDeviceEntity.__init__(self, values, DOMAIN)
|
2016-12-27 21:08:35 +00:00
|
|
|
self._state = None
|
|
|
|
self._notification = None
|
2017-01-20 21:01:36 +00:00
|
|
|
self._lock_status = None
|
2017-02-02 04:57:57 +00:00
|
|
|
self._v2btze = None
|
2018-11-06 10:00:48 +00:00
|
|
|
self._state_workaround = False
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
self._track_message_workaround = False
|
|
|
|
self._previous_message = None
|
2019-01-05 08:59:43 +00:00
|
|
|
self._alarm_type_workaround = False
|
2017-02-02 04:57:57 +00:00
|
|
|
|
|
|
|
# Enable appropriate workaround flags for our device
|
|
|
|
# Make sure that we have values for the key before converting to int
|
2017-03-14 23:55:33 +00:00
|
|
|
if (self.node.manufacturer_id.strip() and
|
|
|
|
self.node.product_id.strip()):
|
|
|
|
specific_sensor_key = (int(self.node.manufacturer_id, 16),
|
|
|
|
int(self.node.product_id, 16))
|
2017-02-02 04:57:57 +00:00
|
|
|
if specific_sensor_key in DEVICE_MAPPINGS:
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
workaround = DEVICE_MAPPINGS[specific_sensor_key]
|
|
|
|
if workaround & WORKAROUND_V2BTZE:
|
2017-02-02 04:57:57 +00:00
|
|
|
self._v2btze = 1
|
|
|
|
_LOGGER.debug("Polycontrol Danalock v2 BTZE "
|
|
|
|
"workaround enabled")
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
if workaround & WORKAROUND_DEVICE_STATE:
|
2018-11-06 10:00:48 +00:00
|
|
|
self._state_workaround = True
|
|
|
|
_LOGGER.debug(
|
|
|
|
"Notification device state workaround enabled")
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
if workaround & WORKAROUND_TRACK_MESSAGE:
|
|
|
|
self._track_message_workaround = True
|
|
|
|
_LOGGER.debug("Message tracking workaround enabled")
|
2019-01-05 08:59:43 +00:00
|
|
|
if workaround & WORKAROUND_ALARM_TYPE:
|
|
|
|
self._alarm_type_workaround = True
|
|
|
|
_LOGGER.debug(
|
|
|
|
"Alarm Type device state workaround enabled")
|
2016-12-27 21:08:35 +00:00
|
|
|
self.update_properties()
|
2016-05-03 02:38:48 +00:00
|
|
|
|
2016-12-27 21:08:35 +00:00
|
|
|
def update_properties(self):
|
2017-05-02 20:47:20 +00:00
|
|
|
"""Handle data changes for node values."""
|
2017-03-14 23:55:33 +00:00
|
|
|
self._state = self.values.primary.data
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
_LOGGER.debug("lock state set to %s", self._state)
|
2017-03-14 23:55:33 +00:00
|
|
|
if self.values.access_control:
|
|
|
|
notification_data = self.values.access_control.data
|
2017-02-09 12:40:35 +00:00
|
|
|
self._notification = LOCK_NOTIFICATION.get(str(notification_data))
|
2018-11-06 10:00:48 +00:00
|
|
|
if self._state_workaround:
|
|
|
|
self._state = LOCK_STATUS.get(str(notification_data))
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
_LOGGER.debug("workaround: lock state set to %s", self._state)
|
2017-03-14 23:55:33 +00:00
|
|
|
if self._v2btze:
|
|
|
|
if self.values.v2btze_advanced and \
|
|
|
|
self.values.v2btze_advanced.data == CONFIG_ADVANCED:
|
|
|
|
self._state = LOCK_STATUS.get(str(notification_data))
|
2017-05-02 16:18:47 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"Lock state set from Access Control value and is %s, "
|
|
|
|
"get=%s", str(notification_data), self.state)
|
2017-03-14 23:55:33 +00:00
|
|
|
|
Set lock status correctly for Schlage BE469 Z-Wave locks (#18737)
* Set lock status correctly for Schlage BE469 Z-Wave locks
PR #17386 attempted to improve the state of z-wave lock tracking for
some problematic models. However, it operated under a flawed
assumptions. Namely, that we can always trust `self.values` to have
fresh data, and that the Schlage BE469 sends alarm reports after every
lock event. We can't trust `self.values`, and the Schlage is very
broken. :)
When we receive a notification from the driver about a state change,
we call `update_properties` - but we can (and do!) have _stale_
properties left over from previous updates. #17386 really works best
if you start from a clean slate each time. However, `update_properties`
is called on every value update, and we don't get a reason why.
Moreover, values that weren't just refreshed are not removed. So blindly
looking at something like `self.values.access_control` when deciding to
apply a workaround is not going to always be correct - it may or may not
be, depending on what happened in the past.
For the sad case of the BE469, here are the Z-Wave events that happen
under various circumstances:
RF Lock / Unlock:
- Send: door lock command set
- Receive: door lock report
- Send: door lock command get
- Receive: door lock report
Manual lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Keypad lock / Unlock:
- Receive: alarm
- Send: door lock command get
- Receive: door lock report
Thus, this PR introduces yet another work around - we track the current
and last z-wave command that the driver saw, and make assumptions based
on the sequence of events. This seems to be the most reliable way to go
- simply asking the driver to refresh various states doesn't clear out
alarms the way you would expect; this model doesn't support the access
control logging commands; and trying to manually clear out alarm state
when calling RF lock/unlock was tricky.
The lock state, when the z-wave network restarts, may look out of sync
for a few minutes. However, after the full network restart is complete,
everything looks good in my testing.
* Fix linter
2018-12-07 20:17:34 +00:00
|
|
|
if self._track_message_workaround:
|
|
|
|
this_message = self.node.stats['lastReceivedMessage'][5]
|
|
|
|
|
|
|
|
if this_message == zwave.const.COMMAND_CLASS_DOOR_LOCK:
|
|
|
|
self._state = self.values.primary.data
|
|
|
|
_LOGGER.debug("set state to %s based on message tracking",
|
|
|
|
self._state)
|
|
|
|
if self._previous_message == \
|
|
|
|
zwave.const.COMMAND_CLASS_DOOR_LOCK:
|
|
|
|
if self._state:
|
|
|
|
self._notification = \
|
|
|
|
LOCK_NOTIFICATION[NOTIFICATION_RF_LOCK]
|
|
|
|
self._lock_status = \
|
|
|
|
LOCK_ALARM_TYPE[ALARM_RF_LOCK]
|
|
|
|
else:
|
|
|
|
self._notification = \
|
|
|
|
LOCK_NOTIFICATION[NOTIFICATION_RF_UNLOCK]
|
|
|
|
self._lock_status = \
|
|
|
|
LOCK_ALARM_TYPE[ALARM_RF_UNLOCK]
|
|
|
|
return
|
|
|
|
|
|
|
|
self._previous_message = this_message
|
|
|
|
|
2017-03-14 23:55:33 +00:00
|
|
|
if not self.values.alarm_type:
|
|
|
|
return
|
|
|
|
|
|
|
|
alarm_type = self.values.alarm_type.data
|
|
|
|
if self.values.alarm_level:
|
|
|
|
alarm_level = self.values.alarm_level.data
|
|
|
|
else:
|
|
|
|
alarm_level = None
|
|
|
|
|
2017-02-09 12:40:35 +00:00
|
|
|
if not alarm_type:
|
|
|
|
return
|
2019-01-05 08:59:43 +00:00
|
|
|
|
|
|
|
if self._alarm_type_workaround:
|
|
|
|
self._state = LOCK_STATUS.get(str(alarm_type))
|
|
|
|
_LOGGER.debug("workaround: lock state set to %s -- alarm type: %s",
|
|
|
|
self._state, str(alarm_type))
|
|
|
|
|
2017-07-06 03:02:16 +00:00
|
|
|
if alarm_type == 21:
|
2017-02-09 12:40:35 +00:00
|
|
|
self._lock_status = '{}{}'.format(
|
|
|
|
LOCK_ALARM_TYPE.get(str(alarm_type)),
|
|
|
|
MANUAL_LOCK_ALARM_LEVEL.get(str(alarm_level)))
|
2017-03-21 15:55:21 +00:00
|
|
|
return
|
|
|
|
if str(alarm_type) in ALARM_TYPE_STD:
|
2017-02-09 12:40:35 +00:00
|
|
|
self._lock_status = '{}{}'.format(
|
|
|
|
LOCK_ALARM_TYPE.get(str(alarm_type)), str(alarm_level))
|
|
|
|
return
|
2017-07-06 03:02:16 +00:00
|
|
|
if alarm_type == 161:
|
2017-02-09 12:40:35 +00:00
|
|
|
self._lock_status = '{}{}'.format(
|
|
|
|
LOCK_ALARM_TYPE.get(str(alarm_type)),
|
|
|
|
TAMPER_ALARM_LEVEL.get(str(alarm_level)))
|
|
|
|
return
|
|
|
|
if alarm_type != 0:
|
|
|
|
self._lock_status = LOCK_ALARM_TYPE.get(str(alarm_type))
|
|
|
|
return
|
2017-01-20 21:01:36 +00:00
|
|
|
|
2016-05-03 02:38:48 +00:00
|
|
|
@property
|
|
|
|
def is_locked(self):
|
|
|
|
"""Return true if device is locked."""
|
|
|
|
return self._state
|
|
|
|
|
|
|
|
def lock(self, **kwargs):
|
|
|
|
"""Lock the device."""
|
2017-03-14 23:55:33 +00:00
|
|
|
self.values.primary.data = True
|
2016-05-03 02:38:48 +00:00
|
|
|
|
|
|
|
def unlock(self, **kwargs):
|
|
|
|
"""Unlock the device."""
|
2017-03-14 23:55:33 +00:00
|
|
|
self.values.primary.data = False
|
2016-12-27 21:08:35 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def device_state_attributes(self):
|
|
|
|
"""Return the device specific state attributes."""
|
|
|
|
data = super().device_state_attributes
|
|
|
|
if self._notification:
|
|
|
|
data[ATTR_NOTIFICATION] = self._notification
|
2017-01-20 21:01:36 +00:00
|
|
|
if self._lock_status:
|
|
|
|
data[ATTR_LOCK_STATUS] = self._lock_status
|
2016-12-27 21:08:35 +00:00
|
|
|
return data
|