diff --git a/homeassistant/components/lock/services.yaml b/homeassistant/components/lock/services.yaml index 40a7c3ffe38..6b12d49302d 100644 --- a/homeassistant/components/lock/services.yaml +++ b/homeassistant/components/lock/services.yaml @@ -1,21 +1,57 @@ -lock: - description: Lock all or specified locks - - fields: - entity_id: - description: Name of lock to lock - example: 'lock.front_door' - code: - description: An optional code to lock the lock with - example: 1234 - -unlock: - description: Unlock all or specified locks - - fields: - entity_id: - description: Name of lock to unlock - example: 'lock.front_door' - code: - description: An optional code to unlock the lock with - example: 1234 +clear_usercode: + description: Clear a usercode from lock + + fields: + node_id: + description: Node id of the lock + example: 18 + code_slot: + description: Code slot to clear code from + example: 1 + +get_usercode: + description: Retrieve a usercode from lock + + fields: + node_id: + description: Node id of the lock + example: 18 + code_slot: + description: Code slot to retrive a code from + example: 1 + +lock: + description: Lock all or specified locks + + fields: + entity_id: + description: Name of lock to lock + example: 'lock.front_door' + code: + description: An optional code to lock the lock with + example: 1234 + +set_usercode: + description: Set a usercode to lock + + fields: + node_id: + description: Node id of the lock + example: 18 + code_slot: + description: Code slot to set the code + example: 1 + usercode: + description: Code to set + example: 1234 + +unlock: + description: Unlock all or specified locks + + fields: + entity_id: + description: Name of lock to unlock + example: 'lock.front_door' + code: + description: An optional code to unlock the lock with + example: 1234 diff --git a/homeassistant/components/lock/zwave.py b/homeassistant/components/lock/zwave.py index 9dbbb8e733f..16f2f82d81e 100644 --- a/homeassistant/components/lock/zwave.py +++ b/homeassistant/components/lock/zwave.py @@ -7,14 +7,25 @@ https://home-assistant.io/components/lock.zwave/ # Because we do not compile openzwave on CI # pylint: disable=import-error import logging +from os import path + +import voluptuous as vol from homeassistant.components.lock import DOMAIN, LockDevice from homeassistant.components import zwave +from homeassistant.config import load_yaml_config_file _LOGGER = logging.getLogger(__name__) ATTR_NOTIFICATION = 'notification' ATTR_LOCK_STATUS = 'lock_status' +ATTR_CODE_SLOT = 'code_slot' +ATTR_USERCODE = 'usercode' + +SERVICE_SET_USERCODE = 'set_usercode' +SERVICE_GET_USERCODE = 'get_usercode' +SERVICE_CLEAR_USERCODE = 'clear_usercode' + LOCK_NOTIFICATION = { 1: 'Manual Lock', 2: 'Manual Unlock', @@ -80,6 +91,22 @@ ALARM_TYPE_STD = [ 113 ] +SET_USERCODE_SCHEMA = vol.Schema({ + vol.Required(zwave.const.ATTR_NODE_ID): vol.Coerce(int), + vol.Required(ATTR_CODE_SLOT): vol.Coerce(int), + vol.Required(ATTR_USERCODE): vol.Coerce(int), +}) + +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), +}) + # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): @@ -90,13 +117,81 @@ def setup_platform(hass, config, add_devices, discovery_info=None): node = zwave.NETWORK.nodes[discovery_info[zwave.const.ATTR_NODE_ID]] value = node.values[discovery_info[zwave.const.ATTR_VALUE_ID]] + descriptions = load_yaml_config_file( + path.join(path.dirname(__file__), 'services.yaml')) + + def set_usercode(service): + """Set the usercode to index X on the lock.""" + node_id = service.data.get(zwave.const.ATTR_NODE_ID) + lock_node = zwave.NETWORK.nodes[node_id] + 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 + if len(str(usercode)) > 4: + _LOGGER.error('Invalid code provided: (%s)' + ' usercode must %s or less digits', + usercode, len(value.data)) + 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) + lock_node = zwave.NETWORK.nodes[node_id] + 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 + _LOGGER.info('Usercode at slot %s is: %s', value.index, value.data) + break + + def clear_usercode(service): + """Set usercode to slot X on the lock.""" + node_id = service.data.get(zwave.const.ATTR_NODE_ID) + lock_node = zwave.NETWORK.nodes[node_id] + 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 + _LOGGER.info('Usercode at slot %s is cleared', value.index) + break + if value.command_class != zwave.const.COMMAND_CLASS_DOOR_LOCK: return if value.type != zwave.const.TYPE_BOOL: return if value.genre != zwave.const.GENRE_USER: return - + if node.has_command_class(zwave.const.COMMAND_CLASS_USER_CODE): + hass.services.register(DOMAIN, + SERVICE_SET_USERCODE, + set_usercode, + descriptions.get(SERVICE_SET_USERCODE), + schema=SET_USERCODE_SCHEMA) + hass.services.register(DOMAIN, + SERVICE_GET_USERCODE, + get_usercode, + descriptions.get(SERVICE_GET_USERCODE), + schema=GET_USERCODE_SCHEMA) + hass.services.register(DOMAIN, + SERVICE_CLEAR_USERCODE, + clear_usercode, + descriptions.get(SERVICE_CLEAR_USERCODE), + schema=CLEAR_USERCODE_SCHEMA) value.set_change_verified(False) add_devices([ZwaveLock(value)])