An alarm that arms through a vendor’s server stops arming when that server or your internet line goes away. A Home Assistant alarm system keeps the decision on your own box: sensors report over Z-Wave, Zigbee or wired zones, Home Assistant decides whether the house is armed, and the siren and keypad answer locally. The hardware matters less than people expect, because the arming logic is where most setups fail quietly.
We cover the alarm brain (the manual panel and Alarmo), Konnected wired retrofits, Envisalink for an existing DSC or Honeywell panel, Ring Alarm through ring-mqtt, and the Zigbee and Z-Wave keypads, sensors and sirens that finish a local build. The alarm logic was tested in October 2026 on Home Assistant 2026.10.4 with Alarmo 1.10.19 on an Ubuntu 26.04 lab VM, and every arm, trigger and refused code quoted below came from that box.
Quick picks for a Home Assistant alarm system
Read the path column first, because it decides what else you have to own and whether the alarm still arms with the internet down. Prices are Amazon US bands checked on 29 October 2026, so check the live price before you order.
| Job | Pick | Path into Home Assistant | Local? | Price band |
|---|---|---|---|---|
| Wired house, old alarm sensors | Konnected Alarm Panel Pro 12-zone kit | ESPHome | Yes | about $220 |
| Cheapest real keypad | Ring Alarm Keypad (2nd Gen) | Z-Wave JS, no Ring base needed | Yes | about $20 |
| Zigbee keypad with RFID | frient Intelligent Keypad | Zigbee2MQTT | Yes | about $100 to $105 |
| Monitored kit you can also automate | Ring Alarm Pro 8-piece | ring-mqtt over MQTT | No, Ring cloud | about $300 |
| Keep a DSC or Vista panel | EyezOn Envisalink EVL-4EZR | envisalink integration | Yes | about $100 |
| Z-Wave door contact | Zooz ZSE41 Open|Close XS | Z-Wave JS | Yes | about $40 |
| Zigbee door contact | Aqara Door and Window Sensor | ZHA or Zigbee2MQTT | Yes | about $17 |
| Siren with battery backup | Zooz ZSE50 Siren & Chime | Z-Wave JS | Yes | about $50 |
| Z-Wave radio for all of it | Home Assistant Connect ZWA-2 | Z-Wave JS | Yes | about $79 |
Every row marked local keeps working when the WAN drops, provided Home Assistant itself is on a UPS. The one cloud row is there on purpose, because plenty of people want professional monitoring and Home Assistant automations at the same time, and ring-mqtt is the cleanest way to get both.
How we tested and picked
Alarm hardware is easy to rank on paper and hard to judge without the software behind it. So the lab work went into the part every pick depends on. We ran Home Assistant 2026.10.4 in Docker on an Ubuntu 26.04 VM, built a manual alarm panel and an Alarmo panel over three template door and motion sensors, and drove both through the REST API while polling the state every few seconds. We also ran the ring-mqtt 5.9.3 container far enough to reach its login page, without entering a Ring account.
No keypad, siren or contact sensor was fitted in the lab, so these hardware claims come from documents rather than our hands:
- Every spec traces to the maker’s page or the Z-Wave JS and Zigbee2MQTT device databases.
- Every ASIN resolved to the exact model and showed a buy box on 29 October 2026.
- Each pick names at least one reason not to buy it.
- Integration claims were read from the manifests inside the Home Assistant 2026.10.4 container.
Keep that split in mind when you read a pick. A sentence about arming delays or refused codes is something we watched happen; a sentence about a keypad’s RFID range is the maker’s claim.
The alarm logic lives in Home Assistant, not the keypad
A keypad, a siren and a box of door sensors do not make an alarm. Something has to hold the armed state, run the exit and entry delays, check codes and decide when an open door means trouble. In a local build that something is an alarm_control_panel entity in Home Assistant, and you get it from one of two places: the built-in manual panel or Alarmo. Every hardware pick below plugs into that entity, so choose it first.

Radio choice is secondary, and the diagram shows why. Z-Wave, Zigbee, a wired Konnected board and an Envisalink bridge all end at the same panel entity, so you can mix them freely. Ring is the exception: its base station talks to Ring’s servers, and ring-mqtt pulls the state back down into your MQTT broker.
The built-in manual panel arms with a door open
The manual panel ships with Home Assistant and is configured only in YAML (manual integration docs). It gives you a code, arming and entry delays and a trigger time, which is enough for a small flat. Our lab config used three template sensors so the doors could be opened from the API. The lab ran Home Assistant Container with /opt/ha mapped as its config directory, the layout our Home Assistant install on Ubuntu 26.04 uses, so adjust the path if yours differs. Open the configuration file:
sudo vim /opt/ha/configuration.yaml
This is the whole file the lab ran on a fresh test instance. On an existing install, add these blocks to your own file instead of replacing it, or the !include lines that load your UI automations, scripts and scenes disappear. The input_boolean helpers stand in for real contacts:
default_config:
input_boolean:
front_door_open:
name: Front door open
back_door_open:
name: Back door open
hallway_motion:
name: Hallway motion
template:
- binary_sensor:
- name: Front Door
unique_id: front_door
device_class: door
state: "{{ is_state('input_boolean.front_door_open', 'on') }}"
- name: Back Door
unique_id: back_door
device_class: door
state: "{{ is_state('input_boolean.back_door_open', 'on') }}"
- name: Hallway Motion
unique_id: hallway_motion
device_class: motion
state: "{{ is_state('input_boolean.hallway_motion', 'on') }}"
alarm_control_panel:
- platform: manual
name: Home Alarm
unique_id: home_alarm_manual
code: "1234"
arming_time: 30
delay_time: 20
trigger_time: 120
disarmed:
trigger_time: 0
armed_home:
arming_time: 0
delay_time: 0
Restart Home Assistant (Developer tools, YAML, Restart) so the panel and helpers load. To drive it from a shell, export a long-lived access token created under your profile, Security tab, Long-lived access tokens:
export HA_TOKEN='paste-your-long-lived-token-here'
Open the front door helper first, the way a real contact would report an open door:
curl -s -X POST -H "Authorization: Bearer ${HA_TOKEN}" -H "Content-Type: application/json" \
http://127.0.0.1:8123/api/services/input_boolean/turn_on \
-d '{"entity_id":"input_boolean.front_door_open"}'
Then arm the panel away with its code:
curl -s -X POST -H "Authorization: Bearer ${HA_TOKEN}" -H "Content-Type: application/json" \
http://127.0.0.1:8123/api/services/alarm_control_panel/alarm_arm_away \
-d '{"entity_id":"alarm_control_panel.home_alarm","code":"1234"}'
Polling the state every five seconds shows the 30 second exit delay run out and the panel arm, door open and all:
03:44:07 arming
03:44:12 arming
03:44:17 arming
03:44:22 arming
03:44:28 arming
03:44:33 arming
03:44:38 armed_away
So the manual panel never looks at your sensors. It happily arms a house with the patio door open, and only an automation you write that calls alarm_trigger fires it. Codes are enforced properly, though: a wrong code returned HTTP 400 with invalid_code, an arm call with no code was refused with “Arming requires a code but none was given”, and the trigger moved the panel to pending at 03:45:28 and triggered at 03:45:48, exactly the 20 second delay_time.
Alarmo refuses to arm with a door open
Alarmo (nielsfaber/alarmo on GitHub) is a custom integration you install through HACS or by copying the release into custom_components. It adds a panel in the sidebar where you pick sensors from entities you already have, assign them to arm modes, set delays per mode, add users with their own PIN codes and wire up notifications. It runs entirely inside Home Assistant, and its manifest declares local_push with no external Python requirements.

The difference showed once the sensors were assigned to the alarm’s area. Calling alarmo.arm in away mode with the front door open returned HTTP 200, but the panel stayed disarmed. A websocket subscription on a repeat of the same test caught the event Alarmo fires, naming the door:
{"event_type": "alarmo_failed_to_arm", "data": {"command": "arm_away", "context_id": null, "area_id": "1793245589", "entity_id": "alarm_control_panel.alarmo", "reason": "open_sensors", "sensors": ["binary_sensor.front_door"]}, "origin": "LOCAL", "time_fired": "2026-10-29T03:57:35.062664+00:00"}
The entity’s open_sensors attribute held {"binary_sensor.front_door": "open"} at the same moment, which is what a dashboard card or keypad automation should read back to the person at the door. Passing force: true armed anyway after the 30 second exit delay and recorded the door in bypassed_sensors, so a deliberate bypass leaves a trace. With the door closed, arming took about 30 seconds, and opening the door afterwards put the panel into pending for the 20 second entry delay before it went to triggered at 03:55:11.

Three things in Alarmo cost us a test run each, and none of them shows an error in the UI:
- A sensor saved without an area is ignored, so the alarm arms and never triggers.
- With exit delay enabled on a door, arming with it open counts down, then falls back to disarmed.
- A wrong disarm code returns HTTP 200 with an empty response, and the state stays put.
Our first run shows why the missing area is the dangerous one. It armed away with the front door open and stayed armed_away when the door opened, because we had posted the sensors through Alarmo’s API without an area ID, and Alarmo skips any sensor whose area does not match. Adding sensors through the Sensors tab is the normal path and is less exposed to this, but check that every sensor shows its arm modes there before you trust the panel. The wrong disarm code is not silent underneath: Alarmo’s 1.10.19 source fires alarmo_failed_to_arm with command: disarm and reason: invalid_code (the event name is misleading), so a keypad automation should listen for that event rather than the HTTP response.
Alarmo stores its config in .storage/alarmo.storage inside the Home Assistant config directory. User codes are kept as bcrypt hashes rather than plain digits, while the code format and length sit in clear text. The manual panel’s code, by contrast, is plain text in configuration.yaml unless you move it to secrets.yaml. For anything beyond a single door, Alarmo is the panel to run, and the rest of this guide assumes it.

That on-screen keypad works on any wall tablet, and our tablets for a Home Assistant dashboard cover the mounting side. A physical keypad by the door is still worth having, because it works when the tablet is asleep, out of battery or being used to watch cartoons.
Konnected Alarm Panel Pro for wired houses
If your house came with a dead ADT or Brinks panel and wired contacts on every door, the Konnected Alarm Panel Pro reuses all of that copper. The 12-zone conversion kit takes over the existing zone wiring and runs ESPHome, so Home Assistant adopts it through the ESPHome integration with no cloud step.

You get 12 zones, of which zones 1 to 8 can be switched to outputs, an ESP32, 2.4 GHz Wi-Fi and a 10/100 Ethernet port that also takes PoE+ (802.3at). The kit ships with a 12 V 2.5 A adapter and has two 2 A alarm outputs for a wired siren or strobe. Ethernet wins when both links are up and Wi-Fi takes over if the cable drops, which is the order you want for a security device.
Konnected’s “standalone alarm” firmware runs the arming and delays on the board itself and exposes an alarm panel entity, so the siren still sounds if Home Assistant is rebooting (Konnected’s guide). Use the ESPHome route, not the old konnected integration: Konnected has deprecated it with removal planned during 2026, and in our 2026.10.4 container it has no alarm panel platform at all.
Pass on it when the house has no alarm wiring, since running new cable to every window costs more than wireless contacts. Several boards also do not merge into one standalone alarm, so a large house with two panels needs Alarmo on top to act as one system. The kit sits around $220, so check the live price.
Ring Alarm Keypad on Z-Wave, without a Ring base
The second-generation Ring Alarm Keypad is a Z-Wave device, and it pairs straight to Z-Wave JS with no Ring base station or account. The Z-Wave JS device database lists it as “Keypad v2” (model 4AK1SZ, manufacturer ID 0x0346) with configurable volume, brightness, proximity display and battery thresholds, and Home Assistant users run it as an Alarmo keypad.

At about $20 with over 8,700 Amazon reviews, nothing else comes close on price. You do need a Z-Wave controller, and the keypad is an input device only: Z-Wave JS has no alarm panel platform (we checked the 2026.10.4 container), so the keypad reports button presses and codes as Z-Wave events. An automation turns those into Alarmo arm and disarm calls, and a second one sets the keypad’s mode lights to match the panel.
The direct-pairing route comes from Home Assistant community reports rather than a Ring document, and it assumes a fresh keypad: one already joined to a Ring base has to be removed from that network or factory reset first, and you should include it with S2 security so codes are not sent in the clear. Expect some YAML before the keypad feels finished, and test that its lights match the real panel state after every change. Amazon showed the listing with a ship date rather than a plain in-stock flag on 29 October, so confirm delivery timing before you order.
frient Intelligent Keypad for Zigbee and RFID tags
RFID tags are the reason to pay five times the Ring keypad’s price for the frient Intelligent Keypad, the Zigbee option here: family members tap a tag instead of remembering a PIN. frient lists 4 AA batteries with a rated 12 month life, an IP56 rating, a 0 to 50 C operating range, PIN or RFID entry and tamper reporting.
On Zigbee2MQTT it appears as the Develco KEYZB-110, the white-label original, and exposes an action (disarm, arm_day_zones, arm_night_zones, arm_all_zones, exit_delay, emergency) along with action_code, action_transaction, tamper and battery. The keypad expects an answer: your server has to validate each request and send back arm_mode with the same transaction number, or the keypad never confirms the change. That makes it a good match for Alarmo, which can check the code, but you write the automation that closes the loop.
We did not confirm the keypad on ZHA, so buy it if you already run Zigbee2MQTT rather than ZHA. It costs about $100 to $105, so check the live price.
Ring Alarm Pro through ring-mqtt
Some households want a monitoring centre to call the police, and Ring Alarm with a Ring Protect plan is a common way to get it. For Home Assistant, the core Ring integration does not expose the alarm at all. Its docs list doorbells, cameras, chimes and intercoms, and in our 2026.10.4 container it has no alarm panel platform and is classed cloud_polling (Ring integration docs).

ring-mqtt fills that gap. It logs into your Ring account, then publishes the base station as an MQTT alarm panel with arm, disarm and bypass, plus the keypad, contact, motion, flood and smoke sensors, the siren and panic switches. We ran the 5.9.3 image with a broker address and no credentials, and it stopped at the token step:
ring-mqtt.js version: 5.9.3
Node version v22.22.1
NPM version 10.9.4
git version 2.52.0
-------------------------------------------------------
Running ring-mqtt...
2026-10-29T03:48:46.372Z ring-mqtt Detected runmode: docker
2026-10-29T03:48:46.385Z ring-mqtt Configuration file: /data/config.json
2026-10-29T03:48:47.093Z ring-mqtt State file /data/ring-state.json not found. No saved state data available.
2026-10-29T03:48:47.099Z ring-mqtt No refresh token was found in the state file, use the Web UI at http://<host_ip_address>:55123/ to generate a token.
2026-10-29T03:48:47.102Z ring-mqtt Successfully started the ring-mqtt web UI
2026-10-29T03:48:47.112Z ring-mqtt MQTT URL: mqtt://127.0.0.1:1883
Port 55123 serves a page titled “Ring-MQTT Authenticator” where you sign in with 2FA. Without /data/config.json mapped, the container refuses to start at all, so create that file with your mqtt_url first. The project’s README is explicit that this is cloud only and does not give local control. Arming from Home Assistant goes out to Ring and back, so that path stops when the WAN does.
Buy this when monitoring is the requirement and automation comes second. The 8-piece Pro kit runs about $300 and the plain Ring Alarm 8-piece (2nd Gen) about $250; panic features need a Ring Protect plan on top.
Envisalink for an existing DSC or Vista panel
A working DSC PowerSeries or Honeywell Vista panel is better hardware than most wireless kits, with a battery-backed siren driver and wired zones. Ripping it out is wasteful. The EyezOn Envisalink EVL-4EZR wires to the panel’s keypad bus and puts it on your LAN over 100BaseT, and Home Assistant’s envisalink integration reads it locally.

The integration is still in core as of 2026.10.4, declares local_push and ships an alarm panel platform, giving you the partition as a panel entity, every zone as a binary sensor and keypress actions. It is configured in YAML, not through the UI. The old panel keeps all of its own logic, so the arming rules live in its installer programming, not in Alarmo, and you will need the installer code to change them.
Skip it on any panel other than DSC PowerSeries or Honeywell Vista. The board costs about $100.
Door contacts that push state locally
Contacts are the part of an alarm you buy in bulk, so the per-unit price and the radio you already run matter most. Both of these report open and closed as a local push, which Alarmo needs for its open-sensor check.
The Zooz ZSE41 Open|Close XS is the Z-Wave pick. The 800 Long Range version on Amazon has S2 security, SmartStart, a CR2032 cell and a body about 0.3 inches thick, small enough for a window frame. Zooz extends the warranty to five years once you register it. The image shows the 700-series housing Zooz still uses on its product page.
At about $40 each it is not cheap, and every door adds a Z-Wave node to secure, so price out the whole house first.
The Aqara Door and Window Sensor (MCCGQ11LM) is the Zigbee pick at about $17 a unit, and the cheapest way to cover every window. Zigbee2MQTT lists a CR1632 battery and exposes contact, battery and the chip’s own device temperature. Aqara devices are known to drop off some third-party Zigbee routers, so pair them close to the coordinator and keep an eye on the last-seen time.
A dead contact is invisible to an alarm until the night it matters, so add a low-battery alert for every sensor whichever brand you buy. If you are still choosing a stick, our Zigbee coordinator picks cover the radio side.
Zooz ZSE50 siren with a battery backup
A siren that dies when someone flips the breaker is a doorbell. The Zooz ZSE50 800LR plugs into an outlet and carries a 400 mAh backup battery, reports when mains power is lost and joins with S2 Authenticated security. It also takes uploaded sound files, so the same unit can chime for a door and scream for an intrusion.
Zooz rates it at 90 dB, which is loud indoors but quiet next to a dedicated intrusion siren, and it is indoor only with a battery you cannot replace. Put it in a hallway where an intruder hears it rather than in a cupboard, and add a second unit upstairs in a larger house. Zooz’s own page quotes up to 24 hours of backup in one place and about 12 in its FAQ, so plan on the lower figure.
For the siren to follow the alarm, Alarmo’s actions tab turns it on at triggered and off at disarmed. It runs about $50.
Connect ZWA-2 for the Z-Wave side
The Ring keypad, the Zooz contacts and the Zooz siren all need a Z-Wave controller, and Nabu Casa’s Connect ZWA-2 is the obvious one: an 800-series chip with Long Range, sold by the people who build Home Assistant. Amazon lists it around $79 against a $69 recommended MSRP, and the official product page at home-assistant.io links regional resellers that may come in closer to it.
Any 800-series stick works just as well, and the Z-Wave 800 stick comparison covers the alternatives. Pass on it when you already run a Z-Wave controller, because two Z-Wave networks in one house just split your mesh.
Which alarm integrations stay local
Every Home Assistant integration declares an IoT class, and for an alarm it tells you whether arming survives an internet outage. We read the manifests inside the 2026.10.4 container and checked which integrations ship an alarm_control_panel platform (the acp column):
manual iot_class=calculated acp=yes
template iot_class=local_push acp=yes
mqtt iot_class=local_push acp=yes
zha iot_class=local_polling acp=yes
zwave_js iot_class=local_push acp=no
ring iot_class=cloud_polling acp=no
konnected iot_class=local_push acp=no
envisalink iot_class=local_push acp=yes
elkm1 iot_class=local_push acp=yes
alarmdecoder iot_class=local_push acp=yes
risco iot_class=local_push acp=yes
simplisafe iot_class=cloud_polling acp=yes
abode iot_class=cloud_push acp=yes
totalconnect iot_class=cloud_polling acp=yes
yale_smart_alarm iot_class=cloud_polling acp=yes
Three lines decide most purchases. zwave_js has no alarm panel of its own, which is why a Z-Wave keypad needs Alarmo or the manual panel behind it. ring has none either, which is why Ring Alarm arrives through mqtt. And SimpliSafe, Abode, Total Connect and Yale Smart Alarm all show up as alarm panels but poll or push through the vendor’s cloud, so they belong in the same bucket as Ring. Elk M1, AlarmDecoder and Risco are local alternatives to Envisalink if you own one of those panels.
What a reliable alarm build needs
Most local alarm builds fail on power and placement, not on the brand of sensor. Before you order, make sure the plan covers each of these:
- Home Assistant, the network switch and the Z-Wave or Zigbee radio sit on a UPS.
- The siren has its own battery and reports when mains power drops.
- Every door contact reports as a local push, not a cloud poll.
- The keypad’s mode lights follow the real panel state after a restart.
- Codes live in Alarmo users or
secrets.yaml, never in a shared dashboard. - Some way to reach you when the WAN is down, such as a cellular backup.
Off-site alerts are the honest limit of a fully local alarm. Arming, the siren and the keypad all work offline, but a push notification to your phone needs a path out of the house. Put the router on the UPS, add a 4G failover if the alarm matters, and run Home Assistant on hardware that survives a power blip. Our mini PC picks for Home Assistant and the Ubuntu 26.04 install guide we used for the lab cover that part. The smart lock guide and the garage door controllers follow the same local rule, and Alarmo can lock both when it arms.
Test the alarm before you trust it
An alarm you have never triggered on purpose is an untested backup. Run this drill after the build and again after every Home Assistant or Alarmo upgrade:
- Open a door, then arm away. Alarmo must refuse and list the door in
open_sensors. - Close it, arm away and wait out the exit delay. The state must read
armed_away. - Open the door. The panel must sit in
pendingfor the entry delay, then go totriggered. - Enter a wrong code at the keypad. The state must not change, and
alarmo_failed_to_armmust fire withreason: invalid_code. - Enter the right code. The siren must stop and the state must read
disarmed. - Unplug the WAN and repeat steps 2 to 5. Nothing in the result may change.
- Pull the siren’s plug while armed. You must get a power-loss alert.
Step 6 is the one most builds skip, and it is the only proof the alarm is really local. If arming stops working with the WAN unplugged, some part of the chain still runs through a cloud, and the integration table above tells you which one.




