Skip to content

Instantly share code, notes, and snippets.

@e7d
Last active April 1, 2026 19:57
Show Gist options
  • Select an option

  • Save e7d/b361111875dc9db79ecb5da9ce2c75f9 to your computer and use it in GitHub Desktop.

Select an option

Save e7d/b361111875dc9db79ecb5da9ce2c75f9 to your computer and use it in GitHub Desktop.
Keypad Frient (KEPZB-110) for Alarmo
blueprint:
name: Keypad Frient (KEPZB-110) for Alarmo
description: >
Manage multiple PIN codes to arm and disarm an alarm using the Frient Keypad,
synchronizing the state with an Alarmo central or other alarm panel.<br>
You will need to create the same PIN codes in Alarmo, which also supports RFID badges.
domain: automation
input:
keypad:
name: Frient Keypad
description: >
The Frient KEPZB-110 keypad device.<br>
Must be paired via ZHA integration.
selector:
device:
integration: zha
alarm_panel:
name: Alarm Panel
description: >
The Frient alarm control panel entity.
selector:
entity:
domain: alarm_control_panel
mirror_alarm_panel:
name: Alarm Panel to Mirror
description: >
The alarm panel to sync with the Frient keypad (e.g., Alarmo).<br>
State changes on this panel will be mirrored to the Frient keypad and vice versa.
default: alarm_control_panel.alarmo
selector:
entity:
domain: alarm_control_panel
pin_list:
name: Valid PIN Codes
description: >
List of valid PIN codes for arming and disarming, separated by semicolons (;).<br>
Example: 1234;5678;9012
default: ""
selector:
text: {}
rfid_list:
name: RFID List
description: >
A list of valid RFID identifiers accepted by the keypad, separated by semicolons (;).<br>
Example: ABC123;DEF456
default: ""
selector:
text: {}
default_pin:
name: Default PIN Code for Frient Keypad
description: >
The default PIN code used to control the Frient alarm panel internally.<br>
This is sent when syncing state from the mirrored panel back to the Frient keypad.
default: ""
selector:
text: {}
mirror_default_pin:
name: Default PIN Code for Mirrored Alarm
description: >
The default PIN code used when forwarding commands to the mirrored alarm panel.<br>
Used as a fallback when no code is provided by the keypad event.
default: ""
selector:
text: {}
trigger:
- platform: event
event_type: zha_event
event_data:
device_id: !input keypad
command: arm
id: keypad_event
- platform: state
entity_id: !input mirror_alarm_panel
id: alarmo_state_change
variables:
pin_list_raw: !input pin_list
pin_list: "{{ pin_list_raw.split(';') | reject('eq', '') | list }}"
rfid_list_raw: !input rfid_list
rfid_list: "{{ rfid_list_raw.split(';') | reject('eq', '') | list }}"
mirror_default_pin: !input mirror_default_pin
default_pin: !input default_pin
alarm_panel: !input alarm_panel
mirror_alarm_panel_entity: !input mirror_alarm_panel
event_code: >
{{ (trigger.event.data.args.code | default('')) | string if trigger.id == 'keypad_event' else '' }}
event_arm_mode: >
{{ trigger.event.data.args.arm_mode | default(-1) | int if trigger.id == 'keypad_event' else -1 }}
code_to_send: >
{{ event_code if event_code != '' else mirror_default_pin }}
is_valid_pin: >
{{ (event_code | string) in pin_list if pin_list | length > 0 else true }}
is_valid_rfid: >
{{ event_code in rfid_list if rfid_list | length > 0 else false }}
is_authorized: >
{{ is_valid_pin or is_valid_rfid or event_code == '' }}
action:
- choose:
# ══════════════════════════════════════════════
# ── Keypad Event: forward to mirrored panel ──
# ══════════════════════════════════════════════
- conditions:
- condition: trigger
id: keypad_event
sequence:
# ── Circuit breaker: ignore echo from mirror sync ──
- condition: template
alias: "Circuit breaker: ignore default_pin echo from mirror sync"
value_template: "{{ event_code != default_pin }}"
- choose:
# ── Unauthorized code ──
- conditions:
- condition: template
value_template: "{{ not is_authorized }}"
sequence: []
# ── Disarm (arm_mode 0) ──
- conditions:
- condition: template
value_template: "{{ event_arm_mode == 0 and is_authorized }}"
- condition: not
conditions:
- condition: state
entity_id: !input mirror_alarm_panel
state: "disarmed"
sequence:
- service: alarm_control_panel.alarm_disarm
target:
entity_id: !input alarm_panel
data:
code: "{{ code_to_send }}"
- service: alarm_control_panel.alarm_disarm
target:
entity_id: !input mirror_alarm_panel
data:
code: "{{ code_to_send }}"
# ── Arm Home (arm_mode 1) ──
- conditions:
- condition: template
value_template: "{{ event_arm_mode == 1 and is_authorized }}"
- condition: not
conditions:
- condition: state
entity_id: !input mirror_alarm_panel
state: "armed_home"
sequence:
- service: alarm_control_panel.alarm_arm_home
target:
entity_id: !input alarm_panel
data:
code: "{{ code_to_send }}"
- service: alarm_control_panel.alarm_arm_home
target:
entity_id: !input mirror_alarm_panel
data:
code: "{{ code_to_send }}"
# ── Arm Night (arm_mode 2) ──
- conditions:
- condition: template
value_template: "{{ event_arm_mode == 2 and is_authorized }}"
- condition: not
conditions:
- condition: state
entity_id: !input mirror_alarm_panel
state: "armed_night"
sequence:
- service: alarm_control_panel.alarm_arm_night
target:
entity_id: !input alarm_panel
data:
code: "{{ code_to_send }}"
- service: alarm_control_panel.alarm_arm_night
target:
entity_id: !input mirror_alarm_panel
data:
code: "{{ code_to_send }}"
# ── Arm Away (arm_mode 3) ──
- conditions:
- condition: template
value_template: "{{ event_arm_mode == 3 and is_authorized }}"
- condition: not
conditions:
- condition: state
entity_id: !input mirror_alarm_panel
state: "armed_away"
sequence:
- service: alarm_control_panel.alarm_arm_away
target:
entity_id: !input alarm_panel
data:
code: "{{ code_to_send }}"
- service: alarm_control_panel.alarm_arm_away
target:
entity_id: !input mirror_alarm_panel
data:
code: "{{ code_to_send }}"
# ══════════════════════════════════════════════════════════
# ── Alarmo State Change: mirror back to Frient keypad ────
# ══════════════════════════════════════════════════════════
- conditions:
- condition: trigger
id: alarmo_state_change
- condition: template
value_template: >
{{ trigger.to_state.context.parent_id != this.context.id }}
sequence:
- condition: template
value_template: "{{ states(alarm_panel) != trigger.to_state.state }}"
- choose:
# ── Mirror: Disarmed ──
- conditions:
- condition: state
entity_id: !input mirror_alarm_panel
state: "disarmed"
sequence:
- service: alarm_control_panel.alarm_disarm
target:
entity_id: !input alarm_panel
data:
code: !input default_pin
# ── Mirror: Armed Home ──
- conditions:
- condition: state
entity_id: !input mirror_alarm_panel
state: "armed_home"
sequence:
- service: alarm_control_panel.alarm_arm_home
target:
entity_id: !input alarm_panel
data:
code: !input default_pin
# ── Mirror: Armed Night ──
- conditions:
- condition: state
entity_id: !input mirror_alarm_panel
state: "armed_night"
sequence:
- service: alarm_control_panel.alarm_arm_night
target:
entity_id: !input alarm_panel
data:
code: !input default_pin
# ── Mirror: Armed Away ──
- conditions:
- condition: state
entity_id: !input mirror_alarm_panel
state: "armed_away"
sequence:
- service: alarm_control_panel.alarm_arm_away
target:
entity_id: !input alarm_panel
data:
code: !input default_pin
# ── Mirror: Triggered ──
- conditions:
- condition: state
entity_id: !input mirror_alarm_panel
state: "triggered"
sequence:
- service: alarm_control_panel.alarm_trigger
target:
entity_id: !input alarm_panel
default: []
mode: single
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment