// ********************************* // tibberHTML v1.0.29 // ********************************* // Copyright ©MCU // v1.0.1 - Erweiterung Umschaltung Line/bar im Graph, Anzeige der Werte in Tabelle // v1.0.2 - Vis Auswahl -> fete Breite für VIS, darkMode // v1.0.3 - Min,max,avg kleiner, untereinander, rechts Breite kleiner // v1.0.4 - ShowMinMaxAvgTexte optional // v1.0.5 - toolBox -Speichern mit Namen+Datum // v1.0.6 - y-Skala auf beiden Kurven gleich // v1.0.7 - splitNumber auf 10 vorher 3, grid rechts 10 statt 30, visualMap - mobile optimiert // v1.0.8 - CurveArea optional, Korrektur wenn tomorrow.mim = 0 -> graphMin =0 // v1.0.9 - graphMax angepasst auf nächste 0.01 // v1.0.10- graphMin angepassz auf nächste 0.01 // v1.0.11- Label optional, falls man schon Widget-Title nutzt // v1.0.12 - Trennung Label Today und Tomorrow // v1.0.13 - optionaler Text für Tomorrow Wait // v1.0.14 - automatischer Reload bei Umschaltung wait-Text Tomorrow // v1.0.15 - aktuelle Zeit highlightCurrentHour // v1.0.16 - Symbol für Line aktuelle Stunde angepasst // v1.0.17 - stepped line hinzugefügt, Korrekturen graphMin // v1.0.18 - Korrektur showwait // v1.0.19 - Korrektur showwait // v1.0.20 - Abschaltung Anzeige Graph, wenn leer, Korrektur highlightPoint // v1.0.21 - Korrektur dataNull für visualMapPieces // v1.0.22 - Inhalt von DPs als Datei speichern // v1.0.23 - Korrektur 1.Farbelement lte > maxValue // v1.0.24 - Korrektur 1. Farbelement Umstellung Linie, falsche Berechnung // v1.0.25 - Umstellung tibberDP für VIS-File Erstellung vorher fest // v1.0.26 - Neue Grafik mit beiden Tagen // v1.0.27 - Korrektur Labelanzeige bei both // v1.0.28 - Abfrage tibberlink vorhanden, da tibberconnect noch da ist // v1.0.29 - seteChart rausgenommen -> keine History // ToDo // - optionale Zeit vor günstiger Stunde telegram senden, alexa info, pushOver senden // - Ersparnis in Prozent -> Tabelle / Anzeige? // - Stunden erweitern für steppedLine? let logging = true; let tibberDP = '0_userdata.0.tibberHistory'; let dbHistoryDP = tibberDP + '.historyDB'; let tibberGraphTypeDP = tibberDP + '.graphTyp'; // bar oder line let tibberInstDP = tibberDP + '.instanz'; // 0 let tibberColorDP = tibberDP + '.colorJSON'; // visualMapPieces [ {gt: 0, lte: 0.15, color: "lightgreen"},] let tibberHTMLTodayDP = tibberDP + '.htmlToday'; let tibberHTMLTomorrowDP = tibberDP + '.htmlTomorrow'; let tibberHTMLBothDP = tibberDP + '.htmlTodayAndTomorrow'; let tibberShowTaxEnergyDP = tibberDP + '.showTaxEnergy'; // boolean let tibberShowMinMaxAvgDP = tibberDP + '.showMinMaxAvg'; // boolean let visDP = tibberDP + '.vis'; let darkModeDP = tibberDP + '.darkMode'; let tibberShowMinMaxAvgTextDP = tibberDP + '.showMinMaxAvgText'; let tibberAreaDP = tibberDP + '.curveArea'; let tibberShowLabelTodayDP = tibberDP + '.showLabelToday'; let tibberLabelTodayDP = tibberDP + '.labelToday'; let tibberShowLabelTomorrowDP = tibberDP + '.showLabelTomorrow'; let tibberLabelTomorrowDP = tibberDP + '.labelTomorrow'; let tibberWaitTomorrowTextDP = tibberDP + '.labelTomorrowWait'; let tibberShowWaitLabelTomorrowDP = tibberDP + '.showLabelTomorrowWait'; let tibberHighlightCurrentHourDP = tibberDP +'.highlightCurrentHour'; let tibberHighlightColorDarkDP = tibberDP + '.highlightColorDark'; let tibberHighlightColorLightDP = tibberDP + '.highlightColorLight'; let tibberSteppedLineDP = tibberDP + '.steppedLine'; let tibberShowGraphIfEmptyDP = tibberDP + '.showGraphIfEmpty'; let dpArray = []; createStateAsync(tibberHTMLTodayDP, {read: true, write: true, name: "Tibber HTML für Heute", desc:"HTML-DP", type: "string", role: "", def: "" }); createStateAsync(tibberHTMLTomorrowDP, {read: true, write: true, name: "Tibber HTML für Morgen", desc:"HTML-DP", type: "string", role: "", def: "" }); createStateAsync(tibberHTMLBothDP, {read: true, write: true, name: "Tibber HTML für Heute und Morgen", desc:"HTML-DP", type: "string", role: "", def: "" }); createStateAsync(tibberColorDP, {read: true, write: true, name: "Farbdefinition für Balken", desc:"JSON-DP", type: "string", role: "", def: JSON.stringify([{ 'gt': 0,'lte': 0.15, 'color': 'lightgreen' }, {'gt': 0.15,'lte': 0.22,'color': 'green'}, {'gt': 0.22,'lte': 0.29,'color': 'yellow'}, {'gt': 0.29,'lte': 0.35,'color': 'orange'}, {'gt': 0.35,'color': 'red'}]) }); createStateAsync(tibberGraphTypeDP, {read: true, write: true, name: "Tibber Graph Typ Auswahl", desc:"GraphType-DP", type: "string", role: "", states: {"bar":"bar","line":"line"} ,def: "bar" }); createStateAsync(tibberInstDP, {read: true, write: true, name: "Tibber Instanz", desc:"Instanz-DP", type: "string", role: "", def: "0" }); createStateAsync(tibberShowTaxEnergyDP, {read: true, write: true, name: "Tax und Energy anzeigen bei Hover", desc:"Show-DP", type: "boolean", role: "", def: false }); createStateAsync(tibberShowMinMaxAvgDP, {read: true, write: true, name: "Min, Max und Mittelwert anzeigen", desc:"Show-DP", type: "boolean", role: "", def: false }); createStateAsync(tibberShowMinMaxAvgTextDP, {read: true, write: true, name: "Min, Max und Mittelwert Texte anzeigen", desc:"ShowText-DP", type: "boolean", role: "", def: false }); createStateAsync(visDP, {read: true, write: true, name: "Welche VIS soll genutzt werden?", desc:"VIS-DP", type: "string", role: "",states: {"jarvis":"jarvis","VIS":"VIS"} , def: "jarvis" }); createStateAsync(darkModeDP, {read: true, write: true, name: "DarkMode an?", desc:"DarkMode-DP", type: "boolean", role: "", def: false }); createStateAsync(tibberAreaDP, {read: true, write: true, name: "Fläche unter Kurve?", desc:"CurveArea-DP", type: "boolean", role: "", def: false }); createStateAsync(tibberShowLabelTodayDP, {read: true, write: true, name: "Label Today anzeigen?", desc:"showLabel-DP", type: "boolean", role: "", def: false }); createStateAsync(tibberLabelTodayDP, {read: true, write: true, name: "Label Today für das Chart", desc:"Label-DP", type: "string", role: "", def: "Today-Tageswerte" }); createStateAsync(tibberShowLabelTomorrowDP, {read: true, write: true, name: "Label Tomorrow anzeigen?", desc:"showLabel-DP", type: "boolean", role: "", def: false }); createStateAsync(tibberLabelTomorrowDP, {read: true, write: true, name: "Label Tomorrow für das Chart", desc:"Label-DP", type: "string", role: "", def: "Tomorrow-Tageswerte" }); createStateAsync(tibberShowWaitLabelTomorrowDP, {read: true, write: true, name: "Label Wait Tomorrow anzeigen?", desc:"showLabel-DP", type: "boolean", role: "", def: false }); createStateAsync(tibberWaitTomorrowTextDP, {read: true, write: true, name: "Label Wait Tomorrow für das Chart", desc:"Label-DP", type: "string", role: "", def: "Werte erst ab 14.00 Uhr verfügbar" }); createStateAsync(tibberHighlightCurrentHourDP, {read: true, write: true, name: "Aktuelle Stunde markieren?", desc:"HighlightAktHour-DP", type: "boolean", role: "", def: false }); createStateAsync(tibberHighlightColorDarkDP, {read: true, write: true, name: "HighLight Color DarkMode", desc:"HighLight Dark-DP", type: "string", role: "", def: "white" }); createStateAsync(tibberHighlightColorLightDP, {read: true, write: true, name: "HighLight Color LightMode", desc:"HighLight Light-DP", type: "string", role: "", def: "grey" }); createStateAsync(tibberSteppedLineDP, {read: true, write: true, name: "Gestufte Linie?", desc:"SteppedLine-DP", type: "boolean", role: "", def: false }); createStateAsync(tibberShowGraphIfEmptyDP, {read: true, write: true, name: "Graph anzeigen ohne Daten?", desc:"showGraph-DP", type: "boolean", role: "", def: false }); // getHistoryAdapter(); // setDPs('0'); // -> dpArray setTimeout(function(){ getTibberData(dpArray); let inst = getState(tibberInstDP).val; $('tibberlink.'+inst+'.Homes.*.PricesTomorrow.json').on(function(obj) { setTimeout(function(){ getTibberData(dpArray); },3000) }); $('tibberlink.'+inst+'.Homes.*.PricesToday.json').on(function(obj) { setTimeout(function(){ getTibberData(dpArray); },3000); }); },3000); let scheduleId ; if(getState(tibberHighlightColorDarkDP).val){ scheduleId = schedule('00 * * * *', () => { getTibberData(dpArray); }) } on({id: tibberHighlightCurrentHourDP, change: "any"}, function (obj) { let value = obj.state.val; if(value){ getTibberData(dpArray); clearSchedule(scheduleId); scheduleId = schedule('00 * * * *', () => { getTibberData(dpArray); }) }else{ clearSchedule(scheduleId); getTibberData(dpArray); } }); on({id: tibberShowTaxEnergyDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberColorDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberShowMinMaxAvgDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberShowMinMaxAvgTextDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberGraphTypeDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: darkModeDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberAreaDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberShowLabelTodayDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberShowLabelTomorrowDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberShowWaitLabelTomorrowDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberHighlightColorDarkDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberHighlightColorLightDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberSteppedLineDP, change: "any"}, function (obj) { getTibberData(dpArray); }); on({id: tibberShowGraphIfEmptyDP, change: "any"}, function (obj) { getTibberData(dpArray); }); function getTibberData(arr){ // arr wird nur für eCharts benötigt // tibberDataDP selbst ermitteln let tibberInst = getState(tibberInstDP).val; let priceArr = ['PricesToday','PricesTomorrow']; let tibberDataBoth = []; let tibberlinkOK = false; for(let x=0;x< priceArr.length;x++){ let tibberData = []; let tibberLink = $('tibberlink.'+tibberInst+'.Homes.*.'+priceArr[x]+'.json') if(tibberLink[0] != undefined){ tibberlinkOK = true; let tblLevel = levelObject(tibberLink[0]); let dpLeveltbl = getDPLevel(tblLevel,4); let tibberJSON = JSON.parse(getState(dpLeveltbl+'.json').val); // tibberJSON for(let i=0;i Keine Generierung der HTML','warn') } } if(tibberlinkOK){ setHTML(tibberDataBoth,'both'); if(getState(visDP).val=='VIS'){ createHtmlFile('both') } } } // createHtmlFile(); async function createHtmlFile(name) { let dp = ''; let file = ''; if(name == 'PricesToday'){ dp = tibberDP + '.htmlToday'; file = 'today'; } if(name == 'PricesTomorrow'){ dp = tibberDP + '.htmlTomorrow'; file = 'tomorrow'; } if(name == 'both'){ dp = tibberDP + '.htmlTodayandTomorrow'; file = 'today_tomorrow'; } const htmlContent = getState(dp).val; writeFile('vis.0', '/tibber/'+file+'.html',htmlContent, (err) => { if (err) { console.error('Fehler beim Speichern der HTML-Datei, tibber-Verzeichnis angelegt?', err); } else { console.log('HTML-Datei '+file+'.html erfolgreich gespeichert'); } }); } function getMinValue(arr) { return Math.min(...arr.map(item => item.value)); } function getMaxValue(arr) { return Math.max(...arr.map(item => item.value)); } function getAverageValue(arr) { const sum = arr.reduce((acc, item) => acc + item.value, 0); return sum / arr.length; } function getEndOfDay(timeTS) { var now = new Date(timeTS); var endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1); endOfDay.setHours(0, 0, 0, 0); return endOfDay.getTime() - 1; } function setHTML(origData,priceDP){ // log(origData); let data = []; let dataMin = ''; let dataMinTime = ''; let dataMax = ''; let dataMaxTime = ''; let dataAvg = ''; let dataNull = false; // log(priceDP); let tibberInst = getState(tibberInstDP).val; let tibberLink = $('tibberlink.'+tibberInst+'.Homes.*.PricesToday.json') let tblLevel = levelObject(tibberLink[0]); let dpLeveltbl = getDPLevel(tblLevel,3); let minToday = getState(dpLeveltbl + '.PricesToday.minimum.total').val; //minToday = minToday-(Math.trunc(minToday*0.1)) // log(minToday) let maxToday = getState(dpLeveltbl + '.PricesToday.maximum.total').val let minTomorrow = getState(dpLeveltbl + '.PricesTomorrow.minimum.total').val //minTomorrow = minTomorrow-(Math.trunc(minTomorrow*0.1)) let maxTomorrow = getState(dpLeveltbl + '.PricesTomorrow.maximum.total').val let jsonTomorrow = JSON.parse(getState(dpLeveltbl + '.PricesTomorrow.json').val); let graphMin = 0; if(minToday >= minTomorrow){ if(minTomorrow != 0){ graphMin = minTomorrow; }else{ graphMin = minToday; } }else{ graphMin = minToday; } let graphMax = 0; if(maxToday >= maxTomorrow){ graphMax = maxToday; }else{ graphMax = maxTomorrow; } // graphMax = Math.round((graphMax + parseFloat((graphMax / 20).toFixed(2))) * 100) / 100; // graphMax = Math.max(Math.ceil(graphMax * 100) / 100, 0.35); if(jsonTomorrow.length ==0 ){ graphMin = minToday; graphMax = maxToday } graphMin = Math.ceil(graphMin * 50) / 50 - 0.02; graphMax = Math.ceil(graphMax * 50) / 50 + 0.01; //log(graphMin) for(let i=0;i3){ for(let i=0;i typeof item.value === 'number' && item.min == false && item.max == false && item.average == false); dataAvg = String(getAverageValue(filteredData)); dataMin = String(getMinValue(filteredData)); dataMax = String(getMaxValue(filteredData)); } // Werte holen let showTaxEnergy = getState(tibberShowTaxEnergyDP).val; let minMaxAvg = getState(tibberShowMinMaxAvgDP).val; let colorJSON = getState(tibberColorDP).val; let html = ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += 'Tibber-Tageswert'; html += ''; html += ''; html += ''; html += '' if(getState(visDP).val == 'VIS'){ html += '
'; }else{ html += '
'; } //log(data); html += ''; html += ''; html += ''; // log(html); if(priceDP == 'both'){ setState(tibberHTMLBothDP,html); }else{ if(priceDP == 'PricesToday'){ setState(tibberHTMLTodayDP,html); }else{ setState(tibberHTMLTomorrowDP,html); } } } function sortByName(x, y) { // Wenn x oder y nicht-numerisch sind, setze sie an das Ende if (isNaN(x.hour) || isNaN(y.hour)) { return isNaN(x.hour) ? 1 : -1; } // Vergleiche numerische Werte return x.hour - y.hour; } on({id: dbHistoryDP, change: "any"}, function (obj) { let value = obj.state.val; // history für die DPs aktiv? if (value!=''){ if (getState('system.adapter.'+value+'.alive').val){ if (isHistoryActiveAllDPs(dpArray)){ if(logging){ log('History-Future-DPs sind angelegt und History aktiviert!'); } // getPVData(dpArray); //runScript(); }else{ if(logging){ log('History wird angelegt für die future-DPs!'); } activateDPsHistory(dpArray); setTimeout(function(){ deleteAllHistoryDPs(dpArray); // log('gelöscht'); // getPVData(dpArray); },3000) //runScript(); } }else{ log('Der History-Adapter ist nicht gestartet! -> Es werden keine DPs mit History aktiviert','warn'); } } }); function setDPs(tibberInst){ // Ist der Adapter aktiv // //let pvAliveDP = 'system.adapter.pvForecast.'+tibberInst+'.alive'; //if(existsState(pvAliveDP)){ // if(getState(pvAliveDP).val){ // Adapter ist gestartet let tibberKeys = ['Value','Tax','Energy'] for(let i=0;i0){ // delDPs(dpArray); for(let i=0;i0){ createDPs(dpArray); } },3000); }else{ createDPs(dpArray); } } function delDPs(arr){ for(let i=0; i=0){ //'history' return true; } } } return false; } function activateDPsHistory(dpArray){ let historyDB = getState(dbHistoryDP).val; //log('HistoryDB: '+historyDB); if (historyDB != ''){ if (getState('system.adapter.'+historyDB+'.alive').val){ if (historyDB!=''){ for(let i= 0;i keine Aktivierung möglich','warn'); } }else{ log('Aktivierung der DPs für die DB erst möglich, wenn man eine DB ausgewählt hat!','warn'); } } // deleteAllHistoryDPs(); function deleteAllHistoryDPs(dpArray){ let dbHistory = getState(dbHistoryDP).val; if (getState('system.adapter.'+dbHistory+'.alive').val){ log('History DPs dpArray löschen!'); for(let i= 0;i {//console.log('deleted') }); }else{ log('Löschen nicht durchgeführt, da keine HistoryDB festgelegt'); } } function getHistoryAdapter(){ // history, influxdb, sql log('Einlesen der vorhandenen History-DBs.'); let histArr = ['history','influxdb','sql']; let arr =[]; for (let i=0; i alive auf false setzen'); } } } } let devStates = {'':'keine Auswahl'}; for (let i=0; i< arr.length;i++){ devStates[arr[i]] = arr[i]; } //log(devStates); if (existsState(dbHistoryDP)){ let obj = getObject(dbHistoryDP); obj.common.states = devStates; setObject(dbHistoryDP,obj); }else{ createStateAsync(dbHistoryDP, {read: true, write: true, name: "DB-Auswahl", type: "string", role: "", def: '',states:devStates }); } } function setActiveHistory(dp){ let dbHistory = getState(dbHistoryDP).val; //log('dbHistory setActive: '+dbHistory); if (dbHistory !=''){ sendTo(dbHistory, 'enableHistory', { // 'history.0' id: dp, // 'system.adapter.history.0.memRss' options: { changesOnly: true, debounce: 0, retention: 31536000, maxLength: 3, changesMinDelta: 0, //ignoreZero: true, aliasId: '', enabled: true, debounceTime: 0, blockTime: 0, changesRelogInterval: "0", ignoreBelowNumber: "", disableSkippedValueLogging: false, customRetentionDuration: 365, enableDebugLogs: false } }, function (result) { if (result.error) { console.log(result.error); } if (result.success) { //successfull enabled //log(dp +' wurde in history aktiviert'); } }); } } // setHistoryData(futureHumidityDP,dpValueArr); function setHistoryData(dp,states){ let dbHistory = getState(dbHistoryDP).val; if (dbHistory!=''){ sendTo(dbHistory, 'storeState', { // 'history.0' id: dp, state: states }, result => { // console.log('added') }); } } function levelObject(id){ // 0_userdata.0.jarvis.system.tabWidgetStateEffects.0.Desktop.0.Bad.0b5dd758-8d7d-4259-8945-821b1c942960.states.0.displayOff let idArr=id.split('.'); return idArr; //log(idArr[10]); } // log(getDPLevel(levelObject('0_userdata.0.jarvis.system.tabWidgetStateEffects.0.Desktop.0.Bad.0b5dd758-8d7d-4259-8945-821b1c942960.states.0.displayOff'),9)); function getDPLevel(idArr,level){ let id =''; for (let i=0;i<=level;i++){ id += idArr[i] +'.' } id = id.substr(0,id.length-1); // letzten Punkt wegnehmen return id; }