/*
########################################################################################################################
# ALARMSYSTEM
#
# Das Skript bildet eine einfache Alarmanlage nach mit der Schaltmöglichkeit
# für intern und extern.
# Datenpunkte für Inputs und Outputs werden angelegt.
# Nähere Beschreibung siehe im ioBroker-Forum unter
# https://forum.iobroker.net/topic/32885/umfassendes-alarmanlagen-skript
# Änderungshistorie:
# 2020-05-01 Andreas Kos Erstellt
# 2020-05-02 Andreas Kos Schaltwunsch mit Number-Datenpunkt Input.SwitchNumber (Idee von @Homer.J.)
# Schaltstatus mit Number-Datenpunkt Output.ActiveNumber (Idee von @Homer.J.)
# 2020-05-03 Andreas Kos Korrekturen, u.a. für Melderauswertung (chage: "ne") & AlarmText
# 2020-05-04 Andreas Kos - Melder werden aus den Functions (Aufzählungen, enums) dafür geholt. Auch beim Unscharf-
# schalten, dadurch ist kein Neustarten des Skripts notwendig bei
# Änderungen an diesen Aufzählungen.
# - Eine Schaltung von einem scharf-Zustand auf einen anderen
# wird verhindert. ZB von scharf intern auf scharf extern.
# Es muss immer unscharf dazwischen geschaltet werden.
# 2020-05-09 Andreas Kos Zusätzliche Objekte mit JSON-Strings für:
# - den auslösenden Melder
# - alle offenen Melder
# - alle offenen Melder der Außenhaut
# - alle offenen Melder des Innenraums
# Die JSON-String beinhalten das auslösende Objekt, sowie (falls vorhanden)
# das Parent und das ParentsParent-Objekt mit allen in ioBroker verfügbaren Eigenschaften.
# Kleinere Verbesserungen, z.B. bezüglich setzen der AlarmTexte.
# 2020-05-12 Andreas Kos Setzen des Datenpunkts idReady zur Bereitschaftsanzeige neu gemacht.
# 2021-06-13 Andreas Kos Einbau der Funktion zum Ausnehmen einzelner Melder der Aussenhülle
# von der Melder-Überwachung. Soll zum Kippen von Fenstern dienen u.ä.
########################################################################################################################
*/
// EINBRUCHSMELDER
// Jeder Melder muss ein State sein, der bei Auslösung true liefert und in Ruhe (geschlossen) false.
// Die Melder sind in Arrays zusammengefasst, d.h. sie müssen jeweils mit Beistrich voneinander getrennt werden.
// Die Namen der Melder sollten gut gepflegt sein für eine sinnvolle Verwendung (Attribut name bei den Objekten)
// Melder der Außenhaut
// Dies können Öffnungskontakte sein von Fenster und Türen in den Außenmauern des Objekts.
// EINGABE: In der Aufzählung "alarmanlage_aussenhaut" die States einfügen.
// Melder des Innenraums
// Dies können Bewegungsmelder sein aus dem Inneren.
// EINGABE: In der Aufzählung "alarmanlage_innenraum" die States einfügen.
// Verzögerte Melder
// Diese kommen in den Gruppen oben auch vor. Sie bewirken eine Aktivierung der Eingangsverzögerung
// bei scharf geschalteter Anlage und erlauben während der Ausgangsverzögerung nach dem
// Scharfschalten das Haus zu verlassen.
// EINGABE: In der Aufzählung "alarmanlage_verzoegert" die States einfügen.
// EINSTELLUNGEN
const entryDelay = 30; // Eingangsverzögerung in Sekunden (sollte maximal 60s sein)
const exitDelay = 30; // Ausgangsverzögerung in Sekunden (sollte maximal 60s sein)
const alarmDurationAccoustical = 180; // Dauer des akkustischen Alarms in Sekunden (ACHTUNG: in Ö sind maximal 180s erlaubt!)
const alarmDurationOptical = -1; // Dauer des optischen Alarm in Sekunden, -1 für unendlich
// TEXTE FÜR SCHALTZUSTAND
// Diese Text geben Auskunft über den Zustand der Anlage.
// Sie werden in den Datenpunkt "javascript.X.Output.StatusText" geschrieben.
const textStatusInactive = "unscharf";
const textStatusActiveInternal = "scharf intern";
const textStatusActiveExternal = "scharf extern";
const textActiveExternalDelayed = "scharf extern verzögert";
const textEntryDelayActive = "Eingangsverzögerung aktiv";
const textExitDelayActive = "Ausgangsverzögerung aktiv";
// TEXTE FÜR ALARMIERUNG UND FEHLER
// Diese Text geben im unscharfen Zustand der Anlage Auskunft über die Bereitschaft
// zum Scharfschalten (nur möglich, wenn alle Melder geschlossen - in Ruhe - sind) und
// Fehler bei der Scharfschaltung bzw. bei scharfer Anlage über den Zustand Frieden oder Alarm.
// Sie werden in den Datenpunkt "javascript.X.Output.AlarmText" geschrieben.
const textAlarmInactive = "Alles OK";
const textAlarmActive = "Alarm!!";
const textReady = "Bereit";
const textNotReady = "Nicht bereit";
const textError = "Fehler bei der Scharfschaltung";
// EXPERTEN-EINSTELLUNGEN
const pathToCreatedStates = "Alarmanlage"; // Beispiel: States werden erzeugt unter javascript.X.Alarmanlage
const seperator = ", "; // Trenn-String, der zwischen den Meldernamen verwendet wird, im Datenpunkt "OpenDetectors"
const loglevel = 2; // 0 bis 3. 0 ist AUS, 3 ist maximales Logging
// Empfehlung für Nachvollziehbarkeit aller Handlungen ist 2 (Ereignisliste)
const functionOuterSkin = "alarmanlage_aussenhaut";
const functionIndoor = "alarmanlage_innenraum";
const functionDelayedDetectors = "alarmanlage_verzoegert";
/*
###############################################################################
DO NOT CHANGE ANYTHING BELOW THIS LINE
AB HIER NICHTS MEHR ÄNDERN
###############################################################################
*/
// ===============================================================================
// Variablen
// ===============================================================================
// Arrays für die Melder
var detectorsOuterSkin = [];
var detectorsIndoor = [];
var detectorsDelayed = [];
// Array für die IgnoreOpen-Melder (beinhaltet nur Flags).
// Das Array ist inital deckungsgleich mit detectorsOuterSkin, da diese
// nur aus dieser Function kommen können.
// Dieses Array ist 2-Dimensional: [Melder-ID, IgnoreOpen-Zustands-ID]
var detectorsIgnoreOpen = [];
// Javascript-Instanz mit der das Alarmanlagen-Skript ausgeführt wird
var javascriptInstance = instance;
// States, die erzeugt werden für Status-Ausgaben
var idActive = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.Active";
var idActiveExternal = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.ActiveExternal";
var idActiveInternal = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.ActiveInternal";
var idActiveNumber = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.ActiveNumber";
var idAlarm = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.Alarm";
var idAlarmAccoustical = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.AlarmAccoustical";
var idAlarmOptical = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.AlarmOptical";
var idReady = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.Ready";
var idEntryDelayActive = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.EntryDelayActive";
var idExitDelayActive = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.ExitDelayActive";
var idAlarmingDetector = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.AlarmingDetector";
var idAlarmingDetectorJSON = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.AlarmingDetectorJSON";
var idOpenDetectors = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.OpenDetectors";
var idOpenDetectorsJSON = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.OpenDetectorsJSON";
var idOpenDetectorsOuterSkinJSON = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.OpenDetectorsOuterSkinJSON";
var idOpenDetectorsIndoorJSON = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.OpenDetectorsIndoorJSON";
var idOpenDetectorsWithIgnoreOpenFlagSet = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.OpenDetectorsIgnoreOpen";
var idStatusText = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.StatusText";
var idAlarmText = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.AlarmText";
var idError = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Output.Error";
// States, die erzeugt werden für Eingaben
var idSwitchExternal = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Input.SwitchExternal";
var idSwitchInternal = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Input.SwitchInternal";
var idSwitchExternalDelayed = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Input.SwitchExternalDelayed";
var idSwitchNumber = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Input.SwitchNumber";
// Sonstige globale Variablen, die gebraucht werden
var timerExitDelay = null;
var timerTexts = null;
// ===============================================================================
// Precode
// ===============================================================================
// Logging
if (loglevel >= 1) log ("Alarmsystem started.");
getAllDetectors();
// States erzeugen
myCreateState(idActive, "boolean", false, "Switch Status Total", false, "info.status");
myCreateState(idActiveExternal, "boolean", false, "Switch Status External", false, "info.status");
myCreateState(idActiveInternal, "boolean", false, "Switch Status Internal", false, "info.status");
myCreateState(idAlarm, "boolean", false, "Alarm Status", false, "sensor.alarm");
myCreateState(idAlarmAccoustical, "boolean", false, "Accoustical Alarm Status", false, "sensor.alarm");
myCreateState(idAlarmOptical, "boolean", false, "Optical Alarm Status", false, "sensor.alarm");
myCreateState(idReady, "boolean", false, "Alarmsystem Ready", false, "info.status");
myCreateState(idError, "boolean", false, "Error Switching Active", false, "info.status");
myCreateState(idEntryDelayActive, "boolean", false, "Entry Delay Active Status", false, "info.status");
myCreateState(idExitDelayActive, "boolean", false, "Exit Delay Active Status", false, "info.status");
myCreateState(idAlarmingDetector, "string", "", "Alarming Detector", false, "text");
myCreateState(idAlarmingDetectorJSON, "string", "", "Alarming Detector JSON", false, "json");
myCreateState(idOpenDetectors, "string", "", "Open Detectors", false, "info.name");
myCreateState(idOpenDetectorsJSON, "string", "", "Open Detectors JSON", false, "json");
myCreateState(idOpenDetectorsOuterSkinJSON, "string", "", "Open Detectors Outer Skin JSON", false, "json");
myCreateState(idOpenDetectorsIndoorJSON, "string", "", "Open Detectors Indoor JSON", false, "json");
myCreateState(idOpenDetectorsWithIgnoreOpenFlagSet, "string", "", "Open Detectors with IgnoreOpen-Flag set", false, "text");
myCreateState(idStatusText, "string", "", "Status Text", false, "text");
myCreateState(idAlarmText, "string", "", "Alarm Text", false, "text");
myCreateState(idSwitchExternal, "boolean", false, "Enable Surveillance External", true, "switch");
myCreateState(idSwitchInternal, "boolean", false, "Enable Surveillance Internal", true, "switch");
myCreateState(idSwitchExternalDelayed, "boolean", false, "Enable Surveillance External Delayed", true, "switch");
myCreateMultiState (idActiveNumber, "number", 0, "Switch Status Number", false, 0, 4,"0:"+textStatusInactive+"; 1:"+textStatusActiveInternal+"; 2:"+textStatusActiveExternal+"; 3:"+textExitDelayActive+"; 4:"+textEntryDelayActive);
myCreateMultiState (idSwitchNumber, "number", 0, "Switch by Number", true, 0, 3, "0:"+textStatusInactive+"; 1:"+textStatusActiveInternal+" ; 2:"+textStatusActiveExternal+" ; 3:"+textActiveExternalDelayed);
// Erzeugen der Datenpunkte für die IgnoreOpen-Einstellung
myCreateIgnoreOpenDPs();
// Melder nach dem Starten checken
checkDetectors(detectorsOuterSkin.concat(detectorsIndoor));
// ===============================================================================
// ON-Subscribtions
// ===============================================================================
// Auf Schaltstellen EXTERN verzögert reagieren (schalten EIN/AUS)
on ({id: idSwitchExternalDelayed, change: "any"}, function(obj){
if (loglevel >= 3) log ("Switching External Delayed, Value: " + getState(obj.id).val);
if (getState(obj.id).val) { // Einschalten, scharf extern VERZÖGERT
if (loglevel >= 2) log ("Switching required: Delayed External Active");
if (getState(idActive).val) {
if (loglevel >= 3) log ("Alarmsystem already active, switch to inactive first!");
setState(idError, true);
} else {
setState(idExitDelayActive, true);
if (timerExitDelay) clearTimeout(timerExitDelay);
timerExitDelay = setTimeout(switchActiveExternal, exitDelay * 1000);
}
}
else { // Ausschalten, unscharf SOFORT
if (loglevel >= 2) log ("Switching required: Inactive");
switchInactive();
}
});
// Auf Schaltstellen EXTERN sofort reagieren (schalten EIN/AUS)
on ({id: idSwitchExternal, change: "any"}, function(obj){
if (loglevel >= 3) log ("Switching External Immediately, Value: " + getState(obj.id).val);
if (getState(obj.id).val) { // Einschalten, scharf extern
if (loglevel >= 2) log ("Switching required: External Active");
if (getState(idActive).val) {
if (loglevel >= 3) log ("Alarmsystem already active, switch to inactive first!");
setState(idError, true);
} else {
switchActiveExternal();
}
}
else { // Ausschalten, unscharf
if (loglevel >= 2) log ("Switching required: Inactive");
switchInactive();
}
});
// Auf Schaltstellen INTERN sofort reagieren (schalten EIN/AUS)
on ({id: idSwitchInternal, change: "any"}, function(obj){
if (loglevel >= 3) log ("Switching Internal, Value: " + getState(obj.id).val);
if (getState(obj.id).val) { // Einschalten, scharf intern
if (loglevel >= 2) log ("Switching required: Internal Active");
if (getState(idActive).val) {
if (loglevel >= 3) log ("Alarmsystem already active, switch to inactive first!");
setState(idError, true);
} else {
switchActiveInternal();
}
}
else { // Ausschalten, unscharf
switchInactive();
if (loglevel >= 2) log ("Switching required: Inactive");
}
});
// Auf Schaltstelle mit Datenpunkt SwitchNumber reagieren
// Folgende Reaktionen:
// 0 ... unscharf schalten
// 1 ... scharf intern schalten
// 2 ... scharf extern schalten
// 3 ... verzögert scharf extern schalten
on ({id: idSwitchNumber, change: "any"}, function(obj){
if (loglevel >= 3) log ("Switching Number, Value: " + obj.state.val);
switch (obj.state.val) {
case 0:
if (loglevel >= 2) log ("Switching required: Inactive");
switchInactive();
break;
case 1:
if (loglevel >= 2) log ("Switching required: Internal Active");
if (getState(idActive).val) {
if (loglevel >= 3) log ("Alarmsystem already active, switch to inactive first!");
setState(idError, true);
} else {
switchActiveInternal();
}
break;
case 2:
if (loglevel >= 2) log ("Switching required: External Active");
if (getState(idActive).val) {
if (loglevel >= 3) log ("Alarmsystem already active, switch to inactive first!");
setState(idError, true);
} else {
switchActiveExternal();
}
break;
case 3:
if (loglevel >= 2) log ("Switching required: Delayed External Active");
if (getState(idActive).val) {
if (loglevel >= 3) log ("Alarmsystem already active, switch to inactive first!");
setState(idError, true);
} else {
setState(idExitDelayActive, true);
if (timerExitDelay) clearTimeout(timerExitDelay);
timerExitDelay = setTimeout(switchActiveExternal, exitDelay * 1000);
}
break;
default:
if (loglevel >= 3) log ("idSwitchNumber has unknown number!");
}
});
// Auf Fehler bei der Scharfschaltung reagieren
on ({id: idError, val: true, change: "any"}, function(obj){
if (loglevel >= 1) log ("Error when switching to active.");
setState(idAlarmText, textError);
});
// Auf Eingangsverzögerung reagieren
on({id: idEntryDelayActive, val: true, change: "ne"}, function (obj) {
if (loglevel >= 2) log ("Entry Delay is active (" + entryDelay + " seconds)");
setState(idStatusText, textEntryDelayActive);
setStateDelayed(idAlarmAccoustical, true, entryDelay * 1000);
setStateDelayed(idAlarmOptical, true, entryDelay * 1000);
});
// Auf Ausgangsverzögerung reagieren
on({id: idExitDelayActive, val: true, change: "ne"}, function (obj) {
if (loglevel >= 2) log ("Exit Delay is active (" + exitDelay + " seconds)");
setState(idStatusText, textExitDelayActive);
});
// Auf Akustischen Alarm reagieren
on ({id: idAlarmAccoustical, val: true, change: "ne"}, function(obj){
if (loglevel >= 1) log ("ALARM is active!");
setState(idEntryDelayActive, false);
setState(idAlarmText, textAlarmActive);
setState(idAlarm, true);
setStateDelayed(idAlarmAccoustical, false, alarmDurationAccoustical * 1000);
if (getState(idActiveExternal).val) setState(idStatusText, textStatusActiveExternal);
if (getState(idActiveInternal).val) setState(idStatusText, textStatusActiveInternal);
});
// Auf Optischen Alarm reagieren
on ({id: idAlarmOptical, val: true, change: "ne"}, function(obj){
if (alarmDurationOptical >= 0)
setStateDelayed(idAlarmOptical, false, alarmDurationOptical * 1000);
});
// Melderauswertung
on( {id: detectorsOuterSkin.concat(detectorsIndoor), change: "ne"}, function(obj){
detectorSurveillance(obj);
});
// Status in Active.Number schreiben
on ({id: [idActiveInternal, idActiveExternal, idEntryDelayActive, idExitDelayActive], change: "ne"}, function(obj){
if (loglevel >= 3) log ("on for writing ActiveNumber, Trigger: " + obj.id);
setActiveNumber();
});
// Texte korrekt setzen
on ({id: [idAlarm, idActive, idOpenDetectors], change: "any"}, function (obj) {
// Das Timeout ggf. abbrechen
if (timerTexts) clearTimeout(timerTexts);
// Nach einem Timeout den Check anfangen
timerTexts = setTimeout(setTexts, 200);
});
// ===============================================================================
// Funktionen
// ===============================================================================
// Funktion checkIfIgnoreOpenFlagSet
// =====================================
// Parameter: Prüft, ob das IgnoreOpen-Flag für einen Melder gesetzt ist.
// Übergabewert: Melder-ID
// Rückgabewert: Flag-Zustand
function checkIfIgnoreOpenFlagSet(id){
var i=0;
while (i<detectorsIgnoreOpen.length){
if ( detectorsIgnoreOpen[i][0] == id )
if (getState(detectorsIgnoreOpen[i][1]).val)
return true;
i++;
}
return false;
}
// Function setTexts
// =================
// Parameter: keiner
// Funktion: Abhängig von den Zuständen scharf/unscharf und
// Alarm / Kein Alarm werden Texte für
// den AlarmText richtig gesetzt. Auch der Datenpunkt idReady wird hier gesetzt.
// Rückgabewert: keiner
function setTexts(){
var textListOfDetectors = getState(idOpenDetectors).val;
if (textListOfDetectors.length > 0) {
if (!getState(idActive).val)
// Offene Melder gefunden und unscharf
setState(idAlarmText, textNotReady);
setState(idReady, false);
return false;
} else {
if (getState(idActive).val && !getState(idAlarm).val)
// kein offener Melder gefunden und scharf und kein Alarm
setState(idAlarmText, textAlarmInactive);
else if (!getState(idActive).val)
// kein offener Melder gefunden und unscharf
setState(idAlarmText, textReady);
setState(idReady, true);
return true;
}
}
// Function getAllDetectors
// ========================
// Parameter: keiner
// Funktion: Über die Funktion getDetectorsFromFunction werden
// alle Melder von den Functions geholt und in die
// globalen Variablen dafür geschrieben.
// Rückgabewert: keiner
function getAllDetectors(){
var i=0;
detectorsOuterSkin = getDetectorsFromFunction(functionOuterSkin);
detectorsIgnoreOpen = [];
while (i<detectorsOuterSkin.length) {
detectorsIgnoreOpen.push([detectorsOuterSkin[i++]]);
}
detectorsIndoor = getDetectorsFromFunction(functionIndoor);
detectorsDelayed = getDetectorsFromFunction(functionDelayedDetectors);
}
// Function getDetectorsFromFunction
// =================================
// Parameter: functionString
// Name der Function, in der die Melder enthalen sind.
// Funktion: Alle Teilnehmer der übergebenen Function werden
// in ein Array geschrieben.
// Rückgabewert: Array der Melder-IDs
function getDetectorsFromFunction( functionString ) {
if (loglevel >= 3) log ("Function getDetectorsFromFunction");
var detectors = [];
$('state(functions='+functionString+')').each(function(id, i) {
detectors.push(id);
if (loglevel >= 3) log ("This detector was added to surveillance from function "+functionString+": " + id);
});
return detectors;
}
// Function detectorSurveillance
// =============================
// Parameter: obj
// Objekt des auslösenden Melders
// Funktion: Abhängig vom Schaltzustand werden die Melder überprüft.
// Bei unscharf wird nur die Liste der offenen Melder und die
// Bereitschaft der Anlage zum Scharfschalten gepfelgt.
// Bei scharf geschalteter Anlage ist es anders:
// Es wird geprüft, ob der auslösende Melder in den Außenhaut- oder
// Innenraum-Meldern enthalten ist und ob dieser ein verzögerter Melder ist.
// Abhängig davon wird entweder sofort oder verzögert Alarm ausgelöst.
// Rückgabewert: keiner
function detectorSurveillance (obj) {
var active = getState(idActive).val;
var activeExternal = getState(idActiveExternal).val;
var activeInternal = getState(idActiveInternal).val;
var ready;
var ignoreOpenSet;
if (loglevel >= 2) log ("Surveillance of detectors started, triggering detector: " + obj.common.name);
// Alle offenen Melder feststellen
ready = checkDetectors(detectorsOuterSkin.concat(detectorsIndoor));
// Auslösenden Melder schreiben
setState(idAlarmingDetector, obj.common.name);
setState(idAlarmingDetectorJSON, JSON.stringify( getDetectorObject(obj.id) ));
// Prüfen, wie der der Schaltzustand ist
// bei unscharf
if (!active) {
if (loglevel >= 2) log ("Alarmsystem is Inactive");
}
// bei scharf intern
if (activeInternal) {
if (loglevel >= 2) log ("Alarmsystem is Internal Active");
if (detectorsOuterSkin.indexOf(obj.id) != -1) {
if (loglevel >= 3) log ("Detector is part of Outer Skin");
if (detectorsDelayed.indexOf(obj.id) != -1) {
if (loglevel >= 3) log ("Detector is part of Delayed Detectors");
if(!getState(idAlarm).val) setState(idEntryDelayActive, true);
else {
if (loglevel >= 3) log ("EntryDelay was already active, alarming now");
setState(idAlarmAccoustical, true);
setState(idAlarmOptical, true);
}
} else {
if (loglevel >= 3) log ("Detector is not delayed");
setState(idAlarmAccoustical, true);
setState(idAlarmOptical, true);
}
}
}
// bei scharf extern
if (activeExternal) {
if (loglevel >= 2) log ("Alarmsystem is External Active");
// Prüfen, ob er der Melder das IgnoreOpen-Flag gesetzt hat.
ignoreOpenSet = checkIfIgnoreOpenFlagSet(obj.id);
if (loglevel >= 2) log ("Detector has IgnoreOpen-Flag set true!");
if (ignoreOpenSet) return;
if (detectorsOuterSkin.concat(detectorsIndoor).indexOf(obj.id) != -1) {
if (loglevel >= 3) log ("Detector is part of Outer Skin or Indoor");
if (detectorsDelayed.indexOf(obj.id) != -1) {
if (loglevel >= 3) log ("Detector is part of Delayed Detectors");
if(!getState(idAlarm).val) setState(idEntryDelayActive, true);
else {
if (loglevel >= 3) log ("EntryDelay was already active, alarming now");
setState(idAlarmAccoustical, true);
setState(idAlarmOptical, true);
}
} else {
if (loglevel >= 3) log ("Detector is not delayed");
if (loglevel >= 2) log ("System is ALARMING now!");
setState(idAlarmAccoustical, true);
setState(idAlarmOptical, true);
}
}
}
}
// Function myCreateState
// =======================
// Parameter: id ... id des neu anzulegenden Datenpunkts
// typ ... typ des anzulegenden Datenpunkts ("boolean", "string", "number", etc.)
// val ... Wert, den der Datenpunkt nach dem Anlegen haben soll
// descr ... Name als String
// writeaccess Schreibrechte true oder false
// Funktion: Mit der Funktion createState wird der neue Datenpunkt mit den übergebenen
// Parametern angelegt.
// Rückgabewert: keiner
function myCreateState(id, typ, val, descr, writeaccess, role) {
if (loglevel >= 3) log ("Function myCreateState for " + id);
createState(id, val, {read: !writeaccess,
write: writeaccess,
name: descr,
type: typ,
def: val,
role: role ? role : "state"
});
}
// Function myCreateMultiState
// ===========================
// Parameter: id ... id des neu anzulegenden Datenpunkts
// typ ... typ des anzulegenden Datenpunkts ("boolean", "string", "number", etc.)
// val ... Wert, den der Datenpunkt nach dem Anlegen haben soll
// descr ... Name als String
// min ... Minimalwert
// max ... Maximalwert
// list ... Liste mit Werten und Bezeichnern im Format "0:Text0; 1:Text1; 2:Text2"
// writeaccess Schreibrechte true oder false
// Funktion: Mit der Funktion createState wird der neue Datenpunkt mit den übergebenen
// Parametern angelegt.
// Rückgabewert: keiner
function myCreateMultiState(id, typ, val, descr, writeaccess, minimum, maximum, list, role) {
if (loglevel >= 3) log ("Function myCreateMultiState for " + id);
createState(id, val, { read: true,
write: writeaccess,
name: descr,
type: typ,
def: val,
min: minimum,
max: maximum,
states: list,
role: role ? role : "state"
});
}
// Funktion myCreateIgnoreOpenDPs
// ==============================
// Parameter: Keine
// Funktion: Erzeugt die Datenpunkte für die Ignoge-Open-Melder.
// Dafür werden die Namen aus dem detectorsOuterSkin-Array geholt.
// Die IDs der Ignore-Open Datenpunkte für jeden Melder werden auch in
// das Array detectorsIgnoreOpen geschrieben. Dieses wird damit 3-Dimensional.
// Rückgabewert: Keiner.
function myCreateIgnoreOpenDPs(){
var detectorName;
var idBase = "javascript." + javascriptInstance + "." + pathToCreatedStates + ".Input.IgnoreOpen.";
var i=0;
while (i<detectorsOuterSkin.length) {
detectorName = getObject(detectorsOuterSkin[i]).common.name.replace(/\./g,"_");
myCreateState(idBase + detectorName, "boolean", false, "Ignore Open for " + detectorName, false, "switch");
detectorsIgnoreOpen[i].push(idBase + detectorName);
i++;
}
}
// Function switchActiveExternal
// =============================
// Parameter: keiner
// Funktion: Nach Prüfung auf Bereitschaft zum externen Scharfschalten, wird
// scharf geschaltet oder ein Fehler angezeigt.
// Rückgabewert: keiner
function switchActiveExternal () {
if (loglevel >= 3) log ("Function switchActiveExternal");
setState(idExitDelayActive, false);
var ok = checkDetectors(detectorsOuterSkin.concat(detectorsIndoor));
if (ok) {
setState(idActiveExternal, true);
setState(idActive, true);
setState(idStatusText, textStatusActiveExternal);
setState(idAlarmText, textAlarmInactive);
setState(idAlarmingDetector,"");
setState(idError, false);
if (loglevel >= 2) log ("Switched to External Active");
} else {
if (loglevel >= 2) log ("NOT ready to switch to External Active!");
setState(idError, true);
}
}
// Function switchActiveInternal
// =============================
// Parameter: keiner
// Funktion: Nach Prüfung auf Bereitschaft zum internen Scharfschalten, wird
// scharf geschaltet oder ein Fehler angezeigt.
// Rückgabewert: keiner
function switchActiveInternal () {
if (loglevel >= 3) log ("Function switchActiveInternal");
var ok = checkDetectors(detectorsOuterSkin);
if (ok) {
setState(idActiveInternal, true);
setState(idActive, true);
setState(idStatusText, textStatusActiveInternal);
setState(idAlarmText, textAlarmInactive);
setState(idAlarmingDetector,"");
setState(idError, false);
if (loglevel >= 2) log ("Switched to Internal Active");
} else {
if (loglevel >= 2) log ("NOT ready to switch to Internal Active!");
setState(idError, true);
}
;}
// Function switchInactive
// =============================
// Parameter: keiner
// Funktion: Es wird unscharf geschaltet und die ganze Anlage resetiert.
// Rückgabewert: keiner
function switchInactive () {
if (loglevel >= 3) log ("Function switchInactive");
if (timerExitDelay) clearTimeout(timerExitDelay);
setState(idEntryDelayActive, false);
setState(idExitDelayActive, false);
setState(idActiveExternal, false);
setState(idActiveInternal, false);
setState(idActive, false);
setState(idError, false);
clearStateDelayed(idAlarmAccoustical);
clearStateDelayed(idAlarmOptical);
setState(idAlarmAccoustical, false);
setState(idAlarmOptical, false);
setState(idAlarm, false);
setState(idAlarmText, textAlarmInactive);
setState(idStatusText, textStatusInactive);
checkDetectors(detectorsOuterSkin.concat(detectorsIndoor));
if (loglevel >= 2) log ("Switched to Inactive");
getAllDetectors();
}
// Function setActiveNumber
// =======================
// Parameter: keine
// Funktion: Prüft ob intern scharf, extern scharf oder unscharf ist und
// ob jeweils Eingangs- oder Ausgangsverzögerung aktiv ist.
// Abhängig davon wird der Datenpunt "Output.ActiveNumber"
// wie folgt gesetzt:
// 0 ... unscharf
// 1 ... intern scharf
// 2 ... extern scharf
// 3 ... Eingangsverzögerung aktiv
// 4 ... Ausgangsverzögerung aktiv
// Rückgabewert: keiner
function setActiveNumber () {
if (loglevel >= 3) log ("Function setActiveNumber");
var internal = getState(idActiveInternal).val;
var external = getState(idActiveExternal).val;
var entry = getState(idEntryDelayActive).val;
var exit = getState(idExitDelayActive).val;
if (!external && !internal) {
if (exit)
setState(idActiveNumber, 4);
else
setState(idActiveNumber, 0);
}
if (internal) setState(idActiveNumber, 1);
if (external){
if (entry)
setState(idActiveNumber, 3);
else
setState(idActiveNumber, 2);
}
}
// Function checkDetectors
// =======================
// Parameter: detectors
// Array mit Melder-IDs
// Funktion: Alle Melder aus dem Array werden geprüft und alle offenen
// Melder werden in einen Datenpunkt geschrieben als String.
// Das Trennzeichen zwischen den Meldernamen ist die globale
// Variable "seperator".
// Rückgabewert: true, wenn alle Melder in Ruhe sind
// false, wenn ein oder mehr Melder ausgelöst sind
function checkDetectors ( detectors ) {
if (loglevel >= 3) log ("Function checkDetectors");
var i=0;
var textListOfDetectors="";
var textListOfDetectorsIgnoreOpen="";
var objOpenDetectors = {
title: "All open detectors",
zone: null,
listOfDetectors: []
};
var objOpenDetectorsOuterSkin = {
title: "Open detectors outer skin",
zone: functionOuterSkin,
listOfDetectors: []
};
var objOpenDetectorsIndoor = {
title: "Open detectors indoor",
zone: functionIndoor,
listOfDetectors: []
};
while(i < detectors.length) {
if (getState(detectors[i]).val) {
if (checkIfIgnoreOpenFlagSet(detectors[i])) {
if (textListOfDetectorsIgnoreOpen.length > 0) textListOfDetectorsIgnoreOpen += seperator;
textListOfDetectorsIgnoreOpen += getObject(detectors[i]).common.name;
}
else {
if (textListOfDetectors.length > 0) textListOfDetectors += seperator;
textListOfDetectors += getObject(detectors[i]).common.name;
}
var detObj = getDetectorObject(detectors[i]);
objOpenDetectors.listOfDetectors.push(detObj);
if (detectorsOuterSkin.indexOf(detectors[i]) != -1) objOpenDetectorsOuterSkin.listOfDetectors.push(detObj);
if (detectorsIndoor.indexOf(detectors[i]) != -1) objOpenDetectorsIndoor.listOfDetectors.push(detObj);
}
i++;
}
if (loglevel >= 3) log ("Open Detectors found: " + (textListOfDetectors.length ? textListOfDetectors : "none"));
// Datenpunkte schreiben
setState(idOpenDetectors, textListOfDetectors);
setState(idOpenDetectorsWithIgnoreOpenFlagSet, textListOfDetectorsIgnoreOpen);
setState(idOpenDetectorsJSON, JSON.stringify(objOpenDetectors));
setState(idOpenDetectorsOuterSkinJSON, JSON.stringify(objOpenDetectorsOuterSkin));
setState(idOpenDetectorsIndoorJSON, JSON.stringify(objOpenDetectorsIndoor));
if (textListOfDetectors.length > 0) {
return false;
} else {
return true;
}
}
// Function getDetectorObject
// ==========================
// Parameter: id
// id eines Melder-States
// Funktion: Vom Melder mit der id wird das Objekt über getObjekt(id) geholt,
// sowie von seinem Parent und ParentsParent.
// Alle Objekte kommen in ein großes Objekt und werden zurück gegeben.
// Rückgabewert: Das Objekt des Melders samt Parent und ParentsParent (sofern es welche gibt)
function getDetectorObject (id) {
if (loglevel >= 3) log ("Function getDetectorObject for id: " + id);
// ioBroker Parent-Datenpunkt (z.B. Kanal), kann auch null sein (zB bei KNX)
var idParent = id.substr(0, id.lastIndexOf("."));
// ioBroker ParentsParent-Datenpunkt (z.B. Gerät), kann auch null sein (zB bei KNX)
var idParentsParent = idParent.substr(0, idParent.lastIndexOf("."));
// Objekte dazu holen
var obj = getObject(id);
var objParent = getObject(idParent);
var objParentsParent = getObject(idParentsParent);
// Alle Objekte in ein großes Objekt sammeln
var detectorsObj = {
id: id,
self: obj,
parent: objParent,
parentsparent: objParentsParent
};
// Rückgeben
return detectorsObj;
}