I´m Sorry.
You are totally right.
Half of the it is moreless impossible to help.
The hole rule is a long one an written in .js. I will post it here. All the comments are in German.
Best regards Julian
const { rules, triggers, items, actions } = require('openhab');
let userStatus = {}; // Speichert den Wecker-Status pro Benutzer
let yourTimeItem = {};
// Benutzer initialisieren
["Juli", "Luisa", "Jonas"].forEach(user => {
if (!userStatus[user]) {
userStatus[user] = { isAlarmTriggered: false, announcementDone: false, outputTriggered: false, timeTrigger: time.ZonedDateTime.now() };
console.info(`NestWecker /isSystemStarted/: System Variable angelegt: ${user} mit den Werten ${JSON.stringify(userStatus[user])}`);
}
});
let checkAnnouncementEnd = {}; // Speichert Timeouts pro Nutzer
let checkPlayed = {}; // Speichert Timeouts pro Nutzer
let waitInterval = {};
let waitedTime = {};
const streamMappings = {
"https://liveradio.swr.de/sw282p3/swr3/play.mp3": "SWR3",
"https://stream.sunshine-live.de/edm/mp3-192/stream.sunshine-live.de/": "Sunshine EDM",
"https://stream.regenbogen.de/karlsruhe/mp3-128": "Radio Regenbogen",
"https://stream.rockantenne.de/rockantenne/stream/mp3": "RockAntenne"
};
// **🚀 Mapping von Benutzern auf ihre Chromecast-Geräte**
const userDevices = {
"Juli": "chromecast:audio:ChromeNestJuli",
"Luisa": "chromecast:audio:ChromeNestLuisa",
"Jonas": "chromecast:audio:ChromeNestJonas"
};
// **🚀 Prüft, ob OpenHAB länger als 5 Minuten läuft**
function isSystemStarted() {
let uptime = parseInt(items["RP_Betriebszeit"]?.state ?? 0);
console.info(`NestWecker /isSystemStarted/:System läuft seit: ${uptime} Minuten`);
return uptime > 5;
};
// **🚀 Sound-Ordner für jeden Benutzer**
// const soundFolders = {
// "Juli": "etc/openhab/sounds/Juli/",
// "Luisa": "etc/openhab/sounds/Luisa/",
// "Jonas": "etc/openhab/sounds/Jonas/"
// };
function setOutputTriggered(user) {
// let nowTime = time.ZonedDateTime.now();
// userStatus[user].timeTrigger = nowTime;
// console.error(`NestWecker /setOutputTriggered/:✅ Neuer Time ${userStatus[user].timeTrigger} für ${user} gesetzt`);
};
function isTimeTriggerOlderThan2Seconds(user) {
// let nowTime = time.ZonedDateTime.now();
// let timeTrigger = userStatus[user]?.timeTrigger; // Stelle sicher, dass der Wert existiert
// if (!timeTrigger) {
// console.warn(`NestWecker /sTimeTriggerOlderThan2Seconds/:🚨 Kein TimeTrigger für ${user} gefunden.`);
// return false;
// }
// console.warn(`NestWecker /sTimeTriggerOlderThan2Seconds/:🚨 Berechne Zeitunterschied für ${user}.`);
// let duration = nowTime.toInstant().toEpochMilli() - timeTrigger.toInstant().toEpochMilli();
// console.warn(`NestWecker /sTimeTriggerOlderThan2Seconds/:🚨 Zeitunterschied ${duration} in Millisekunden für ${user} .`);
// return duration > 2000;
return true;
};
var now = time.ZonedDateTime.now();
userStatus["Jonas"].timerID = actions.ScriptExecution.createTimer('My Timer Jonas', now.plusSeconds(1), resetOutputTriggerJonas);
userStatus["Luisa"].timerID = actions.ScriptExecution.createTimer('My Timer Luisa', now.plusSeconds(1), resetOutputTriggerLuisa);
userStatus["Juli"].timerID = actions.ScriptExecution.createTimer('My Timer_ Juli', now.plusSeconds(1), resetOutputTriggerJuli);
function setOutputTriggered(user) {
userStatus[user]?.outputTriggered = true;
console.info(`NestWecker /setOutputTriggered/:✅ OutputTrigger für ${user} auf TRUE geändert`);
let nowTime = time.ZonedDateTime.now(); // Vermeidung von Race Conditions
switch(user) {
case "Jonas":
console.error(`NestWecker /setOutputTriggered/:✅ CASE Jonas`);
if (userStatus["Jonas"].timerID !== null && !userStatus["Jonas"].timerID.hasTerminated()) {
userStatus["Jonas"].timerID.cancel();
console.error(`NestWecker /setOutputTriggered/:✅ Timer Jonas gecancelt`);
}
userStatus["Jonas"].timerID = actions.ScriptExecution.createTimer('My Timer Jonas', nowTime.plusSeconds(2), resetOutputTriggerJonas);
console.error(`NestWecker /setOutputTriggered/:✅ Neuer Timer Jonas gestartet`);
break;
case "Luisa":
console.error(`NestWecker /setOutputTriggered/:✅ CASE Luisa`);
if (userStatus["Luisa"].timerID !== null && !userStatus["Luisa"].timerID.hasTerminated()) {
userStatus["Luisa"].timerID.cancel();
console.error(`NestWecker /setOutputTriggered/:✅ Timer Luisa gecancelt`);
}
userStatus["Luisa"].timerID = actions.ScriptExecution.createTimer('My Timer Luisa', nowTime.plusSeconds(2), resetOutputTriggerLuisa);
console.error(`NestWecker /setOutputTriggered/:✅ Neuer Timer Luisa gestartet`);
break;
case "Juli":
console.error(`NestWecker /setOutputTriggered/:✅ CASE Juli`);
if (userStatus["Juli"].timerID !== null && !userStatus["Juli"].timerID.hasTerminated()) {
userStatus["Juli"].timerID.cancel();
console.error(`NestWecker /setOutputTriggered/:✅ Timer Juli gecancelt`);
}
userStatus["Juli"].timerID = actions.ScriptExecution.createTimer('My Timer Juli', nowTime.plusSeconds(2), resetOutputTriggerJuli);
console.error(`NestWecker /setOutputTriggered/:✅ Neuer Timer Juli gestartet`);
break;
default:
return;
}
console.error(`NestWecker /setOutputTriggered/: 🚨 TimerID ${userStatus[user].timerID} für ${user}`);
}
function resetOutputTriggerJonas() {
let userRE = "Jonas";
userStatus[userRE].outputTriggered = false;
userStatus[userRE].timerID = null;
console.error(`NestWecker /setOutputTriggered/:❌ Timer ${userRE} gelöscht und OutputTrigger auf FALSE gesetzt`);
}
function resetOutputTriggerLuisa() {
let userRE = "Luisa";
userStatus[userRE].outputTriggered = false;
userStatus[userRE].timerID = null;
console.error(`NestWecker /setOutputTriggered/:❌ Timer ${userRE} gelöscht und OutputTrigger auf FALSE gesetzt`);
}
function resetOutputTriggerJuli() {
let userRE = "Juli";
userStatus[userRE].outputTriggered = false;
userStatus[userRE].timerID = null;
console.error(`NestWecker /setOutputTriggered/:❌ Timer ${userRE} gelöscht und OutputTrigger auf FALSE gesetzt`);
}
// function resetOutputTrigger(user) {
// //userStatus[user]?.outputTriggered = false;
// // console.error(`NestWecker /setOutputTriggered/: 🚨 Trigger gelöscht für ${user}`);
// //clearTimeout(userStatus[user].timerID);
// };
// Falls nötig, kann man den Timeout vor Ablauf abbrechen
// clearTimeout(timeoutId);
rules.JSRule({
name: "Nest Audio Control",
description: "Steuert Nest Audio Geräte und löst Streams aus",
triggers: [
triggers.ItemStateChangeTrigger("ChromeNestJuliPlayer"),
triggers.ItemStateChangeTrigger("ChromeNestJuliVolume"),
triggers.ItemStateChangeTrigger("ChromeNestLuisaPlayer"),
triggers.ItemStateChangeTrigger("ChromeNestLuisaVolume"),
triggers.ItemStateChangeTrigger("ChromeNestJonasPlayer"),
triggers.ItemStateChangeTrigger("ChromeNestJonasVolume")
],
execute: (event) => {
if (!isSystemStarted()) {
console.info("NestWecker /Nest Audio Control/⚠️: OpenHAB ist noch im Startvorgang (< 5 min). Ignoriere Regel.");
return;
}
let user = event.itemName.replace("ChromeNest", "").replace(/(Player|Volume)/, "");
if (userStatus[user].isAlarmTriggered) {
console.info(`NestWecker /Nest Audio Control/⚠️: Wecker für ${user} läuft, Befehle werden ignoriert.`);
return;
}
// //if (items[outputTriggerItem] === "ON") {
// if (isTimeTriggerOlderThan2Seconds(user)) {
// console.log(`Die gespeicherte Zeit für ${user} liegt mehr als 2 Sekunden zurück.`);
// } else {
// console.log(`NestWecker /Nest Audio Control/⚠️: Die gespeicherte Zeit für ${user} ist noch aktuell.`);
// console.info(`NestWecker /Nest Audio Control/⚠️: Ignoriert für ${user}, da OutputTriggered aktiv ist.`);
// return;
// }
// if (userStatus[user]?.outputTriggered == true) {
// console.info(`NestWecker /Nest Audio Control/⚠️: Ignoriert für ${user}, da OutputTriggered aktiv ist.`);
// return;
// }
console.info(`NestWecker /Nest Audio Control/: Regel getriggert durch ${event.itemName}`);
let playerControlItem = `ChromeNest${user}Control`;
//let outputTriggerItem = `${user}_OutputTriggered`;
let device = userDevices[user];
if (!items.existsItem(playerControlItem)) {
console.warn(`NestWecker /Nest Audio Control/⚠️: Item '${playerControlItem}' existiert nicht.`);
return;
}
if (items[playerControlItem].state !== "PAUSE") {
console.info(`NestWecker /Nest Audio Control/⚠️: Player für ${user} läuft bereits, neuer Stream wird nicht gestartet.`);
return;
}
if (event.itemName.endsWith("Volume") && event.newState !== null) {
let prevVolume = parseFloat(event.oldState);
let currentVolume = parseFloat(event.newState);
if (!isNaN(prevVolume) && !isNaN(currentVolume)) {
let streams = {
"Juli": [items.ChromeNestJuliStreamLaut?.state, items.ChromeNestJuliStreamLeise?.state],
"Luisa": [items.ChromeNestLuisaStreamLaut?.state, items.ChromeNestLuisaStreamLeise?.state],
"Jonas": [items.ChromeNestJonasStreamLaut?.state, items.ChromeNestJonasStreamLeise?.state]
};
if (streams[user] && streams[user][0] && streams[user][1]) {
let streamURL = prevVolume > currentVolume ? streams[user][1] : streams[user][0];
let streamName = streamMappings[streamURL] || "Unbekannter Stream";
let lautLeise = prevVolume > currentVolume ? "leiser" : "lauter";
console.info(`NestWecker /Nest Audio Control/🎶: Lautstärke Änderung erkannt. Starte ${lautLeise} Stream: ${streamName} (${streamURL}) auf ${device}`);
announceStreamStart(user, streamName, device);
waitForAnnouncementToEnd(user, device, streamURL);
} else {
console.warn(`NestWecker /Nest Audio Control/⚠️: Keine gültige Stream-Konfiguration für ${user} gefunden`);
}
}
} else if (event.itemName.endsWith("Player")) {
let action = event.newState;
console.info(`NestWecker /Nest Audio Control/🎵: Aktion erkannt für ${user}: ${action}`);
let radioStreams = {
"SWR3": "https://liveradio.swr.de/sw282p3/swr3/play.mp3",
"RockAntenne": "https://stream.rockantenne.de/rockantenne/stream/mp3",
"Regenbogen": "https://stream.regenbogen.de/karlsruhe/mp3-128",
"Sunshine_EDM": "https://stream.sunshine-live.de/edm/mp3-192/stream.sunshine-live.de/"
};
if (radioStreams[action]) {
console.info(`NestWecker /Nest Audio Control/: Starte Radiostream ${action} mit der URL ${radioStreams[action]} auf ${device}`);
announceStreamStart(user, action, device);
waitForAnnouncementToEnd(user, device, radioStreams[action]);
} else {
console.warn(`NestWecker /Nest Audio Control/: ⚠️ Unbekannte Aktion ${action}`);
}
}
}
});
/**
* 🚀 **Warte auf Ende der Sprachausgabe, dann starte Stream**
*/
function waitForAnnouncementToEnd(user, device, streamURL) {
let currentTimeItem = `ChromeNest${user}CurrentTime`;
let maxWaitTime = 10000; // Max. 10 Sekunden warten
waitInterval[user] = 4000; // Prüfe Anfangs nach 4 s und dann alle 500ms
waitedTime[user] = 0;
console.info(`NestWecker /waitForAnnouncementToEnd/:⏳ Warte auf Ende der Ansage für ${user} auf ${device}...`);
// Falls eine alte Instanz läuft, vorher beenden
if (checkAnnouncementEnd[user]) clearInterval(checkAnnouncementEnd[user]);
if (checkPlayed[user]) clearTimeout(checkPlayed[user]);
checkAnnouncementEnd[user] = setInterval(() => {
let currentTime = items[currentTimeItem]?.state?.toString() || "UNDEF";
if (currentTime === "0 s" || currentTime === "UNDEF") {
if (checkAnnouncementEnd[user]) {
clearInterval(checkAnnouncementEnd[user]);
delete checkAnnouncementEnd[user];
}
console.info(`NestWecker /waitForAnnouncementToEnd/:✅ Ansage für ${user} beendet. Starte Stream ${streamURL} auf ${device}`);
setOutputTriggered(user);
actions.Audio.playStream(device, null); // Stoppt aktuellen Stream
console.info(`NestWecker /waitForAnnouncementToEnd/:✅ Stream gelöscht für ${user}`);
// Falls bereits ein Timeout existiert, löschen
if (checkPlayed[user]) clearTimeout(checkPlayed[user]);
setTimeout(() => {
setOutputTriggered(user);
actions.Audio.playStream(device, streamURL);
console.info(`NestWecker /waitForAnnouncementToEnd/:✅ Stream aktiviert für ${user}`);
}, 1000); // 1 Sekunde warten, bevor der neue Stream startet
console.info(`NestWecker /waitForAnnouncementToEnd/:✅ TimeOut gestartet für ${user}`);
} else if (waitedTime[user] >= maxWaitTime) {
if (checkAnnouncementEnd[user]) {
clearInterval(checkAnnouncementEnd[user]);
delete checkAnnouncementEnd[user];
}
console.warn(`NestWecker /waitForAnnouncementToEnd/:⚠️ Timeout erreicht für ${user} auf ${device}! Starte Stream trotzdem.`);
setOutputTriggered(user);
actions.Audio.playStream(device, null); // Stoppt aktuellen Stream
// Falls bereits ein Timeout existiert, löschen
if (checkPlayed[user]) clearTimeout(checkPlayed[user]);
setTimeout(() => {
setOutputTriggered(user);
actions.Audio.playStream(device, streamURL);
}, 1000); // 1 Sekunde warten, bevor der neue Stream startet
}
waitedTime[user] += waitInterval[user];
waitInterval[user] = 500; // Nach dem ersten Durchlauf auf 500ms reduzieren
}, waitInterval[user]);
};
/**
* 🚀 **Sprachausgabe vor dem Stream**
* Diese Methode kündigt an, dass ein Stream für den Benutzer gestartet wird.
*/
function announceStreamStart(user, streamName, device) {
console.info(`NestWecker /announceStreamStart/:📢 Ankündigung für ${user} auf ${device}: ${streamName}`);
// **Setzt OutputTriggered bei Lautstärkeänderung**
setOutputTriggered(user);
actions.Voice.say(`Der Stream ${streamName} für ${user} wird gespielt.`, "pipertts:ramona-de_DE", device);
};
// // **🚀 Methode zum Abrufen einer zufälligen lokalen Audiodatei für einen Benutzer**
// function getRandomSoundFile(user) {
// let folder = soundFolders[user];
// if (!folder) {
// console.warn(`NestWecker /getRandomSoundFile/:⚠️ Kein Sound-Ordner für ${user} definiert.`);
// return null;
// }
// try {
// let files = os.listFiles(folder);
// let audioFiles = files.filter(file => file.endsWith(".mp3") || file.endsWith(".wav"));
// if (audioFiles.length > 0) {
// let selectedFile = "file://" + folder + audioFiles[Math.floor(Math.random() * audioFiles.length)];
// console.info(`NestWecker /getRandomSoundFile/:🎵 Zufällige Datei für ${user}: ${selectedFile}`);
// return selectedFile;
// } else {
// console.warn(`NestWecker /getRandomSoundFile/:⚠️ Keine Audiodateien im Ordner von ${user} gefunden.`);
// return null;
// }
// } catch (error) {
// console.error(`NestWecker /getRandomSoundFile/:🚨 Fehler beim Abrufen der Audiodateien für ${user}: ${error}`);
// return null;
// }
// };
// **🚀 Methode zum Abrufen des aktuellen Wochentags**
function getCurrentWeekday() {
const weekdays = ["SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY"];
return weekdays[new Date().getDay()]; // Gibt immer einen verlässlichen Wochentag zurück
}
rules.JSRule({
name: "Weckersteuerung",
description: "Prüft jede Minute, ob ein Wecker aktiviert ist und gestartet werden muss",
triggers: [triggers.GenericCronTrigger("0 * * * * ?")], // Läuft jede Minute
execute: function () {
// console.info(`NestWecker /Weckersteuerung/:🔔 Wecker-Check gestartet.`);
let now = new Date();
let hour = now.getHours();
let minute = now.getMinutes();
let weekday = getCurrentWeekday(); // "MONDAY", "TUESDAY", etc.
// Ausgabe des kompletten Status aller Benutzer
// console.info(`NestWecker /Weckersteuerung/: ⏰ Aktueller Benutzerstatus: ${JSON.stringify(userStatus)}`);
// console.info(`NestWecker /Weckersteuerung/:⏰ Aktuelle Uhrzeit ${hour}:${minute}.`);
["Juli", "Luisa", "Jonas"].forEach(user => {
let weckerItem = `${user}_Wecker_${weekday}`;
let weckerHourItem = `${user}_Wecker_h_${weekday}`;
let weckerMinuteItem = `${user}_Wecker_m_${weekday}`;
// **Sichere Werte für Wecker, Stunde & Minute setzen**
let weckerState = items[weckerItem]?.state?.toString() ?? "OFF";
let weckerHour = parseInt(items[weckerHourItem]?.state ?? NaN);
let weckerMinute = parseInt(items[weckerMinuteItem]?.state ?? NaN);
// **Fehlermeldung, falls Werte ungültig sind**
if (isNaN(weckerHour) || isNaN(weckerMinute)) {
console.warn(`NestWecker /Weckersteuerung/:⚠️ Wecker-Fehler für ${user}: Ungültige Werte für Stunde (${items[weckerHourItem]?.state ?? "NULL"}) oder Minute (${items[weckerMinuteItem]?.state ?? "NULL"})`);
return;
}
// console.info(`NestWecker /Weckersteuerung/:⏰ Prüfe Wecker für ${user} am ${weekday} mit ${weckerHour}:${weckerMinute}.`);
if (weckerState === "ON" && weckerHour === hour && weckerMinute === minute) {
startAlarm(user);
console.info(`NestWecker /Weckersteuerung/:⏰ Wecker für ${user} am ${weekday} wird gestartet.`);
}
});
}
});
// /**
// * 🚀 **Prüft, ob eine Audiodatei existiert**
// */
// function isValidAudioFile(filePath) {
// if (!filePath || typeof filePath !== "string") {
// return false;
// }
// let fileSystemPath = filePath.replace("file://", ""); // Entfernt `file://` für lokale Datei-Pfade
// let directory = fileSystemPath.substring(0, fileSystemPath.lastIndexOf("/")); // Extrahiere den Ordner
// let fileName = fileSystemPath.split("/").pop(); // Nur den Dateinamen extrahieren
// try {
// let files = os.listFiles(directory); // Liste alle Dateien im Verzeichnis
// return files.includes(fileName); // Prüfe, ob die Datei in der Liste enthalten ist
// } catch (error) {
// console.error(`Player Wecker: ❌ Fehler bei der Datei-Prüfung (${filePath}): ${error}`);
// return false;
// }
// };
/**
* 🚀 **Wecker-Start mit Lautstärkeregelung & Random**
*/
function startAlarm(user) {
console.info(`NestWecker /startAlarm/:🎵 Wecker: Starte für ${user}`);
//if (!userStatus[user]) userStatus[user] = { isAlarmTriggered: true, announcementDone: false };
// jwu
userStatus[user].isAlarmTriggered = true;
userStatus[user].announcementDone = false;
// let userFolder = soundFolders[user] || "/etc/openhab/sounds/";
// let isRandom = (items[`${user}_Wecker_Random`]?.state?.toString() === "ON");
// let songToPlay = isRandom ? getRandomSoundFile(user) : items[`${user}_Wecker_StartSong`]?.state?.toString() ?? null;
let songToPlay = items[`${user}_Wecker_StartSong`]?.state?.toString() ?? null;
let device = userDevices[user];
// **Prüfen, ob die Datei existiert, sonst Fallback auf "relax.mp3"**
// let songPath = `${userFolder}${songToPlay}`;
// if (!isValidAudioFile(songPath)) {
// console.warn(`NestWecker //:⚠️ Datei ${songPath} nicht gefunden. Verwende Ersatzdatei relax.mp3.`);
// songPath = `relax.mp3`;
// }
let startVolume = parseInt(items[`${user}_Wecker_StartVolume`]?.state ?? 5);
let maxVolume = parseInt(items[`${user}_Wecker_MaxVolume`]?.state ?? 40);
let upVolume = parseInt(items[`${user}_Wecker_UpVolume`]?.state ?? 5);
let upTimeVolume = (parseInt(items[`${user}_Wecker_UpTimeVolume`]?.state ?? 10) * 1000);
let volumeItem = `ChromeNest${user}Volume`;
console.info(`NestWecker /startAlarm/:🔊 Starte mit Lautstärke: ${startVolume}% (Max: ${maxVolume}%) für ${user} Steigerung alle ${upTimeVolume} Millisekunden um ${upVolume}%`);
// **Setzt OutputTriggered**
setOutputTriggered(user);
// **Setze initiale Lautstärke mit `sendCommand`**
items[volumeItem].sendCommand(startVolume);
//console.info(`Player Wecker: ⏰ Spiele Wecklied für ${user} auf Engerät §{device}: ${songPath}`);
console.info(`NestWecker /startAlarm/: ⏰ Spiele Wecklied für ${user} auf ${device}: ${songToPlay}`);
setOutputTriggered(user);
actions.Audio.playSound(device, songToPlay);
// **🚀 Schrittweise Lautstärkenerhöhung (Snooze-Erkennung)**
let volumeIncrease = setInterval(() => {
if (!userStatus[user]?.isAlarmTriggered) {
console.info(`NestWecker /startAlarm/:🛑 Lautstärkenanpassung gestoppt, da Wecker für ${user} deaktiviert wurde.`);
clearInterval(volumeIncrease);
return;
}
let currentVolume = parseInt(items[volumeItem]?.state) ?? startVolume;
if (currentVolume < maxVolume) {
let newVolume = Math.min(currentVolume + upVolume, maxVolume);
// **Setzt OutputTriggered bei Lautstärkeänderung**
setOutputTriggered(user);
// **Setzt neue Lautstärke mit `sendCommand`**
items[volumeItem].sendCommand(newVolume);
console.info(`NestWecker /startAlarm/:🔊 Erhöhe Lautstärke auf: ${newVolume}% für ${user}`);
} else {
console.info(`NestWecker /startAlarm/:🔊 Maximale Lautstärke erreicht: ${maxVolume}% für ${user}`);
clearInterval(volumeIncrease);
}
}, upTimeVolume);
};
// **🚀 Sprachausgabe**
function makeAnnouncement(user) {
if (!userStatus[user]?.isAlarmTriggered) {
console.warn(`NestWecker /makeAnnouncement/:❌ Wecker wurde deaktiviert. Sprachausgabe abgebrochen.`);
return;
}
let now = new Date();
let currentTime = `${now.getHours()} Uhr ${now.getMinutes().toString().padStart(2, '0')}`;
let device = userDevices[user];
let genderItem = `${user}_Wecker_Gender`;
let gender = (items[genderItem] === "ON") ? "Lieber" : "Liebe";
console.info(`NestWecker /makeAnnouncement/:📢 Wecker: Sprachausgabe für ${user} mit Gender: ${gender} auf ${device}`);
setOutputTriggered(user);
actions.Voice.say(`Guten Morgen ${gender} ${user}, es ist ${currentTime}. Bitte werde langsam wach! Guten morgen ${user}!`, "pipertts:ramona-de_DE", device);
};
// **🚀 Radiostream starten mit Fallback**
function startRadio(user) {
if (!userStatus[user]?.isAlarmTriggered) {
console.warn(`NestWecker /startRadio/:❌ Wecker wurde deaktiviert. Radio nicht gestartet.`);
return;
}
let device = userDevices[user];
let streamURL = items[`${user}_Wecker_Stream`]?.state?.toString() ?? "https://stream.rockantenne.de/rockantenne/stream/mp3";
console.info(`NestWecker /startRadio/:📻 Starte Radiostream für ${user} mit folgendem Stream: ${streamURL} auf ${device}`);
setOutputTriggered(user);
actions.Audio.playStream(device, null); // Stoppt aktuellen Stream
setTimeout(() => {
if (!userStatus[user]?.isAlarmTriggered) {
console.warn(`NestWecker /startRadio_Warten/:❌ Wecker wurde deaktiviert. Radio nicht gestartet.`);
return;
}
userStatus[user].isAlarmTriggered = false;
userStatus[user].announcementDone = false;
setOutputTriggered(user);
actions.Audio.playStream(device, streamURL);
}, 1000); // 1 Sekunde warten, bevor der neue Stream startet
};
// **🚀 Gemeinsame Regel für Lied-Ende & Sprachausgabe-Ende**
rules.JSRule({
name: "Wecker Lied- oder Sprachausgabe-Ende",
description: "Reagiert, wenn ein Song oder die Sprachausgabe endet",
triggers: [
triggers.ItemStateChangeTrigger("ChromeNestJuliCurrentTime"),
triggers.ItemStateChangeTrigger("ChromeNestJonasCurrentTime"),
triggers.ItemStateChangeTrigger("ChromeNestLuisaCurrentTime")
],
execute: handleWeckerEvent
});
function handleWeckerEvent(event) {
if (!isSystemStarted()) {
console.info("NestWecker /Nest Audio Control/⚠️: OpenHAB ist noch im Startvorgang (< 5 min). Ignoriere Regel.");
return;
}
let itemName = event.itemName;
let match = itemName.match(/ChromeNest(.*)CurrentTime/);
let user = match ? match[1] : null;
if (!user) {
console.warn(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:❌ Kein Benutzer aus ${itemName} extrahiert.`);
return;
}
//let outputTriggerItem = `${user}_OutputTriggered`; // OutputTrigger-Check
let outputDurationItem = `ChromeNest${user}Duration`;
let maxVolumeItem = `${user}_Wecker_MaxVolume`;
let volumeItem = `ChromeNest${user}Volume`;
let outputDuration = items[outputDurationItem]?.state?.toString();
if (!userStatus[user]?.isAlarmTriggered) {
// console.info(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:⚠️ Ignoriert für ${user}, da kein Alarm aktiv ist.`);
return;
}
// **Prüfen, ob `OutputTriggered` aktiv ist**
//if (items[outputTriggerItem] === "ON") {
// if (isTimeTriggerOlderThan2Seconds(user)) {
// console.log(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:⚠️ Die gespeicherte Zeit für ${user} liegt mehr als 2 Sekunden zurück.`);
// } else {
// console.log(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:⚠️ Die gespeicherte Zeit für ${user} ist noch aktuell.`);
// console.info(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:⚠️ Ignoriert für ${user}, da OutputTriggered aktiv ist.`);
// return;
// }
// if (userStatus[user]?.outputTriggered == true) {
// console.info(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:⚠️ Ignoriert für ${user}, da OutputTriggered aktiv ist.`);
// return;
// }
let newState = event.newState !== null ? event.newState.toString() : "UNDEF";
let oldState = event.oldState !== null ? event.oldState.toString() : "0 s";
let isStateTransitionToUndef = (oldState === "0 s" && newState === "UNDEF");
console.info(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:📻 Playtime für ${user} geändert: Alt=${oldState}, Neu=${newState}, Spielzeit: ${outputDuration}`);
if (isStateTransitionToUndef) {
console.info(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:🔄 Zustand von 0 auf UNDEF erkannt für ${user}.`);
if (!userStatus[user]?.announcementDone) {
// **Sprachausgabe starten**
userStatus[user].announcementDone = true;
console.info(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:🔊 Musik für ${user} beendet. Starte Sprachausgabe.`);
// **Setzt OutputTriggered bei Lautstärkeänderung**
setOutputTriggered(user);
items[volumeItem].sendCommand(maxVolumeItem);
makeAnnouncement(user);
} else {
// **Radio starten**
console.info(`NestWecker /Wecker Lied- oder Sprachausgabe-Ende/:📻 Sprachausgabe für ${user} beendet. Starte Radiostream.`);
startRadio(user);
}
}
};
rules.JSRule({
name: "Wecker Stop/Snooze",
description: "Reagiert auf Benutzerinteraktion mit dem Wecker",
triggers: [
triggers.ItemStateChangeTrigger("ChromeNestLuisaControl"),
triggers.ItemStateChangeTrigger("ChromeNestJonasControl"),
triggers.ItemStateChangeTrigger("ChromeNestJuliControl"),
triggers.ItemStateChangeTrigger("ChromeNestLuisaVolume"),
triggers.ItemStateChangeTrigger("ChromeNestJonasVolume"),
triggers.ItemStateChangeTrigger("ChromeNestJuliVolume")
],
execute: function (event) {
// **Ignorieren während OpenHAB-Start**
if (!isSystemStarted()) {
console.info("NestWecker /Wecker Stop/Snooze/:⚠️ OpenHAB ist noch im Startvorgang (< 5 min). Ignoriere Regel.");
return;
};
let user = event.itemName.replace("ChromeNest", "").replace(/(Control|Volume)/, "");
// **Prüfen, ob `userStatus` existiert**
if (!userStatus[user]?.isAlarmTriggered) {
console.info(`NestWecker /Wecker Stop/Snooze/:⚠️ Ignoriert für ${user}, da kein Alarm aktiv ist.`);
return;
};
// let outputTriggerItem = `${user}_OutputTriggered`;
// // **Prüfen, ob `OutputTriggered` aktiv ist**
if (isTimeTriggerOlderThan2Seconds(user)) {
console.log(`NestWecker /Wecker Stop/Snooze/:⚠️ Die gespeicherte Zeit für ${user} liegt mehr als 2 Sekunden zurück.`);
} else {
console.log(`NestWecker /Wecker Stop/Snooze/:⚠️ Die gespeicherte Zeit für ${user} ist noch aktuell.`);
console.info(`NestWecker /Wecker Stop/Snooze/:⚠️ Ignoriert für ${user}, da OutputTriggered aktiv ist.`);
return;
}
// if (!userStatus[user]?.outputTriggered) {
// console.info(`NestWecker /Wecker Stop/Snooze/:⚠️ Ignoriert für ${user}, da OutputTriggered aktiv ist.`);
// userStatus[user]?.outputTriggered = false;
// console.info(`NestWecker /Wecker Stop/Snooze/:✅ OutputTrigger durch Timer für ${user} auf FALSE geändert`);
// return;
// };
console.info(`NestWecker /Wecker Stop/Snooze/:🔔 Regel getriggert durch ${event.itemName}`);
if (event.itemName.endsWith("Control")) {
if (event.newState === "PAUSE" || event.newState === "STOP") {
console.info(`NestWecker /Wecker Stop/Snooze/:🛑 Wecker für ${user} gestoppt.`);
// userStatus[user].isAlarmTriggered = false;
// userStatus[user].announcementDone = false;
// **Setzt OutputTriggered bei Wecker-Stopp**
setOutputTriggered(user);
}
} else if (event.itemName.endsWith("Volume")) {
console.info(`NestWecker /Wecker Stop/Snooze/:⏳ Snooze für ${user} erkannt.`);
// userStatus[user].isAlarmTriggered = false;
// userStatus[user].announcementDone = false;
// **Setzt OutputTriggered bei Snooze**
setOutputTriggered(user);
}
}
});
// **🚀 Mapping der Schulstundenzeiten**
let schoolStartTimes = {
1: { time: "07:33", label: "erste Stunde" },
2: { time: "08:23", label: "zweite Stunde" },
3: { time: "09:03", label: "dritte Stunde" },
4: { time: "10:07", label: "vierte Stunde" },
5: { time: "10:57", label: "fünfte Stunde" },
6: { time: "11:43", label: "sechste Stunde" }
};
// **🚀 Prüfen, ob Schulzeiten definiert sind**
if (!schoolStartTimes || Object.keys(schoolStartTimes).length === 0) {
console.error("NestWecker /Schulansagen/: ❌ Fehler: Keine Schulzeiten definiert!");
} else {
console.info(`NestWecker /Schulansagen/: ✅ Schulzeiten erfolgreich geladen.`);
};
// **🚀 Generiere Cron-Trigger für Wochentage und Schulstunden
const cronExpressions = Object.values(schoolStartTimes)
.map(({ time }) => {
let [hour, minute] = time.split(":");
return `0 ${minute} ${hour} ? * MON-FRI *`; // Nur Montag bis Freitag
});
// **🚀 Regel mit dynamischen CRON-Triggern für jede Schulstunde**
rules.JSRule({
name: "Schulansagen",
description: "Prüft zu den Schulzeiten, ob eine Schulansage nötig ist",
triggers: cronExpressions.map(cron => triggers.GenericCronTrigger(cron)), // Dynamische CRONs
execute: function () {
console.info("NestWecker /Schulansagen/: 🔔 Schulansagen-Check gestartet.");
let now = new Date();
let weekday = getCurrentWeekday(); // "MONDAY", "TUESDAY", etc.
let currentTime = now.getHours().toString().padStart(2, '0') + ":" + now.getMinutes().toString().padStart(2, '0');
["Jonas", "Luisa", "Juli"].forEach(user => {
let weckerTagItem = `${user}_Wecker_${weekday}`;
let weckerSchoolItem = `${user}_Wecker_s_${weekday}`;
// **🚀 Sicherstellen, dass das Item existiert**
if (!items.existsItem(weckerSchoolItem)) {
console.warn(`NestWecker /Schulansagen/: ⚠️ Item ${weckerSchoolItem} existiert nicht! Überspringe ${user}.`);
return;
}
let schoolArryElement = items[weckerSchoolItem]?.numericState ?? 0;
// Prüfen ob eine Schulstunde ausgewählt wurde
if (schoolArryElement == 0) return;
let weckerState = items[weckerTagItem]?.state?.toString() ?? "OFF";
if (weckerState === "ON" && schoolStartTimes[schoolArryElement]?.time === currentTime) {
makeSchoolAnnouncement(user, schoolArryElement);
}
});
}
});
// **🚀 Funktion für die Schulansage mit Geräteziel**
function makeSchoolAnnouncement(user, schoolHour) {
let device = userDevices[user];
if (!device) {
console.warn(`NestWecker /Schulansagen/: ❌ Kein Gerät für ${user} gefunden. Schulansage abgebrochen.`);
return;
}
let now = new Date();
let currentTime = now.getHours() + ":" + now.getMinutes().toString().padStart(2, '0');
if (schoolStartTimes[schoolHour]) {
console.info(`NestWecker /Schulansagen/: 📢 Schulansage für ${user} auf ${device} - Start: ${schoolStartTimes[schoolHour].label}`);
// **Setzt OutputTriggered bei Lautstärkeänderung**
setOutputTriggered(user);
actions.Audio.say(`Es ist ${currentTime}. Bitte stelle das Spielen ein und richte dich, damit du pünktlich zur ${schoolStartTimes[schoolHour].label} in die Schule kommst.`, "pipertts:ramona-de_DE", device, 75);
}
};
Some of the rule is commented out, due to future improvements like random music file and the right now received Error.
Comments are mostly in German.
Best regards