Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add zwave_js.set_lock_configuration service #103595

Merged
merged 8 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions homeassistant/components/zwave_js/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
SERVICE_RESET_METER = "reset_meter"
SERVICE_SET_CONFIG_PARAMETER = "set_config_parameter"
SERVICE_SET_LOCK_USERCODE = "set_lock_usercode"
SERVICE_SET_LOCK_CONFIGURATION = "set_lock_configuration"
SERVICE_SET_VALUE = "set_value"

ATTR_NODES = "nodes"
Expand All @@ -118,6 +119,19 @@
# invoke CC API
ATTR_METHOD_NAME = "method_name"
ATTR_PARAMETERS = "parameters"
# lock set configuration
ATTR_AUTO_RELOCK_TIME = "auto_relock_time"
ATTR_BLOCK_TO_BLOCK = "block_to_block"
ATTR_HOLD_AND_RELEASE_TIME = "hold_and_release_time"
ATTR_INSIDE_HANDLES_CAN_OPEN_DOOR_CONFIGURATION = (
"inside_handles_can_open_door_configuration"
)
ATTR_LOCK_TIMEOUT = "lock_timeout"
ATTR_OPERATION_TYPE = "operation_type"
ATTR_OUTSIDE_HANDLES_CAN_OPEN_DOOR_CONFIGURATION = (
"outside_handles_can_open_door_configuration"
)
ATTR_TWIST_ASSIST = "twist_assist"

ADDON_SLUG = "core_zwave_js"

Expand Down
92 changes: 87 additions & 5 deletions homeassistant/components/zwave_js/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
ATTR_USERCODE,
LOCK_CMD_CLASS_TO_LOCKED_STATE_MAP,
LOCK_CMD_CLASS_TO_PROPERTY_MAP,
DoorLockCCConfigurationSetOptions,
DoorLockMode,
OperationType,
)
from zwave_js_server.exceptions import BaseZwaveJSServerError
from zwave_js_server.util.lock import clear_usercode, set_usercode
from zwave_js_server.util.lock import clear_usercode, set_configuration, set_usercode

from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN, LockEntity
from homeassistant.config_entries import ConfigEntry
Expand All @@ -26,10 +28,19 @@
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import (
ATTR_AUTO_RELOCK_TIME,
ATTR_BLOCK_TO_BLOCK,
ATTR_HOLD_AND_RELEASE_TIME,
ATTR_INSIDE_HANDLES_CAN_OPEN_DOOR_CONFIGURATION,
ATTR_LOCK_TIMEOUT,
ATTR_OPERATION_TYPE,
ATTR_OUTSIDE_HANDLES_CAN_OPEN_DOOR_CONFIGURATION,
ATTR_TWIST_ASSIST,
DATA_CLIENT,
DOMAIN,
LOGGER,
SERVICE_CLEAR_LOCK_USERCODE,
SERVICE_SET_LOCK_CONFIGURATION,
SERVICE_SET_LOCK_USERCODE,
)
from .discovery import ZwaveDiscoveryInfo
Expand All @@ -47,6 +58,7 @@
STATE_LOCKED: True,
},
}
UNIT16_SCHEMA = vol.All(vol.Coerce(int), vol.Range(min=0, max=65535))


async def async_setup_entry(
Expand Down Expand Up @@ -92,6 +104,30 @@ def async_add_lock(info: ZwaveDiscoveryInfo) -> None:
"async_clear_lock_usercode",
)

platform.async_register_entity_service(
SERVICE_SET_LOCK_CONFIGURATION,
{
vol.Required(ATTR_OPERATION_TYPE): vol.All(
cv.string,
vol.Upper,
vol.In(["TIMED", "CONSTANT"]),
lambda x: OperationType[x],
),
vol.Optional(ATTR_LOCK_TIMEOUT): UNIT16_SCHEMA,
vol.Optional(ATTR_OUTSIDE_HANDLES_CAN_OPEN_DOOR_CONFIGURATION): vol.All(
[cv.boolean], vol.Length(4, 4)
),
vol.Optional(ATTR_INSIDE_HANDLES_CAN_OPEN_DOOR_CONFIGURATION): vol.All(
[cv.boolean], vol.Length(4, 4)
),
vol.Optional(ATTR_AUTO_RELOCK_TIME): UNIT16_SCHEMA,
vol.Optional(ATTR_HOLD_AND_RELEASE_TIME): UNIT16_SCHEMA,
vol.Optional(ATTR_TWIST_ASSIST): vol.Coerce(bool),
vol.Optional(ATTR_BLOCK_TO_BLOCK): vol.Coerce(bool),
},
"async_set_lock_configuration",
)


class ZWaveLock(ZWaveBaseEntity, LockEntity):
"""Representation of a Z-Wave lock."""
Expand Down Expand Up @@ -138,16 +174,62 @@ async def async_set_lock_usercode(self, code_slot: int, usercode: str) -> None:
await set_usercode(self.info.node, code_slot, usercode)
except BaseZwaveJSServerError as err:
raise HomeAssistantError(
f"Unable to set lock usercode on code_slot {code_slot}: {err}"
f"Unable to set lock usercode on lock {self.entity_id} code_slot "
f"{code_slot}: {err}"
) from err
LOGGER.debug("User code at slot %s set", code_slot)
LOGGER.debug("User code at slot %s on lock %s set", code_slot, self.entity_id)

async def async_clear_lock_usercode(self, code_slot: int) -> None:
"""Clear the usercode at index X on the lock."""
try:
await clear_usercode(self.info.node, code_slot)
except BaseZwaveJSServerError as err:
raise HomeAssistantError(
f"Unable to clear lock usercode on code_slot {code_slot}: {err}"
f"Unable to clear lock usercode on lock {self.entity_id} code_slot "
f"{code_slot}: {err}"
) from err
LOGGER.debug("User code at slot %s cleared", code_slot)
LOGGER.debug(
"User code at slot %s on lock %s cleared", code_slot, self.entity_id
)

async def async_set_lock_configuration(
self,
operation_type: OperationType,
lock_timeout: int | None = None,
outside_handles_can_open_door_configuration: list[bool] | None = None,
inside_handles_can_open_door_configuration: list[bool] | None = None,
auto_relock_time: int | None = None,
hold_and_release_time: int | None = None,
twist_assist: bool | None = None,
block_to_block: bool | None = None,
) -> None:
"""Set the lock configuration."""
params: dict[str, Any] = {"operation_type": operation_type}
for attr, val in (
("lock_timeout_configuration", lock_timeout),
(
"outside_handles_can_open_door_configuration",
outside_handles_can_open_door_configuration,
),
(
"inside_handles_can_open_door_configuration",
inside_handles_can_open_door_configuration,
),
("auto_relock_time", auto_relock_time),
("hold_and_release_time", hold_and_release_time),
("twist_assist", twist_assist),
("block_to_block", block_to_block),
):
if val is not None:
params[attr] = val
configuration = DoorLockCCConfigurationSetOptions(**params)
result = await set_configuration(
self.info.node.endpoints[self.info.primary_value.endpoint or 0],
configuration,
)
if result is None:
return
msg = f"Result status is {result.status}"
if result.remaining_duration is not None:
msg += f" and remaining duration is {str(result.remaining_duration)}"
LOGGER.info("%s after setting lock configuration for %s", msg, self.entity_id)
59 changes: 59 additions & 0 deletions homeassistant/components/zwave_js/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,65 @@ set_lock_usercode:
selector:
text:

set_lock_configuration:
target:
entity:
domain: lock
integration: zwave_js
fields:
operation_type:
required: true
example: timed
selector:
select:
options:
- constant
- timed
lock_timeout:
required: false
example: 1
selector:
number:
min: 0
max: 65535
unit_of_measurement: sec
outside_handles_can_open_door_configuration:
required: false
example: [true, true, true, false]
selector:
object:
inside_handles_can_open_door_configuration:
required: false
example: [true, true, true, false]
selector:
object:
auto_relock_time:
required: false
example: 1
selector:
number:
min: 0
max: 65535
unit_of_measurement: sec
hold_and_release_time:
required: false
example: 1
selector:
number:
min: 0
max: 65535
unit_of_measurement: sec
twist_assist:
required: false
example: true
selector:
boolean:
block_to_block:
required: false
example: true
selector:
boolean:

set_config_parameter:
target:
entity:
Expand Down
38 changes: 38 additions & 0 deletions homeassistant/components/zwave_js/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,44 @@
"description": "The Notification Event number as defined in the Z-Wave specs."
}
}
},
"set_lock_configuration": {
"name": "Set lock configuration",
"description": "Sets the configuration for a lock.",
"fields": {
"operation_type": {
"name": "Operation Type",
"description": "The operation type of the lock."
},
"lock_timeout": {
"name": "Lock timeout",
"description": "Seconds until lock mode times out. Should only be used if operation type is `timed`."
},
"outside_handles_can_open_door_configuration": {
"name": "Outside handles can open door configuration",
"description": "A list of four booleans which indicate which outside handles can open the door."
raman325 marked this conversation as resolved.
Show resolved Hide resolved
},
"inside_handles_can_open_door_configuration": {
"name": "Inside handles can open door configuration",
"description": "A list of four booleans which indicate which inside handles can open the door."
},
"auto_relock_time": {
"name": "Auto relock time",
"description": "Duration in seconds until lock returns to secure state. Only enforced when operation type is `constant`."
},
"hold_and_release_time": {
"name": "Hold and release time",
"description": "Duration in seconds the latch stays retracted."
},
"twist_assist": {
"name": "Twist assist",
"description": "Enable Twist Assist."
},
"block_to_block": {
"name": "Block to block",
"description": "Enable block-to-block functionality."
}
}
}
}
}