Inhalt
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 TestDummyopenHAB Automatisierung: Modernes JS Scripting
In modernen openHAB-Installationen (openHAB 4 & 5) wird die gesamte Logik für Anwesenheitssimulation, Ausfallerkennung und Alarmierung über ein einziges, modulares openHAB JS Scripting Skript (ECMAScript 2021+) abgewickelt. Dadurch entfallen die unzähligen, fragmentierten Rules-Dateien und blockierenden Thread-Sleeps der alten DSL.
Die Kernfunktionen der modernen Architektur:
- Modulare Raum-Simulation (
roomTags): Räume definieren flexibel ihre Startzeiten (z. B. relativer Sonnenuntergang mit Zufallsverzögerung 0–30 Minuten) und Abschaltzeiten. Im Wohnzimmer werden dynamische Lichtszenen (TV-Simulation) und Alexa-Sprachkommandos über Echo-Lautsprecher zeitgesteuert ausgelöst. - Verzögerte Scharfschaltung: Nach dem Aktivieren der Alarmanlage läuft eine Karenzzeit von 5 Minuten ab, damit Bewohner das Haus ohne Fehlalarm verlassen können.
- Sensor-Watchdog (Ausfallerkennung): Prüft minütlich, ob Sensoren unerwartet lange keine Daten mehr geliefert haben (z. B. bei Funkabriss oder Sabotage).
- Alarmierung: Bei Detektion über Fensterkontakte (
fgIntrusion) oder Kameramelder (gCamAlert) schalten alle Leuchtmittel auf Alarm-Rot und es erfolgt sofort eine Telegram-Benachrichtigung. - Sauberes Timer-Management: Über
cache.privatewerden alle aktiven Simulations- und Alarm-Timer gespeichert und beim Deaktivieren der Alarmanlage restlos abgebrochen (clearAllSimTimers).
/**
* Watchdog / Alarm System
*/
const { rules, triggers, items, time, actions } = require("openhab");
// Cache für Timer, damit wir sie bei "Disarm" abbrechen können
const CACHE = cache.private;
if (!CACHE.get("simTimers")) CACHE.put("simTimers", {});
if (!CACHE.get("alarmTimers")) CACHE.put("alarmTimers", []);
// Konstanten
const TELEGRAM_BOT = "telegram:telegramBot:home_Bot";
const CHAT_ID = -552188908; // Long in JS ist einfach Number
const SUNSET_START_ITEM = "LokaleSonnendaten_Set_Startzeit";
const CONFIRM_IF_ITEM = "Everywhere_Interface";
const roomTags = {
Office: {
startAfter: {
delay: [0, 30], // Start: Sunset + 0-30min
},
endAfter: { // Ausschalten planen: 23:00 + 0-120min
h: 23, // optional
m: 0, // optional
delay: [0,120],
},
},
Kitchen: {
startAfter: {
delay: [0, 180],
},
endAfter: { // nach 45-120min
delay: [45,120],
},
},
Livingroom: {
startAfter: {
delay: [30, 90],
actions:[
["WzBackground_Helligkeit", 50],
["WzTisch_Lightshow", "TV-Simulation"],
["Echo_Wohnzimmer_Volume", 50],
["Echo_Wohnzimmer_TextCommand", "echo spiele Robin Schulz"],
],
},
endAfter: { // Ende: 22:30 + 0-120min
h: 22,
m: 30,
delay: [0,120],
actions:[
["WzBackground_Helligkeit", 0],
["WzTisch_Lightshow", 0],
["Echo_Wohnzimmer_TextCommand", "echo stop"],
["Echo_Wohnzimmer_Volume", 20],
],
},
},
Bedroom: {
startAfter: {
h: 21, // optional
m: 0, // optional
delay: [0, 150], // Start: Sunset + 0-30min
},
endAfter: { // Ausschalten planen: 23:00 + 0-120min
h: 23, // optional
m: 0, // optional
delay: [5,65],
},
},
};
// --- HELPER FUNCTIONS ---
/**
* Fügt einen Timer zur Verwaltung hinzu
*/
function registerSimTimer(key, timerId) {
const timers = CACHE.get("simTimers");
// Falls schon einer existiert, vorher löschen (clean up)
if (timers[key]) clearTimeout(timers[key]);
timers[key] = timerId;
}
/**
* Löscht alle Simulationstimer (bei Alarm-Ende)
*/
function clearAllSimTimers() {
const timers = CACHE.get("simTimers");
for (const key in timers) {
clearTimeout(timers[key]);
}
CACHE.put("simTimers", {});
}
/**
* Hilfsfunktion für zufällige Minuten (min bis max)
*/
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
/**
* Berechnet die Verzögerung in Millisekunden bis zu einem ZDT
*/
function getDelayMillis(targetZDT) {
const now = time.toZDT();
let diff = targetZDT.toInstant().toEpochMilli() - now.toInstant().toEpochMilli();
return Math.max(0, diff); // Keine negativen Delays
}
// --- Watchdog Leuchtsimulation ---
rules.JSRule({
name: "Watchdog Leuchtsimulation",
triggers: [
triggers.ItemStateChangeTrigger("WatchdogManual", null, "ON"),
triggers.ItemStateChangeTrigger("TestDummy", null, "ON"),
triggers.GenericCronTrigger("0 0 6 ? * * *") // Täglich 6 Uhr Berechnung
],
execute: (event) => {
const watchdogManual = items.getItem("WatchdogManual");
const testDummy = items.getItem("TestDummy");
const triggeringItemName = event.itemName;
var logPrefix = "Watchdog-Test";
if(testDummy.state === "OFF"){
logPrefix = "Watchdog";
}
// Prüfen ob aktiv
if (watchdogManual.state === "ON" || testDummy.state === "ON") {
console.info(logPrefix + ": Simulation gestartet (An)");
// Optische Bestätigung
if (testDummy.state === "OFF") {
items.getItem(CONFIRM_IF_ITEM).sendCommand('{"effect": "blink"}');
}
// Sonnenuntergang holen
const sunsetStr = items.getItem(SUNSET_START_ITEM).state;
let sunset = time.toZDT(sunsetStr);
const now = time.toZDT();
// Falls Sonnenuntergang heute schon vorbei ist oder wir vor 6 Uhr sind, Logik prüfen.
// Wenn sunset < now, starten Timer mit 0ms Delay sofort.
for(const tag in roomTags){
const fgPresSim = items.getItem("fgPresSim");
const rooms = fgPresSim.members.filter(i => i.tags.includes(tag));
rooms.forEach(room => {
// Start: Sunset + n min
let timeStart;
const startConf = roomTags[tag]['startAfter'];
if (typeof startConf['h'] !== 'undefined') {
// Basis ist 'sunset', nicht 'now', um ungewollte Tagessprünge zu vermeiden
timeStart = sunset.withHour(startConf['h']).withMinute(startConf['m'] || 0).plusMinutes(randomInt(startConf.delay[0], startConf.delay[1]));
// if after midnight: +1 day
if (timeStart.isBefore(sunset)) {
timeStart = timeStart.plusDays(1);
}
} else {
// Relative Endzeit basierend auf timeStart
timeStart = sunset.plusMinutes( randomInt(startConf.delay[0], startConf.delay[1]) );
}
// ENDZEIT BERECHNEN (Vor dem Timeout!)
let timeEnd;
const endConf = roomTags[tag]['endAfter'];
if (typeof endConf['h'] !== 'undefined') {
// Basis ist 'sunset', nicht 'now', um ungewollte Tagessprünge zu vermeiden
timeEnd = sunset.withHour(endConf['h']).withMinute(endConf['m'] || 0).plusMinutes(randomInt(endConf.delay[0], endConf.delay[1]));
// if after midnight: +1 day
if (timeEnd.isBefore(sunset)) {
timeEnd = timeEnd.plusDays(1);
}
} else {
// Relative Endzeit basierend auf timeStart
const durationMinutes = randomInt(endConf.delay[0], endConf.delay[1]);
timeEnd = timeStart.plusMinutes(durationMinutes);
}
// LOGIK-CHECK: Ist das geplante Ende bereits in der Vergangenheit?
const now = time.toZDT();
if (timeEnd.isBefore(now)) {
console.info(`${logPrefix}: ${room.name} - Geplantes Ende (${timeEnd.toString()}) liegt in der Vergangenheit. Überspringe Einschalten.`);
return; // Bricht den Durchlauf für diesen Raum ab
}
// EINSCHALT-TIMER SETZEN
const delayStart = getDelayMillis(timeStart); // Sollte bei negativen Werten 0 zurückgeben!
const tStart = setTimeout(() => {
const brightness = room.members.find(m => m.tags.includes("Brightness"));
if (testDummy.state === "OFF") {
if (typeof startConf['actions'] !== 'undefined') {
for(const i in startConf['actions']){
const action = startConf['actions'][i];
items.getItem(action[0]).sendCommand(action[1])
}
}
else if (brightness) {
brightness.sendCommand(100);
}
else{
console.warn(`${logPrefix}: Fehler - ${room.name} ohne Action und Brightness-Member-Item`);
return;
}
}
console.info(`${logPrefix}: ${room.name} An (bis ca. ${timeEnd.toLocalTime().toString()})`);
// AUSSCHALT-TIMER SETZEN
// Die verbleibende Zeit bis zum Ende wird erst jetzt final berechnet,
// damit Verzögerungen beim Ausführen des Start-Timers keine Rolle spielen.
const delayEnd = getDelayMillis(timeEnd);
registerSimTimer('end_' + room.name, setTimeout(() => {
if (testDummy.state === "OFF") {
if (typeof endConf['actions'] !== 'undefined') {
for(const i in endConf['actions']){
const action = endConf['actions'][i];
items.getItem(action[0]).sendCommand(action[1])
}
}
else {
brightness.sendCommand(0);
}
}
console.info(`${logPrefix}: ${room.name} Aus`);
}, delayEnd));
}, delayStart);
registerSimTimer('begin_' + room.name, tStart);
});
}
}
}
});
// --- Watchdog Aktivierungs-Verzögerung ---
rules.JSRule({
name: "Watchdog changed Timestamp",
triggers: [triggers.ItemStateChangeTrigger("gWatchdog", null, "ON")],
execute: (event) => {
// Speichert aktuellen Zeitpunkt als DateTime
items.getItem("WatchdogTimestamp").postUpdate(time.toZDT().toString());
}
});
// --- Fault and disturbance detection ---
rules.JSRule({
name: "Fault and disturbance detection",
triggers: [triggers.GenericCronTrigger("0 * * ? * * *")], // Jede Minute
execute: (event) => {
const now = time.toZDT();
const fgFaultDetection = items.getItem("fgFaultDetection");
const gWatchdog = items.getItem("gWatchdog");
// 1. Check 5 Minuten (Device anpingen)
// Wir suchen das erste Item, das > 5min nichts gesendet hat
const faultItem5 = fgFaultDetection.members.find(i => {
const lastUpdate = time.toZDT(i.state);
return now.minusMinutes(5).isAfter(lastUpdate);
});
if (faultItem5) {
// Gruppe finden
const groupName = faultItem5.groupNames.find(g => g.startsWith("ig"));
if (groupName) {
const igGroup = items.getItem(groupName);
const switchItem = igGroup.members.find(m => m.tags.includes("Switch") && m.tags.includes("Power"));
if (switchItem) {
// Refresh Trigger: Sendet den eigenen Status an sich selbst
switchItem.sendCommand(switchItem.state);
// console.info(`Watchdog Fault: Pinge ${switchItem.name}`);
}
}
}
// 2. Check 8 Minuten (Alarmierung)
const faultItem8 = fgFaultDetection.members.find(i => {
const lastUpdate = time.toZDT(i.state);
return now.minusMinutes(8).isAfter(lastUpdate);
});
if (faultItem8 && gWatchdog.state !== "ON") {
const telegram = actions.get("telegram", TELEGRAM_BOT);
if (telegram) {
telegram.sendTelegram(CHAT_ID, `Wartung nötig: ${faultItem8.name} hat sich länger als 8min nicht gemeldet!`);
}
}
}
});
// --- End Watchdog Alarm ---
rules.JSRule({
name: "End Watchdog Alarm",
triggers: [
triggers.ItemStateChangeTrigger("gWatchdog", null, "OFF"),
triggers.ItemStateChangeTrigger("TestDummy", null, "OFF")
],
execute: (event) => {
const triggeringItem = items.getItem(event.itemName);
var logPrefix = "Watchdog";
if(triggeringItem.name == "TestDummy"){
logPrefix = "Watchdog-Test";
}
console.info(logPrefix + ": System disarmed / Reset");
// Lightshow Reset
const tgLightshow = items.getItem("tgLightshow");
tgLightshow.members
.filter(i => i.tags.includes("Alarm") && i.tags.includes("DynamicScene"))
.forEach(i => {
if (i.state === "Alarm") i.sendCommand(67); // 67 scheint ein "Normal"-Code zu sein?
});
// Alarm Sounds Stop
items.getItem("gAlexa_PlayAlarmSound").members
.filter(i => i.tags.includes("Alarm"))
.forEach(i => i.sendCommand(""));
// Volume Reset
items.getItem("gAlexa_Volume").members
.filter(i => i.tags.includes("Alarm"))
.forEach(i => i.sendCommand(20));
// Simulation Timer löschen
clearAllSimTimers();
// Aktive Alarm-Sequenz Timer löschen (falls man mitten im Alarm unscharf schaltet)
const alarmTimers = CACHE.get("alarmTimers");
alarmTimers.forEach(id => clearTimeout(id));
CACHE.put("alarmTimers", []);
}
});
// --- Watchdog Alarm ---
rules.JSRule({
name: "Watchdog Alarm Execution",
triggers: [
triggers.GroupStateChangeTrigger("gCamAlert"),
triggers.GroupStateChangeTrigger("fgIntrusion")
],
execute: (event) => {
const triggeringItemName = event.itemName;
const testDummy = items.getItem("TestDummy");
const gWatchdog = items.getItem("gWatchdog");
const watchdogTimestamp = items.getItem("WatchdogTimestamp");
if (testDummy.state === "ON") {
console.info("Watchdog-Test: ALARM TRIGGERED by " + triggeringItemName);
}
// Grace Period Check: Ist Alarm "Scharf" und älter als 5 Minuten?
const armedTime = time.toZDT(watchdogTimestamp.state);
const isSystemReady = gWatchdog.state === "ON" && time.toZDT().minusMinutes(5).isAfter(armedTime);
// Trigger Logik prüfen (gCamAlert immer, fgIntrusion nur wenn neuer Status "höher"/schlimmer ist??)
// Vereinfachung: Wir reagieren auf Change. Im Original: (newState > previousState) für Intrusion.
// Da wir JS Event Object nutzen, können wir newState prüfen.
let isValidTrigger = false;
if (triggeringItemName === "gCamAlert") isValidTrigger = true;
else {
// Bei fgIntrusion gehen wir davon aus, dass ON/OPEN der Alarmzustand ist
const newState = event.newState;
if (newState === "ON" || newState === "OPEN") isValidTrigger = true;
}
if (isSystemReady && isValidTrigger) {
console.warn("Watchdog: ECHTER ALARM AUSGELÖST!");
items.getItem("Office_Lightshow").sendCommand("Alarm");
// Alarm-Sequenz starten (Verschachtelte Timeouts)
// Wir speichern die Timer IDs in einer Liste, um sie bei "End Watchdog" abbrechen zu können.
const alarmTimers = [];
// 1. Verzögerung 60 Sekunden (Voralarm / Entry Delay)
const t1 = setTimeout(() => {
// Nochmals prüfen, ob Alarm noch an ist (vielleicht wurde er in den 60s deaktiviert)
if (items.getItem("gWatchdog").state === "ON") {
// Licht-Alarm
items.getItem("tgLightshow").members
.filter(i => i.tags.includes("Alarm") && i.tags.includes("DynamicScene"))
.forEach(i => {
i.sendCommand(67); // Reset?
i.sendCommand("Alarm");
});
// Alexa Sequenz starten
const gAlexaPlay = items.getItem("gAlexa_PlayAlarmSound").members.filter(i => i.tags.includes("Alarm"));
const gAlexaVol = items.getItem("gAlexa_Volume").members.filter(i => i.tags.includes("Alarm"));
const gAlexaTTS = items.getItem("gAlexa_TTS").members.filter(i => i.tags.includes("Alarm"));
// Sirene an
gAlexaPlay.forEach(i => i.sendCommand("ECHO:system_alerts_rhythmic_02"));
// 2. Sirene für 10 Sekunden
const t2 = setTimeout(() => {
// Sirene Stop
gAlexaPlay.forEach(i => i.sendCommand(""));
if (items.getItem("gWatchdog").state === "ON") {
// Lautstärke hoch
gAlexaVol.forEach(i => i.sendCommand(80));
// Ansage
gAlexaTTS.forEach(i => i.sendCommand("Eindringling entdeckt! Notruf wurde abgesetzt. Polizei ist unterwegs."));
// 3. Warten auf Ansage (4s), dann Sirene wieder an
const t3 = setTimeout(() => {
if (items.getItem("gWatchdog").state === "ON") {
gAlexaPlay.forEach(i => i.sendCommand("ECHO:system_alerts_rhythmic_02"));
}
}, 4000);
alarmTimers.push(t3);
}
}, 10000);
alarmTimers.push(t2);
// Telegram Meldung
const fgIntrusion = items.getItem("fgIntrusion");
fgIntrusion.members.forEach(item => {
// Prüfen welches Item ausgelöst hat (Im Original logic: if item.state == OFF ?? Das macht wenig Sinn bei Alarm.
// Vermutlich war gemeint: Sende Info über alle Sensoren.
// Wir senden einfach den Auslöser.
if (item.name === triggeringItemName || item.state.toString() === "OPEN" || item.state.toString() === "ON") {
const groupLabels = item.groupNames.map(gn => items.getItem(gn).label).join(" ");
const telegram = actions.get("telegram", TELEGRAM_BOT);
if(telegram) {
telegram.sendTelegram(CHAT_ID, `Eindringling-Alarm wurde ausgelöst!: ${groupLabels} - ${item.label}`);
}
}
});
}
}, 60000);
alarmTimers.push(t1);
CACHE.put("alarmTimers", alarmTimers);
}
}
});
// --- Start/End Nightshift ---
rules.JSRule({
name: "Start Nightshift",
triggers: [
triggers.ItemStateChangeTrigger("KalenderWatchdog_CurrentEventPresence", null, "ON"),
triggers.ItemStateChangeTrigger("SmartButton1_DimmerSwitch")
],
execute: (event) => {
const calendarWecken = items.getItem("KalenderWecken_CurrentEventPresence");
// Logik aus Original: Wenn Wecken-Kalender aus ist
if (calendarWecken.state === "OFF") {
items.getItem("WatchdogNightshift").sendCommand("ON");
}
}
});
rules.JSRule({
name: "End Nightshift",
triggers: [triggers.ItemStateChangeTrigger("KalenderWatchdog_CurrentEventPresence", null, "OFF")],
execute: (event) => {
items.getItem("WatchdogNightshift").sendCommand("OFF");
}
});Historisches Archiv: Legacy Rules DSL (openHAB 2/3)
Für bestehende Installationen auf Basis von openHAB 2.x oder 3.x, die noch mit der alten Xtend-basierten Rules DSL arbeiten, ist nachfolgend die ursprüngliche Regelsammlung als Archiv aufgeführt.
Hinweis: Diese Regeln sind veraltet und nutzen globale Maps sowie Thread::sleep. Für Neuinstallationen wird dringend das oben stehende JS Scripting empfohlen.
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
// --------------------------------------------------
//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"
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
// --------------------------------------------------
//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"
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"
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"
when
Item KalenderWatchdog_CurrentEventPresence changed to OFF
then {
WatchdogNightshift.sendCommand(OFF)
}
end
// --------------------------------------------------
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
// --------------------------------------------------
// --- 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