Skip to content
raman325 edited this page Oct 27, 2026 · 4 revisions

External Keypads and Code Readers

Lock Code Manager can check a code entered somewhere it doesn't control — an ESPHome keypad, a Z-Wave alarm keypad, a Wiegand reader, an intercom — against the users, PINs and conditions you already configured, and record that the credential was used.

Because the check runs against the same users driving your real locks, one PIN and one schedule cover both. Disable a user and their code stops working at the keypad too.

Lock Code Manager never opens anything. It answers, records, and fires an event; what happens next is yours.

A keypad-only setup needs no lock

Since 5.3.0, a configuration can manage no locks at all. Create one, leave the lock picker empty, and add your users and PINs as usual — credentials live in the configuration, and use_credential checks against them. Nothing needs to stand in for a lock you don't have.

Earlier versions required at least one lock, and the advice was to add a Virtual lock as a placeholder. That is no longer necessary. An existing entry set up that way keeps working; you can remove the Virtual lock from it if it was only ever a placeholder.

The action

- action: lock_code_manager.use_credential
  data:
    config_entry_title: House Locks   # or config_entry_id
    code: "{{ entered_code }}"
    source: sensor.keypad_code        # where the code was entered
    target: lock.garage               # what it was used against
  response_variable: check
Response Value
valid true / false
user the user's name when valid, else null
reason unknown_code, user_disabled, or condition_not_met when invalid

source and target are both required and may be any entity — a cover, an alarm panel, a switch, anything you associate the use with. If you have no natural entity for one, a template entity works.

Naming the same entity as both is fine, and is what a lock reporting a code used at its own keypad looks like.

reason distinguishes a real-but-inactive credential from an unknown one, and the action does no rate limiting or logging — so don't wire it to untrusted input.

What a valid code produces

The lock_code_manager_credential_used event, always:

Field
name the user
config_entry_id, config_entry_title which configuration
source where entered
target what it was used against
credential_type the kind of credential — pin today
operation what the device did: lock, unlock, or unknown

use_credential always reports operation: unknown: it was told a credential was used, not what happened next, and Lock Code Manager never actuates anything.

The same event fires when one of your locks reports a code used at its own keypad, so a single trigger covers both.

The user's credential_used event entity, always. It records every use of that user's credential whatever the target was — a lock, a cover, an alarm panel, or something Lock Code Manager knows nothing about. The entity stays available even when every lock in the entry is unreachable, so a use reported while the locks are down is not lost.

An invalid code produces nothing.

Act on it

Import the Credential Used blueprint (see Blueprints): trigger on a credential use, optionally filter by configuration, users, sources or targets, and supply whatever actions you like — unlock a door, open a cover, start a vacuum. Lock Code Manager needs to know nothing about what you're driving.

By hand:

automation:
  - alias: "Garage: open on a valid keypad code"
    triggers:
      - trigger: event
        event_type: lock_code_manager_credential_used
        event_data:
          source: sensor.keypad_code
    actions:
      - action: cover.open_cover
        target: {entity_id: cover.garage_door}

Security: keep the code out of your history

A keypad that publishes entered codes to an entity puts a cleartext PIN in that entity's state, and the recorder keeps state history. Exclude it:

recorder:
  exclude:
    entities:
      - sensor.keypad_code

Lock Code Manager never publishes that entity's state — only its entity ID. The recorder is the one place it can't help, because the entity belongs to the integration that publishes it.

An ESPHome keypad can ask Lock Code Manager directly, so the code never becomes an entity state at all; that's the most private option (see ESPHome keypads).

A keypad that sends codes as events instead, such as a Z-Wave keypad, puts the code in the event, and the recorder keeps events too. See Z-Wave alarm keypads for how to exclude it.

Recipes

  • ESPHome keypads: publish the code to an entity, let the keypad ask for itself, give different feedback per rejection reason, or fire an event instead.
  • Z-Wave alarm keypads: a Ring Alarm Keypad v2 (or another Entry Control keypad) arming and disarming Alarmo.

Things to know

PIN length is set by your strictest lock. Every user's PIN must satisfy every lock in the entry, so make your keypad's length limits agree (key_collector's min_length and max_length on ESPHome). A configuration with no locks has nothing imposing a length, so any code your keypad can send will do.

Whitespace is ignored on both stored PINs and submitted codes.

Codes are matched as text — nothing requires digits, though PINs are what this is designed for.

No rate limiting. Brute-force defence belongs on the device; a keypad that ignores input after several failures never troubles Home Assistant.

See also

Clone this wiki locally