Hello fellow members, I have been trying to implement home alarm / intruder alarm in my openHAB setup for some time now. There are quite a few threads here in the community pages on this subject. Being a complete noob in coding / programming, I can only understand little bit of file-based config. With the great help from [this] thread and [this] and many other discussions, I am sharing my config here. Apologies if this is not the ideal solution one finds, though it’s working fine on my side and wanted to share this with our great openHAB community !
I do agree with other users and moderators that this DOESN’T REPLACE A REAL ALARM SYSTEM but I think it could be a good backup in general.
Three zones : doors (contact), windows (contact) and PIRs (switch, though I prefer contact for motion sensors too). A countdown timer to see how many seconds are remaining, which updates every second. Panic button behind selection to avoid accidental touch, deactivation of panic alarm is via panic button only.
Four zones for audio : buzzer1, buzzer2, indoor siren and outdoor siren). I have opted to use DFPlayer for audio notifications, but user have a choice to connect physical beeper (Zone11), buzzer (Zone12) and sirens (Zone21 & Zone22) accordingly. DFPlayer with Tasmota is what I have found quite useful to play pre-recorded audio notifications locally, without cloud, and has been working brilliant for last few years. Three MP3 files for this to replicate Zone11, Zone12 and Zone21 (Sound files ‘21’, ‘22’ and ‘23’ as in rules file).
(Optional) RFTransiver with Tasmota to receive and send 433MHz data, which can replicate to trigger corresponding command (arm, stay, disarm and panic/sos ).
I presume user already have door, window and motion sensor things and items defined in their openHAB system, which need to include in relevant group (gAlarmdoors, gAlarmwindows, gAlarmsensors) accordingly.
Arm/Stay/Disarm can be activated via sitemap obviously, as well as with RF remote if using RFTransiver and defined RF codes will replicate the same.
Arm/Stay/Disarm won’t work from any of above if Panic button is activated (either via sitemap or RF remote).
Delay timer switch to input arm, stay and entry delay times in seconds.
Mute switch to turn off audio notifications, but bypassed if alarm is triggered.
RFLink switch to activate / deactivate RF data.
items file :
Group gAlarm "Alarm" <siren> ["Equipment"] // group for alarm items to persist
Group:Contact:OR(OPEN, CLOSED) gAlarmDoors <doorfront> (gHome)
Group:Contact:OR(OPEN, CLOSED) gAlarmWindows <window> (gHome)
Group:Switch:OR(ON,OFF) gAlarmSensors <man> (gHome)
Group:Switch:OR(ON,OFF) gAlarmOutputs (gHome)
Group:Switch:OR(ON,OFF) gZones "Zones"
Contact Zone1 "Zone1 Door" <door> (gAlarmDoors) // { channel="mqtt:topic:TuyaDoor:TuyaDoor" }
Contact Zone2 "Zone2 Window" <window> (gAlarmWindows) // { channel="mqtt:topic:TuyaDoor:TuyaDoor" }
Switch Zone3 "Zone3 PIR" <motion> (gAlarmSensors)
Switch Zone11 "Buzzer1" <soundvolume> (gZones) // waiting buzzer - Physical active beeper buzzer
Switch Zone12 "Buzzer2" <soundvolume> (gZones) { expire="01s,command=OFF" } // confirm buzzer - Physical active buzzer
Switch Zone21 "Indoor Siren" <soundvolume> (gAlarmOutputs, gZones) // Physical active siren
Switch Zone22 "Outdoor Siren" <soundvolume> (gAlarmOutputs, gZones) // Physical active siren
Switch Panic_Confirm "Panic Confirm" (gAlarm) // to confirm panic button
Switch Panic_Button "Panic Button" (gAlarm) // for indoor and outdoor sirens
Switch Mute "Mute" (gAlarm) // enable / disable Buzzer1, Buzzer2 and Indoor Siren during arming & disarming
// But bypassed if Alarm is triggered.
String AlarmMode "Alarm Mode" <alarm> (gAlarm)
String currentAlarm "Current alarm" <alarm> (gAlarm)
String Countdown "Countdown [%s]" <time>
Switch Arm "Arm" (gAlarm) [ "Switchable" ] //Switches for Google Home/alexa - need to define in new rules
Switch Disarm "Disarm" (gAlarm) [ "Switchable" ] //Switches for Google Home/alexa - need to define in new rules
Switch Stay "Stay " (gAlarm) [ "Switchable" ] //Switches for Google Home/alexa - need to define in new rules
Switch Delay_Timer "Delay Timer" (gAlarm) // for sitemap visibility of delay timers input.
Number Arm_Delay_Time "Arm Delay Time [%.0f s]" (gAlarm) // exit delay timer
Number Stay_Delay_Time "Stay Delay Time [%.0f s]" (gAlarm) // home armed delay timer
Number Entry_Delay_Time "Entry Delay Time [%.0f s]" (gAlarm) // entry delay timer
///// BELOW ITEMS FOR RF REMOTE OF ALARM PANEL ///////////
Switch RFLink "RF Link" (gAlarm) ["Switchable"]
String Disarm_RfCode "Disarm RfCode" (gRfCodes) ["Status"] // for RF remote control of alarm panel
String Arm_RfCode "Arm RfCode" (gRfCodes) ["Status"] // for RF remote control of alarm panel
String Stay_RfCode "Stay RfCode" (gRfCodes) ["Status"] // for RF remote control of alarm panel
String Panic_RfCode "Panic RfCode" (gRfCodes) ["Status"] // for RF remote control of alarm panel
///// BELOW ITEMS FOR RF TRANSIVER ///////////
Group gRfTransiver1 "Rf Transiver 1" <sensor> (gHome) ["Equipment"]
String RfTransiver1 "Rf Transiver 1" <sensor> (gRfTransiver1) ["Sensor"] { channel="mqtt:topic:RfTransiver1:Rf_Transiver1"}
Switch RfTransiver1_Reachable "RfTransiver1_Reachable" <switch> (gRfTransiver1, gMonitor) ["Switch"] { channel="mqtt:topic:RfTransiver1:reachable"}
String RfSend1 "RfSend 1" <sensor> (gRfTransiver1) ["Sensor"] { channel="mqtt:topic:RfTransiver1:RfSend1"}
/////// BELOW ITEMS FOR SOUND /AUDIO //////////////////////
Group gSound "Sound" <light> (gHome) ["Equipment"]
String Sound "Sound" <light> (gSound) ["Control"] {channel="mqtt:topic:Sound:Sound"}
String SoundStop "Sound Stop" <light> (gSound) ["Control"] {channel="mqtt:topic:Sound:SoundStop"}
String SoundVolume "Sound Volume" <light> (gSensors, gSound) ["SoundVolume"] {channel="mqtt:topic:SoundVolume:SoundVolume"}
Number SoundVolumeSetpoint "Sound Volume Setpoint [%.1f]" <soundvolume> (gSound) ["SoundVolume"]
rules file :
var Timer delayTimer = null
var Timer disarmTimer = null
var Timer countdownTimer = null
var int remainingTime = 0
rule "Alarm"
when
Item AlarmMode received command
then
if(delayTimer !== null) {
delayTimer?.cancel()
delayTimer = null
}
Thread::sleep (1000)
if(AlarmMode.state == "Disarm" && Panic_Button.state != ON) {
if(disarmTimer !== null) {
disarmTimer?.cancel()
disarmTimer = null
}
if(countdownTimer !== null) {
countdownTimer?.cancel()
countdownTimer = null
}
sendCommand(Countdown, "---")
currentAlarm.postUpdate(AlarmMode.state)
Disarm.sendCommand(ON)
Arm.sendCommand(OFF)
Stay.sendCommand(OFF)
if (RFLink.state == ON ) {
RfSend1.sendCommand (Disarm_RfCode.state.toString)
}
Zone11.sendCommand(OFF)
//Zone12.sendCommand(ON)
Zone21.sendCommand(OFF)
Zone22.sendCommand(OFF)
if(Mute.state == OFF || Mute.state == NULL) {
Sound.sendCommand ("22")
Zone12.sendCommand(ON)
}
if(Mute.state == ON) {
SoundStop.sendCommand ("stop")
}
logInfo("Alarm", "Alarm is Disarmed.")
sendBroadcastNotification("Alarm is Disarmed.")
}
/////////////////////////////////////////////////////////////////////////////////
if(AlarmMode.state == "Stay" && Panic_Button.state != ON) {
val armDelay = (Stay_Delay_Time.state as Number).intValue
logInfo("Alarm", "Delayed arming started for " + armDelay + " seconds.")
currentAlarm.postUpdate("Pending " + AlarmMode.state.toString)
if(Mute.state == OFF) {
Sound.sendCommand ("21")
Zone11.sendCommand(ON)
}
//Zone11.sendCommand(ON)
Thread::sleep (1000)
remainingTime = armDelay
delayTimer = createTimer(now.plusSeconds(armDelay), [|
if (gAlarmDoors.state != CLOSED || gAlarmWindows.state != CLOSED) {
delayTimer?.cancel()
delayTimer = null
currentAlarm.postUpdate("Alarm failed to set.")
logInfo("Alarm", "Alarm failed to set.")
sendBroadcastNotification("Alarm failed to set.")
if(countdownTimer !== null) {
countdownTimer?.cancel()
countdownTimer = null
}
Countdown.sendCommand ("- stay failed-")
Sound.sendCommand ("23")
Zone21.sendCommand (ON)
if (RFLink.state == ON ) {
RfSend1.sendCommand (Panic_RfCode.state.toString)
}
}
else {
Stay.sendCommand(ON)
Disarm.sendCommand(OFF)
Arm.sendCommand(OFF)
currentAlarm.postUpdate(AlarmMode.state.toString)
//Zone11.sendCommand(OFF)
//Zone12.sendCommand(ON)
if (RFLink.state == ON ) {
RfSend1.sendCommand (Stay_RfCode.state.toString)
}
if(Mute.state == OFF) {
Sound.sendCommand ("22")
Zone11.sendCommand(OFF)
Zone12.sendCommand(ON)
}
logInfo("Alarm", "Home Arm is Set")
sendBroadcastNotification("Home Arm is Set.")
}
])
if(countdownTimer !== null) {
countdownTimer?.cancel()
countdownTimer = null
}
countdownTimer = createTimer(now.plusSeconds(1), [|
if (remainingTime > 0) {
remainingTime -= 1
Countdown.sendCommand (remainingTime.toString + " seconds remaining")
countdownTimer.reschedule(now.plusSeconds(1))
}
else {
Countdown.sendCommand ("---")
countdownTimer = null
}
])
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(AlarmMode.state == "Arm" && Panic_Button.state != ON) {
val armDelay = (Arm_Delay_Time.state as Number).intValue
logInfo("Alarm", "Delayed arming started for " + armDelay + " seconds.")
currentAlarm.postUpdate("Pending " + AlarmMode.state.toString)
if(Mute.state == OFF) {
Sound.sendCommand ("21")
Zone11.sendCommand(ON)
}
//Zone11.sendCommand(ON)
Thread::sleep (1000)
remainingTime = armDelay
delayTimer = createTimer(now.plusSeconds(armDelay), [|
if (gAlarmDoors.state != CLOSED || gAlarmWindows.state != CLOSED || gAlarmSensors.state != OFF) {
delayTimer?.cancel()
delayTimer = null
currentAlarm.postUpdate("Alarm failed to set")
logInfo("Alarm", "Alarm failed to set")
sendBroadcastNotification("Alarm failed to set.")
Zone21.sendCommand(ON)
Zone22.sendCommand(ON)
if(countdownTimer !== null) {
countdownTimer?.cancel()
countdownTimer = null
}
Countdown.sendCommand ("-arm failed-")
Sound.sendCommand ("23")
Zone21.sendCommand (ON)
Zone22.sendCommand (ON)
if (RFLink.state == ON ) {
RfSend1.sendCommand (Panic_RfCode.state.toString)
}
}
else {
Arm.sendCommand(ON)
Stay.sendCommand(OFF)
Disarm.sendCommand(OFF)
currentAlarm.postUpdate(AlarmMode.state.toString)
// Zone11.sendCommand(OFF)
// Zone12.sendCommand(ON)
if (RFLink.state == ON ) {
RfSend1.sendCommand (Arm_RfCode.state.toString)
}
if(Mute.state == OFF) {
Sound.sendCommand ("22")
Zone11.sendCommand(OFF)
Zone12.sendCommand(ON)
}
logInfo("Alarm", "Away Arm is Set")
sendBroadcastNotification("Away Arm is Set.")
}
])
if(countdownTimer !== null) {
countdownTimer?.cancel()
countdownTimer = null
}
countdownTimer = createTimer(now.plusSeconds(1), [|
if (remainingTime > 0) {
remainingTime -= 1
sendCommand(Countdown, remainingTime.toString + " seconds remaining")
countdownTimer.reschedule(now.plusSeconds(1))
}
else {
Countdown.sendCommand ("---")
countdownTimer = null
}
])
}
end
rule "Door or Window Opened and alarm is set to Away or stay"
when
Member of gAlarmDoors changed to OPEN or
Member of gAlarmWindows changed to OPEN
then
if (currentAlarm.state == "Arm" || currentAlarm.state == "Stay") {
disarmTimer?.cancel()
disarmTimer = null
logInfo("Alarm", triggeringItem.label+" opened while alarm is on !!")
val disarmDelay = (Entry_Delay_Time.state as Number).intValue
logInfo("Alarm", "Delayed disarming started for " + disarmDelay + " seconds.")
currentAlarm.postUpdate("Siren Pending in" + disarmDelay + " seconds.")
Sound.sendCommand ("21")
Zone11.sendCommand(ON)
Thread::sleep (1000)
remainingTime = disarmDelay
disarmTimer = createTimer(now.plusSeconds(disarmDelay), [|
Zone11.sendCommand(OFF)
Zone21.sendCommand(ON)
Zone22.sendCommand(ON)
Sound.sendCommand ("23")
if (RFLink.state == ON ) {
RfSend1.sendCommand (Panic_RfCode.state.toString)
}
currentAlarm.postUpdate("Alarm Sirens activated..!")
sendBroadcastNotification("Alarm Sirens activated..!","error","high")
])
if(countdownTimer !== null) {
countdownTimer?.cancel()
countdownTimer = null
}
countdownTimer = createTimer(now.plusSeconds(1), [|
if (remainingTime > 0) {
remainingTime -= 1
sendCommand(Countdown, remainingTime.toString + " seconds remaining")
countdownTimer.reschedule(now.plusSeconds(1))
}
else {
Countdown.sendCommand ("---")
countdownTimer = null
}
])
}
end
rule "Motion sensor trigered and alarm is set to arm"
when
Member of gAlarmSensors changed to ON
then
if (currentAlarm.state == "Arm") {
disarmTimer?.cancel()
disarmTimer = null
logInfo("Alarm", triggeringItem.label+" motion alarm is triggered !!")
val disarmDelay = (Entry_Delay_Time.state as Number).intValue
logInfo("Alarm", "Delayed disarming started for " + disarmDelay + " seconds.")
currentAlarm.postUpdate("Siren Pending in" + disarmDelay + " seconds.")
Sound.sendCommand ("21")
Zone11.sendCommand(ON)
Thread::sleep (1000)
remainingTime = disarmDelay
disarmTimer = createTimer(now.plusSeconds(disarmDelay), [|
Zone11.sendCommand(OFF)
Zone21.sendCommand(ON)
Zone22.sendCommand(ON)
Sound.sendCommand ("23")
if (RFLink.state == ON ) {
RfSend1.sendCommand (Panic_RfCode.state.toString)
}
currentAlarm.postUpdate("Alarm Sirens activated..!")
sendBroadcastNotification("Alarm Sirens activated..!","error","high")
])
if(countdownTimer !== null) {
countdownTimer?.cancel()
countdownTimer = null
}
countdownTimer = createTimer(now.plusSeconds(1), [|
if (remainingTime > 0) {
remainingTime -= 1
sendCommand(Countdown, remainingTime.toString + " seconds remaining")
countdownTimer.reschedule(now.plusSeconds(1))
}
else {
Countdown.sendCommand ("---")
countdownTimer = null
}
])
}
end
rule "Panic"
when
Item Panic_Button received update
then
if (Panic_Button.state == (ON)) {
if(countdownTimer !== null) {
countdownTimer?.cancel()
countdownTimer = null
}
if(delayTimer !== null) {
delayTimer?.cancel()
delayTimer = null
}
if(disarmTimer !== null) {
disarmTimer?.cancel()
disarmTimer = null
}
Countdown.sendCommand ("---")
if(Panic_Confirm.state != (ON)) {
Panic_Confirm.sendCommand (ON)
}
Zone21.sendCommand(ON)
Zone22.sendCommand(ON)
SoundStop.sendCommand ("stop")
Thread::sleep (500)
Sound.sendCommand ("23")
if (RFLink.state == ON ) {
RfSend1.sendCommand (Panic_RfCode.state.toString)
}
currentAlarm.postUpdate("Alarm Sirens activated..!")
sendBroadcastNotification("Alarm Sirens activated..!","error","high")
}
if (Panic_Button.state == (OFF)) {
AlarmMode.sendCommand ("Disarm")
Panic_Confirm.sendCommand (OFF)
}
end
rule "Panic confirm "
when
Item Panic_Confirm received update
then
if (Panic_Confirm.state == OFF && Panic_Button.state == ON) {
Panic_Confirm.postUpdate (ON)
logInfo("Alarm", "Panic Button is still ON")
}
end
rule "RFLink down"
when
Item RFLink received update
then
if (RFLink.state == ON && RfTransiver1_Reachable.state != ON) {
RFLink.postUpdate (OFF)
sendBroadcastNotification("..RF Link Down..!!!","error","high")
logInfo("Alarm", "..RF Link Down..!!!")
}
end
///////////// BELOW RULE FOR RF REMOTE AND ALARM PANEL ////////////////////////////////////
rule "Rf Receiver1"
when
Item RfTransiver1 received update // Need received update if Data has not changed but button still pressed
then
// Thread::sleep(100) // Need the sleep to allow for the state to change
if(RFLink.state == ON ) {
if (RfTransiver1.state == Stay_RfCode.state && Panic_Button.state != ON ) {
AlarmMode.postUpdate ("Stay")
}
if (RfTransiver1.state == Arm_RfCode.state && Panic_Button.state != ON) {
AlarmMode.postUpdate ("Arm")
}
if (RfTransiver1.state == Disarm_RfCode.state && Panic_Button.state != ON) {
AlarmMode.postUpdate ("Disarm")
}
if (RfTransiver1.state == Panic_RfCode.state ) {
if(Panic_Button.state != (ON)) {
Panic_Button.postUpdate (ON)
}
if(Panic_Button.state != (OFF)) {
Panic_Button.postUpdate (OFF)
}
}
}
end
other rules for RF & Audio :
rule "Set Default Values WHEN SYSTEM STARTS"
when
System started
then
Thread::sleep(10 * 1000) // let persistance finish restoring a few seconds
Disarm_RfCode.postUpdate('0xAAAAAA')
Arm_RfCode.postUpdate('0xBBBBBB')
Stay_RfCode.postUpdate('0xCCCCCC')
Panic_RfCode.postUpdate('0xDDDDDD')
SoundVolumeSetpoint.sendCommand(50)
end
rule "Sound Volume setpoint"
when
Item SoundVolumeSetpoint received update
then
sendCommand(SoundVolume, SoundVolumeSetpoint.state.toString)
end
things file for RF & Audio :
Thing mqtt:topic:RfTransiver1 " RfTransiver1 " (mqtt:broker:myMqtt) @ "Porch"
{
Channels:
Type switch : reachable "Reachable" [ stateTopic="tele/RfTransiver1/LWT", transformationPattern="MAP:reachable.map" ]
Type string : Rf_Transiver1 "RfTransiver1" [ stateTopic="tele/RfTransiver1/RESULT" , transformationPattern="JSONPATH:$.RfReceived.Data" ]
Type string : RfSend1 "RfSend1" [ commandTopic="cmnd/RfTransiver1/rfsend" ]
}
Thing mqtt:topic:Sound " Sound " (mqtt:broker:myMqtt) @ "Porch"
{
Channels:
Type switch : reachable "Reachable" [ stateTopic="tele/Sound/LWT", transformationPattern="MAP:reachable.map" ]
Type string : Sound "Sound" [ commandTopic="cmnd/Sound/mp3track" ]
Type string : SoundStop "Sound Stop" [ commandTopic="cmnd/Sound/mp3stop" ]
Type string : SoundVolume "Sound Volume" [ commandTopic="cmnd/Sound/mp3volume" ]
}
sitemap file :
sitemap Smarthome label="Smarthome" {
Frame {
Text label="Alarm" icon="siren" {
Selection item=Panic_Confirm label="Panic" icon="error" labelcolor=[=="OFF"="orange",=="ON"="red"] valuecolor=[=="OFF"="orange",=="ON"="red"] mappings=[ON="YES", OFF="NO"]
Switch item=Panic_Button label="Panic Button" icon="sun" iconcolor=[=="OFF"="green",!="OFF"="red"] labelcolor=[=="OFF"="green",!="OFF"="red"] visibility=[Panic_Confirm == ON]
Text item=gAlarmOutputs label="Siren [%s]" icon="siren" valuecolor=[=="OFF"="green",!="OFF"="red"]
Text item=currentAlarm label="Alarm State[%s]" icon="shield" iconcolor=[currentAlarm == "Disarm"="green",currentAlarm != "Disarm"="red"] valuecolor=[currentAlarm == "Disarm"="green",currentAlarm!="Disarm"="red"] labelcolor=[currentAlarm=="Disarm"="green",currentAlarm!="Disarm"="red"]
Text item=Countdown label="Countdown [%s]" icon="time" valuecolor=[=="---"="green",!="---"="red"]
Switch item=AlarmMode label="" icon="none" mappings=[Arm="Arm",Stay="Stay",Disarm="Disarm"]
Group item=gAlarmDoors label="Doors [%s]" icon="door" valuecolor=[=="CLOSED"="green",!="CLOSED"="red"] labelcolor=[=="CLOSED"="green",!="CLOSED"="red"]
Group item=gAlarmWindows label="Windows [%s]" icon="window" valuecolor=[=="CLOSED"="green",!="CLOSED"="red"] labelcolor=[=="CLOSED"="green",!="CLOSED"="red"]
Group item=gAlarmSensors label="PIRs [%s]" icon="motion" valuecolor=[=="OFF"="green",!="OFF"="red"] labelcolor=[=="OFF"="green",!="OFF"="red"]
Group item=gZones label="Audio Zones" icon="pump"
Switch item=Mute label="Mute"
Setpoint item=SoundVolumeSetpoint label="Alarm Volume" minValue=5 maxValue=95 step=5
Switch item=Delay_Timer label="Delay Timer"
Setpoint item=Arm_Delay_Time label="Arm Delay Time" minValue=0 maxValue=300 step=1 visibility=[Delay_Timer==ON]
Setpoint item=Stay_Delay_Time label="Stay Delay Time" minValue=0 maxValue=300 step=1 visibility=[Delay_Timer==ON]
Setpoint item=Entry_Delay_Time label="Entry Delay Time" minValue=0 maxValue=300 step=1 visibility=[Delay_Timer==ON]
Switch item=RFLink label="RF Link"
}
}
}
map file for reachable items :
Online=ON
Offline=OFF
persist file :
Strategies
{
everyMinute : "0 * * * * ?"
everyHour : "0 0 * * * ?"
everyDay : "0 0 0 * * ?"
default = everyUpdate
}
Items {
gRfCodes*, gAlarm* : strategy = everyChange, restoreOnStartup
}
I think that’s about it…
cheers