Non-addressable Ademco alarm panels (4120/Vista) → Home Assistant via ESP32 + ESPHome

Hi all,

I wanted to share a project I’ve been working on: reading an old Ademco 4120 alarm panel into Home Assistant, natively, over its keypad bus — no addressable-bus board, no cloud, no subscription. Just an ESP32 running ESPHome and a couple of optocouplers.

These older panels (4110/4120, Vista-10 and relatives) use a non-addressable keypad bus — the kind that drives fixed-word keypads at “Address 31.” Existing tools like AlarmDecoder/VistaECP target the addressable ECP bus, so they don’t help here. This project fills that gap.

What it does:

  • Armed / disarmed / alarm state
  • Zone reporting (which zone is open / which tripped the alarm), with named zones
  • Chime, ready, bypass, low-battery flags
  • Mains-loss (blackout) detection — the panel signals it on the bus and the firmware decodes it, with hysteresis
  • A custom mobile-friendly Lovelace keypad card that mirrors the physical keypad
  • Sends keys back to the panel (arm/disarm) over the bus

How: the whole thing is a custom ESPHome component that decodes the 2400-baud keybus. The tricky part was reverse-engineering the fixed-word keypad’s frame format — the README documents the protocol in detail (state matrix, zone mapping, blackout frame, etc.), so it doubles as reference material if you want to adapt it to a different panel/keypad.

Hardware: an ESP32, two 4N35 optocouplers, a handful of resistors, a diode. Roughly €10 in parts. Full schematic and wiring in the repo.

Tested on: Ademco 4120 with a 4127 fixed-word keypad. Other fixed-word keypads (4137, 5137, 6128, 6150) are likely compatible but untested — if you have one, bus logs are very welcome to help build a universal map.

Credit to gregrenda’s earlier work on these panels, which gave me cross-checks for the bus timing and several state bits; this project maps a different keypad model (4127) and ports everything to ESP32/ESPHome with native HA integration.

Repo (code, wiring, protocol docs): GitHub - Stef86fal/esphome-ademco31: ESPHome component for non-addressable Ademco/Honeywell alarm panels (keypad Address 31) — read arming, alarm, zones & more in Home Assistant. An AlarmDecoder alternative for older Vista/4100-series panels with fixed-word keypads. · GitHub

Happy to answer questions, and PRs / logs from other panels are welcome. Hope it’s useful to someone else with a 30-year-old panel they’d rather integrate than replace.

:new_button: Version 2 is up — ESPHome 2026.x support, bypass/alarm-zone detection, and more reliable state reading

Hi all — a big update just landed in the repo for anyone following this non-addressable Ademco keypad-bus → Home Assistant project (tested on a 4120 + 4127 fixed-word keypad).

Everything below was validated on a real panel through diagnostic bus captures. The core protocol decoding from v1 is unchanged — v2 is modernization, three new protocol discoveries, more robust reading, and quality-of-life sensors.

What’s new:

:gear: ESPHome 2026.x compatible. Ported key transmission from the removed legacy driver/rmt.h to the new driver/rmt_tx.h API — v1 was pinned to ESPHome 2025.3.3, v2 compiles cleanly on 2026.7. (Includes a core-affinity fix that the new RMT API needs, and the explicit esp_driver_rmt dependency declaration.)
:prohibited: Bypassed-zone readout (new sensor): shows which zones are currently excluded, decoded passively from the bus — no * scan needed. The key finding: a real bypass emits the zone separator followed by a dedicated event frame (B1=D6 B2=5E B3=16), which cleanly distinguishes it from a look-alike transient frame that appears when zones are just physically open.
:police_car_light: Alarm-triggered zone entity is now actually wired up (it existed in the code but was never exposed, and had a latent vote-timing bug — both fixed).
:white_check_mark: More reliable state reading. Chime / ready / bypass / battery are now read only from the real status frame (identified by B0=0xF9), instead of whatever frame happened to precede the 0x0C. This kills the occasional flicker/false readings. Fails safe — if a frame isn’t recognized, sensors hold their last value; armed/alarm are unaffected.
:bar_chart: Diagnostic sensors: ESP32 temperature, WiFi signal + BSSID (handy on a mesh), uptime, bus-communication watchdog, restart button.
:memo: Documented several panel quirks for others hitting them: the panel’s idle “quiet mode” (status frame goes sparse until you interact), and the fact that — like gregrenda’s reference implementation — the first command after a long idle can be lost and need re-sending (inherent to the one-key-per-sync protocol; auto-retransmission was tried and reverted because it’s unsafe on a security panel).
Still only confirmed on the 4120/4127; other fixed-word keypads (4137/5137/6128) are likely compatible but unconfirmed. If you have different hardware, bus logs and PRs are very welcome — the README explains how to capture with diagnostic_mode: true.

Full changelog and protocol notes in the README’s new VERSION 2 section