//Version 13.6
//nachbearbeitet von ticaki
/*
*/
/* ************************************************************************* */
/*             Script zum Übertragen der DWD-Wetterwarnungen über            */
/*             Telegram, Pushover, Home24-Mediaplayer oder SayIt             */
/*     mit freundlicher Unterstützung von Paul53 (Tausend Dank nochmals)     */
/*                    Stand: 130022017    PrinzEisenherz1                    */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* ************************************************************************* */
/* ************************************************************************* */
/* NICHT EDITIEREN */
var konstanten = [
   {"name":'telegram',"value":1},
   {"name":'pushover',"value":2},
   {"name":'email',"value":4},
   {"name":'sayit',"value":8},
   {"name":'home24',"value":16},
   {"name":'alexa',"value":32},
   {"name":'state',"value":64},
   {"name":'iogo',"value":128}
];
const TELEGRAM = konstanten[0].value;
const PUSHOVER = konstanten[1].value;
const EMAIL = konstanten[2].value;
const SAYIT = konstanten[3].value;
const HOMETWO = konstanten[4].value;
const ALEXA = konstanten[5].value;
const STATE = konstanten[6].value;
const IOGO = konstanten[7].value;
var pushdienst=0;
/* ************************************************************************* */
/*                       Konfiguration ab hier                               */
/* ************************************************************************* */
/* Konfiguration der zu nutzenden Ausgabe um //pushdienst+= PUSHOVER; zu aktivieren, bitte die // enfernen, also pushdienst+= PUSHOVER; */
//pushdienst+= TELEGRAM;          // Auskommentieren zum aktivieren
//pushdienst+= PUSHOVER;          // Auskommentieren zum aktivieren
//pushdienst+= EMAIL;             // Auskommentieren zum aktivieren. Einstellungen nicht vergessen
//pushdienst+= SAYIT;             // Auskommentieren zum aktivieren. Einstellungen nicht vergessen
//pushdienst+= HOMETWO;           // Auskommentieren zum aktivieren. Einstellungen nicht vergessen
//pushdienst+= ALEXA;             // Auskommentieren zum aktivieren. Einstellungen nicht vergessen
//pushdienst+= STATE;             // Auskommentieren zum aktivieren. State befindet sich unter onClickMessageState.message
//pushdienst+= IOGO;              // Auskommentieren zum aktivieren. Einstellungen nicht vergessen
/* Einstellungen zur Emailbenachrichtigung*/
var senderEmailID = ""; // mit Sender Emailadresse füllen. email Adapter muß installiert sein
var empfaengerEmailID = "";// mit Empfänger Emailadresse füllen.
/* Konfiguration Sprachausgabe über Home24-Mediaplayer */
var idMediaplayer = "192.168.178.68:50000"; // Eingabe IP-Adresse incl. Port für Home24-Mediaplayer
/* Konfiguration Sprachausgabe über SayIt */
var idSayIt = "sayit.0.tts.text";
var lautstaerke = 60;
//Konfiguration Sprachausgabe über Alexa
var idAlexaSerial =''; // die reine Seriennummer des Echos z.B.: var idAlexaSerial ='G090RV32984110Y';
var alexaVolumen = 40; // Lautstärke 0-100 (denke ich :) )
//Konfiguration von ioGo
var ioGoUser = ''; // Einzelnutzer 'Hans'; Multinutzer ['Hans','Peter']; Nutzer vom Adapter übernehmen '';
//StatePfad um Mitteilungen auszulösen darunter werden jeweils Punkte für jede Ausgabemöglichkeit erstellt.
var onClickMessageState = 'javascript.0.DWD_Script.'; // abschließender Punkt . nicht vergessen
// Filtereinstellungen
const minlevel = 0 // Warnungen gleich oder unterhalb dieses Levels nicht senden;
const maxhoehe = 1410 // Warnung für eine Höhe oberhalb dieses Wertes nicht senden
//Formatierungsstring für Datum/Zeit Alternative "TT.MM.YYYY SS:mm" KEINE Anpassung nötig
const formatierungString = "TT.MM.YYYY SS:mm";
// Sprachausgabe Zeiten
// Für durchgehende Sprachausgabe die Einstellung der Zeiten auf '' setzen. z.B. var startTimeSpeak = '';
var startTimeSpeak = '6:45';// Zeiten mo-fr ab der Sprachausgaben ok sind. Nicht unter 6 Uhr gehen oder den Schedule ändern
var startTimeSpeakWeekend = '9:00';// sa + so Bemerkung siehe oben
var endTimeSpeak = '22:30'; // ab diesem Zeitpunkt gibt es keine Sprachausgabe
// Automatikmodus auschalten
var autoMode = true;
//Auslösen der Pushnachricht über States ignoriert Sprachausgabezeiten
var forcedSpeak = false;
// ignoriere Warnmeldungen von geringerer Schwere, wenn ansonsten vergleichbare Warnmeldungen vorliegen.
var SkipDuplicates = true;
/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */
/*                       Konfiguration Ende                                  */
/* ************************************************************************* */
/*        Keine Anpassungen ab hier, außer du weißt was du tuest             */
/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */
var idAlexa = 'alexa2.0.Echo-Devices.'+idAlexaSerial+'.Commands.announcement';
var idAlexaVolumen = 'alexa2.0.Echo-Devices.'+idAlexaSerial+'.Commands.speak-volume';
var forceSpeak = false;
/* Arrays festlegen */
const InitArrayLenght = 9;
var newDescriptions = [InitArrayLenght];
var oldDescriptions = [InitArrayLenght];
var newHeadlines = [InitArrayLenght];
var oldHeadlines = [InitArrayLenght];
var newBegins = [InitArrayLenght];
var oldBegins = [InitArrayLenght];
var newEnds = [InitArrayLenght];
var oldEnds = [InitArrayLenght];
var newInstruction = [InitArrayLenght];
var oldData = [InitArrayLenght];
var newData = [InitArrayLenght];
var timer = null;
/* erstmaliges Befüllen der arrays */
for (let a=0;a<InitArrayLenght;a++) {
   var id = "dwd.0.warning";
   if (a!=0) id+=a.toString();
   id+='.object';
   var warn = {};
   if (existsState(id)) warn = JSON.parse(getState(id).val);
   warn = convertJsonDWD(warn);
   newDescriptions[a] = warn.description;
   oldDescriptions[a] = warn.description;
   newHeadlines[a] = warn.headline;
   oldHeadlines[a] = warn.headline;
   newBegins[a] = warn.start;
   oldBegins[a] = warn.start;
   newEnds[a] = warn.end;
   oldEnds[a] = warn.end;
   newData[a] = {"level":warn.level,"type":warn.type};
   oldData[a] = {"level":warn.level,"type":warn.type};
}
// State der Pushnachrichten über pushover/telegram spiegelt
const mirrorMessageState = onClickMessageState+'message';
if (!existsState(mirrorMessageState)) {
   createState(mirrorMessageState,'', {
       read: true,
       write: false,
       desc: "Beschreibung",
       type: "string",
   });
}
// State über den man gesonderte Aktionen auslösen kann, gibt die höchste Warnstufe aus.
const AlertState = onClickMessageState+'alertlevel';
if (!existsState(AlertState)) {
   createState(AlertState,0, {
       read: true,
       write: false,
       desc: "Beschreibung",
       type: "number"
   });
} else {
   let AlertLevel = -1;
   newData.forEach((item, i) => {if (AlertLevel<item.level) AlertLevel = item.level;});
   oldData.forEach((item, i) => {if (AlertLevel<item.level) AlertLevel = item.level;});
   setState(AlertState,AlertLevel);
}
// Nachrichtenversand per Click States erzeugen und subscript
for (var a=0;a<konstanten.length;a++){
   if (!existsState(onClickMessageState+'Commands.'+konstanten[a].name)) {
       createState(onClickMessageState+'Commands.'+konstanten[a].name,false, {
           read: true,
           write: true,
           desc: "Beschreibung",
           type: "boolean",
           def: false
       });
   }
   if (existsState(onClickMessageState+'Commands.'+konstanten[a].name)){
       subscribe({id: onClickMessageState+'Commands.'+konstanten[a].name},function(obj){
           if (!obj.state.val) return;
           setState(obj.id,false,true);
           let b = obj.id.split('.');
           let d = konstanten.findIndex(function(c){return (c.name===b[b.length-1]);})
           if (d == -1) {log('Fehler. State nicht in Konstanten enthalten','error'); return;}
           if ((pushdienst & konstanten[d].value) == 0) return;
           let oldPushdienst = pushdienst;
           pushdienst = konstanten[d].value*1;
           let tempHeadlines = oldHeadlines.slice();
           let tempDescriptions = oldDescriptions.slice();
           let tempBegins = oldBegins.slice();
           let tempEnds = oldEnds.slice();
           for (let i=0;i<oldDescriptions.length;i++) {
               oldHeadlines[i] = '';
               oldDescriptions[i] = '';
               oldBegins[i] = '';
               oldEnds[i] = '';
           }
           forceSpeak = forcedSpeak;
           check();
           forceSpeak = false;
           pushdienst = oldPushdienst;
           // sollte überflüssig sein
           //oldHeadlines = tempHeadlines.slice();
           //oldDescriptions = tempDescriptions.slice();
           //oldBegins = tempBegins.slice();
           //oldEnds = tempEnds.slice();
       })
   }
}
// Zeitsteuerung für SayIt & Alexa
var START = new Date();
var ENDE = new Date();
setWeekend();
function setWeekend()
{
   if (forceSpeak) return;
   let date = new Date();
   let n = date.getDay();
   let weekend = 0;
   weekend = (n === 0 || n == 6) ? 1 : 0;
   let d = new Date();
   if(weekend == 1){                     // wenn Wochenende, dann setze Start auf 9h, sonst 6:45h
       var e = startTimeSpeakWeekend.split(':');
       d.setHours(e[0]);
       d.setMinutes(e[1]);
       d.setSeconds(0);
       START = d;
   }
   else{
       var e = startTimeSpeak.split(':');
       d.setHours(e[0]);
       d.setMinutes(e[1]);
       d.setSeconds(0);
       START = d;
   }
   var e = endTimeSpeak.split(':');
   ENDE = new Date();
   ENDE.setHours(e[0]);
   ENDE.setMinutes(e[1]);
   ENDE.setSeconds(0);
}
function check() {
   if (!forcedSpeak) forceSpeak = (!startTimeSpeakWeekend||!startTimeSpeak||!endTimeSpeak);
   setWeekend();
   // Werfe doppelte Nachrichten raus
   if(SkipDuplicates && newDescriptions.findIndex(function(a){return (!a);})!=-1 ) {
       for (let a=0; a<newData.length;a++) {
           if (newData[a].type==-1) continue;
           for (let b=0; b<newData.length;b++) {
               if (newData[b].type==-1) continue;
               if (
                   (
                       a!=b
                       && newData[b].type!=-1 // vergleiche neue Daten mit neuen Daten
                       && newData[a].type == newData[b].type
                       && newData[a].level <= newData[b].level
                       && newBegins[a] >= newBegins[b]
                       && newEnds[a] <= newEnds[b]
                   )||(
                       oldData[b].type!=-1 // vergleiche neue Daten mit alten Daten
                       && newData[a].type == oldData[b].type
                       && newData[a].level <= oldData[b].level
                       && newEnds[a] < oldEnds[b]
                   )
               ) { // lösche Daten da diese eine niedrigeres level haben
                   newDescriptions[a] = '';
                   newHeadlines[a] = '';
                   newBegins[a] = '';
                   newEnds[a] = '';
                   newData[a].type = -1;
                   newData[a].level = -1;
               }
           }
       }
   }
   let AlertLevel = -1;
   newData.forEach((item, i) => {if (AlertLevel<item.level) AlertLevel = item.level;});
   oldData.forEach((item, i) => {if (AlertLevel<item.level) AlertLevel = item.level;});
   setState(AlertState,AlertLevel);
   /* Bereich für 'Alle Wetterwarnungen wurden aufgehoben' */
   if(newDescriptions.findIndex(function(a){return (!!a);})==-1 && oldDescriptions.findIndex(function(a){return (!!a);})!=-1) {
       MeldungSpracheDWD = 'Achtung' + '  .  ' + 'Alle Warnmeldungen des DWD wurden aufgehoben';
       /* Bereich für Sprachausgabe über SayIt & Alexa & Home24*/
       if ( forceSpeak || compareTime(START, ENDE, 'between')){                  // Ansage über Sayit nur im definierten Zeitbereich
           if ((pushdienst & SAYIT)!=0) {
               setState(idSayIt, lautstaerke + ";" + MeldungSpracheDWD);
           }
           if ((pushdienst & ALEXA)!=0) {
               setState(idAlexaVolumen, alexaVolumen);
               setState(idAlexa, MeldungSpracheDWD);
           }
           if((pushdienst & HOMETWO)!=0 ){
               var Url = "http://" + idMediaplayer + "/track=4fachgong.mp3|tts=" + MeldungSpracheDWD;
               request(Url);
           }
       }
       let pushmsg = 'Alle Warnmeldungen des DWD wurden aufgehoben';
       if ((pushdienst & TELEGRAM)!=0 ) {
           sendTo ("telegram.0", pushmsg);
       }
       if ((pushdienst & PUSHOVER)!=0 ) {
           sendTo("pushover.0", pushmsg);
       }
       if ((pushdienst & IOGO)!=0 ) {
           sendioGo('Wetterentwarnung', pushmsg);
       }
       if ((pushdienst & STATE)!=0 ) {
           setState(mirrorMessageState, pushmsg);
       }
       if ((pushdienst & EMAIL)!=0 && senderEmailID != '' && empfaengerEmailID !='') {
           sendEmail('Wetterentwarnung des DWD(iobroker)',pushmsg);
       }
       /* alle Sicherungen Wetterwarnung löschen */
       oldHeadlines = newHeadlines.slice();
       oldDescriptions = newDescriptions.slice();
       oldBegins = newBegins.slice();
       oldEnds = newEnds.slice();
       oldData = newData.slice();
       return;
   }
   /* Variablen für Meldungen Text */
   var MeldungNew = '';
   /* Variablen für Meldungen Sprache */
   var MeldungSpracheDWD = '';
   var MeldungNewSprache = '';
   var MeldungOldSprache = '';
   var AllEmailMsg = '';
   var AllEmailMsgDelete = '';
   var headline;
   var description;
   var begin;
   var end;
   var i;
   var warn;
   /* Bereich für 'Wetterwarnung gültig bis wurde aufgehoben' */
   for(i = 0; i < newDescriptions.length; i++) {
       headline = oldHeadlines[i];
       description = oldDescriptions[i];
       begin = oldBegins[i];
       end = oldEnds[i];
       if(description !== undefined && headline !== undefined && description !== '' && newDescriptions.indexOf(description) == -1 ) {
           MeldungOldSprache = headline + ' gültig bis ' + getFormatDateSpeak(end) + ' Uhr wurde aufgehoben' + '  .  ';
           let pushmsg = "Die Wetterwarnung " +"'"+ headline + " gültig bis " + end + "'" + " des DWD wurde aufgehoben";
           if ((pushdienst & TELEGRAM)!=0) {
               sendTo ("telegram.0", pushmsg);
           }
           if ((pushdienst & PUSHOVER)!=0) {
               sendTo("pushover.0", pushmsg);
           }
           if ((pushdienst & IOGO)!=0) {
               sendioGo('Wetterentwarnung',pushmsg);
           }
           if ((pushdienst & STATE)!=0 ) {
               setState(mirrorMessageState, pushmsg);
           }
           if ((pushdienst & EMAIL)!=0 && senderEmailID!= '' && empfaengerEmailID!='') {
               AllEmailMsgDelete+=pushmsg+'\n\n';
           }
           /* Verknüpfen aller aufgehobenen Wetterwarnungen */
           if(MeldungOldSprache !== "" && MeldungOldSprache !== undefined){
               MeldungSpracheDWD = MeldungSpracheDWD + MeldungOldSprache;
           }
       }
   }
   /* Bereich für 'Neue Amtliche Wetterwarnung' */
   for(i = 0; i < newDescriptions.length; i++) {
       headline = newHeadlines[i];
       description = newDescriptions[i];
       begin = newBegins[i];
       end = newEnds[i];
       var instruction = newInstruction[i];
       if(description !== undefined && description !== "" && oldDescriptions.indexOf(description) == -1 ) {
           MeldungNew = headline + "\ngültig vom " + begin + " Uhr bis " + end + " Uhr\n" + description;
           if (!!instruction && typeof instruction === 'string' && instruction.length > 2) MeldungNew+='\nHandlungsanweisungen: '+instruction;
           /* Entfernen °C für Sprachausgabe */
           var replaceDescription0 = entferneDatenpunkt(description);
           MeldungNewSprache = headline + " gültig vom " + getFormatDateSpeak(begin) + " Uhr, bis " + getFormatDateSpeak(end) + " Uhr. " + replaceDescription0 + '  .  ';
           if (!!instruction && typeof instruction === 'string' && instruction.length > 2) MeldungNewSprache+='Handlungsanweisungen: '+instruction;
           if ((pushdienst & TELEGRAM)!=0) {
               sendTo ("telegram.0", MeldungNew);
           }
           if ((pushdienst & PUSHOVER)!=0) {
               sendTo("pushover.0", MeldungNew);
           }
           if ((pushdienst & IOGO)!=0) {
               sendioGo('Wetterwarnung',MeldungNew);
           }
           if ((pushdienst & STATE)!=0 ) {
               setState(mirrorMessageState,MeldungNew);
           }
           if ((pushdienst & EMAIL)!=0 && senderEmailID!= '' && empfaengerEmailID!='') {
               AllEmailMsg+=MeldungNew+'\n\n';
           }
           /* Verknüpfen aller neuen Warnmeldungen */
           if(MeldungNewSprache !== "" && MeldungNewSprache !== undefined){
               MeldungSpracheDWD = MeldungSpracheDWD + MeldungNewSprache;
           }
       }
   }
   /* Verknüpfen aller neuen und abgelaufenen Warnmeldungen */
   MeldungSpracheDWD = 'Achtung   .  ' + MeldungSpracheDWD;
   /* Bereich für Sprachausgabe über Home24-Mediaplayer */
   if((forceSpeak || compareTime(START, ENDE, 'between') ) && (pushdienst & HOMETWO)!=0 && (MeldungSpracheDWD !== "" )){
       var Url2 = "http://" + idMediaplayer + "/track=4fachgong.mp3|tts=" + MeldungSpracheDWD;
       log('Url2 :' + Url2);
       request(Url2);
   }
   /* Bereich für Sprachausgabe über SayIt + Alexa */
   if (MeldungSpracheDWD !== "" && (forceSpeak || compareTime(START, ENDE, 'between'))){
       if ((pushdienst & SAYIT)!=0) setState(idSayIt, lautstaerke + ";" + MeldungSpracheDWD);
       if ((pushdienst & ALEXA)!=0) {
           setState(idAlexaVolumen, alexaVolumen);
           setState(idAlexa, MeldungSpracheDWD);
       }
   }
   AllEmailMsg+=AllEmailMsgDelete;
   if ((pushdienst & EMAIL)!=0 && senderEmailID != '' && empfaengerEmailID != '' && AllEmailMsg != '') {
       sendEmail("Wetterwarnungen des DWD(iobroker)",AllEmailMsg);
   }
   /* Neue Werte sichern */
   oldHeadlines = newHeadlines.slice();
   oldDescriptions = newDescriptions.slice();
   oldBegins = newBegins.slice();
   oldEnds = newEnds.slice();
   oldData = newData.slice();
}
/* Entfernt "°C" aus Sprachmeldung und ersetzt es durch "Grad" */
function entferneDatenpunkt(beschreibung) {
   var rueckgabe;
   rueckgabe = beschreibung;
   try {
       rueckgabe = rueckgabe.replace(/\°C/g, "Grad");
       rueckgabe = rueckgabe.replace(/\km\/h/g, "Kilometer pro Stunde");
       rueckgabe = rueckgabe.replace(/\kn/g, " Knoten");
       rueckgabe = rueckgabe.replace(/\bft/g, " Windstärke");
       rueckgabe = rueckgabe.replace(/\m\/s/g, " Meter pro Sekunde");
   }
   catch(e) {}
   return rueckgabe;
}
on(/^dwd\.0\..*\.object$/, function(dp) {
   let i = getIdIndex(dp.id);
   var warn = null;
   if (dp.state.val != '') warn = JSON.parse(dp.state.val);
   warn = convertJsonDWD(warn);
   newDescriptions[i] = warn.description ;
   newHeadlines[i] = warn.headline;
   newBegins[i] = warn.start;
   newEnds[i] = warn.end;
   newInstruction[i] = warn.instruction;
   newData[i].level = warn.level;
   newData[i].type = warn.type;
   if(timer) clearTimeout(timer);
   if (autoMode) timer = setTimeout(check, 10000);
});
function convertJsonDWD(warn) {
   warn = (!warn || warn === ''? {} : warn);
   if (warn != {} && (warn.altitudeStart>maxhoehe || warn.level < minlevel)) warn = {};
   let a = warn.description === undefined ? '' : warn.description;
   let b = warn.headline === undefined ? '' : warn.headline;
   let c = warn.start === undefined ? '' : getFormatDate(new Date(warn.start));
   let d = warn.end === undefined ? '' : getFormatDate(new Date(warn.end));
   let e = warn.instruction === undefined ? '' : warn.instruction;
   let f = warn.type === undefined ? -1 : warn.type;
   let g = warn.level === undefined ? -1 : warn.level;
   return {"description":a,"headline":b,"start":c,"end":d,"instruction":e,"type":f,"level":g};
}
function getIdIndex(a) {
   a = a.split('.');
   if (a[2].length == 7) return 0
   return a[2][7];
}
function getFormatDate(a) {
   if (!a || a === '') return '';
   return formatDate(a.getTime(), formatierungString);
}
// @PARAM Rückgabe von getFormatDate
function getFormatDateSpeak(a) {
   if (!a || a === '') return '';
   let b = a.split('.');
   let m = '';
   switch (b[1]) {
       case '01': m='Januar';break;
       case '02': m='Februar';break;
       case '03': m='März';break;
       case '04': m='April';break;
       case '05': m='Mai';break;
       case '06': m='Juni';break;
       case '07': m='Juli';break;
       case '08': m='August';break;
       case '09': m='September';break;
       case '10': m='Oktober';break;
       case '11': m='November';break;
       case '12': m='Dezember';break;
       default: m='';
   }
   b[1]=m; // setze Monatsname
   // entferne Jahr
   let c = b[2].split(' ');
   c[0]='';
   b[2] = c.join(' ');
   return b.join(' ');
}
function sendEmail(topic, msg) {
   if (senderEmailID=='') {
       log('senderEmailID ist nicht definiert!','warn');
       return;
   }
   if (empfaengerEmailID=='') {
       log('empfaengerEmailID ist nicht definiert!','warn');
       return;
   }
   sendTo("email", {
       from:    senderEmailID,
       to:      empfaengerEmailID,
       subject: topic,
       text:    msg
   });
}
function sendioGo(topic, msg) {
   if ((pushdienst & IOGO)!=0) {
       if (ioGoUser) {
           sendTo('iogo.0', "send", {
               user:                   ioGoUser,
               text:                   topic,
               title:                  msg
           });
       } else {
           sendTo('iogo.0', "send", {
               text:                   topic,
               title:                  msg
           });
       }
   }
}