/* ANSAGESKRIPT MIT DATUM, UHRZEIT, TEMPERATUR AKTUELL UND MAX SOWIE REGEN UND TERMINEN. TRIGGERBAR. OB HEUTE, HEUTE UND MORGEN ODER BIS EINSCHLIESSLICH UEBERMORGEN
ORIGNARES SKRIPT FÜR TEMPERATUR UND DATUM PLUS UHRZEIT VON SKORPIL UND PIX, IOBROKER FORUM
ERGAENZT UM AUSLESEN DER TERMINE AUS HTML ANSTATT AUS DATA OBJECT ZUR VERMEIDUNG DER OBJECT PROBLEMATIK (TERMINE WERDEN NICHT IMMER EINGELESEN)
HTML TO PLAIN TEXT KONVERTIERUNG AUS STACKOVERFLOW FORUM; ALL CREDIT TO USER ELENDURWEN
SUCHFUNKTION STACKOVERFLOW FORUM USER iammatthew2
ZUSAMMENFUERHUNRG UND TRIGGER FUNKTION TEMPESTAS
VERSION 0.4 STATUS 23. JANUAR 2018, Radiosenderauswahl via States / VIS sowie Terminauswahl via States/Vis eingebaut
VERSION V.03 STATUS 5. OKTOBER 2017, auf Wunsch der GöGa Zeitplanung für Ferien, Feiertage und Wochenende aufgenommen
VERSION V.02 STATUS 30. APRIL 2017
DER ICAL MUSS "ERSETZE DATUM MIT WORTEN" AKTIVIERT HABEN
*/
// EINSTELLUNGEN
var debug = true;
const force = false;
// ##########################################################
// ############### Zeitplanungs-States anlegen ##############
// ##########################################################
// Generelle Einstellungen
createState('Ansage.Radiosender','1000 Oldies', {
name: 'Einstellung Radiosender',
type: 'string',
});
createState('Ansage.Terminvorschau',1, force, {
name: 'Termine heute, morgen oder bis einschließlich übermorgen',
type: 'Number',
min: '1',
max: '3',
states: '1:heute;2:morgen;3:übermorgen',
});
// IDs
var idRadio = 'javascript.0.Ansage.Radiosender';
var idTerminvorschau = "javascript.0.Ansage.Terminvorschau"/*Termine heute, morgen oder bis einschließlich übermorgen*/;
var radio;
// Wochenende bzw Ferien und Feiertage
createState('Ansage.Timing.Wochenende.Aktiv', false, {
read: true,
write: true,
name: "Ansage am Wochenende aktiv oder aus?",
type: "boolean",
def: false
});
createState('Ansage.Timing.Wochenende.Start.Stunde',9, force, {
name: 'Startstunde Wochenende',
type: 'number',
min: '0',
max: '23',
});
createState('Ansage.Timing.Wochenende.Start.Minute',30, force, {
name: 'Startminute Wochenende',
type: 'number',
min: '0',
max: '59',
});
createState('Ansage.Timing.Wochenende.Start.MinuteString',30, force, { // String für Anzeige in VIS; wird via function an numerischen Wert angeglichen
name: 'Startminute Wochenende',
type: 'string',
min: '0',
max: '59',
});
createState('Ansage.Timing.Wochenende.Ende.Stunde',11, force, {
name: 'Endstunde Wochenende',
type: 'number',
min: '0',
max: '23',
});
createState('Ansage.Timing.Wochenende.Ende.Minute',30, force, {
name: 'Endminute Wochenende',
type: 'number',
min: '00',
max: '59',
});
createState('Ansage.Timing.Wochenende.Ende.MinuteString',30, force, { // String für Anzeige in VIS; wird via function an numerischen Wert angeglichen
name: 'Endminute Wochenende',
type: 'string',
min: '00',
max: '59',
});
// Arbeitstage
createState('Ansage.Timing.Arbeitstag.Aktiv', false, {
read: true,
write: true,
name: "Ansage am Arbeitstag aktiv oder aus?",
type: "boolean",
def: false
});
createState('Ansage.Timing.Arbeitstag.Start.Stunde',6, force, {
name: 'Startstunde Arbeitstag',
type: 'number',
min: '0',
max: '23',
});
createState('Ansage.Timing.Arbeitstag.Start.Minute',30, force, {
name: 'Startminute Arbeitstag',
type: 'number',
min: '0',
max: '59',
});
createState('Ansage.Timing.Arbeitstag.Start.MinuteString','30', force, { // String für Anzeige in VIS; wird via function an numerischen Wert angeglichen
name: 'Startminute Arbeitstag',
type: 'string',
});
createState('Ansage.Timing.Arbeitstag.Ende.Stunde',9, force, {
name: 'Endstunde Arbeitstag',
type: 'number',
min: '0',
max: '23',
});
createState('Ansage.Timing.Arbeitstag.Ende.Minute',0, force, {
name: 'Endminute Arbeitstag',
type: 'number',
min: '0',
max: '59',
});
createState('Ansage.Timing.Arbeitstag.Ende.MinuteString','00', force, { // String für Anzeige in VIS; wird via function an numerischen Wert angeglichen
name: 'Endminute Arbeitstag',
type: 'string',
});
// ###########################################################################
// ### Functions zur Anpassung der Minuten Strings an die numerischen Werte ##
// ###########################################################################
var idWochenendeAktiv = 'javascript.0.Ansage.Timing.Wochenende.Aktiv';
var idWochenendeMinStart = 'javascript.0.Ansage.Timing.Wochenende.Start.Minute' ;
var idWochenendeStdStart = 'javascript.0.Ansage.Timing.Wochenende.Start.Stunde' ;
var idWochenendeMinStartString = 'javascript.0.Ansage.Timing.Wochenende.Start.MinuteString';
var idWochenendeMinEnd = 'javascript.0.Ansage.Timing.Wochenende.Ende.Minute';
var idWochenendeStdEnd = 'javascript.0.Ansage.Timing.Wochenende.Ende.Stunde';
var idWochenendeMinEndString = 'javascript.0.Ansage.Timing.Wochenende.Ende.MinuteString';
var idArbeitAktiv = 'javascript.0.Ansage.Timing.Arbeitstag.Aktiv';
var idArbeitMinStart = 'javascript.0.Ansage.Timing.Arbeitstag.Start.Minute' ;
var idArbeitStdStart = 'javascript.0.Ansage.Timing.Arbeitstag.Start.Stunde' ;
var idArbeitMinStartString = 'javascript.0.Ansage.Timing.Arbeitstag.Start.MinuteString';
var idArbeitMinEnd = 'javascript.0.Ansage.Timing.Arbeitstag.Ende.Minute';
var idArbeitStdEnd = 'javascript.0.Ansage.Timing.Arbeitstag.Ende.Stunde';
var idArbeitMinEndString = 'javascript.0.Ansage.Timing.Arbeitstag.Ende.MinuteString';
// Bei Änderung Minuten Startzeit am Wochenende
on(idWochenendeMinStart, function() {
var minuten = getState(idWochenendeMinStart).val;
var minutenString = "";
if(minuten < 10) {
minutenString = '0' +minuten;
}
else minutenString = minuten.toString();
setState(idWochenendeMinStartString, minutenString);
});
// Bei Änderung Minuten Endzeit am Wochenende
on(idWochenendeMinEnd, function() {
var minuten = getState(idWochenendeMinEnd).val;
var minutenString = "";
if(minuten < 10) {
minutenString = '0' +minuten;
}
else minutenString = minuten.toString();
setState(idWochenendeMinEndString, minutenString);
});
// Bei Änderung Minuten Startzeit am Arbeitstag
on(idArbeitMinStart, function() {
var minuten = getState(idArbeitMinStart).val;
var minutenString = "";
if(minuten < 10) {
minutenString = '0' +minuten;
}
else minutenString = minuten.toString();
setState(idArbeitMinStartString, minutenString);
});
// Bei Änderung Minuten Endzeit am Arbeitstag
on(idArbeitMinEnd, function() {
var minuten = getState(idArbeitMinEnd).val;
var minutenString = "";
if(minuten < 10) {
minutenString = '0' +minuten;
}
else minutenString = minuten.toString();
setState(idArbeitMinEndString, minutenString);
});
// Variablen
var WochenendeStart,
WochenendeEnd,
ArbeitstagStart,
ArbeitstagEnde,
ArbeitstagAktiv,
WochenendeAktiv;
// ##################################
// ####### TRIGGER FÜR ANSAGE #######
// ##################################
createState('Ansage.Trigger', false, {
read: true,
write: true,
name: "Trigger",
type: "boolean",
def: false
});
var stopit = null;
var move = false;
var idTrigger = 'javascript.0.Ansage.Trigger'; // damit nur einmal pro Tag ausgelöst wird
var zeitplan;
var idMove = "hm-rpc.1.000915699602FD.1.MOTION"; // ID des Bewegungsmelders
// **************************************************************
// ************* Wechsel des Radiosenders ***********************
// **************************************************************
on({id: idRadio, change: "any"}, function(){
checkRadio();
});
function checkRadio(){
radio = getState(idRadio).val;
if(debug) log("Radiosender "+radio +" wurde eingestellt");
}
//**********************************************************************************************************************************
// Festlegen, ob nur heute [1], heute und morgen [2] oder einschliesslich uebermorgen angesagt wird [3] --> wird via VIS festgelegt
//**********************************************************************************************************************************
var terminAnsage;
on({id: idTerminvorschau, change: "ne"}, function(){
checkTermine();
});
function checkTermine(){
terminAnsage = parseInt(getState(idTerminvorschau).val);
if(debug) log("Terminvorschau wurde auf "+terminAnsage+" Tage eingestellt");
}
// ######################################################
// ############# Function zur Ansage ####################
// ######################################################
on({id: idMove, val: true}, function() {
zeitplan = getState("javascript.0.Zeitplanung.Tage.Modus").val; // abholen Wert Arbeitstag (0) oder Ferien, Wochenende, Feiertag (1)
ArbeitstagStart = getState(idArbeitStdStart).val.toString() + ':'+getState(idArbeitMinStartString).val;
ArbeitstagEnde = getState(idArbeitStdEnd).val.toString() + ':'+getState(idArbeitMinEndString).val;
ArbeitstagAktiv = getState(idArbeitAktiv).val;
WochenendeStart = getState(idWochenendeStdStart).val.toString() + ':'+getState(idWochenendeMinStartString).val;
WochenendeEnd = getState(idWochenendeStdEnd).val.toString() + ':'+getState(idWochenendeMinEndString).val;
WochenendeAktiv = getState(idWochenendeAktiv).val;
switch(zeitplan) {
case 0: // case 0: Arbeitstag
if(!move && ArbeitstagAktiv && compareTime(ArbeitstagStart, ArbeitstagEnde, 'between')) {
move = true;
setState(idTrigger, move);
if(debug) log("Bewegung erkannt, Ansage wird an einem Arbeitstag zwischen " +ArbeitstagStart +" und " +ArbeitstagEnde +" ausgeführt");
}
break;
case 1: // case 1: Freier Tag
if(!move && WochenendeAktiv && compareTime(WochenendeStart, WochenendeEnd, 'between')) {
move = true;
setState(idTrigger, move);
if(debug) log("Bewegung erkannt, Ansage wird an einem freien Tag zwischen " +WochenendeStart +" und " +WochenendeEnd +" ausgeführt");
}
break;
}
});
// Trigger nachts zurücksetzen
schedule('15 0 * * *', function() { // täglich 0:15 Uhr
move = false;
setState(idTrigger, move);
if(debug) log("SayIt Trigger um 6.15h auf false gesetzt");
});
// ************************************************************************
// START DER EIGENTLICHEN ANSAGE
// ************************************************************************
//Trigger
on(idTrigger, function (data) {
// Definition
var tts = "sayit.0.tts.text";
// Nochmal Terminvorschau abholen
//terminAnsage = 1;
//Lautstärke einstellen
var vol ="sayit.0.tts.volume";
setState (vol,15);
//Wochentag ermitteln
var d = new Date ();
var weekday = new Array("Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag");
var w = weekday[d.getDay()];
//Tagesdatum ermitteln
var t = d.getDate();
//Monat ermitteln
var month = new Array("Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember");
var m = month[d.getMonth()];
//Jahr ermitteln
var j = d.getFullYear();
//Stunde ermitteln
h = d.getHours();
//Minute ermitteln
mi = d.getMinutes();
// ********************************************************************
// Die Außentemperatur ist xx.
// Einfache Temperaturansage mit SayIt.
// Variante 1 mittels splitten der Temperatur, damit die Ansage nicht
// "Es sind 18 Punkt 2 Grad " sagt.
// *********************************************************************
var Temperatursensor = getState("daswetter.0.NextHours.Location_1.Day_1.current.temp_value"/*temperature*/); /*Temperatursensor:1.TEMPERATURE*/
var temperatur = Temperatursensor.val.toString();
var temp_array = [];
temp_array = temperatur.split(".");
// Fange leere Nachkommastellen ab. Das passiert, wenn die Temperatur z. B. 18.0 ist.
// Es wird dann nur "18" gelesen.
if (!temp_array[1]) {
temp_array[1] = "0";
if(debug) log("Die Nach-Kommastelle in temp_array[1] war nicht vorhanden und wird nun fest auf 0 gesetzt.");
}
// ******************************
// Wird Regen erwartet?
// ******************************
var idRain = "javascript.0.Wetter.Regentimer"/*Regentimer*/; // wird in anderem Skript angelegt
var idRainValue;
var idRainValueText;
idRainValue = getState(idRain).val;
idRainValueText = idRainValue.replace(':00h', ' Uhr'); // bereinigen des Anzeigetextes in eine sprechbare Form
//log(idRainValue);
//log(idRainValueText);
// *********************************
// Tageshöchstemperatur
// *********************************
var maxTemp;
maxTemp = getState("weatherunderground.0.forecast_day.0d.temp.high"/*high temperature*/).val;
// **************************************************************************************
// Termine auswerten aus html. Bereinigung der HTML Tags und Konvertierung in Plain Text
// **************************************************************************************
var inhalt = getState("ical.0.data.html"/*HTML iCal table*/);
var inhaltString = inhalt.val.toString();
var inhaltStringReplace = inhaltString;
var inhaltStringText;
var i_search;
//**************************************************************************************
// Suchfunktion für Termin-Cutoff
// Sucht nach dem n-ten definierten Muster, hier "§$%" und gibt die Fundstelle zurück.
// Hinter dieser Fundstelle wird dann der Text gekürzt
// Sinnloses Muster genommen, da dies wohl nirgends normalerweise vorkommt
//*****************************************************************************************
// Suchfunktion für Termin-Cutoff
function nthIndex(str, pat, n){
var L= str.length, i= -1;
while(n-- && i++<L){
i= str.indexOf(pat, i);
if (i < 0) break;
}
i_search =i;
}
// remove all inside SCRIPT and STYLE tags
inhaltStringReplace=inhaltStringReplace.replace(/<script.*>[\w\W]{1,}(.*?)[\w\W]{1,}<\/script>/gi, "");
inhaltStringReplace=inhaltStringReplace.replace(/<style.*>[\w\W]{1,}(.*?)[\w\W]{1,}<\/style>/gi, "");
// remove BR tags. Werden durch sinnlose Zeichenkette ersetzt, nach der später gesucht wird
inhaltStringReplace=inhaltStringReplace.replace(/<br>/gi, ". §$%");
inhaltStringReplace=inhaltStringReplace.replace(/<br\s\/>/gi, ". §$%");
inhaltStringReplace=inhaltStringReplace.replace(/<br\/>/gi, ". §$%");
// remove all else
inhaltStringReplace=inhaltStringReplace.replace(/<(?:.|\s)*?>/g, "");
// get rid of html-encoded characters:
inhaltStringReplace=inhaltStringReplace.replace(/ /gi," ");
inhaltStringReplace=inhaltStringReplace.replace(/&/gi,"&");
inhaltStringReplace=inhaltStringReplace.replace(/"/gi,'"');
inhaltStringReplace=inhaltStringReplace.replace(/</gi,'<');
inhaltStringReplace=inhaltStringReplace.replace(/>/gi,'>');
// Punkt ans Ende setzen
inhaltStringReplace=inhaltStringReplace+". §$%";
// log("Text ohne HTML mit 'sinnlos String': "+inhaltStringReplace);
// Termine heute zaehlen
var terminHeuteCount = inhaltStringReplace.split("Heute").length -1;
//log("Anzahl Termine heute: "+terminHeuteCount);
// Termine morgen zählen
var terminMorgenCount = inhaltStringReplace.split("Morgen").length -1;
// log("Anzahl Termine morgen: "+terminMorgenCount);
// Termine übermorgen zählen
var terminUebermorgenCount = inhaltStringReplace.split("Übermorgen").length -1;
// log("Anzahl Termine übermorgen: "+terminUebermorgenCount);
// Termine addieren
var termineCount = (terminHeuteCount + terminMorgenCount + terminUebermorgenCount);
var termineHeuteMorgenCount = (terminHeuteCount +terminMorgenCount);
// log("Termine heute und morgen gesamt: " +termineHeuteMorgenCount);
// log("Anzahl Termine gesamt: " +termineCount);
//*******************************************
// Text kürzen je nach gewählter Selektion
// ******************************************
if (termineCount === 0) {
inhaltStringText ="Es liegen keine Termine an";
}
else {
switch(terminAnsage) {
case 1:
if (terminHeuteCount === 0) {
inhaltStringText ="Es liegen keine Termine an";
}
else{
nthIndex(inhaltStringReplace,"§$%", terminHeuteCount); // nutzen der Suchfunktion zum Suchen der n-ten sinnlosen Zeichenkette
inhaltStringText = inhaltStringReplace.slice(0,i_search);
for (k =0; k < terminHeuteCount; k++) {
inhaltStringText = inhaltStringText.replace("§$%", ""); // rausnehmen der sinnlosen Zeichen, damit diese nicht mitgesprochen werden
}
inhaltStringText = "Folgende Termine stehen an: "+inhaltStringText; // neu eingefügt
}
break;
case 2:
if (termineHeuteMorgenCount === 0) {
inhaltStringText ="Es liegen keine Termine an";
}
else{
nthIndex(inhaltStringReplace,"§$%", termineHeuteMorgenCount); // nutzen der Suchfunktion zum Suchen der n-ten sinnlosen Zeichenkette
inhaltStringText = inhaltStringReplace.slice(0,i_search);
for (k =0; k < termineHeuteMorgenCount; k++) {
inhaltStringText = inhaltStringText.replace("§$%", ""); // rausnehmen der sinnlosen Zeichen, damit diese nicht mitgesprochen werden
}
inhaltStringText = "Folgende Termine stehen an: "+inhaltStringText; // neu eingefügt
}
break;
case 3:
if (termineCount === 0) {
inhaltStringText ="Es liegen keine Termine an";
}
else{
nthIndex(inhaltStringReplace,"§$%", termineCount); // nutzen der Suchfunktion zum Suchen der n-ten sinnlosen Zeichenkette
inhaltStringText = inhaltStringReplace.slice(0,i_search);
for (k =0; k < termineCount; k++) {
inhaltStringText = inhaltStringText.replace("§$%", ""); // rausnehmen der sinnlosen Zeichen, damit diese nicht mitgesprochen werden
}
inhaltStringText = "Folgende Termine stehen an: "+inhaltStringText; // neu eingefügt
}
}
}
// log("Letzte Fundstelle an Position "+i_search);
log("Die bereinigte Ansage ist: " +inhaltStringText);
// ANSAGE ausfuehren, danach sonos box starten für 30 Minuten
if (data.newState.val === true) {
setState("sonos.0.root.192_168_2_7.favorites_set"/*favorites_set*/, radio); // gewählte Radiosender eingestellt
setState("sonos.0.root.192_168_2_7.volume"/*volume*/, 12); // Anpassen der Lautstärke
setState (tts, "Guten Morgen, heute ist " + w + " der " + t + "te " + m + j + ". Es ist" + h + " Uhr und " + mi + " Minuten."
+ " Die Aussentemperatur beträgt aktuell " + temp_array[0] + "," + temp_array[1] + " Grad und das Tagesmaximum soll "
+maxTemp +" Grad erreichen. " +idRainValueText+ " " +inhaltStringText );
}
});
// NEU EINGEFÜGT 3.10.2018
on({id: idMove, change: "ne" }, function(dp) {
if (!dp.state.val && getState('sonos.0.root.192_168_2_7.state'/*state*/).val != "stop"){
stopit = setTimeout(stopmusic, 600000); // nach 10 Minuten wird stopmusic gerufen
if(debug) log ("Keine Bewegung erkannt, Stop Funktion mit 10 Minuten initiiert");
}
else if(stopit) {
clearTimeout(stopit);
stopit = null;
if(debug) log("Es wurde binnen 10 Minuten bei laufender Musik Bewegung erkannt, Stop Funktion ausgeschaltet");
}
});
function stopmusic(){
setState("sonos.0.root.192_168_2_7.state", "stop");
if(debug) log("Badezimmer Sonos automatisch ausgeschaltet mangels Anwesenheit");
};
// Bei Start des Skripts Einstellungen zum Radiosender und Terminvorschau einlesen
checkRadio();
checkTermine();
//Debuggen
//console.log(JSON.stringify(getState(idTerminvorschau)));
//console.log(JSON.stringify(getState(idRadio)));