Es gibt verschiedene Gründe, eine Alarmanlage realisieren zu wollen. Und viele Aspekte, die daran beteiligt sind. Das nachfolgende Beispiel funktioniert gut bei mir. Natürlich kannst du es leicht erweitern und auf deine Bedürfnisse anpassen.
Das nachfolgende System hat zwei Aufgaben:
- Einen Alarm zu verursachen, wenn ein Eindringen entdeckt wurde
- Anwesenheit vorzutäuschen
Ein Alarm kann auf verschiedene Wege verursacht werden. Er kann optisch oder akustisch sein oder still. Darüber hinaus kann ein Signal versandt werden.
„Wenn ein Eindringen entdeckt wurde“ kann auch vielfältige Auslöser haben. Das kann ein aufgebrochenes oder eingeschlagenes Fenster oder Tür sein, das kann auch eine Person sein, die sich an einem Ort aufhält, wo niemand sein sollte (z.B. in deinem Garten während deiner Abwesenheit). Das können auch Geräusche sein, die nicht da sein sollten (zerbrechendes Glas oder Schritte).
Auch Anwesenheit kann auf verschiedene Wege vorgetäuscht werden. Geräusche, Licht, vielleicht bestimmte dynamische Lichtszenen, ein Gartensprinkler, abgeholte Post, sich bewegende Rollos, …
Letztlich muss des System noch gegen Ausfall und Sabotage gesichert werden. Was passiert bei ausfallenden Geräten? Was passiert, wenn notwendige Funkübertragungen nicht mehr verfügbar sind (z.B. weil sie gestört werden).
Dieses Kapitel wird mehrere Projekte wiederverwenden, die ich bereits an anderer Stelle umgesetzt habe. Und sich im laufe der Zeit sicherlich noch erweitern und verbessern.
Hardware für eine Alarmanlage
- dein Raspberry Pi mit bereits funktionierendem OpenHAB
- mögliche Sensoren (kabelgefunden oder per Funk)
- Tür- / Fensterkontakte
- Glasbruchsensoren
- Bewegungsmelder (nur für drinnen zu empfehlen. draußen kann sich immer mal was bewegen)
- Anwesenheitssensoren
- Kameras (ggf. mit Objekterkennung)
- Beleuchtung
- smarte Leuchtmittel, die vielleicht schon in deinem Smarthome vorhanden sind
- Akustik
- smarte Lautsprecher (zur Alarm-Ausgabe und ggf. zur Geräuscherkennung)
- Ausfallsicherheit
- eine kleine USV, nur für den Pi
- einen LTE-USB-Stick als Ausfall-Leitung für Alarm-Benachrichtigungen
- auch hier sind Konstruktionen möglich, die einen Single-Point-of-Failure ausschließen
OpenHAB: Vorbereitungen
Deine Hardware musst du entsprechend deinen Bedrüfnissen nun in dein OpenHAB einbinden. Mein Beispiel geht davon aus, dass du smarte Leuchtmittel verwendet und dass du im besten Fall dynamische Szenen abspielen kannst. Mit den dynamischen Szenen täusche ich eine Aktivität des Fernsehers vor.
Alle Items müssen vorhanden sein. Inbesondere gehe ich davon aus, dass du
Wichtige Items und Gruppen der Alarmanlage
- gWatchdog
- Damit fasse ich den manuellen Modus, sowie den nächtlichen automatischen Modus zusammen. Ist einer der Beiden aktiv, ist das Alarmsystem aktiv. So können Beide zuverlässig funktionieren, ohne sich gegenseitig zu beeinflussen.
- WatchdogManual
- Dieses Item kannst du mittels OpenHAB GUI (oder in diesem Fall auch mittels Alexa) ein- und ausschalten. Es ist nur dafür gedacht, manuell betätigt zu werden.
- Dieses Item wird weiter unten nicht weiter beschrieben und muss von dir auf deine Bedürfnisse eingerichtet werden!
- WatchdogNightshift
- Dieser Modus soll nächtlich laufen. Von dem Zeitpunkt, wo ich ins Bett gehe, bis ich aufstehe. Automatisch wird es nachts zu einem bestimmten Zeitpunkt aktiviert und später wieder deaktiviert. Das kann mittels Kalender geschehen, aber auch mittels eigenem .rules-File. In meinem Fall aktiviere ich es zusätzlich manuell. Wenn ich meinen „Haus-Aus-Knopf“ am Bettrand betätige, wird die Alarmanlage scharf. Später in der Nacht würde sie automatisch aktiviert. Das Ereignis schaltet einen Schalter von „ON“ dann nur nochmal auf „ON“.
- Dieses Item wird weiter unten nicht weiter beschrieben und muss von dir auf deine Bedürfnisse eingerichtet werden!
- Weitere unabhängige Modi sind denkbar. Teile als Kommentar doch bitte deine Vorschläge mit mir 😉
- WatchdogTimestamp
- Hier wird der Zeitpunkt der Aktivierung des WatchdogManual gespeichert. Damit kann die Alarmanlage leicht verzögert aktiviert werden. Wenn ich sie also EInschalte, habe ich noch ein paar Minuten Zeit das Haus zu verlassen. Das wird im watchdog.rules-File weiter unten berücksichtigt.
- TestDummy
- Dieser aktiviert einen Test-Modus, um die Routinen zu testen. Regelmäßige Alarmanlagentest sind wichtig!
Group:Switch:OR(ON, OFF) gWatchdog
Switch WatchdogManual "Watchdog" (fgPersist,gWatchdog,tgWatchdogEvents) { alexa="Switch" }
Switch WatchdogNightshift (fgPersist,gWatchdog)
DateTime WatchdogTimestamp
Switch TestDummy
OpenHAB: Rules
Die nachfolgende watchdog.rules muss in jedem Fall auf deine Bedürfnisse angepasst werden und kann nicht einfach kopiert werden.
Folgende Situationen werden dabei betrachtet:
Leuchtsimulation
- rule „Watchdog Leuchtsimulation“
- Für jeden Raum werden Timer gesetzt, die Anwesenheit simulieren. Dabei lässt sich möglichst nicht mehr unterscheiden, ob dies eine Simulation ist, oder ob tatsächlich jemand anwesend ist. Verschiedene Räume werden unterschiedlich behandelt.
- Auf die Verwendung von semantischen Gruppen habe ich hier verzichtet, da die Funktionen ohnehin konkret auf deine Bedürfnisse angepasst werden müssen!
import java.util.Map
import java.util.List
import org.openhab.core.model.script.ScriptServiceUtil
var Map<String, Timer> timers = newHashMap
rule "Watchdog Leuchtsimulation"
when
Item WatchdogManual changed to ON
or Time cron "0 0 6 ? * * *"
then {
if(WatchdogManual.state == ON){
//Optische Bestätigung: Watchdog ist an!
Everywhere_Interface.sendCommand('{"effect": "blink"}')
val java.util.Random rand = new java.util.Random()
var sunset = (LokaleSonnendaten_Set_Startzeit.state as DateTimeType).getZonedDateTime()
var randMinutes = 0
//if(sunset.isBefore( now )){ sunset = now; }
//Büro Alex
//von (Dunkel) bis (23:00 - 01:00) : Licht
timers.put('begin_BueroAlex',createTimer(sunset, [ |
logInfo("Watchdog", "Büro Alex: An")
BueroAlex_Helligkeit.sendCommand(100)
val endMinutes = 23*60 + rand.nextInt(120)
timers.put('end_BueroAlex',createTimer(now.truncatedTo(java.time.temporal.ChronoUnit.DAYS).plusMinutes(endMinutes), [ |
logInfo("Watchdog", "Büro Alex: Aus")
BueroAlex_Helligkeit.sendCommand(0)
]))
]))
//Küche
// von (Dunkel + (30 - 180min)) bis (45 - 90min lang) : Licht + Ton
randMinutes = 30 + rand.nextInt(150)
timers.put('begin_Kueche',createTimer(sunset.plusMinutes(randMinutes), [ |
logInfo("Watchdog", "Küche: An")
//TODO: Licht
val endMinutes = 45 + rand.nextInt(45)
timers.put('end_Kueche',createTimer(now.plusMinutes(endMinutes), [ |
logInfo("Watchdog", "Küche: Aus")
//TODO: Licht aus
]))
]))
//Wohnzimmer
// von (Dunkel + (30 - 90min)) bis (22:30 - 00:30) : TV-Simulation + Ton
randMinutes = 30 + rand.nextInt(60)
timers.put('begin_Wohnzimmer',createTimer(sunset.plusMinutes(randMinutes), [ |
//Licht
logInfo("Watchdog", "Wohnzimmer Tisch: An")
WzTisch_Helligkeit.sendCommand(100)
logInfo("Watchdog", "Wohnzimmer Couch: TV-Simulation")
WzCouch_Lightshow.sendCommand("TV-Simulation") //TV-Simluation
//Ton
logInfo("Watchdog", "Wohnzimmer Echo: Musik")
Echo_Wohnzimmer_TextCommand.sendCommand("echo spiele Robin Schulz")
val endMinutes = 22*60 + 30 + rand.nextInt(120)
timers.put('end_Wohnzimmer',createTimer(now.truncatedTo(java.time.temporal.ChronoUnit.DAYS).plusMinutes(endMinutes), [ |
//TODO: Licht und Ton aus
logInfo("Watchdog", "Wohnzimmer (Tisch, Couch, Echo): Aus")
WzTisch_Helligkeit.sendCommand(0)
WzCouch_Helligkeit.sendCommand(0) //TV-Simluation
Echo_Wohnzimmer_TextCommand.sendCommand("echo stop")
]))
]))
//Schlafzimmer
// von (21:00 - 23:30) bis (23:35 - 00:30) : Licht
randMinutes = 21*60 + rand.nextInt(150)
timers.put('begin_Schlafzimmer',createTimer(now.truncatedTo(java.time.temporal.ChronoUnit.DAYS).plusMinutes(randMinutes), [ |
//Licht
logInfo("Watchdog", "Schlafzimmer : An")
SchlafzimmerRoom_Helligkeit.sendCommand(100)
val endMinutes = 5 + rand.nextInt(60)
timers.put('end_Schlafzimmer',createTimer(now.plusMinutes(endMinutes), [ |
//Licht aus
logInfo("Watchdog", "Schlafzimmer : Aus")
SchlafzimmerRoom_Helligkeit.sendCommand(0)
]))
]))
//Musiksimulation
//createTimer(now, [ |
// Echo_BueroAlex_TextCommand.sendCommand("echo spiele Robin Schulz")
// while(gWatchdog.state == ON){
// Thread::sleep(500)
// }
// Echo_BueroAlex_TextCommand.sendCommand("echo stop")
//])
}
}
end
Steuermechanismen der Alarmanlage
- rule „Watchdog changed Timestamp“
- Hier wird lediglich die Aktivierungszeit der Alarmanlage gespeichert, um Alarme nicht in den ersten 5min auszulösen. Z.B. wenn du die Alarmanlage aktivierst und dann noch das Haus verlassen willst.
//Verzögerung, bis Alarm tatsächlich scharf: 5min
rule "Watchdog changed Timestamp"
when
Item gWatchdog changed to ON
then {
WatchdogTimestamp.postUpdate(new DateTimeType)
}
end
- rule „Fault and disturbance detection“
- Ich habe Zigbee-Steckdosen dauerhaft in Betrieb, einfach nur um Störungen auf der 2,4GHz-Frequenz zu erkennen. Es wäre möglich, dass ein Angreifer versucht, dein Netz zu stören um Alarmkomponenten außer Betrieb zu setzen. Wenn diese dauerhaft plappernden Steckdosen sich nicht mehr melden, kann ein Problem vermutet werden. Dann fragt die Regel nach. Kommt dann immer noch keine Antwort, wird Alarm ausgelöst. Oder eine Wartungsmeldung versandt, wenn der Alarmmodus gerade inaktiv ist. Ich habe die hier hier angegebenen Zeiten sogar noch weiter verkürzt.
- Vergiss nicht, deinen Telegram-Bot dafür zu konfigurieren.
rule "Fault and disturbance detection"
when
Time cron "0 * * ? * * *"
then {
//if 5min nothing received: ask
if(now.minusMinutes(5).isAfter((fgFaultDetection.state as DateTimeType).getZonedDateTime())){
val GenericItem faultItem = fgFaultDetection.members.findFirst [ GenericItem i | now.minusMinutes(5).isAfter((i.state as DateTimeType).getZonedDateTime()) ]
val String faultIGName = faultItem.getGroupNames.findFirst[ String groupName | groupName.startsWith("ig") ]
val GroupItem faultIG = ScriptServiceUtil.getItemRegistry.getItem(faultIGName) as GroupItem
val SwitchItem switchItem = faultIG.members.findFirst[ a | a.tags.contains("Switch") && a.tags.contains("Power")]
switchItem.sendCommand(switchItem.state)
}
//if 8min nothing received: alert
if(now.minusMinutes(8).isAfter((fgFaultDetection.state as DateTimeType).getZonedDateTime())){
val GenericItem faultItem = fgFaultDetection.members.findFirst [ GenericItem i | now.minusMinutes(8).isAfter((i.state as DateTimeType).getZonedDateTime()) ]
//TODO: WatchdogDummyAlertContact.sendCommand(ON)
if(gWatchdog.state != ON){
getActions("telegram","telegram:telegramBot:home_Bot").sendTelegram(-000000000L,
"Wartung nötig: %s hat sich länger als 8min nicht gemeldet!",
faultItem.name
)
}
}
}
end
- rule „End Watchdog Alarm“
- Wenn ein Alarm beendet wird, passieren zwei Dinge: ein laufender Alarm muss abgebrochen werden und ein regulärer Aktiv-Status wird inaktiv.
//To end an Alarm, but also just to end the Night
rule "End Watchdog Alarm"
when
Item gWatchdog changed to OFF
then {
if(Everywhere_Lightshow.state.toString == "Alarm"){
Everywhere_Farbtemperatur.sendCommand(67)
}
//Everywhere_Helligkeit.sendCommand(0)
gAlexa_PlayAlarmSound.members.forEach[ StringItem item |
item.sendCommand('') //...PlayAlarmSound wird mit '' unterbrochen
]
gAlexa_Volume.members.forEach[ DimmerItem item |
item.sendCommand(20)
]
timers.keySet().forEach[String key |
if(key.startsWith("begin")){
timers.get(key).cancel()
}
if(key.startsWith("end")){
timers.get(key).reschedule(now)
}
]
}
end
- rule „Test Alarm start“ / rule „Test Alarm end“
- Zwei Regeln um verschiedene Tests durchzuführen
rule "Test Alarm start"
when
Item TestDummy changed to ON
then {
//Everywhere_Lightshow.sendCommand("Alarm")
BueroAlex_Lightshow.sendCommand("Alarm")
}
end
rule "Test Alarm end"
when
Item TestDummy changed to OFF
then {
//if(Everywhere_Lightshow.state.toString == "Alarm"){
// Everywhere_Farbtemperatur.sendCommand(67)
//}
if(BueroAlex_Lightshow.state.toString == "Alarm"){
BueroAlex_Farbtemperatur.sendCommand(67)
}
}
end
- rule „Smart Button – Start Nightshift“
- Drücke ich den „Haus-Aus-Knopf“, wird auch der nächtliche Alarm aktiviert.
rule "Smart Button - Start Nightshift"
when
//Channel "mqtt:topic:99a3e7ba:SmartButton1:action" triggered
Item SmartButton1_DimmerSwitch changed
then {
if(
KalenderWecken_CurrentEventPresence.state == OFF
|| SmartButton1_DimmerSwitch == NULL
){
WatchdogNightshift.sendCommand(ON)
}
}
end
- rule „End Nightshift“
- Schalte zu einem bestimmten Zeitpunkt automatisch
rule "End Nightshift"
when
Item KalenderWatchdog_CurrentEventPresence changed to OFF
then {
WatchdogNightshift.sendCommand(OFF)
}
end
Alarme
- rule „Watchdog Alarm“
- Bei welchen Auslösern soll reagiert werden?
- Betrachte dabei, dass in den ersten 5min nach Scharfschaltung kein Alarm ausgelöst werden soll.
- Schalte alle Lichter ein und lasse sie rot blinken (Everywhere_Lightshow.sendCommand(„Alarm“) mittels dynamischer Szenen)
- Lass alle SmartSpeaker Alarm schlagen!
- Versende Meldungen an dich selbst!
- Gib ggf. eine Alarmmeldung direkt an eine dafür geschaffene Stelle weiter!
rule "Watchdog Alarm"
when
Item gCamAlert changed
or Item fgIntrusion changed
then {
if( gWatchdog.state == ON && now.minusMinutes(5).isAfter((WatchdogTimestamp.state as DateTimeType).getZonedDateTime())){
logInfo("Test", "triggeringItemName: {}", triggeringItemName)
//Licht-Alarm
Everywhere_Lightshow.sendCommand("Alarm")
//Alexa-Warnrufe
createTimer(now, [ |
gAlexa_PlayAlarmSound.members.forEach[ StringItem item |
item.sendCommand('ECHO:system_alerts_rhythmic_02')
]
Thread::sleep(10000)
gAlexa_PlayAlarmSound.members.forEach[ StringItem item |
item.sendCommand('')
]
if(gWatchdog.state == ON){
gAlexa_Volume.members.forEach[ DimmerItem item |
item.sendCommand(80)
]
gAlexa_TTS.members.forEach[ StringItem item |
item.sendCommand("Eindringling entdeckt! Notruf wurde abgesetzt. Polizei ist unterwegs.")
]
Thread::sleep(4000)
}
if(gWatchdog.state == ON){
gAlexa_PlayAlarmSound.members.forEach[ StringItem item |
item.sendCommand('ECHO:system_alerts_rhythmic_02')
]
}
])
//Telegram-Meldungen
fgIntrusion.members.forEach[ GenericItem item |
if(item.state == OFF){
var groupLabels = ""
item.groupNames.forEach[ String groupName |
var groupLabel = ScriptServiceUtil.getItemRegistry.getItem(groupName).label
groupLabels = groupLabels + " " + groupLabel
]
getActions("telegram","telegram:telegramBot:home_Bot").sendTelegram(-000000000L,
"Eindringling-Alarm wurde ausgelöst!: %s - %s",
groupLabels,
item.label
)
}
]
}
}
end
- rule „Send telegram on object sighted (Vordertuer)“ / rule „Send telegram on object sighted (Hintertuer)“
- Regeln um Meldungen an mich zu versenden, wenn eine Person auf einer Kamera entdeckt wurde
- Wenn eine Person in deiner Abwesenheit an deiner Hintertür ist, kann das ein unbefugtes Eindringen sein. Eine Person an der Vordertür hingegen kann normaler Verhalten sein (Postbote)
- Bei der Verwendung von Kameras auch im eigenen Bereich sind immer rechtliche Richtlinien zu betrachten!
// --- TensorPi ---
rule "Send telegram on object sighted (Vordertuer)"
when
Item Tensorpi_Vordertuer_time changed
then
getActions("telegram","telegram:telegramBot:home_Bot").sendTelegramPhoto(-000000000L,
Tensorpi_Vordertuer_last_img.state.toString,
Tensorpi_Vordertuer_object.state.toString)
end
// --- TensorPi2 ---
rule "Send telegram on object sighted (Hintertuer)"
when
Item Tensorpi_Hintertuer_time changed
then
if(gWatchdog.state == ON){
getActions("telegram","telegram:telegramBot:home_Bot").sendTelegramPhoto(-000000000L,
Tensorpi_Hintertuer_last_img.state.toString,
Tensorpi_Hintertuer_object.state.toString)
}
end