Intro
For those interested, I went down the route of creating a YAML package for an Olarm device (sold in South Africa). Unfortunately, this device relies on the cloud, but is otherwise very easy to setup and use.
One benefit of a YAML package is that you can customise this to your own needs.
The package uses the Olarm REST API and webhooks to create an HA manual alarm panel.
I should also call out the existing integration for HA which is more feature rich than this package, but due to technological and operational constraints you might hit API limits. This makes certain features less useful (such as zone monitoring).
Note that local monitoring and automation of your alarm system doesn’t replace subscribing to a security service.
Requirements
- An Olarm device connected to your physical alarm panel.
- An active Olarm API.
- Your config must support packages.
- Your HA instance must be reachable by Olarm (i.e. be reachable via the Internet).
Installation & Configuration
Define these secrets in your secrets.yaml
:
olarm_webhook_id: "<as configured under your Olarm API profile>"
olarm_bearer_token: " <as provided by Olarm>"`
olarm_device_endpoint: "https://apiv4.olarm.co/api/v4/devices/<device_id>"
olarm_actions_endpoint: "https://apiv4.olarm.co/api/v4/devices/<device_id>/actions"
alarm_code: "<alarm pin>"
Put this in e.g. an olarm.yaml
package:
Package Contents
# features:
# * real-time alarm state updates and triggers
# * basic alarm management: arming/disarming (advanced management via the olarm app)
# * presence aware: ask to arm/disarm when leaving/arriving
# * extensible with more custom automations and integration of one's own sensors
# * area/partition aware (although not set up for multiple areas/partitions; make a copy)
#
# unsupported:
# * zone monitoring (no real-time updates via webhooks or mqtt)
# * bypassing zones (not implemented)
# * alarm action history (not implemented)
# * managing more than one area/partition (clone the package in order to do so)
# * how or who armed/disarmed the alarm (complex)
#
# future:
# * distinct alerts (notifications) for breach, panic, fire and emergency (currently no distinction)
# * hoping for an mqtt integration with additional features (multiple requests made; apparently wip)
#
# known issues & bugs:
# * arming when a zone is active (open) will not fail when calling the api (issue reported)
#
# references:
# https://community.home-assistant.io/t/olarm-integration/384560/28
# https://community.home-assistant.io/t/olarm-integration/384560/52
# https://github.com/rainepretorius/olarm-ha-integration
# https://github.com/rainepretorius/olarm-ha-integration/discussions/85
# https://www.home-assistant.io/integrations/alarm_control_panel/
# https://www.home-assistant.io/integrations/manual/
# https://www.home-assistant.io/integrations/alarm_control_panel.template/
# might switch to this once we can properly monitor zones:
# https://github.com/nielsfaber/alarmo
alarm_control_panel:
- platform: manual
name: "Home"
code: !secret alarm_code
code_arm_required: true
arming_time: 0
delay_time: 0
trigger_time: 120
disarm_after_trigger: false
disarmed:
trigger_time: 0
armed_home:
arming_time: 0
delay_time: 0
armed_away:
arming_time: 0
delay_time: 0
# https://www.home-assistant.io/integrations/rest_command/
# https://user.olarm.co/#/api
# https://user.olarm.co/#/api/documentation
rest_command:
olarm_get_state:
method: "GET"
headers:
Authorization: !secret olarm_bearer_token
url: !secret olarm_device_endpoint
content_type: "application/json"
olarm_disarmed:
method: "POST"
headers:
Authorization: !secret olarm_bearer_token
url: !secret olarm_actions_endpoint
# areas are 1-indexed (i.e. starts at 1 not 0)
payload: >-
{
"actionCmd": "area-disarm",
"actionNum": {{ area }}
}
content_type: "application/json"
olarm_armed_home:
method: "POST"
headers:
Authorization: !secret olarm_bearer_token
url: !secret olarm_actions_endpoint
payload: >-
{
"actionCmd": "area-stay",
"actionNum": {{ area }}
}
content_type: "application/json"
olarm_armed_away:
method: "POST"
headers:
Authorization: !secret olarm_bearer_token
url: !secret olarm_actions_endpoint
payload: >-
{
"actionCmd": "area-arm",
"actionNum": {{ area }}
}
content_type: "application/json"
automation:
- alias: "Sync Alarm State"
id: "e8f36533-e1c8-40d5-b35f-3001a159ec8e"
initial_state: true
trigger:
- platform: homeassistant
event: start
action:
- variables:
state_map:
notready: "disarm"
disarm: "disarm"
stay: "arm_home"
arm: "arm_away"
- service: rest_command.olarm_get_state
response_variable: olarm_response
# index 0 is area/partition 1
# values are: stay, arm, disarm
- service: "alarm_control_panel.alarm_{{ state_map[olarm_response.content.deviceState.areas[0]] }}"
target:
entity_id: alarm_control_panel.home
data:
code: !secret alarm_code
# examples for state changes:
# {'deviceId': '<uuid>', 'eventTime': 1708664464105, 'eventAction': 'area', 'eventState': 'disarm', 'eventNum': 1, 'eventMsg': 'DISARMED - Area 1 - Home', 'userFullname': ''}
# {'deviceId': '<uuid>', 'eventTime': 1708797060201, 'eventAction': 'area', 'eventState': 'stay', 'eventNum': 1, 'eventMsg': 'STAY ARMED - Area 1 - Home', 'userFullname': ''}
# {'deviceId': '<uuid>', 'eventTime': 1708882676603, 'eventAction': 'area', 'eventState': 'stay', 'eventNum': 1, 'eventMsg': 'STAY ARMED - Area 1 - Home', 'userFullname': 'Pieter Rautenbach'}
# {'deviceId': '<uuid>', 'eventTime': 1709539839721, 'eventAction': 'area', 'eventState': 'arm', 'eventNum': 1, 'eventMsg': 'ARMED - Area 1 - Home', 'userFullname': ''}
#
# examples for emergencies
# these are all panics. these two events seem to say that eventNum refers to the area/partition.
# {'deviceId': '<uuid>', 'eventTime': 1709539853196, 'eventAction': 'area', 'eventState': 'emergency', 'eventNum': 1, 'eventMsg': 'EMERGENCY! - Area 1 - Home', 'userFullname': ''}
# {'deviceId': '<uuid>', 'eventTime': 1709539853197, 'eventAction': 'area', 'eventState': 'emergency', 'eventNum': 2, 'eventMsg': 'EMERGENCY! - Area 2 - Flatlet', 'userFullname': ''}
# but here eventNum seem to refer to the zone. thus, one needs to parse the eventMsg. seems like there are events for zones activated during a panic.
# {'deviceId': '<uuid>', 'eventTime': 1709539853200, 'eventAction': 'area', 'eventState': 'emergency', 'eventNum': 5, 'eventMsg': 'EMERGENCY! - Area 5', 'userFullname': ''}
# {'deviceId': '<uuid>', 'eventTime': 1709539853199, 'eventAction': 'area', 'eventState': 'emergency', 'eventNum': 4, 'eventMsg': 'EMERGENCY! - Area 4', 'userFullname': ''}
# then there's this one (note the eventAction):
# {'deviceId': '<uuid>', 'eventTime': 1709539853544, 'eventAction': 's_alm', 'eventState': 'panic', 'eventNum': 1, 'eventMsg': 'EMERGENCY! - Panic', 'userFullname': ''}
- alias: "Receive Olarm Event"
id: "fbf7afaa-c357-4a85-b50d-ba5d57b9e187"
initial_state: true
trigger:
- platform: webhook
webhook_id: !secret olarm_webhook_id
local_only: false
allowed_methods:
- POST
mode: queued
action:
- variables:
state_map:
disarm: "disarm"
stay: "arm_home"
arm: "arm_away"
- service: system_log.write
data:
message: "{{ trigger.webhook_id ~ ': ' ~ trigger.json }}"
level: debug
logger: "homeassistant.components.olarm"
- choose:
# if triggered
- conditions:
- condition: template
value_template: "{{ trigger.json.eventState in ['panic', 'alarm'] }}"
sequence:
- service: alarm_control_panel.alarm_trigger
target:
entity_id: alarm_control_panel.home
# elif state changed
- conditions:
# compare with current state (noop it if matches; means it was actioned from HA, so don't cause a loop)
- condition: template
value_template: >-
{# eventNum == '1' is the partition/area check #}
{% set new_state = state_map[trigger.json.eventState] %}
{{ trigger.json.eventNum == 1 and
trigger.json.eventState in ['disarm', 'stay', 'arm'] and
not is_state('alarm_control_panel.home', new_state) }}
sequence:
- service: "alarm_control_panel.alarm_{{ state_map[trigger.json.eventState] }}"
target:
entity_id: alarm_control_panel.home
data:
code: !secret alarm_code
default:
- service: system_log.write
data:
message: "{{ trigger.webhook_id ~ ': Unknown event received: ' ~ trigger.json.eventState }}"
level: warning
logger: "homeassistant.components.olarm"
- alias: "Notify When Alarm Triggered"
id: "45f0ac46-8bbb-42cb-a051-64ea1ca695e2"
initial_state: true
trigger:
- platform: state
entity_id: alarm_control_panel.home
to: "triggered"
action:
- service: notify.mobile_app_ceres
data:
title: "🚨 Security"
message: "The alarm has been triggered!"
data:
group: "home-security"
url: homeassistant://navigate/lovelace/security
push:
sound:
name: default
critical: 1
volume: 0.5
actions:
- action: "DISARM_ALARM"
title: "Disarm now"
destructive: true
- alias: "Ask To Arm Alarm When Last Person Left"
id: "e8a63da5-4d84-40ab-9c05-dca93ba4900d"
initial_state: true
trigger:
- platform: state
entity_id: binary_sensor.pieter_present
to: "off"
mode: single
action:
- wait_template: "{{ is_state('binary_sensor.anybody_home', 'off') }}"
timeout: "00:02:00"
continue_on_timeout: false
- condition: state
entity_id: alarm_control_panel.home
state: "disarmed"
- service: notify.mobile_app_ceres
data:
title: "🔒 Security"
message: >-
The last person left home. Would you like to arm the alarm?
data:
group: "home-security"
url: homeassistant://navigate/lovelace/security
actions:
- action: "ARM_ALARM"
title: "Arm now"
destructive: true
push:
sound:
name: default
critical: 1
volume: 0.5
- alias: "Ask To Disarm Alarm When Arriving"
id: "85753536-12f8-4a94-b292-7672ac96a4c2"
initial_state: true
trigger:
- platform: state
entity_id: binary_sensor.pieter_present
to: "on"
condition:
- condition: not
conditions:
- condition: state
entity_id: alarm_control_panel.home
state: "disarmed"
action:
- service: notify.mobile_app_ceres
data:
title: "🔒 Security"
message: >-
You arrived home. Would you like to disarm the alarm?
data:
group: "home-security"
url: homeassistant://navigate/lovelace/security
actions:
- action: "DISARM_ALARM"
title: "Disarm now"
destructive: true
push:
sound:
name: default
critical: 1
volume: 0.5
- alias: "Handle Alarm Notification Event"
id: "c7a55b28-a909-4cf0-8f91-23f1d8a0e45a"
initial_state: true
trigger:
- platform: event
event_type: mobile_app_notification_action
event_data:
action: ARM_ALARM
id: "arm_away"
- platform: event
event_type: mobile_app_notification_action
event_data:
action: DISARM_ALARM
id: "disarm"
action:
- service: "alarm_control_panel.alarm_{{ trigger.id }}"
target:
entity_id: alarm_control_panel.home
data:
code: !secret alarm_code
- alias: "Alarm State Changed"
id: "c217ca42-8a3f-40c6-9945-2c77bdf16239"
initial_state: true
trigger:
- platform: state
entity_id: alarm_control_panel.home
to:
- "disarmed"
- "armed_home"
- "armed_away"
action:
- variables:
state_map:
disarmed: "disarmed"
armed_home: "armed (stay)"
armed_away: "armed"
state: "{{ state_map[trigger.to_state.state] }}"
- service: "rest_command.olarm_{{ trigger.to_state.state }}"
data:
area: 1
response_variable: olarm_response
- if: "{{ olarm_response.status == 200 and olarm_response.content.actionStatus == 'OK' }}"
then:
- service: notify.mobile_app_ceres
data:
title: "🔒 Security"
# todo: user (can be from olarm or HA context; tricky)
message: >-
The alarm was {{ state }} at {{ now().strftime('%H:%M') }}.
data:
group: "home-security"
url: homeassistant://navigate/lovelace/security
else:
- service: system_log.write
data:
message: "{{ olarm_response.status }}: {{ olarm_response.content }}"
level: error
logger: "homeassistant.components.olarm"
- service: notify.mobile_app_ceres
data:
title: "⛔️ Security"
message: >-
The alarm couldn't be {{ state }} at {{ now().strftime('%H:%M') }}. Check the log for errors.
data:
group: "home-security"
url: homeassistant://navigate/config/logs
# my alarm only has a stay mode – not also a night mode
# the alarm frontend card used shouldn't have night mode as a valid feature
# also, if exposing the alarm entity to homekit, you cannot specify the features that are supported
# (so it will allow setting the alarm to home (stay), away, night or off)
# neither this package, olarm, my alarm panel or homekit supports the vacation arm mode or a custom bypass command
- alias: "Set To Stay If Night Mode Activated"
id: "651adacf-f189-48ef-814a-24a051e60bd5"
initial_state: true
trigger:
- platform: state
entity_id: alarm_control_panel.home
to:
- "armed_night"
action:
- service: "alarm_control_panel.alarm_arm_home"
target:
entity_id: alarm_control_panel.home
data:
code: !secret alarm_code
Features
- Real-time alarm state updates and triggers
- Basic alarm management: arming/disarming (advanced management such as bypassing must be done via the Olarm app)
- Presence aware: ask to arm/disarm when leaving/arriving
- Extensible with more custom automations and integration of one’s own sensors
- Area/partition aware (although not set up for multiple areas/partitions; make a copy if you want to manage multiple partitions)
Unsupported Features
- Zone monitoring (no real-time updates via webhooks or mqtt)
- Bypassing zones (not implemented; not supported by the alarm integration)
- Alarm action history (not implemented; not supported by the alarm integration)
- Managing more than one area/partition (clone the package in order to do so)
- How or who armed/disarmed the alarm (complex)
Future
- Distinct alerts (notifications) for breach, panic, fire and emergency (currently no distinction)
- Hoping for an MQTT integration with additional features (multiple requests made to Olarm; apparently it is WIP)
Known Issues & Bugs
- Arming when a zone is active (open) will not fail when calling the API (issue reported to Olarm)
Dashboard
If you’re like me and don’t like green for an armed alarm on your UI (and you want it to match your Olarm app), you can customise this.
What this will do, is to use red for an armed alarm, yellow for armed stay/home/night and green for unarmed. It will also show the triggered state in blue (think, police) with a green disarm button.
Tile Card and History Graph
- type: tile
entity: alarm_control_panel.home
name: "Home Alarm"
features:
- type: "alarm-modes"
modes:
- armed_home
- armed_away
- disarmed
card_mod:
# this works in conjunction with www/custom.js
style:
hui-card-features $ hui-alarm-modes-card-feature $ div ha-control-select $ div: |
#option-armed_home {
--control-select-color: #ffa800 !important;
}
#option-armed_away {
--control-select-color: #aa0000 !important;
}
#option-disarmed {
--control-select-color: #40b100 !important;
}
hui-card-features $ div hui-card-feature $ hui-alarm-modes-card-feature $ ha-control-button-group ha-control-button $ button: |
.button {
background-color: #40b100 !important;
}
- type: history-graph
name: Alarm
hours_to_show: 24
entities:
- entity: alarm_control_panel.home
name: Alarm
Put this in custom.js
in your www
directory.
Theme Customisations
/*
Idea taken from https://gist.github.com/thomasloven/2a37152725c582fec4420ecedb65ebe3
Add this to your configuration.yaml
frontend:
extra_module_url:
- /local/ThemeOverride.js
And put the following into <config-dir>/www/ThemeOverride.js
*/
// setting these here means it will work for both the panel and the history graph cards
document.documentElement.style.setProperty('--state-alarm_control_panel-armed_home-color', '#ffa800');
document.documentElement.style.setProperty('--state-alarm_control_panel-armed_away-color', '#aa0000');
document.documentElement.style.setProperty('--state-alarm_control_panel-disarmed-color', '#40b100');
document.documentElement.style.setProperty('--state-alarm_control_panel-triggered-color', '#135dd8');
References: