Skip to content

ISSUE: ZWaveJS provider: async_clear_usercode fails silently on a User Code CC lock — codes can't be set/cleared, no log reason (v0.5.x) #665

Description

@gpatlanta

Describe the bug

NOTE: This was generated by Claude after resolving issues with keymaster on my environment (1 of 2 issues identified)

Environment
keymaster v0.5.1 (HACS)
Home Assistant 2026.5.4, Z-Wave JS (core)
Lock: Allegion/Schlage BE468, Z-Wave node 4, legacy User Code CC (not Credential CC)
Worked fine on the pre-provider line (v0.3.x/v0.4.0); regression appeared after updating to v0.5.x.
Symptom (default log level): code set/clear via keymaster does nothing, and the only log line is:

[custom_components.keymaster.coordinator] [Coordinator] side_door: Code Slot 8: Unable to clear PIN via provider
There is no accompanying [ZWaveJSProvider] line explaining why it failed. A device re-interview does not fix it; only a full Home Assistant restart does.

Analysis — in providers/zwave_js.py, async_clear_usercode() returns False, and the only return paths that log nothing route through _is_node_alive():

if not self._node:
return False # silent
try:
if self._node.status == NodeStatus.DEAD:
...
except Exception:
return False # silent
At the time of failure sensor.node_4_node_status reads alive, and the lock itself locks/unlocks normally (that's the zwave_js entity, not the provider) — yet the provider's clear fails silently. The most consistent explanation is that keymaster is holding a stale self._node reference whose .status access raises (the reference goes stale after a node re-interview, which replaces the underlying zwave_js node object). That also explains why a re-interview doesn't help but a full restart does.
Separately, the post-clear verification path (get_usercode read-back → "not yet cleared, will retry") only logs at DEBUG, so at default log level there is no diagnostic for either failure mode.

Suggested fixes

Re-resolve self._node from the current Z-Wave JS node registry on each operation (or refresh it on zwave_js reconnect / node re-interview) rather than caching a reference that can become stale.
Make the two silent return False paths in _is_node_alive() log a reason.
Promote the "not yet cleared, will retry" message above DEBUG after N retries so the failure is visible.
Workaround for others hitting this: manage codes via Dev Tools zwave_js.set_lock_usercode / clear_lock_usercode (writes straight to the lock, bypassing the provider), then full-restart HA.

Note: the usual "reload the integration to recover" doesn't work here because the reload itself crashes — see companion issue #___.

(Reporter resolved via restart + the zwave_js bypass and can't easily re-trigger for a live debug trace, but the failing code paths are above; a maintainer can confirm the stale-reference hypothesis with custom_components.keymaster: debug.)

Home Assistant version

2026.5.4, Z-Wave JS (core)

Keymaster version

0.5.1

Home Assistant installation type

Home Assistant Container (Docker)

Lock provider

schlage

Lock make and model

Schlage BE468

Steps to reproduce

At the time of failure sensor.node_4_node_status reads alive, and the lock itself locks/unlocks normally (that's the zwave_js entity, not the provider) — yet the provider's clear fails silently. The most consistent explanation is that keymaster is holding a stale self._node reference whose .status access raises (the reference goes stale after a node re-interview, which replaces the underlying zwave_js node object). That also explains why a re-interview doesn't help but a full restart does.
Separately, the post-clear verification path (get_usercode read-back → "not yet cleared, will retry") only logs at DEBUG, so at default log level there is no diagnostic for either failure mode.

Expected behavior

does not fail

Logs

# Setting/clearing a code does nothing. The ONLY line logged is this — note there is
# no accompanying [ZWaveJSProvider] line giving a reason (i.e. async_clear_usercode
# returns False through a path that logs nothing):

2026-06-21 14:10:52.224 ERROR (MainThread) [custom_components.keymaster.coordinator] [Coordinator] side_door: Code Slot 8: Unable to clear PIN via provider
2026-06-21 14:12:25.678 ERROR (MainThread) [custom_components.keymaster.coordinator] [Coordinator] side_door: Code Slot 8: Unable to clear PIN via provider
2026-06-22 11:56:36.493 ERROR (MainThread) [custom_components.keymaster.coordinator] [Coordinator] side_door: Code Slot 8: Unable to clear PIN via provider

# Confirmation of the "silent" part: grepping the entire log set for "ZWaveJSProvider"
# returns ZERO matches across every rotation — so none of the [ZWaveJSProvider] error/
# warning logs in async_clear_usercode fired; it returned via a non-logging path
# (_is_node_alive() -> `if not self._node` / `except Exception`, or the DEBUG-only
# "not yet cleared, will retry" verify path).

# Meanwhile the node reads alive and the lock locks/unlocks normally:
#   sensor.node_4_node_status = alive

Screenshots

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions