Last active
September 6, 2023 18:22
-
-
Save jscmidt/74edf5172109721ee5931187f63c1a70 to your computer and use it in GitHub Desktop.
iOS-widget for openWB using the Scriptable-app.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
iOS-widget for openWB using the Scriptable-app. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Variables used by Scriptable. | |
// These must be at the very top of the file. Do not edit. | |
// icon-color: yellow; icon-glyph: solar-panel; | |
//-------------------------------------------------------------------------------------- | |
// Folgende Werte MÜSSEN angepasst werden: | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// IP-Adresse der openWB (von hier holt das Widget die benötigten Daten): | |
let oWBip = "192.168.1.1"; | |
//-------------------------------------------------------------------------------------- | |
// URL, auf die beim Tippen auf das Widget weitergeleitet werden soll (z.B. Home-Website von oWB): | |
let widgetURL = "http://192.168.1.1/openWB/web/index.php"; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Folgende Werte KÖNNEN angepasst werden: | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Im folgenden kann die Belegung der einzelnen Felder frei festgelegt werden: | |
// 0: Platzhalter (leeres Feld ohne Inhalt und Rahmen) | |
// 1: PV | |
// 2: Netz | |
// 3: Haus | |
// 4: Speicher SoC | |
// 5: Speicherleistung | |
// 6: Timestamp | |
// 7: LP1 SoC | |
// 8: LP2 SoC | |
// 9: LP1 Leistung | |
// 10: LP2 Leistung | |
// 11: LP1 und LP2 Leistung (summiert) | |
// 12: LP1 oder Speicherleistung (LP1 wenn geladen wird, sonst Speicher) | |
// 13: LP2 oder Speicherleistung (LP2 wenn geladen wird, sonst Speicher) | |
// 14: LP1 und LP2 oder Speicherleistung (LP1 und LP2 (summiert) wenn geladen wird, sonst Speicher) | |
// 15: LP1 letzte Lademenge | |
// 16: LP2 letzte Lademenge | |
// 17: LP1 und LP2 letzte Lademenge (summiert) | |
// 18: LP1 Lademenge diesen Monat | |
// 19: LP2 Lademenge diesen Monat | |
// 20: LP1 und LP2 Lademenge diesen Monat (summiert) | |
// 21: Awattar-Strompreis | |
// 22: Lademodus | |
// 23: Leistung Smarthome-Verbraucher 1 | |
// 24: Leistung Smarthome-Verbraucher 2 | |
// 25: Leistung Smarthome 2.0 Gerät 1 | |
// 26: Leistung Smarthome 2.0 Gerät 2 | |
// 27: Leistung Smarthome 2.0 Gerät 3 | |
// 28: Leistung Smarthome 2.0 Gerät 4 | |
// 29: Leistung Smarthome 2.0 Gerät 5 | |
// 30: Leistung Smarthome 2.0 Gerät 6 | |
// 31: Leistung Smarthome 2.0 Gerät 7 | |
// 32: Leistung Smarthome 2.0 Gerät 8 | |
// 33: Leistung Smarthome 2.0 Gerät 9 | |
// 34: Leistung Smarthome-Verbraucher 1 + 2 | |
// 35: Leistung Smarthome 2.0 Geräte 1 - 9 | |
// 36: Leistung Smarthome-Verbraucher 1 + 2 und Smarthome 2.0 Geräte 1 - 9 | |
let panel1 = 1; | |
let panel2 = 2; | |
let panel3 = 3; | |
let panel4 = 4; | |
let panel5 = 5; | |
let panel6 = 6; | |
let panel7 = 0; | |
let panel8 = 0; | |
let panel9 = 0; | |
let panel10 = 0; | |
let panel11 = 0; | |
let panel12 = 0; | |
let panel13 = 0; | |
let panel14 = 0; | |
let panel15 = 0; | |
//-------------------------------------------------------------------------------------- | |
// Großes Widget: | |
// /////////////////////// | |
// // 1 // 2 // 3 // | |
// /////////////////////// | |
// // 4 // 5 // 6 // | |
// /////////////////////// | |
// // 7 // 8 // 9 // | |
// /////////////////////// | |
// // 10 // 11 // 12 // | |
// /////////////////////// | |
// // 13 // 14 // 15 // | |
// /////////////////////// | |
//-------------------------------------------------------------------------------------- | |
// Mittleres Widget: | |
// /////////////////////// | |
// // 1 // 2 // 3 // | |
// /////////////////////// | |
// // 4 // 5 // 6 // | |
// /////////////////////// | |
//-------------------------------------------------------------------------------------- | |
// Kleines Widget: | |
// ///////// | |
// // 1 // | |
// ///////// | |
// // 2 // | |
// ///////// | |
//-------------------------------------------------------------------------------------- | |
// Damit die Werte richtig skaliert werden muss die Größe des Widgets festgelegt werden: | |
// 0: klein, 1: mittel, 2: groß | |
let widgetSize = 1; | |
//-------------------------------------------------------------------------------------- | |
// Soll beim Anzeigen der Ladeleistung eines LPs (auch im Wechel mit anderen Werten) der Lademodus als Footnote angezeigt werden? | |
let lademodusFootnote = 1; | |
//-------------------------------------------------------------------------------------- | |
// Sollen kleine Graphen als Hintergrund angezeigt werden? 0: Nein, 1: Ja | |
let backgroundCharts = 1; | |
//-------------------------------------------------------------------------------------- | |
// Sollen die einzelnen Werte und deren Graphen einen Rahmen haben? | |
// 0: Nein, 1-4: Breite der Rahmen | |
let panelBorder = 3; | |
//-------------------------------------------------------------------------------------- | |
// Wie sollen Namen, Footnotes und die einzelnen Werte in ihren Bereichen angeordnet sein? | |
// 0: Linksbündig, 1: zentriert | |
let contentAlignment = 1; | |
//-------------------------------------------------------------------------------------- | |
// Wie sollen Titel und Logo angeordnet sein? | |
// 0: Linksbündig, 1: zentriert | |
let titleAlignment = 1; | |
//-------------------------------------------------------------------------------------- | |
// Soll im Widget-Titel das openWB-Logo angezeigt werden? 0: Nein, 1: Ja | |
// Beim kleinen Widget ersetzt das Logo den Titel, anstatt diesen daneben anzuzeigen | |
let logo = 1; | |
//-------------------------------------------------------------------------------------- | |
// Titel des Widgets (steht hinter Logo wenn dieses angezeigt wird). Kein Titel: "" | |
let widgetTitle = ""; | |
//-------------------------------------------------------------------------------------- | |
// Soll der Timestamp im Titel angeigt werden? (Alternativ kann auch ein Feld mit demn Timestamp belegt werden) | |
// Geht nur dann, wenn entweder das mittlere oder das große Widget verwendet wird | |
let timestampTitle = 1; | |
//-------------------------------------------------------------------------------------- | |
// Die maximale Zeit, die für das Holen der Daten verwendet wird. Bei Fehlern kann diese Zeit erhöht werden: | |
let timeOut = 2.0; | |
//-------------------------------------------------------------------------------------- | |
// Soll bei einem Fehler (z.B. Zugriff auf das Widget aus dem Mobilfunk) statt dem Fehler | |
// ein Bild angezeigt werden? 0: Nein, 1: Ja | |
let imageAtError = 0; | |
//-------------------------------------------------------------------------------------- | |
// Name des unter File-Bookmarks hinterlegten Bildes, das bei einem Error angezeigt werden soll: | |
let errorImageName = "IMG_4832.jpg"; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Konfiguration der Tresholds für die Farben der verschiedenen Daten | |
// SoC (Auto und Speicher) Tresholds sind festgelegt auf 0-30%: rot, 30-70%: gelb, 70-100%: grün | |
// Speicher Tresholds sind festgelegt auf Laden: Grün, Entladen: Rot, keine Ladung/Entladung: Gelb | |
// Netz Tresholds sind festgelegt auf Einspeisung: Grün, Bezug: Rot, keine Einspeisung/Bezug: Gelb | |
// Lademodus Tresholds sind festgelegt auf Sofort: Grün, Nur PV/min PV: Gelb, Standby/Stop: Rot | |
// Timestamp und Ladeleistung und Awattar sind immer die Standard-Schriftfarbe (siehe unten) | |
// Im folgenden können die Tresholds für Hausverbrauch und PV festgelegt werden: | |
//-------------------------------------------------------------------------------------- | |
// Treshold 1 für PV (über diesem Wert Gelb - darunter Rot), muss kleiner als Treshold2 sein!: | |
let pvTresh1 = 100; | |
//-------------------------------------------------------------------------------------- | |
// Treshold 2 für PV (über diesem Wert Grün - darunter Gelb), muss größer als Treshold1 sein!: | |
let pvTresh2 = 5000; | |
//-------------------------------------------------------------------------------------- | |
// Treshold 1 für Hausverbrauch (über diesem Wert Gelb - darunter Grün), muss kleiner als Treshold2 sein!: | |
let hausTresh1 = 400; | |
//-------------------------------------------------------------------------------------- | |
// Treshold 2 für Hausverbrauch (über diesem Wert Rot - darunter Gelb), muss größer als Treshold1 sein!: | |
let hausTresh2 = 1000; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Im folgenden können die Farben des Widgets angepasst werden: | |
// z.B. weis: "FFFFFF", schwarz: "000000"; Eine gute Website dafür: https://htmlcolorcodes.com/ | |
//-------------------------------------------------------------------------------------- | |
// Standart-Schriftfarbe, für Überschrift, Footnotes usw.: | |
let textColor = "FFFFFF"; | |
//-------------------------------------------------------------------------------------- | |
// Art des Widget-Hintergrunds, 0: einfarbig, 1: Farbverlauf | |
let backgroundType = 1; | |
//-------------------------------------------------------------------------------------- | |
// Farbe bei einfarbigem Hintergrund: | |
let backgroundColor = "000000"; | |
//-------------------------------------------------------------------------------------- | |
// Verlauffarben bei Farbverlauf als Hintergrund: | |
let backgroundGradient1 = "000000"; | |
let backgroundGradient2 = "202020"; | |
//-------------------------------------------------------------------------------------- | |
// Farben der Hintergrundgraphen: | |
let chartsColorPV = "F7DC6F"; | |
let chartsColorNetz = "D7DBDD"; | |
let chartsColorHaus = "7DCEA0"; | |
let chartsColorSpeicher = "85C1E9"; // Leistung und SoC | |
let chartsColorAwattar = "85C1E9"; | |
let chartsColorLP1 = "FFFFFF"; // Leistung und SoC | |
let chartsColorLP2 = "FFFFFF"; // Leistung und SoC | |
let chartsColorBothLP = "FFFFFF"; // Leistung und SoC, wenn LP1 und LP2 summiert wird | |
let chartsColorSmarthome = "FFFFFF"; // wird nur von Smarthome 2.0 Geräten unterstützt | |
// Wie blass oder stark sollen die Graphen sein? (Wert zwischen 0.1 und 1.0) | |
let chartsThickness = 0.4; | |
//-------------------------------------------------------------------------------------- | |
// Farben der Rahmen der einzelnen Werte: | |
let borderColorPV = "F7DC6F"; | |
let borderColorNetz = "D7DBDD"; | |
let borderColorHaus = "7DCEA0"; | |
let borderColorSpeicher = "85C1E9"; // Leistung und SoC | |
let borderColorAwattar = "FFFFFF"; | |
let borderColorLP1 = "FFFFFF"; // Leistung, SoC, letzte Lademenge und Monatslademenge | |
let borderColorLP2 = "FFFFFF"; // Leistung, SoC, letzte Lademenge und Monatslademenge | |
let borderColorBothLP = "FFFFFF"; // Leistung, SoC, letzte Lademenge und Monatslademenge, wenn LP1 und LP2 summiert wird | |
let borderColorSmarthome = "FFFFFF"; | |
let borderColorModus = "FFFFFF"; | |
let borderColorTimestamp = "FFFFFF"; | |
// Wie blass oder stark sollen die Rahmen sein? (Wert zwischen 0.1 und 1.0) | |
let borderThickness = 0.6; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Graph von https://kevinkub.de/ | |
class LineChart { | |
constructor(width, height, values) { | |
this.ctx = new DrawContext(); | |
this.ctx.size = new Size(width, height); | |
this.values = values; | |
} | |
_calculatePath() { | |
let maxValue = Math.max(...this.values); | |
let minValue = Math.min(...this.values); | |
let difference = maxValue - minValue; | |
let count = this.values.length; | |
let step = this.ctx.size.width / (count - 1); | |
let points = this.values.map((current, index, all) => { | |
let x = step*index; | |
let y = this.ctx.size.height - (current - minValue) / difference * this.ctx.size.height; | |
return new Point(x, y); | |
}); | |
return this._getSmoothPath(points); | |
} | |
_getSmoothPath(points) { | |
let path = new Path(); | |
path.move(new Point(0, this.ctx.size.height)); | |
path.addLine(points[0]); | |
for(let i = 0; i < points.length-1; i++) { | |
let xAvg = (points[i].x + points[i+1].x) / 2; | |
let yAvg = (points[i].y + points[i+1].y) / 2; | |
let avg = new Point(xAvg, yAvg); | |
let cp1 = new Point((xAvg + points[i].x) / 2, points[i].y); | |
let next = new Point(points[i+1].x, points[i+1].y); | |
let cp2 = new Point((xAvg + points[i+1].x) / 2, points[i+1].y); | |
path.addQuadCurve(avg, cp1); | |
path.addQuadCurve(next, cp2); | |
} | |
path.addLine(new Point(this.ctx.size.width, this.ctx.size.height)); | |
path.closeSubpath(); | |
return path; | |
} | |
configure(fn) { | |
let path = this._calculatePath(); | |
if(fn) { | |
fn(this.ctx, path); | |
} else { | |
this.ctx.addPath(path); | |
this.ctx.fillPath(path); | |
} | |
return this.ctx; | |
} | |
} | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Allgemeine Widget-Einstellungen | |
let widget = new ListWidget(); | |
widget.url = widgetURL; | |
widget.setPadding(20, 20, 20, 20); | |
let nextRefresh = Date.now() + 1000*30; | |
widget.refreshAfterDate = new Date(nextRefresh); | |
// vStackV ist vertikal, vStack0 ist ist Überschrift, vStack1 ist Zeile 1, vStack2 ist Zeile 2 | |
let vStackV = widget.addStack(); | |
vStackV.layoutVertically(); | |
vStackV.centerAlignContent(); | |
let vStack0 = vStackV.addStack(); | |
vStack0.layoutHorizontally(); | |
vStackV.addSpacer(); | |
let vStack1 = vStackV.addStack(); | |
vStack1.layoutHorizontally(); | |
vStackV.addSpacer(); | |
let vStack2 = vStackV.addStack(); | |
vStack2.layoutHorizontally(); | |
let vStack3; | |
let vStack4; | |
if(widgetSize == 2){ | |
vStackV.addSpacer(); | |
vStack3 = vStackV.addStack(); | |
vStack3.layoutHorizontally(); | |
vStackV.addSpacer(); | |
vStack4 = vStackV.addStack(); | |
vStack4.layoutHorizontally(); | |
vStackV.addSpacer(); | |
vStack5 = vStackV.addStack(); | |
vStack5.layoutHorizontally(); | |
} | |
// aktuellen Timestamp erstellen und formatieren | |
let date = new Date(); | |
let df = new DateFormatter(); | |
df.dateFormat = "HH:mm"; | |
let timestamp = (df.string(date)); | |
// Überprüfen ob Monats-CSV benötigt wird | |
let monthlyUsed = false; | |
for(let i=1; i<16; i++){ | |
let comparePanel = eval("panel" + i.toString()); | |
if(comparePanel == 18 || comparePanel == 19 || comparePanel == 20){ | |
monthlyUsed = true; | |
} | |
} | |
// Historiedaten für Graphen holen | |
let error = 0; | |
let csv; | |
let data; | |
let csvMonthly; | |
let dataMonthly; | |
if(backgroundCharts == 1){ | |
csv = await getCSV(); | |
if(error == 0){ | |
data = csvJSON(csv, ["Zeit", "Bezug", "Einspeisung", "PV", "LP1", "LP2", "LP3", "LPGesamt", "Laden", "Entladen", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "SoC", "SoC1", "SoC2", "24", "25", "26", "Smarthome1", "Smarthome2", "Smarthome3", "Smarthome4", "Smarthome5", "Smarthome6", "Smarthome7", "Smarthome8", "Smarthome9", "36", "37", "38", "39"]); | |
} | |
if(monthlyUsed == true){ | |
csvMonthly = await csvRequest("monthly", new Date()); | |
if(error == 0){ | |
dataMonthly = csvJSON(csvMonthly, ["1", "2", "3", "4", "LP1", "LP2", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29"]); | |
} | |
} | |
} | |
await setTitle(); | |
// Inhalt des Widgets | |
if(error == 0){ | |
setBackground(); | |
vStack1.addSpacer(); | |
await setField(vStack1, panel1); | |
if(widgetSize != 0){ | |
vStack1.addSpacer(); | |
await setField(vStack1, panel2); | |
vStack1.addSpacer(); | |
await setField(vStack1, panel3); | |
} | |
vStack1.addSpacer(); | |
vStack2.addSpacer(); | |
if(widgetSize == 0){ | |
await setField(vStack2, panel2); | |
} | |
else{ | |
await setField(vStack2, panel4); | |
vStack2.addSpacer(); | |
await setField(vStack2, panel5); | |
vStack2.addSpacer(); | |
await setField(vStack2, panel6); | |
} | |
vStack2.addSpacer(); | |
if(widgetSize == 2){ | |
vStack3.addSpacer(); | |
await setField(vStack3, panel7); | |
vStack3.addSpacer(); | |
await setField(vStack3, panel8); | |
vStack3.addSpacer(); | |
await setField(vStack3, panel9); | |
vStack3.addSpacer(); | |
vStack4.addSpacer(); | |
await setField(vStack4, panel10); | |
vStack4.addSpacer(); | |
await setField(vStack4, panel11); | |
vStack4.addSpacer(); | |
await setField(vStack4, panel12); | |
vStack4.addSpacer(); | |
vStack5.addSpacer(); | |
await setField(vStack5, panel13); | |
vStack5.addSpacer(); | |
await setField(vStack5, panel14); | |
vStack5.addSpacer(); | |
await setField(vStack5, panel15); | |
vStack5.addSpacer(); | |
} | |
} | |
if(error != 0){ | |
// Wenn ein Fehler aufgetreten ist | |
if(imageAtError == 0){ | |
// Fehlermeldung anzeigen | |
addDataView(vStack1, "Ein Fehler ist aufgetreten!", "red", "Ist openWB auf der IP " + oWBip + " erreichbar?", " ", 0); | |
addDataView(vStack2, timestamp + " Uhr", "", "Timestamp", " ", 0); | |
} | |
else{ | |
// Bild anzeigen | |
let fm = FileManager.local(); | |
let path = fm.bookmarkedPath(errorImageName); | |
let errorImage = fm.readImage(path); | |
widget.backgroundImage = errorImage; | |
} | |
} | |
Script.setWidget(widget); | |
Script.complete(); | |
// Größe der Widget-Vorschau | |
switch(widgetSize){ | |
case 0: widget.presentSmall(); break; | |
case 1: widget.presentMedium(); break; | |
case 2: widget.presentLarge(); break; | |
} | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
async function setField(widget, panel){ | |
let LP1leistung = await getData("llaktuell")*1; | |
let LP2leistung = await getData("llaktuells1")*1; | |
switch(panel){ | |
case 12: | |
if(LP1leistung != 0){ | |
panel = 9; | |
} | |
else{ | |
panel = 5; | |
} | |
break; | |
case 13: | |
if(LP2leistung != 0){ | |
panel = 10; | |
} | |
else{ | |
panel = 5; | |
} | |
break; | |
case 14: | |
if(LP1leistung + LP2leistung != 0){ | |
panel = 11; | |
} | |
else{ | |
panel = 5; | |
} | |
break; | |
} | |
await setFieldFinal(widget, panel, LP1leistung, LP2leistung); | |
} | |
async function setFieldFinal(widget, panel, LP1leistung, LP2leistung){ | |
let wert; | |
let value; | |
let valueColor; | |
let name; | |
let footnote; | |
let borderColor; | |
let chart; | |
let historicalsBezug; | |
let historicalsEinspeisung; | |
let historicalsLaden; | |
let historicalsEntladen; | |
let historicalsPV; | |
let historicalsLP1; | |
let historicalsLP2; | |
let border = panelBorder; | |
switch(panel){ | |
case 0: | |
//Platzhalter | |
value = " "; | |
name = " "; | |
footnote = " "; | |
border = 0; | |
borderColor = "FFFFFF"; | |
chart = 0; | |
break; | |
case 1: | |
// PV | |
historicals = getCalc(data, "PV"); | |
wert = (await getData("pvallwatt")*-1); // Wert ist negativ, deswegen *-1 | |
value = wert.toString() + "W"; | |
valueColor = defineColor(pvTresh1, pvTresh2, wert); | |
name = "PV"; | |
footnote = " "; | |
borderColor = borderColorPV; | |
chart = createChart(historicals, chartsColorPV); | |
break; | |
case 2: | |
// Netz | |
historicalsBezug = getCalc(data, "Bezug"); | |
historicalsEinspeisung = getCalc(data, "Einspeisung"); | |
historicals = []; | |
for(let i=0; i<12; i++){ | |
historicals[i] = historicalsBezug[i] - historicalsEinspeisung[i]; | |
} | |
wert = (await getData("wattbezug")*1); // Einspeissung ist negativ | |
value = wert.toString() + "W"; | |
valueColor = defineColorNegative(-100, 100, wert); | |
name = "Netz"; | |
footnote = defineFootnote(100, -100, "Bezug", "Einspeisen", "Speicherregelung", wert); | |
borderColor = borderColorNetz; | |
chart = createChart(historicals, chartsColorNetz); | |
break; | |
case 3: | |
// Haus | |
historicalsBezug = getCalc(data, "Bezug"); | |
historicalsEinspeisung = getCalc(data, "Einspeisung"); | |
historicalsLaden = getCalc(data, "Laden"); | |
historicalsEntladen = getCalc(data, "Entladen"); | |
historicalsPV = getCalc(data, "PV"); | |
// Hausverbrauch = Netzbezug + Speicherentladung + PV-Erzeugung - Netzeinspeisung - Speicherladung | |
historicals = []; | |
for(let i=0; i<12; i++){ | |
historicals[i] = historicalsBezug[i] + historicalsEntladen[i] + historicalsPV[i] - historicalsEinspeisung[i] - historicalsLaden[i]; | |
} | |
wert = (await getData("hausverbrauch")*1); | |
value = wert.toString() + "W"; | |
valueColor = defineColorNegative(hausTresh1, hausTresh2, wert); | |
name = "Haus"; | |
footnote = " "; | |
borderColor = borderColorHaus; | |
chart = createChart(historicals, chartsColorHaus); | |
break; | |
case 4: | |
// Speicher SoC | |
historicals = getCalc(data, "SoC"); | |
wert = (await getData("speichersoc")*1); | |
value = wert.toString() + "%"; | |
valueColor = defineColor(30, 70, wert); | |
name = "SoC"; | |
footnote = " "; | |
borderColor = borderColorSpeicher; | |
chart = createChart(historicals, chartsColorSpeicher); | |
break; | |
case 5: | |
// Speicherleistung | |
historicalsLaden = getCalc(data, "Laden"); | |
historicalsEntladen = getCalc(data, "Entladen"); | |
historicals = []; | |
for(let i=0; i<12; i++){ | |
historicals[i] = historicalsLaden[i] - historicalsEntladen[i]; | |
} | |
wert = (await getData("speicherleistung")*1); // Entladen ist negativ | |
value = wert.toString() + "W"; | |
valueColor = defineColor(-50, 50, wert) | |
name = "Speicher"; | |
footnote = defineFootnote(50, -50, "Laden", "Entladen", "Netzregelung", wert); | |
borderColor = borderColorSpeicher; | |
chart = createChart(historicals, chartsColorSpeicher); | |
break; | |
case 6: | |
// Timestamp | |
value = timestamp; | |
valueColor = ""; | |
name = "Timestamp"; | |
footnote = " "; | |
borderColor = borderColorTimestamp; | |
chart = 0; | |
break; | |
case 7: | |
// LP1 SoC | |
historicals = getCalc(data, "SoC1"); | |
wert = (await getData("soc")*1); // SoC von LP1; | |
value = wert.toString() + "%" | |
valueColor = defineColor(30, 70, wert); | |
name = "LP1 SoC"; | |
footnote = " "; | |
borderColor = borderColorLP1; | |
chart = createChart(historicals, chartsColorLP1); | |
break; | |
case 8: | |
// LP2 SoC | |
historicals = getCalc(data, "SoC2"); | |
wert = (await getData("soc1")*1); // SoC von LP2; | |
value = wert.toString() + "%"; | |
valueColor = defineColor(30, 70, wert); | |
name = "LP2 SoC"; | |
footnote = " "; | |
borderColor = borderColorLP2; | |
chart = createChart(historicals, chartsColorLP2); | |
break; | |
case 9: | |
// LP1 Leistung | |
historicals = getCalc(data, "LP1"); | |
wert = LP1leistung; // Leistung von LP1; | |
value = wert.toString() + "W"; | |
valueColor = ""; | |
name = "LP1 Leist."; | |
footnote = " "; | |
borderColor = borderColorLP1; | |
chart = createChart(historicals, chartsColorLP1); | |
break; | |
case 10: | |
// LP2 Leistung | |
historicals = getCalc(data, "LP2"); | |
wert = LP2leistung; // Leistung von LP2; | |
value = wert.toString() + "W"; | |
valueColor = ""; | |
name = "LP2 Leist."; | |
footnote = " "; | |
borderColor = borderColorLP2; | |
chart = createChart(historicals, chartsColorLP2); | |
break; | |
case 11: | |
// LP1 und LP2 Leistung (summiert) | |
historicalsLP1 = getCalc(data, "LP1"); | |
historicalsLP2 = getCalc(data, "LP2"); | |
historicals = []; | |
for(let i=0; i<12; i++){ | |
historicals[i] = historicalsLP1[i] + historicalsLP2[i]; | |
} | |
wert = LP1leistung + LP2leistung; // Leistung von LP1 und LP2; | |
value = wert.toString() + "W"; | |
valueColor = ""; | |
name = "LP1+2 Leist."; | |
footnote = " "; | |
borderColor = borderColorBothLP; | |
chart = createChart(historicals, chartsColorBothLP); | |
break; | |
case 15: | |
// LP1 letzte Lademenge | |
wert = (await getData("aktgeladen")*1); | |
value = wert.toString() + "kWh"; | |
valueColor = ""; | |
name = "LP1 zuletzt"; | |
footnote = " "; | |
borderColor = borderColorLP1; | |
chart = 0; | |
break; | |
case 16: | |
// LP2 letzte Lademenge | |
wert = (await getData("aktgeladens1")*1); | |
value = wert.toString() + "kWh"; | |
valueColor = ""; | |
name = "LP2 zuletzt"; | |
footnote = " "; | |
borderColor = borderColorLP2; | |
chart = 0; | |
break; | |
case 17: | |
// LP1 und LP2 letzte Lademenge (summiert) | |
wert = (await getData("aktgeladen")*1) + (await getData("aktgeladens1")*1); | |
value = wert.toString() + "kWh"; | |
valueColor = ""; | |
name = "LP1+2 zuletzt"; | |
footnote = " "; | |
borderColor = borderColorBothLP; | |
chart = 0; | |
break; | |
case 18: | |
// LP1 Monatslademenge | |
wert = (Number(dataMonthly[dataMonthly.length-2]["LP1"]) - Number(dataMonthly[0]["LP1"]))/1000; | |
value = wert.toString() + "kWh"; | |
valueColor = ""; | |
name = "LP1 Monat"; | |
footnote = " "; | |
borderColor = borderColorLP1; | |
chart = 0; | |
break; | |
case 19: | |
// LP2 Monatslademenge | |
wert = (Number(dataMonthly[dataMonthly.length-2]["LP2"]) - Number(dataMonthly[0]["LP2"]))/1000; | |
value = wert.toString() + "kWh"; | |
valueColor = ""; | |
name = "LP2 Monat"; | |
footnote = " "; | |
borderColor = borderColorLP2; | |
chart = 0; | |
break; | |
case 20: | |
// LP1 und LP2 Monatslademenge (summiert) | |
wert = ((Number(dataMonthly[dataMonthly.length-2]["LP1"]) - Number(dataMonthly[0]["LP1"])) + (Number(dataMonthly[dataMonthly.length-2]["LP2"]) - Number(dataMonthly[0]["LP2"])))/1000; | |
value = wert.toString() + "kWh"; | |
valueColor = ""; | |
name = "LP1+2 Monat"; | |
footnote = " "; | |
borderColor = borderColorBothLP; | |
chart = 0; | |
break; | |
case 21: | |
// Awattar-Strompreis | |
wert = (await getData("etproviderprice")*1); // aktueller Strompreis | |
value = wert.toString() + "ct"; | |
valueColor = defineColorNegative(1, 5, wert); | |
name = "Strompreis"; | |
// Zeitstempel für Footnote erzeugen | |
let hoursDate = new Date(); | |
let hoursDate2 = new Date(); | |
hoursDate2.setHours(hoursDate2.getHours() + 1) | |
let hoursDF = new DateFormatter(); | |
hoursDF.dateFormat = "HH"; | |
let hours = (hoursDF.string(hoursDate)); | |
let hours2 = (hoursDF.string(hoursDate2)); | |
footnote = hours + " - " + hours2 + " Uhr"; | |
borderColor = borderColorAwattar; | |
// Holen und Aufbereiten der Awattar-Graphdaten | |
awattarRaw = await getData("etprovidergraphlist"); | |
let lines = awattarRaw.split("\n"); | |
let historicalsAwattar = []; | |
for(let i = 0; i < 10; i++){ | |
let seperated = lines[i].split(","); | |
historicalsAwattar[i] = Number(seperated[1]); | |
} | |
chart = createChart(historicalsAwattar, chartsColorAwattar); | |
break; | |
case 22: | |
// Lademodus | |
wert = (await getData("lademodus")*1); // Lademodus | |
value = wert.toString(); | |
valueColor = LademodusColor(wert); | |
name = "Lademodus"; | |
footnote = LademodusText(wert); | |
borderColor = borderColorModus; | |
chart = 0; | |
break; | |
// Smarthome-Geräte | |
case 23: | |
wert = (await getData("verbraucher1_watt")*1); | |
name = "Verbr. 1"; | |
footnote = (await getData("verbraucher1_name")).trim(); | |
chart = 0; | |
break; | |
case 24: | |
wert = (await getData("verbraucher2_watt")*1); | |
name = "Verbr. 2"; | |
footnote = (await getData("verbraucher2_name")).trim(); | |
chart = 0; | |
break; | |
case 25: | |
historicals = getCalc(data, "Smarthome1"); | |
wert = (await getData("smarthome_device_1wh0")*1); | |
name = "Gerät 1"; | |
footnote = " "; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
case 26: | |
historicals = getCalc(data, "Smarthome2"); | |
wert = (await getData("smarthome_device_2wh0")*1); | |
name = "Gerät 2"; | |
footnote = " "; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
case 27: | |
historicals = getCalc(data, "Smarthome3"); | |
wert = (await getData("smarthome_device_3wh0")*1); | |
name = "Gerät 3"; | |
footnote = " "; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
case 28: | |
historicals = getCalc(data, "Smarthome4"); | |
wert = (await getData("smarthome_device_4wh0")*1); | |
name = "Gerät 4"; | |
footnote = " "; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
case 29: | |
historicals = getCalc(data, "Smarthome5"); | |
wert = (await getData("smarthome_device_5wh0")*1); | |
name = "Gerät 5"; | |
footnote = " "; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
case 30: | |
historicals = getCalc(data, "Smarthome6"); | |
wert = (await getData("smarthome_device_6wh0")*1); | |
name = "Gerät 6"; | |
footnote = " "; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
case 31: | |
historicals = getCalc(data, "Smarthome7"); | |
wert = (await getData("smarthome_device_7wh0")*1); | |
name = "Gerät 7"; | |
footnote = " "; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
case 32: | |
historicals = getCalc(data, "Smarthome8"); | |
wert = (await getData("smarthome_device_8wh0")*1); | |
name = "Gerät 8"; | |
footnote = " "; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
case 33: | |
historicals = getCalc(data, "Smarthome9"); | |
wert = (await getData("smarthome_device_9wh0")*1); | |
name = "Gerät 9"; | |
footnote = " "; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
// Leistung Smarthome-Verbraucher 1 + 2 | |
case 34: | |
wert = (await getData("verbraucher1_watt")*1) + (await getData("verbraucher2_watt")*1); | |
name = "Verbraucher"; | |
footnote = "1 + 2"; | |
chart = 0; | |
break; | |
// Leistung Smarthome 2.0 Geräte 1 - 9 | |
case 35: | |
wert = 0; | |
for(let i=1; i<10; i++){ | |
let newWert = (await getData("smarthome_device_" + i.toString() + "wh0")*1); | |
let newHistoricals = getCalc(data, "Smarthome" + i.toString()); | |
for(let i=0; i<12; i++){ | |
historicals[i] += newHistoricals[i]; | |
} | |
if(newWert){ | |
wert += newWert; | |
} | |
} | |
name = "Geräte"; | |
footnote = "1 - 9"; | |
chart = createChart(historicals, chartsColorSmarthome); | |
break; | |
// Leistung Smarthome-Verbraucher 1 + 2 und Smarthome 2.0 Geräte 1 - 9 | |
case 36: | |
wert = (await getData("verbraucher1_watt")*1) + (await getData("verbraucher2_watt")*1); | |
for(let i=1; i<10; i++){ | |
let newWert = (await getData("smarthome_device_" + i.toString() + "wh0")*1); | |
if(newWert){ | |
wert += newWert; | |
} | |
} | |
name = "Smarthome"; | |
footnote = "Verbr. + Geräte"; | |
chart = 0; | |
break; | |
// Zuordnung nicht erfolgreich | |
default: | |
error = 4; | |
break; | |
} | |
//Zusatzinformationen für alle Smarthome-Geräte | |
if(panel >= 23 && panel <= 36){ | |
value = wert.toString() + "W"; | |
valueColor = defineColor(1, 11, wert); | |
borderColor = borderColorSmarthome; | |
} | |
if((panel == 9 || panel == 10 || panel == 11) && lademodusFootnote == 1){ | |
footnote = LademodusText(await getData("lademodus")*1); | |
} | |
if(error == 0){ | |
addDataView(widget, value, valueColor, name, footnote, borderColor, chart, border); | |
} | |
} | |
function LademodusText(modus){ | |
switch(modus){ | |
case 0: | |
footnote = "Sofortladen"; | |
break; | |
case 1: | |
footnote = "Min & PV"; | |
break; | |
case 2: | |
footnote = "Nur PV"; | |
break; | |
case 3: | |
footnote = "Stop"; | |
break; | |
case 4: | |
footnote = "Standby"; | |
break; | |
default: | |
footnote = " "; | |
break; | |
} | |
return(footnote); | |
} | |
function LademodusColor(modus){ | |
switch(modus){ | |
case 0: | |
color = "green"; | |
break; | |
case 1: | |
color = "yellow"; | |
break; | |
case 2: | |
color = "yellow"; | |
break; | |
case 3: | |
color = "red"; | |
break; | |
case 4: | |
color = "red"; | |
break; | |
default: | |
color = " "; | |
break; | |
} | |
return(color); | |
} | |
function setBackground(){ | |
// Hintergrundfarbe oder Verlauf festlegen | |
if(backgroundType == 0){ | |
widget.backgroundColor = new Color(backgroundColor); | |
} | |
else{ | |
const gradient = new LinearGradient() | |
gradient.locations = [0, 1] | |
gradient.colors = [ new Color(backgroundGradient1), new Color(backgroundGradient2) ] | |
widget.backgroundGradient = gradient | |
} | |
} | |
// Erstellt den Widget-Titel | |
async function setTitle(){ | |
if(titleAlignment == 1){ | |
vStack0.addSpacer(); | |
} | |
else{ | |
vStack0.addSpacer(10); | |
} | |
if(titleAlignment == 1 && error == 0 && widgetSize != 0){ | |
if(timestampTitle == 0){ | |
if(widgetTitle && logo){ | |
vStack0.addSpacer(36); | |
} | |
else{ | |
vStack0.addSpacer(32); | |
} | |
} | |
else{ | |
if(widgetTitle && logo){ | |
vStack0.addSpacer(70); | |
} | |
else{ | |
vStack0.addSpacer(66); | |
} | |
} | |
} | |
// Logo holen und darstellen | |
if(logo == 1 && error == 0){ | |
img = vStack0.addImage(await setLogo()); | |
img.imageSize = new Size(50,20); | |
} | |
// Überschrift des Widgets | |
if((imageAtError!= 1 || error == 0) && ((widgetSize == 0 && logo == 1)!= 1) && widgetTitle){ | |
if(logo == 1 && error == 0){ | |
vStack0.addSpacer(10); | |
} | |
let header = vStack0.addText(widgetTitle); | |
header.font = Font.mediumSystemFont(12); | |
header.textColor = new Color(textColor); | |
} | |
vStack0.addSpacer(); | |
// Timestamp in Titel | |
if(timestampTitle == 1 && error == 0 && widgetSize != 0){ | |
let ts = vStack0.addText(timestamp); | |
ts.font = Font.mediumSystemFont(12); | |
ts.textColor = new Color(textColor); | |
} | |
} | |
// Holt das Logo von oWB und bearbeitet dieses | |
async function setLogo(){ | |
let result; | |
try{ | |
let imgReq = new Request("http://" + oWBip + "/openWB/web/img/favicons/apple-icon-180x180.png"); | |
imgReq.timeoutInterval = timeOut; | |
let image = await imgReq.loadImage(); | |
dc = new DrawContext(); | |
dc.opaque = true; | |
dc.size = new Size(180,56); | |
dc.drawImageAtPoint(image, new Point(-0, -54)); | |
result = dc.getImage(); | |
} | |
catch(err){ | |
error = 1; | |
} | |
return(result); | |
} | |
// zentriert/linksbündigt/rechtbündigt Text | |
function AlignStack(stack, pos){ | |
if(contentAlignment == 1 || (contentAlignment == 0 && pos == 1)){ | |
stack.addSpacer(); | |
} | |
} | |
// legt ein neues Datenstack an | |
function addDataView(widget, data, color, name, foot, borderColor, img, border){ | |
let viewStack = widget.addStack(); | |
viewStack.layoutVertically(); | |
viewStack.cornerRadius = 5; | |
if(borderColor != 0){ | |
viewStack.size = new Size(97, 47); | |
viewStack.borderWidth = border; | |
viewStack.borderColor = new Color(borderColor, borderThickness); | |
viewStack.setPadding(5, 5, 5, 3); | |
} | |
let labelStack = viewStack.addStack(); | |
AlignStack(labelStack, 0); | |
let label = labelStack.addText(name); | |
label.font = Font.mediumSystemFont(12); | |
label.textColor = new Color(textColor); | |
AlignStack(labelStack, 1); | |
let footnoteStack = viewStack.addStack(); | |
AlignStack(footnoteStack, 0); | |
let footnote = footnoteStack.addText(foot); | |
footnote.font = Font.mediumSystemFont(8); | |
footnote.textColor = new Color(textColor); | |
AlignStack(footnoteStack, 1); | |
let valueStack = viewStack.addStack(); | |
AlignStack(valueStack, 0); | |
let value = valueStack.addText(data); | |
value.font = Font.mediumSystemFont(18); | |
value.textColor = colorForString(color); | |
AlignStack(valueStack, 1); | |
if(backgroundCharts == 1){ | |
if(img != 0){ | |
viewStack.backgroundImage = img; | |
} | |
} | |
} | |
// holt die aktuellen Daten | |
async function getData(file){ | |
let result = 0; | |
try { | |
let url = "http://" + oWBip + "/openWB/ramdisk/" + file; | |
let req = new Request(url); | |
req.timeoutInterval = timeOut; | |
result = await req.loadString(); | |
} | |
catch(err) { | |
result = 0; | |
error = 2; | |
} | |
return(result); | |
} | |
// Legt Farbe fest | |
function defineColor(TreshYellow, TreshGreen, value){ | |
if (value >= TreshGreen){ | |
return("green"); | |
} | |
else if (value >= TreshYellow){ | |
return("yellow"); | |
} | |
else{ | |
return("red"); | |
} | |
} | |
// Legt Farbe fest | |
function defineColorNegative(TreshYellow, TreshRed, value){ | |
if (value >= TreshRed){ | |
return("red"); | |
} | |
else if (value >= TreshYellow){ | |
return("yellow"); | |
} | |
else{ | |
return("green"); | |
} | |
} | |
// Legt Footnote fest | |
function defineFootnote(groesserAls, kleinerAls, groesserText, kleinerText, sonstText, value){ | |
if (value > groesserAls){ | |
return(groesserText); | |
} | |
else if (value < kleinerAls){ | |
return(kleinerText); | |
} | |
else{ | |
return(sonstText); | |
} | |
} | |
//Gibt das entsprechende Color-Objekt zurück | |
function colorForString(colorString){ | |
if (colorString == "red") { | |
return Color.red(); | |
} | |
if (colorString == "yellow") { | |
return Color.yellow(); | |
} | |
if (colorString == "green") { | |
return Color.green(); | |
} | |
return (new Color(textColor)); | |
} | |
// Erstellt Graph und liefert Bild davon zurück | |
function createChart(data, color){ | |
if(backgroundCharts == 1){ | |
let chart = new LineChart(97, 47, data).configure((ctx, path) => { | |
ctx.opaque = false; | |
ctx.setFillColor(new Color(color, chartsThickness)); | |
ctx.addPath(path); | |
ctx.fillPath(path); | |
}).getImage(); | |
return(chart); | |
} | |
} | |
// Holt CSV-Daten von oWB | |
async function csvRequest(type, date){ | |
let txt; | |
let df = new DateFormatter(); | |
switch(type){ | |
case "daily": | |
df.dateFormat = "YYYYMMdd"; | |
break; | |
case "monthly": | |
df.dateFormat = "YYYYMM"; | |
break; | |
} | |
let fdate = (df.string(date)); | |
let url = ("http://" + oWBip + "/openWB/web/logging/data/" + type + "/" + fdate + ".csv"); | |
let req = new Request(url); | |
req.timeoutInterval = timeOut; | |
try{ | |
txt = await req.loadString(); | |
} | |
catch(err){ | |
error = 3; | |
} | |
return(txt); | |
} | |
// Zusammensetzen der CSV-Daten des aktuellen und des vorherigen Tages | |
async function getCSV(){ | |
let date = new Date(); | |
let today = await csvRequest("daily", date); | |
date.setDate(date.getDate()-1) | |
let yesterday = await csvRequest("daily", date); | |
let result = yesterday + today; | |
return(result); | |
} | |
// Kalkuliert die Differenz zwischen den Werten der letzten Stunde | |
function getCalc(data, key){ | |
let result = []; | |
if(backgroundCharts == 1){ | |
let resultVar = 11; | |
for(let i=0; i<12; i++){ | |
if(key != "SoC" && key != "SoC1" && key != "SoC2"){ | |
result[resultVar] = Number((data[data.length-2-i][key]) - Number(data[data.length-3-i][key])); | |
} | |
else{ | |
result[resultVar] = Number(data[data.length-2-i][key]); | |
} | |
resultVar --; | |
} | |
} | |
return(result); | |
} | |
// Konvertiert CSV zu einem Objekt | |
function csvJSON(csv, headers){ | |
let lines = csv.split("\n"); | |
let result = []; | |
for(let i=0;i<lines.length;i++){ | |
let obj = {}; | |
let currentline=lines[i].split(","); | |
for(let j=0;j<headers.length;j++){ | |
obj[headers[j]] = currentline[j]; | |
} | |
result.push(obj); | |
} | |
return(result); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Variables used by Scriptable. | |
// These must be at the very top of the file. Do not edit. | |
// icon-color: yellow; icon-glyph: solar-panel; | |
//-------------------------------------------------------------------------------------- | |
// openWB-Widget, Version 6 | |
//-------------------------------------------------------------------------------------- | |
let config = new Object(); | |
//-------------------------------------------------------------------------------------- | |
// INFORMATION | |
//-------------------------------------------------------------------------------------- | |
// Die Konfiguration kann für mehrere Instanzen des Widgets erfolgen. | |
// Die konfigurierten Instanzen können über de Parameter in den Widget-Einstellungen ausgewählt werden. | |
// Wird nur eine Instanz verwendet, muss der Parameter nicht festgelegt werden. | |
// Beispiel ein Widget: config.panel1 = [1]; ODER config.panel1 = 1; | |
// Beispiel mehrere Widgets: config.panel1 = [1,3,2]; --> Konfiguration von drei Widgets. | |
// In diesem Beispiel gilt 1 für Widgets mit dem Parameter 0, 3 für Widgets mit dem Parameter 1 | |
// und 2 für Widgets mit dem Parameter 2 | |
// Wenn ein Konfigurationsparameter für alle Instanzen gleich ist (z.B. oWBip), | |
// muss dieser nur einmal angegeben werden (wie wenn nur eine Instanz verwendet wird) | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Folgende Werte stehen ZUR VERFÜGUNG (LEGENDE): | |
// Die nachfolgende Nummerierung wird in der anschließenden Konfiguration mehrmals benötigt. | |
//-------------------------------------------------------------------------------------- | |
// 0: Platzhalter (leeres Feld ohne Inhalt und Rahmen) | |
// 1: PV | |
// 2: Netz | |
// 3: Haus | |
// 4: Speicher SoC | |
// 5: Speicherleistung | |
// 6: Timestamp | |
// 7: LP1 SoC | |
// 8: LP2 SoC | |
// 9: LP1 Leistung | |
// 10: LP2 Leistung | |
// 11: LP1 und LP2 Leistung (summiert) | |
// 12: LP1 oder Speicherleistung (LP1 wenn geladen wird, sonst Speicher) | |
// 13: LP2 oder Speicherleistung (LP2 wenn geladen wird, sonst Speicher) | |
// 14: LP1 und LP2 oder Speicherleistung (LP1 und LP2 (summiert) wenn geladen wird, sonst Speicher) | |
// 15: LP1 letzte Lademenge | |
// 16: LP2 letzte Lademenge | |
// 17: LP1 und LP2 letzte Lademenge (summiert) | |
// 18: LP1 Lademenge diesen Monat | |
// 19: LP2 Lademenge diesen Monat | |
// 20: LP1 und LP2 Lademenge diesen Monat (summiert) | |
// 21: Awattar-Strompreis | |
// 22: Lademodus | |
// 23: Leistung Smarthome-Verbraucher 1 | |
// 24: Leistung Smarthome-Verbraucher 2 | |
// 25: Leistung Smarthome 2.0 Gerät 1 | |
// 26: Leistung Smarthome 2.0 Gerät 2 | |
// 27: Leistung Smarthome 2.0 Gerät 3 | |
// 28: Leistung Smarthome 2.0 Gerät 4 | |
// 29: Leistung Smarthome 2.0 Gerät 5 | |
// 30: Leistung Smarthome 2.0 Gerät 6 | |
// 31: Leistung Smarthome 2.0 Gerät 7 | |
// 32: Leistung Smarthome 2.0 Gerät 8 | |
// 33: Leistung Smarthome 2.0 Gerät 9 | |
// 34: Leistung Smarthome-Verbraucher 1 + 2 | |
// 35: Leistung Smarthome 2.0 Geräte 1 - 9 | |
// 36: Leistung Smarthome-Verbraucher 1 + 2 und Smarthome 2.0 Geräte 1 - 9 | |
// 37: PV Erzeugung diesen Monat | |
// 38: Netzeinspeisung diesen Monat | |
// 39: Netzbezug diesen Monat | |
// 40: Hausverbrauch diesen Monat | |
// 41: Speicherladung diesen Monat | |
// 42: Speicherentladung diesen Monat | |
// 43: Eigenverbrauch diesen Monat (PV - Netzeinspeissung) | |
// 44: PV Erzeugung heute | |
// 45: Netzeinspeisung heute | |
// 46: Netzbezug heute | |
// 47: Hausverbrauch heute | |
// 48: Speicherladung heute | |
// 49: Speicherentladung heute | |
// 50: Eigenverbrauch heute (PV - Netzeinspeissung) | |
// 51: Autarkiequote diesen Monat Haus + LP | |
// 52: Autarkiequote diese Monat Haus (ohne LP, mit Annahme 100% PV geladen) | |
// 53: Eigenverbrauchsquote diesen Monat | |
// 54: Autarkiequote heute Haus + LP | |
// 55: Autarkiequote heute Haus (ohne LP, mit Annahme 100% PV geladen) | |
// 56: Eigenverbrauchsquote heute | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Folgende Werte MÜSSEN angepasst werden: | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// IP-Adresse der openWB (von hier holt das Widget die benötigten Daten): | |
config.oWBip = ["192.168.1.1"]; | |
//-------------------------------------------------------------------------------------- | |
// URL, auf die beim Tippen auf das Widget weitergeleitet werden soll (z.B. Home-Website von oWB): | |
config.widgetURL = ["http://192.168.1.1/openWB/web/index.php"]; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Folgende Werte KÖNNEN angepasst werden: | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Im folgenden kann die Belegung der einzelnen Felder frei festgelegt werden: | |
// Die Nummerierung richtet sich an die Legende am Anfang der Konfiguration. | |
config.panel1 = [1]; | |
config.panel2 = [2]; | |
config.panel3 = [3]; | |
config.panel4 = [4]; | |
config.panel5 = [5]; | |
config.panel6 = [6]; | |
config.panel7 = [0]; | |
config.panel8 = [0]; | |
config.panel9 = [0]; | |
config.panel10 = [0]; | |
config.panel11 = [0]; | |
config.panel12 = [0]; | |
config.panel13 = [0]; | |
config.panel14 = [0]; | |
config.panel15 = [0]; | |
//-------------------------------------------------------------------------------------- | |
// Großes Widget: | |
// /////////////////////// | |
// // 1 // 2 // 3 // | |
// /////////////////////// | |
// // 4 // 5 // 6 // | |
// /////////////////////// | |
// // 7 // 8 // 9 // | |
// /////////////////////// | |
// // 10 // 11 // 12 // | |
// /////////////////////// | |
// // 13 // 14 // 15 // | |
// /////////////////////// | |
//-------------------------------------------------------------------------------------- | |
// Mittleres Widget: | |
// /////////////////////// | |
// // 1 // 2 // 3 // | |
// /////////////////////// | |
// // 4 // 5 // 6 // | |
// /////////////////////// | |
//-------------------------------------------------------------------------------------- | |
// Kleines Widget: | |
// ///////// | |
// // 1 // | |
// ///////// | |
// // 2 // | |
// ///////// | |
//-------------------------------------------------------------------------------------- | |
// Damit die Werte richtig skaliert werden muss die Größe des Widgets festgelegt werden: | |
// 0: klein, 1: mittel, 2: groß | |
config.widgetSize = [1]; | |
//-------------------------------------------------------------------------------------- | |
// Soll beim Anzeigen der Ladeleistung eines LPs (auch im Wechel mit anderen Werten) der Lademodus | |
// als Footnote angezeigt werden? 0: Nein, 1: Ja | |
config.lademodusFootnote = [1]; | |
//-------------------------------------------------------------------------------------- | |
// Sollen kleine Graphen als Hintergrund angezeigt werden? 0: Nein, 1: Ja | |
config.backgroundCharts = [1]; | |
//-------------------------------------------------------------------------------------- | |
// Sollen die einzelnen Werte und deren Graphen einen Rahmen haben? | |
// 0: Nein, 1-4: Breite der Rahmen | |
config.panelBorder = [3]; | |
//-------------------------------------------------------------------------------------- | |
// Wie sollen Namen, Footnotes und die einzelnen Werte in ihren Bereichen angeordnet sein? | |
// 0: Linksbündig, 1: zentriert | |
config.contentAlignment = [1]; | |
//-------------------------------------------------------------------------------------- | |
// Wie sollen Titel und Logo angeordnet sein? | |
// 0: Linksbündig, 1: zentriert | |
config.titleAlignment = [1]; | |
//-------------------------------------------------------------------------------------- | |
// Soll im Widget-Titel das openWB-Logo angezeigt werden? 0: Nein, 1: Ja | |
// Beim kleinen Widget ersetzt das Logo den Titel, anstatt diesen daneben anzuzeigen | |
config.logo = [1]; | |
//-------------------------------------------------------------------------------------- | |
// Titel des Widgets (steht hinter Logo wenn dieses angezeigt wird). Kein Titel: "" | |
config.widgetTitle = [""]; | |
//-------------------------------------------------------------------------------------- | |
// Soll der Timestamp im Titel angeigt werden? 0: Nein, 1: Ja | |
// (Alternativ kann auch ein Feld mit dem Timestamp belegt werden) | |
// Geht nur dann, wenn entweder das mittlere oder das große Widget verwendet wird | |
config.timestampTitle = [1]; | |
//-------------------------------------------------------------------------------------- | |
// Die maximale Zeit in Sekunden, die für das Holen der Daten verwendet wird. | |
// Bei Fehlern kann dieser Wert erhöht werden: | |
config.timeOut = [5.0]; | |
//-------------------------------------------------------------------------------------- | |
// Soll bei einem globalen Fehler (z.B. Zugriff auf das Widget aus dem Mobilfunk) statt dem Fehler | |
// ein Bild angezeigt werden? 0: Nein, 1: Ja | |
config.imageAtError = [0]; | |
//-------------------------------------------------------------------------------------- | |
// Name des unter File-Bookmarks hinterlegten Bildes, das bei einem Error angezeigt werden soll: | |
config.errorImageName = ["IMG_4832.jpg"]; | |
//-------------------------------------------------------------------------------------- | |
// Was soll bei einem Fehler in einem Feld angezeigt werden? 0: Fehler, 1: Platzhalter | |
config.placeholderAtError = [1]; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Im folgenden können die STATISCHEN FARBEN des Widgets angepasst werden: | |
// z.B. weis: "FFFFFF", schwarz: "000000"; Eine gute Website dafür: https://htmlcolorcodes.com/ | |
//-------------------------------------------------------------------------------------- | |
// Standart-Farbe, wird verwendet für Überschrift, Footnotes usw. | |
// oder wenn benutzerdefinierte Farben falsch oder nicht konfiguriert sind | |
config.defaultColor = ["FFFFFF"]; | |
//-------------------------------------------------------------------------------------- | |
// Art des Widget-Hintergrunds, 0: einfarbig, 1: Farbverlauf | |
config.backgroundType = [1]; | |
//-------------------------------------------------------------------------------------- | |
// Farbe bei einfarbigem Hintergrund: | |
config.backgroundColor = ["000000"]; | |
//-------------------------------------------------------------------------------------- | |
// Verlauffarben bei Farbverlauf als Hintergrund: | |
config.backgroundGradient1 = ["000000"]; | |
config.backgroundGradient2 = ["202020"]; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Im folgenden können die DYNAMISCHEN FARBEN des Widgets angepasst werden: | |
// Die Nummerierung richtet sich an die Legende am Anfang der Konfiguration. | |
// Folgendes Schema wird verwendet: [{"Treshold1": "Farbe1", "Treshold2": "Farbe2", ...}] | |
// Es wird immer die Farbe des höchsten überschrittenen Tresholds verwendet | |
// Mögliche Werte für die Tresholds sind Zahlen (z.B. "100") oder | |
// Berechnungen mit variablen Daten anderer Felder (z.B. "100-$5") oder | |
// "else", welches verwendet wird wenn keine andere Regel zutrifft (z.B. unter kleinstem Treshold) | |
// Mögliche Werte für die Farben sind Farbcodes (z.B. "000000"), | |
// "red", "yellow", "green" und "default" (oben festgelegte Standard-Schriftfarbe) | |
// Bei Oder-Feldern (12/13/14) werden beide Möglichkeiten über die | |
// Ursprungsfelder festgelegt (5/9/10/11) | |
// Gesamtbeispiel: Wenn PV größer als Hausverbrauch ($3) + Puffer (100) wird PV-Wert gelb, | |
// wenn PV größer als Hausverbrauch ($3) + Ladeleistung ($11) + Puffer (101) wird PV-Wert grün | |
// Puffer bei grün 101, damit dieser Treshold höher als Gelb wenn Ladeleistung = 0 | |
// config.textColor1 = [{"else": "red", "$3+100": "yellow", "$3+$11+101": "green"}]; | |
//-------------------------------------------------------------------------------------- | |
// Farben der WERTE: | |
// Ausgenommen ist der Timestamp (6), hier kann nur eine Farbe festgelegt werden. | |
config.textColor1 = [{"else": "red", "100": "yellow", "5000": "green"}]; | |
config.textColor2 = [{"else": "green", "-100": "yellow", "100": "red"}]; | |
config.textColor3 = [{"else": "green", "400": "yellow", "1000": "red"}]; | |
config.textColor4 = [{"else": "red", "30": "yellow", "70": "green"}]; | |
config.textColor5 = [{"else": "red", "-50": "yellow", "50": "green"}]; | |
config.textColor6 = ["FFFFFF"]; //nur eine Farbe je Instanz! | |
config.textColor7 = [{"else": "red", "30": "yellow", "70": "green"}]; | |
config.textColor8 = [{"else": "red", "30": "yellow", "70": "green"}]; | |
config.textColor9 = [{"else": "red", "100": "green"}]; | |
config.textColor10 = [{"else": "red", "100": "green"}]; | |
config.textColor11 = [{"else": "red", "100": "green"}]; | |
config.textColor15 = [{"else": "green", "100": "yellow", "1000": "red"}]; | |
config.textColor16 = [{"else": "green", "100": "yellow", "1000": "red"}]; | |
config.textColor17 = [{"else": "green", "100": "yellow", "1000": "red"}]; | |
config.textColor18 = [{"else": "green", "100": "yellow", "1000": "red"}]; | |
config.textColor19 = [{"else": "green", "100": "yellow", "1000": "red"}]; | |
config.textColor20 = [{"else": "green", "100": "yellow", "1000": "red"}]; | |
config.textColor21 = [{"else": "green", "1": "yellow", "5": "red"}]; | |
config.textColor22 = [{"else": "green", "1": "yellow", "3": "red"}]; | |
config.textColor23 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor24 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor25 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor26 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor27 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor28 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor29 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor30 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor31 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor32 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor33 = [{"else": "green", "500": "yellow", "1000": "red"}]; | |
config.textColor34 = [{"else": "green", "1000": "yellow", "2000": "red"}]; | |
config.textColor35 = [{"else": "green", "4500": "yellow", "9000": "red"}]; | |
config.textColor36 = [{"else": "green", "5500": "yellow", "11000": "red"}]; | |
config.textColor37 = [{"else": "red", "1000": "yellow", "2000": "green"}]; | |
config.textColor38 = [{"else": "red", "100": "yellow", "1000": "green"}]; | |
config.textColor39 = [{"else": "green", "10": "yellow", "100": "red"}]; | |
config.textColor40 = [{"else": "green", "200": "yellow", "500": "red"}]; | |
config.textColor41 = [{"else": "red", "100": "yellow", "200": "green"}]; | |
config.textColor42 = [{"else": "red", "100": "yellow", "200": "green"}]; | |
config.textColor43 = [{"else": "red", "100": "yellow", "500": "green"}]; | |
config.textColor44 = [{"else": "red", "5": "yellow", "15": "green"}]; | |
config.textColor45 = [{"else": "red", "5": "yellow", "15": "green"}]; | |
config.textColor46 = [{"else": "green", "1": "yellow", "5": "red"}]; | |
config.textColor47 = [{"else": "green", "1": "yellow", "5": "red"}]; | |
config.textColor48 = [{"else": "red", "1": "yellow", "3": "green"}]; | |
config.textColor49 = [{"else": "red", "1": "yellow", "3": "green"}]; | |
config.textColor50 = [{"else": "red", "1": "yellow", "3": "green"}]; | |
config.textColor51 = [{"else": "red", "50": "yellow", "80": "green"}]; | |
config.textColor52 = [{"else": "red", "50": "yellow", "80": "green"}]; | |
config.textColor53 = [{"else": "red", "50": "yellow", "80": "green"}]; | |
config.textColor54 = [{"else": "red", "50": "yellow", "80": "green"}]; | |
config.textColor55 = [{"else": "red", "50": "yellow", "80": "green"}]; | |
config.textColor56 = [{"else": "red", "50": "yellow", "80": "green"}]; | |
//-------------------------------------------------------------------------------------- | |
// Farben der HINTERGRUNDGRAPHEN: | |
// Hintergrundgraphen werden nicht unterstützt auf den Feldern 6, 15, 16, 17, 18, 19, 20, 22, 23, 24, 34, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56 | |
// Auch hier kann wieder mit Tresholds gearbeitet werden | |
config.chartsColor1 = [{"else": "F7DC6F"}]; | |
config.chartsColor2 = [{"else": "D7DBDD"}]; | |
config.chartsColor3 = [{"else": "7DCEA0"}]; | |
config.chartsColor4 = [{"else": "85C1E9"}]; | |
config.chartsColor5 = [{"else": "85C1E9"}]; | |
config.chartsColor7 = [{"else": "FFFFFF"}]; | |
config.chartsColor8 = [{"else": "FFFFFF"}]; | |
config.chartsColor9 = [{"else": "FFFFFF"}]; | |
config.chartsColor10 = [{"else": "FFFFFF"}]; | |
config.chartsColor11 = [{"else": "FFFFFF"}]; | |
config.chartsColor21 = [{"else": "FFFFFF"}]; | |
config.chartsColor25 = [{"else": "FFFFFF"}]; | |
config.chartsColor26 = [{"else": "FFFFFF"}]; | |
config.chartsColor27 = [{"else": "FFFFFF"}]; | |
config.chartsColor28 = [{"else": "FFFFFF"}]; | |
config.chartsColor29 = [{"else": "FFFFFF"}]; | |
config.chartsColor30 = [{"else": "FFFFFF"}]; | |
config.chartsColor31 = [{"else": "FFFFFF"}]; | |
config.chartsColor32 = [{"else": "FFFFFF"}]; | |
config.chartsColor33 = [{"else": "FFFFFF"}]; | |
config.chartsColor35 = [{"else": "FFFFFF"}]; | |
// Wie blass oder stark sollen die Graphen sein? (Wert zwischen 0.1 und 1.0) | |
config.chartsOpacity = [0.4]; | |
//-------------------------------------------------------------------------------------- | |
// Farben der RAHMEN der einzelnen Werte: | |
// Auch hier kann mit Ausnahme des Timestamps (6) wieder mit Tresholds gearbeitet werden | |
config.borderColor1 = [{"else": "F7DC6F"}]; | |
config.borderColor2 = [{"else": "D7DBDD"}]; | |
config.borderColor3 = [{"else": "7DCEA0"}]; | |
config.borderColor4 = [{"else": "85C1E9"}]; | |
config.borderColor5 = [{"else": "85C1E9"}]; | |
config.borderColor6 = ["FFFFFF"]; //nur eine Farbe je Instanz! | |
config.borderColor7 = [{"else": "FFFFFF"}]; | |
config.borderColor8 = [{"else": "FFFFFF"}]; | |
config.borderColor9 = [{"else": "FFFFFF"}]; | |
config.borderColor10 = [{"else": "FFFFFF"}]; | |
config.borderColor11 = [{"else": "FFFFFF"}]; | |
config.borderColor15 = [{"else": "FFFFFF"}]; | |
config.borderColor16 = [{"else": "FFFFFF"}]; | |
config.borderColor17 = [{"else": "FFFFFF"}]; | |
config.borderColor18 = [{"else": "FFFFFF"}]; | |
config.borderColor19 = [{"else": "FFFFFF"}]; | |
config.borderColor20 = [{"else": "FFFFFF"}]; | |
config.borderColor21 = [{"else": "FFFFFF"}]; | |
config.borderColor22 = [{"else": "FFFFFF"}]; | |
config.borderColor23 = [{"else": "FFFFFF"}]; | |
config.borderColor24 = [{"else": "FFFFFF"}]; | |
config.borderColor25 = [{"else": "FFFFFF"}]; | |
config.borderColor26 = [{"else": "FFFFFF"}]; | |
config.borderColor27 = [{"else": "FFFFFF"}]; | |
config.borderColor28 = [{"else": "FFFFFF"}]; | |
config.borderColor29 = [{"else": "FFFFFF"}]; | |
config.borderColor30 = [{"else": "FFFFFF"}]; | |
config.borderColor31 = [{"else": "FFFFFF"}]; | |
config.borderColor32 = [{"else": "FFFFFF"}]; | |
config.borderColor33 = [{"else": "FFFFFF"}]; | |
config.borderColor34 = [{"else": "FFFFFF"}]; | |
config.borderColor35 = [{"else": "FFFFFF"}]; | |
config.borderColor36 = [{"else": "FFFFFF"}]; | |
config.borderColor37 = [{"else": "FFFFFF"}]; | |
config.borderColor38 = [{"else": "FFFFFF"}]; | |
config.borderColor39 = [{"else": "FFFFFF"}]; | |
config.borderColor40 = [{"else": "FFFFFF"}]; | |
config.borderColor41 = [{"else": "FFFFFF"}]; | |
config.borderColor42 = [{"else": "FFFFFF"}]; | |
config.borderColor43 = [{"else": "FFFFFF"}]; | |
config.borderColor44 = [{"else": "FFFFFF"}]; | |
config.borderColor45 = [{"else": "FFFFFF"}]; | |
config.borderColor46 = [{"else": "FFFFFF"}]; | |
config.borderColor47 = [{"else": "FFFFFF"}]; | |
config.borderColor48 = [{"else": "FFFFFF"}]; | |
config.borderColor49 = [{"else": "FFFFFF"}]; | |
config.borderColor50 = [{"else": "FFFFFF"}]; | |
config.borderColor51 = [{"else": "FFFFFF"}]; | |
config.borderColor52 = [{"else": "FFFFFF"}]; | |
config.borderColor53 = [{"else": "FFFFFF"}]; | |
config.borderColor54 = [{"else": "FFFFFF"}]; | |
config.borderColor55 = [{"else": "FFFFFF"}]; | |
config.borderColor56 = [{"else": "FFFFFF"}]; | |
// Wie blass oder stark sollen die Rahmen sein? (Wert zwischen 0.1 und 1.0) | |
config.borderOpacity = [0.6]; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Graph von https://kevinkub.de/ | |
class LineChart { | |
constructor(width, height, values) { | |
this.ctx = new DrawContext(); | |
this.ctx.size = new Size(width, height); | |
this.values = values; | |
} | |
_calculatePath() { | |
let maxValue = Math.max(...this.values); | |
let minValue = Math.min(...this.values); | |
let difference = maxValue - minValue; | |
let count = this.values.length; | |
let step = this.ctx.size.width / (count - 1); | |
let points = this.values.map((current, index, all) => { | |
let x = step*index; | |
let y = this.ctx.size.height - (current - minValue) / difference * this.ctx.size.height; | |
return new Point(x, y); | |
}); | |
return this._getSmoothPath(points); | |
} | |
_getSmoothPath(points) { | |
let path = new Path(); | |
path.move(new Point(0, this.ctx.size.height)); | |
path.addLine(points[0]); | |
for(let i = 0; i < points.length-1; i++) { | |
let xAvg = (points[i].x + points[i+1].x) / 2; | |
let yAvg = (points[i].y + points[i+1].y) / 2; | |
let avg = new Point(xAvg, yAvg); | |
let cp1 = new Point((xAvg + points[i].x) / 2, points[i].y); | |
let next = new Point(points[i+1].x, points[i+1].y); | |
let cp2 = new Point((xAvg + points[i+1].x) / 2, points[i+1].y); | |
path.addQuadCurve(avg, cp1); | |
path.addQuadCurve(next, cp2); | |
} | |
path.addLine(new Point(this.ctx.size.width, this.ctx.size.height)); | |
path.closeSubpath(); | |
return path; | |
} | |
configure(fn) { | |
let path = this._calculatePath(); | |
if(fn) { | |
fn(this.ctx, path); | |
} else { | |
this.ctx.addPath(path); | |
this.ctx.fillPath(path); | |
} | |
return this.ctx; | |
} | |
} | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Verarbeite Config-Daten | |
let instance; | |
parseConfig(); | |
// Allgemeine Widget-Einstellungen | |
let widget; | |
let vStackV; | |
let vStack0; | |
let vStack1; | |
let vStack2; | |
let vStack3; | |
let vStack4; | |
createBasicWidget(); | |
// aktuellen Timestamp erstellen und formatieren | |
let date = new Date(); | |
let df = new DateFormatter(); | |
df.dateFormat = "HH:mm"; | |
let timestamp = (df.string(date)); | |
let globalData = {"csv": {}, "actuals": {}, "historicals": {}}; | |
try{ | |
// Überprüfen ob openWB erreichbar ist | |
let url = "http://" + config.oWBip[instance] + "/openWB/web/index.php)"; | |
let result = await stringRequest(url); | |
await setTitle(); | |
// Inhalt des Widgets | |
setBackground(); | |
vStack1.addSpacer(); | |
await setField(vStack1, config.panel1[instance]); | |
if(config.widgetSize[instance] != 0){ | |
vStack1.addSpacer(); | |
await setField(vStack1, config.panel2[instance]); | |
vStack1.addSpacer(); | |
await setField(vStack1, config.panel3[instance]); | |
} | |
vStack1.addSpacer(); | |
vStack2.addSpacer(); | |
if(config.widgetSize[instance] == 0){ | |
await setField(vStack2, config.panel2[instance]); | |
} | |
else{ | |
await setField(vStack2, config.panel4[instance]); | |
vStack2.addSpacer(); | |
await setField(vStack2, config.panel5[instance]); | |
vStack2.addSpacer(); | |
await setField(vStack2, config.panel6[instance]); | |
} | |
vStack2.addSpacer(); | |
if(config.widgetSize[instance] == 2){ | |
vStack3.addSpacer(); | |
await setField(vStack3, config.panel7[instance]); | |
vStack3.addSpacer(); | |
await setField(vStack3, config.panel8[instance]); | |
vStack3.addSpacer(); | |
await setField(vStack3, config.panel9[instance]); | |
vStack3.addSpacer(); | |
vStack4.addSpacer(); | |
await setField(vStack4, config.panel10[instance]); | |
vStack4.addSpacer(); | |
await setField(vStack4, config.panel11[instance]); | |
vStack4.addSpacer(); | |
await setField(vStack4, config.panel12[instance]); | |
vStack4.addSpacer(); | |
vStack5.addSpacer(); | |
await setField(vStack5, config.panel13[instance]); | |
vStack5.addSpacer(); | |
await setField(vStack5, config.panel14[instance]); | |
vStack5.addSpacer(); | |
await setField(vStack5, config.panel15[instance]); | |
vStack5.addSpacer(); | |
} | |
} | |
catch(err){ | |
console.log("globaler Fehler: "); | |
console.log(err); | |
createBasicWidget(); | |
if(config.imageAtError[instance] == 0){ | |
// Fehlermeldung anzeigen | |
addDataView(vStack1, "Ein Fehler ist aufgetreten!", "red", "Eingestellte openWB-IP: " + config.oWBip[instance], " ", 0); | |
addDataView(vStack2, timestamp + " Uhr", "", "Timestamp", " ", 0); | |
} | |
else{ | |
// Bild anzeigen | |
let fm = FileManager.local(); | |
let path = fm.bookmarkedPath(config.errorImageName[instance]); | |
let errorImage = fm.readImage(path); | |
widget.backgroundImage = errorImage; | |
} | |
} | |
Script.setWidget(widget); | |
Script.complete(); | |
// Größe der Widget-Vorschau | |
switch(config.widgetSize[instance]){ | |
case 0: widget.presentSmall(); break; | |
case 1: widget.presentMedium(); break; | |
case 2: widget.presentLarge(); break; | |
} | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Verarbeite Config-Daten | |
function parseConfig(){ | |
instance = Number(args.widgetParameter); | |
if(!instance){ | |
instance = 0; | |
} | |
for(let option in config){ | |
// Schreibe nicht-Array Werte an Position 0 des Arrays | |
if(!Array.isArray(config[option])){ | |
let oldValue = config[option]; | |
config[option] = new Array(); | |
config[option][0] = oldValue; | |
} | |
// Schreibe Position 0 des Arrays an nicht vorhandene Instanzwerte | |
if(!config[option][instance]){ | |
config[option][instance] = config[option][0]; | |
} | |
} | |
} | |
// Allgemeine Widget-Einstellungen | |
function createBasicWidget(){ | |
widget = new ListWidget(); | |
widget.url = config.widgetURL[instance]; | |
widget.setPadding(20, 20, 20, 20); | |
let nextRefresh = Date.now() + 1000*30; | |
widget.refreshAfterDate = new Date(nextRefresh); | |
// vStackV ist vertikal, vStack0 ist ist Überschrift, vStack1 ist Zeile 1, vStack2 ist Zeile 2 | |
vStackV = widget.addStack(); | |
vStackV.layoutVertically(); | |
vStackV.centerAlignContent(); | |
vStack0 = vStackV.addStack(); | |
vStack0.layoutHorizontally(); | |
vStackV.addSpacer(); | |
vStack1 = vStackV.addStack(); | |
vStack1.layoutHorizontally(); | |
vStackV.addSpacer(); | |
vStack2 = vStackV.addStack(); | |
vStack2.layoutHorizontally(); | |
if(config.widgetSize[instance] == 2){ | |
vStackV.addSpacer(); | |
vStack3 = vStackV.addStack(); | |
vStack3.layoutHorizontally(); | |
vStackV.addSpacer(); | |
vStack4 = vStackV.addStack(); | |
vStack4.layoutHorizontally(); | |
vStackV.addSpacer(); | |
vStack5 = vStackV.addStack(); | |
vStack5.layoutHorizontally(); | |
} | |
} | |
// leitet bei Oder-Feldern auf das aktuelle Feld um | |
async function checkOrPanels(panel){ | |
let LP1leistung = await dataHandler("actuals", "9"); | |
let LP2leistung = await dataHandler("actuals", "10"); | |
switch(panel){ | |
case 12: | |
if(LP1leistung != 0){ | |
panel = 9; | |
} | |
else{ | |
panel = 5; | |
} | |
break; | |
case 13: | |
if(LP2leistung != 0){ | |
panel = 10; | |
} | |
else{ | |
panel = 5; | |
} | |
break; | |
case 14: | |
if(LP1leistung + LP2leistung != 0){ | |
panel = 11; | |
} | |
else{ | |
panel = 5; | |
} | |
break; | |
} | |
return(panel); | |
} | |
// holt, berechnet und speichert alle Daten | |
async function dataHandler(key1, key2){ | |
// key1: actuals, historicals, csv | |
// key2: daily, monthly, 0, 1, 2 ... | |
if(globalData[key1][key2] == undefined){ | |
console.log("Getting data for [" + key1 + "][" + key2 + "]"); | |
let value; | |
switch(key1){ | |
case "actuals": | |
switch(key2){ | |
case "0": value = " "; break; | |
case "1": value = Number(await getRamdiskFile("pvallwatt")*-1); break; // Erzeugung ist negativ | |
case "2": value = Number(await getRamdiskFile("wattbezug")); break; // Einspeissung ist negativ | |
case "3": value = Number(await getRamdiskFile("hausverbrauch")); break; | |
case "4": value = Number(await getRamdiskFile("speichersoc")); break; | |
case "5": value = Number(await getRamdiskFile("speicherleistung")); break; // Entladen ist negativ | |
case "6": value = timestamp; break; | |
case "7": value = Number(await getRamdiskFile("soc")); break; | |
case "8": value = Number(await getRamdiskFile("soc1")); break; | |
case "9": value = Number(await getRamdiskFile("llaktuell")); break; | |
case "10": value = Number(await getRamdiskFile("llaktuells1")); break; | |
case "11": value = Number(await dataHandler("actuals", "9")) + Number(await dataHandler("actuals", "10")); break; | |
case "15": value = Number(await getRamdiskFile("aktgeladen")); break; | |
case "16": value = Number(await getRamdiskFile("aktgeladens1")); break; | |
case "17": value = Number(await dataHandler("actuals", "15")) + Number(await dataHandler("actuals", "16")); break; | |
case "18": | |
csv = await dataHandler("csv", "monthly"); | |
value = (Number(csv[csv.length-2]["LP1"]) - Number(csv[0]["LP1"]))/1000; | |
break; | |
case "19": | |
csv = await dataHandler("csv", "monthly"); | |
value = (Number(csv[csv.length-2]["LP2"]) - Number(csv[0]["LP2"]))/1000; | |
break; | |
case "20": value = Number(await dataHandler("actuals", "18")) + Number(await dataHandler("actuals", "19")); break; | |
case "21": value = Number(await getRamdiskFile("etproviderprice")); break; | |
case "22": value = Number(await getRamdiskFile("lademodus")); break; | |
case "23": value = Number(await getRamdiskFile("verbraucher1_watt")); break; | |
case "24": value = Number(await getRamdiskFile("verbraucher2_watt")); break; | |
case "25": value = Number(await getRamdiskFile("smarthome_device_1wh0")); break; | |
case "26": value = Number(await getRamdiskFile("smarthome_device_2wh0")); break; | |
case "27": value = Number(await getRamdiskFile("smarthome_device_3wh0")); break; | |
case "28": value = Number(await getRamdiskFile("smarthome_device_4wh0")); break; | |
case "29": value = Number(await getRamdiskFile("smarthome_device_5wh0")); break; | |
case "30": value = Number(await getRamdiskFile("smarthome_device_6wh0")); break; | |
case "31": value = Number(await getRamdiskFile("smarthome_device_7wh0")); break; | |
case "32": value = Number(await getRamdiskFile("smarthome_device_8wh0")); break; | |
case "33": value = Number(await getRamdiskFile("smarthome_device_9wh0")); break; | |
case "34": value = Number(await dataHandler("actuals", "23")) + Number(await dataHandler("actuals", "24")); break; | |
case "35": | |
value = 0; | |
for(let i=25; i<34; i++){ | |
let newValue = Number(await dataHandler("actuals", i.toString())); | |
if(newValue){ | |
value += newValue; | |
} | |
} | |
break; | |
case "36": value = Number(await dataHandler("actuals", "34")) + Number(await dataHandler("actuals", "35")); break; | |
case "37": | |
csv = await dataHandler("csv", "monthly"); | |
value = Math.round((Number(csv[csv.length-2]["PV"]) - Number(csv[0]["PV"]))/1000); | |
break; | |
case "38": | |
csv = await dataHandler("csv", "monthly"); | |
value = Math.round((Number(csv[csv.length-2]["Einspeisung"]) - Number(csv[0]["Einspeisung"]))/1000); | |
break; | |
case "39": | |
csv = await dataHandler("csv", "monthly"); | |
value = Math.round((Number(csv[csv.length-2]["Bezug"]) - Number(csv[0]["Bezug"]))/1000); | |
break; | |
case "40": | |
csv = await dataHandler("csv", "monthly"); | |
let valueLP40 = (Number(csv[csv.length-2]["LPGesamt"]) - Number(csv[0]["LPGesamt"]))/1000; | |
value = Math.round(Number(await dataHandler("actuals", "37")) - Number(await dataHandler("actuals", "38")) + Number(await dataHandler("actuals", "39")) - valueLP40); | |
break; | |
case "41": | |
csv = await dataHandler("csv", "monthly"); | |
value = Math.round((Number(csv[csv.length-2]["Laden"]) - Number(csv[0]["Laden"]))/1000); | |
break; | |
case "42": | |
csv = await dataHandler("csv", "monthly"); | |
value = Math.round((Number(csv[csv.length-2]["Entladen"]) - Number(csv[0]["Entladen"]))/1000); | |
break; | |
case "43": value = Number(await dataHandler("actuals", "37")) - Number(await dataHandler("actuals", "38")); break; | |
case "44": value = Number(await getRamdiskFile("daily_pvkwhk")); break; | |
case "45": value = Number(await getRamdiskFile("daily_einspeisungkwh")); break; | |
case "46": value = Number(await getRamdiskFile("daily_bezugkwh")); break; | |
case "47": value = Number(await getRamdiskFile("daily_hausverbrauchkwh")); break; | |
case "48": value = Number(await getRamdiskFile("daily_sikwh")); break; | |
case "49": value = Number(await getRamdiskFile("daily_sekwh")); break; | |
case "50": value = Math.round(Number(await dataHandler("actuals", "44")) - Number(await dataHandler("actuals", "45"))); break; | |
case "51": | |
csv = await dataHandler("csv", "monthly"); | |
let valueLP51 = (Number(csv[csv.length-2]["LPGesamt"]) - Number(csv[0]["LPGesamt"]))/1000; | |
value = Math.round((1 - (Number(await dataHandler("actuals", "39")) / (Number(await dataHandler("actuals", "40")) + valueLP51)))*100); | |
break; | |
case "52": | |
value = Math.round((1 - (Number(await dataHandler("actuals", "39")) / Number(await dataHandler("actuals", "40"))))*100); | |
break; | |
case "53": | |
value = Math.round((1 - (Number(await dataHandler("actuals", "43")) / Number(await dataHandler("actuals", "37"))))*100); | |
break; | |
case "54": | |
let valueLP54 = (Number(csv[csv.length-2]["LPGesamt"]) - Number(csv[0]["LPGesamt"]))/1000; | |
value = Math.round((1 - (Number(await dataHandler("actuals", "46")) / (Number(await dataHandler("actuals", "47")) + valueLP54)))*100); | |
break; | |
case "55": | |
value = Math.round((1 - (Number(await dataHandler("actuals", "46")) / Number(await dataHandler("actuals", "47"))))*100); | |
break; | |
case "56": | |
value = Math.round((1 - (Number(await dataHandler("actuals", "50")) / Number(await dataHandler("actuals", "44"))))*100); | |
break; | |
} | |
break; | |
case "historicals": | |
value = new Array(); | |
switch(key2){ | |
case "1": value = await calcCSVDifference("PV"); break; | |
case "2": | |
let valueBezug = await calcCSVDifference("Bezug"); | |
let valueEinspeisung = await calcCSVDifference("Einspeisung"); | |
for(let i=0; i<12; i++){ | |
value[i] = valueBezug[i] - valueEinspeisung[i]; | |
} | |
break; | |
case "3": | |
let valueNetz = await dataHandler("historicals", "2"); | |
let valueSpeicher = await dataHandler("historicals", "5"); | |
let valuePV = await dataHandler("historicals", "1"); | |
// Hausverbrauch = Netzbezug + Speicherentladung + PV-Erzeugung - Netzeinspeisung - Speicherladung | |
// (Netz = Netzbezug - Netzeinspeisung, Speicher = Laden - Entladen) | |
for(let i=0; i<12; i++){ | |
value[i] = valueNetz[i] + valuePV[i] + valueSpeicher[i]; | |
} | |
break; | |
case "4": value = await calcCSVDifference("SoC"); break; | |
case "5": | |
let valueLaden = await calcCSVDifference("Laden"); | |
let valueEntladen = await calcCSVDifference("Entladen"); | |
for(let i=0; i<12; i++){ | |
value[i] = valueLaden[i] - valueEntladen[i]; | |
} | |
break; | |
case "7": value = await calcCSVDifference("SoC1"); break; | |
case "8": value = await calcCSVDifference("SoC2"); break; | |
case "9": value = await calcCSVDifference("LP1"); break; | |
case "10": value = await calcCSVDifference("LP2"); break; | |
case "11": | |
let valueLP1 = await dataHandler("historicals", "9"); | |
let valueLP2 = await dataHandler("historicals", "10"); | |
for(let i=0; i<12; i++){ | |
value[i] = valueLP1[i] + valueLP2[i]; | |
} | |
break; | |
case "21": | |
let awattarRaw = Number(await getRamdiskFile("etprovidergraphlist")); | |
let lines = awattarRaw.split("\n"); | |
for(let i = 0; i < 10; i++){ | |
let seperated = lines[i].split(","); | |
value[i] = Number(seperated[1]); | |
} | |
case "25": value = await calcCSVDifference("Smarthome1"); break; | |
case "26": value = await calcCSVDifference("Smarthome2"); break; | |
case "27": value = await calcCSVDifference("Smarthome3"); break; | |
case "28": value = await calcCSVDifference("Smarthome4"); break; | |
case "29": value = await calcCSVDifference("Smarthome5"); break; | |
case "30": value = await calcCSVDifference("Smarthome6"); break; | |
case "31": value = await calcCSVDifference("Smarthome7"); break; | |
case "32": value = await calcCSVDifference("Smarthome8"); break; | |
case "33": value = await calcCSVDifference("Smarthome9"); break; | |
case "35": | |
for(let i=25; i<34; i++){ | |
let newValue = dataHandler("historical", i.toString()); | |
for(let i=0; i<12; i++){ | |
value[i] += newValue[i]; | |
} | |
} | |
break; | |
} | |
break; | |
case "csv": | |
switch(key2){ | |
case "daily": value = await getDailyCSV(); break; | |
case "monthly": value = await getMonthlyCSV(); break; | |
} | |
break; | |
} | |
if(value != undefined){ | |
globalData[key1][key2] = value; | |
} | |
else{ | |
throw "Daten zu [" + key1 + "][" + key2 + "] nicht gefunden!"; | |
} | |
} | |
return globalData[key1][key2]; | |
} | |
// belegt die Felder | |
async function setField(widget, panel){ | |
if(panel == 12 || panel == 13 || panel == 14){ | |
panel = await checkOrPanels(panel); | |
} | |
let valueColor; | |
let footnote; | |
let borderColor; | |
let chart; | |
let border; | |
let actuals; | |
let value; | |
let name; | |
const chartsNotSupported = [0, 6, 15, 16, 17, 18, 19, 20, 22, 23, 24, 34, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56]; | |
const metaData = { | |
"0":{ | |
"name": " ", | |
"unit": "", | |
"footnote": " " | |
}, | |
"1":{ | |
"name": "PV", | |
"unit": "W" | |
}, | |
"2":{ | |
"name": "Netz", | |
"unit": "W" | |
}, | |
"3":{ | |
"name": "Haus", | |
"unit": "W" | |
}, | |
"4":{ | |
"name": "SoC", | |
"unit": "%" | |
}, | |
"5":{ | |
"name": "Speicher", | |
"unit": "W" | |
}, | |
"6":{ | |
"name": "Timestamp", | |
"unit": "" | |
}, | |
"7":{ | |
"name": "LP1 SoC", | |
"unit": "%" | |
}, | |
"8":{ | |
"name": "LP2 SoC", | |
"unit": "%" | |
}, | |
"9":{ | |
"name": "LP1 Leist.", | |
"unit": "W" | |
}, | |
"10":{ | |
"name": "LP2 Leist.", | |
"unit": "W" | |
}, | |
"11":{ | |
"name": "LP1+2 Leist.", | |
"unit": "W" | |
}, | |
"15":{ | |
"name": "LP1", | |
"unit": "kWh", | |
"footnote": "zuletzt", | |
}, | |
"16":{ | |
"name": "LP2", | |
"unit": "kWh", | |
"footnote": "zuletzt", | |
}, | |
"17":{ | |
"name": "LP1+2", | |
"unit": "kWh", | |
"footnote": "zuletzt", | |
}, | |
"18":{ | |
"name": "LP1", | |
"unit": "kWh", | |
"footnote": "Monat" | |
}, | |
"19":{ | |
"name": "LP2", | |
"unit": "kWh", | |
"footnote": "Monat" | |
}, | |
"20":{ | |
"name": "LP 1+2", | |
"unit": "kWh", | |
"footnote": "Monat" | |
}, | |
"21":{ | |
"name": "Strompreis", | |
"unit": "ct" | |
}, | |
"22":{ | |
"name": "Lademodus", | |
"unit": "" | |
}, | |
"23":{ | |
"name": "Verbr. 1", | |
"unit": "W" | |
}, | |
"24":{ | |
"name": "Verbr. 2", | |
"unit": "W", | |
}, | |
"25":{ | |
"name": "Gerät 1", | |
"unit": "W" | |
}, | |
"26":{ | |
"name": "Gerät 2", | |
"unit": "W" | |
}, | |
"27":{ | |
"name": "Gerät 3", | |
"unit": "W" | |
}, | |
"28":{ | |
"name": "Gerät 4", | |
"unit": "W" | |
}, | |
"29":{ | |
"name": "Gerät 5", | |
"unit": "W" | |
}, | |
"30":{ | |
"name": "Gerät 6", | |
"unit": "W" | |
}, | |
"31":{ | |
"name": "Gerät 7", | |
"unit": "W" | |
}, | |
"32":{ | |
"name": "Gerät 8", | |
"unit": "W" | |
}, | |
"33":{ | |
"name": "Gerät 9", | |
"unit": "W" | |
}, | |
"34":{ | |
"name": "Verbraucher", | |
"unit": "W", | |
"footnote": "1 + 2" | |
}, | |
"35":{ | |
"name": "Geräte", | |
"unit": "W", | |
"footnote": "1 - 9" | |
}, | |
"36":{ | |
"name": "Smarthome", | |
"unit": "W", | |
"footnote": "Verbr. + Geräte" | |
}, | |
"37":{ | |
"name": "PV", | |
"unit": "kWh", | |
"footnote": "Monat", | |
}, | |
"38":{ | |
"name": "Einspeisung", | |
"unit": "kWh", | |
"footnote": "Monat", | |
}, | |
"39":{ | |
"name": "Bezug", | |
"unit": "kWh", | |
"footnote": "Monat", | |
}, | |
"40":{ | |
"name": "Haus", | |
"unit": "kWh", | |
"footnote": "Monat", | |
}, | |
"41":{ | |
"name": "Laden", | |
"unit": "kWh", | |
"footnote": "Monat", | |
}, | |
"42":{ | |
"name": "Entladen", | |
"unit": "kWh", | |
"footnote": "Monat", | |
}, | |
"43":{ | |
"name": "Eigenverbrauch", | |
"unit": "kWh", | |
"footnote": "Monat", | |
}, | |
"44":{ | |
"name": "PV", | |
"unit": "kWh", | |
"footnote": "heute", | |
}, | |
"45":{ | |
"name": "Einspeisung", | |
"unit": "kWh", | |
"footnote": "heute", | |
}, | |
"46":{ | |
"name": "Bezug", | |
"unit": "kWh", | |
"footnote": "heute", | |
}, | |
"47":{ | |
"name": "Haus", | |
"unit": "kWh", | |
"footnote": "heute", | |
}, | |
"48":{ | |
"name": "Laden", | |
"unit": "kWh", | |
"footnote": "heute", | |
}, | |
"49":{ | |
"name": "Entladen", | |
"unit": "kWh", | |
"footnote": "heute", | |
}, | |
"50":{ | |
"name": "Eigenverbrauch", | |
"unit": "kWh", | |
"footnote": "heute", | |
}, | |
"51":{ | |
"name": "AQ Haus", | |
"unit": "%", | |
"footnote": "Monat", | |
}, | |
"52":{ | |
"name": "AQ Haus+LP", | |
"unit": "%", | |
"footnote": "Monat", | |
}, | |
"53":{ | |
"name": "EQ", | |
"unit": "%", | |
"footnote": "Monat", | |
}, | |
"54":{ | |
"name": "AQ Haus", | |
"unit": "%", | |
"footnote": "heute", | |
}, | |
"55":{ | |
"name": "AQ Haus+LP", | |
"unit": "%", | |
"footnote": "heute", | |
}, | |
"56":{ | |
"name": "EQ", | |
"unit": "%", | |
"footnote": "heute", | |
}, | |
}; | |
try{ | |
console.log("----- Panel " + panel + " -----"); | |
// Allgemeines | |
border = config.panelBorder[instance]; | |
actuals = await dataHandler("actuals", panel.toString()); | |
value = actuals.toString() + metaData[panel.toString()]["unit"]; | |
name = metaData[panel.toString()]["name"]; | |
// Footnote | |
if(metaData[panel.toString()]["footnote"] != undefined){ | |
footnote = metaData[panel.toString()]["footnote"]; | |
} | |
else{ | |
switch(panel){ | |
case 2: footnote = calcFootnote(100, -100, "Bezug", "Einspeisen", "Speicherregelung", actuals); break; | |
case 5: footnote = calcFootnote(50, -50, "Laden", "Entladen", "Netzregelung", actuals); break; | |
case 9: if(config.lademodusFootnote[instance] == 1) footnote = calcLademodusFootnote(await dataHandler("actuals", "22")); break; | |
case 10: if(config.lademodusFootnote[instance] == 1) footnote = calcLademodusFootnote(await dataHandler("actuals", "22")); break; | |
case 11: if(config.lademodusFootnote[instance] == 1) footnote = calcLademodusFootnote(await dataHandler("actuals", "22")); break; | |
case 21: footnote = calcAwattarFootnote(); break; | |
case 22: footnote = calcLademodusFootnote(actuals); break; | |
case 23: footnote = (await getRamdiskFile("verbraucher1_name")).trim(); break; | |
case 23: footnote = (await getRamdiskFile("verbraucher2_name")).trim(); break; | |
default: | |
footnote = " "; | |
} | |
} | |
// Farbe der Werte | |
if(config.hasOwnProperty("textColor" + panel) && !isNaN(Number(actuals))){ | |
valueColor = await calcColor(config["textColor" + panel][instance], actuals); | |
} | |
else if(panel == 6){ | |
valueColor = config.textColor6[instance]; | |
} | |
else{ | |
valueColor = config.defaultColor[instance]; | |
} | |
// Farben der Rahmen | |
if(config.hasOwnProperty("borderColor" + panel) && !isNaN(Number(actuals))){ | |
borderColor = await calcColor(config["borderColor" + panel][instance], actuals); | |
} | |
else if(panel == 6){ | |
borderColor = config.borderColor6[instance]; | |
} | |
else{ | |
borderColor = config.defaultColor[instance]; | |
} | |
// Charts | |
if(config.hasOwnProperty("chartsColor" + panel) && !isNaN(Number(actuals)) && !chartsNotSupported.includes(panel)){ | |
let historicals = await dataHandler("historicals", panel.toString()); | |
let chartsColor = await calcColor(config["chartsColor" + panel][instance], actuals); | |
chart = createChart(historicals, stringToColor(chartsColor, config.chartsOpacity[instance])); | |
} | |
else{ | |
chart = 0; | |
} | |
// Platzhalter | |
if(panel == 0){ | |
border = 0; | |
borderColor = "FFFFFF"; | |
chart = 0; | |
} | |
} | |
catch(err){ | |
console.log("Fehler bei Feld " + panel + " : "); | |
console.log(err); | |
valueColor = ""; | |
name = " "; | |
footnote = " "; | |
borderColor = "FFFFFF"; | |
chart = 0; | |
if(config.placeholderAtError[instance]){ | |
value = " "; | |
border = 0; | |
} | |
else{ | |
value = "Fehler!"; | |
} | |
} | |
addDataView(widget, value, valueColor, name, footnote, borderColor, chart, border); | |
} | |
// legt Text des Lademodus fest | |
function calcLademodusFootnote(modus){ | |
switch(modus){ | |
case 0: | |
footnote = "Sofortladen"; | |
break; | |
case 1: | |
footnote = "Min & PV"; | |
break; | |
case 2: | |
footnote = "Nur PV"; | |
break; | |
case 3: | |
footnote = "Stop"; | |
break; | |
case 4: | |
footnote = "Standby"; | |
break; | |
default: | |
footnote = " "; | |
break; | |
} | |
return(footnote); | |
} | |
// erstellt Footnote für Awattar | |
function calcAwattarFootnote(){ | |
let hoursDate = new Date(); | |
let hoursDate2 = new Date(); | |
hoursDate2.setHours(hoursDate2.getHours() + 1); | |
let hoursDF = new DateFormatter(); | |
hoursDF.dateFormat = "HH"; | |
let hours = (hoursDF.string(hoursDate)); | |
let hours2 = (hoursDF.string(hoursDate2)); | |
footnote = hours + " - " + hours2 + " Uhr"; | |
return(footnote); | |
} | |
// legt Hintergrundfarbe oder Verlauf fest | |
function setBackground(){ | |
if(config.backgroundType[instance] == 0){ | |
widget.backgroundColor = new Color(config.backgroundColor[instance]); | |
} | |
else{ | |
const gradient = new LinearGradient() | |
gradient.locations = [0, 1] | |
gradient.colors = [ new Color(config. backgroundGradient1[instance]), new Color(config. backgroundGradient2[instance]) ] | |
widget.backgroundGradient = gradient | |
} | |
} | |
// Erstellt den Widget-Titel mit Überschrift, Logo und Timestamp | |
async function setTitle(){ | |
if(config.titleAlignment[instance] == 1){ | |
vStack0.addSpacer(); | |
} | |
else{ | |
vStack0.addSpacer(10); | |
} | |
if(config.titleAlignment[instance] == 1 && config.widgetSize[instance] != 0){ | |
if(config.timestampTitle[instance] == 0){ | |
if(config.widgetTitle[instance] && config.logo[instance]){ | |
vStack0.addSpacer(36); | |
} | |
else{ | |
vStack0.addSpacer(32); | |
} | |
} | |
else{ | |
if(config.widgetTitle[instance] && config.logo[instance]){ | |
vStack0.addSpacer(70); | |
} | |
else{ | |
vStack0.addSpacer(66); | |
} | |
} | |
} | |
// Logo holen und darstellen | |
if(config.logo[instance] == 1){ | |
img = vStack0.addImage(await getLogo()); | |
img.imageSize = new Size(50,20); | |
} | |
// Überschrift des Widgets | |
if((config.imageAtError[instance]!= 1) && ((config.widgetSize[instance] == 0 && config.logo[instance] == 1)!= 1) && config.widgetTitle[instance]){ | |
if(config.logo[instance] == 1){ | |
vStack0.addSpacer(10); | |
} | |
let header = vStack0.addText(config.widgetTitle[instance]); | |
header.font = Font.mediumSystemFont(12); | |
header.textColor = new Color(config.defaultColor[instance]); | |
} | |
vStack0.addSpacer(); | |
// Timestamp in Titel | |
if(config.timestampTitle[instance] == 1 && config.widgetSize[instance] != 0){ | |
let ts = vStack0.addText(timestamp); | |
ts.font = Font.mediumSystemFont(12); | |
ts.textColor = new Color(config.defaultColor[instance]); | |
} | |
} | |
// Holt das Logo von der openWB und bearbeitet dieses | |
async function getLogo(){ | |
let result; | |
let imgReq = new Request("http://" + config.oWBip[instance] + "/openWB/web/img/favicons/apple-icon-180x180.png"); | |
imgReq.timeoutInterval = config.timeOut[instance]; | |
let image = await imgReq.loadImage(); | |
dc = new DrawContext(); | |
dc.opaque = true; | |
dc.size = new Size(180,56); | |
dc.drawImageAtPoint(image, new Point(-0, -54)); | |
result = dc.getImage(); | |
return(result); | |
} | |
// zentriert/linksbündigt/rechtbündigt Text | |
function alignStack(stack, pos){ | |
if(config.contentAlignment[instance] == 1 || (config.contentAlignment[instance] == 0 && pos == 1)){ | |
stack.addSpacer(); | |
} | |
} | |
// legt ein neues Datenstack an | |
function addDataView(widget, data, color, name, foot, borderColor, img, border){ | |
let viewStack = widget.addStack(); | |
viewStack.layoutVertically(); | |
viewStack.cornerRadius = 5; | |
if(borderColor != 0){ | |
viewStack.size = new Size(97, 47); | |
viewStack.borderWidth = border; | |
viewStack.borderColor = stringToColor(borderColor, config.borderOpacity[instance]); | |
viewStack.setPadding(5, 5, 5, 3); | |
} | |
let labelStack = viewStack.addStack(); | |
alignStack(labelStack, 0); | |
let label = labelStack.addText(name); | |
label.font = Font.mediumSystemFont(12); | |
label.textColor = new Color(config.defaultColor[instance]); | |
alignStack(labelStack, 1); | |
let footnoteStack = viewStack.addStack(); | |
alignStack(footnoteStack, 0); | |
let footnote = footnoteStack.addText(foot); | |
footnote.font = Font.mediumSystemFont(8); | |
footnote.textColor = new Color(config.defaultColor[instance]); | |
alignStack(footnoteStack, 1); | |
let valueStack = viewStack.addStack(); | |
alignStack(valueStack, 0); | |
let value = valueStack.addText(data); | |
value.font = Font.mediumSystemFont(18); | |
value.textColor = stringToColor(color, 1); | |
alignStack(valueStack, 1); | |
if(config.backgroundCharts[instance] == 1){ | |
if(img != 0){ | |
viewStack.backgroundImage = img; | |
} | |
} | |
} | |
// holt Daten von der Ramdisk | |
async function getRamdiskFile(file){ | |
let url = "http://" + config.oWBip[instance] + "/openWB/ramdisk/" + file; | |
let result = await stringRequest(url); | |
return(result); | |
} | |
// führt eigenlichen HTTP-Request aus | |
async function stringRequest(url){ | |
let result = 0; | |
let req = new Request(url); | |
req.timeoutInterval = config.timeOut[instance]; | |
result = await req.loadString(); | |
return(result); | |
} | |
// Gibt die Farbe des zutreffenden Tresholds zurück | |
async function calcColor(colorRule, value){ | |
// Ersetzte $... Werte | |
for(let key in colorRule){ | |
let oldKey = key; | |
for(let i = 56; i>-1; i--){ | |
let searchPattern = "$" + i.toString(); | |
if(key.includes(searchPattern)){ | |
let replacement = await dataHandler("actuals", i.toString()); | |
//console.log("Ersetze " + searchPattern + " durch " + replacement); | |
key = key.replace(searchPattern, replacement); | |
} | |
} | |
if(key != "else") key = eval(key); | |
if(key != oldKey){ | |
colorRule[key] = colorRule[oldKey]; | |
delete colorRule[oldKey]; | |
} | |
//console.log("vor: " + oldKey + ", nach: " + key); | |
} | |
let tresholds = Object.keys(colorRule); | |
tresholds.sort(function(a, b){ | |
return a - b; | |
}); | |
tresholds.reverse(); | |
// Beginne mit höchstem Wert, Rückgabe der Farbe des ersten überschittenen Tresholds | |
for(let treshold of tresholds){ | |
if(!isNaN(Number(treshold))){ | |
if(value >= Number(treshold)){ | |
//console.log("gewählter Treshold: " + treshold); | |
return colorRule[treshold]; | |
} | |
} | |
} | |
// Wenn kein Treshold passt, probiere "else", sonst Standard-Textfarbe | |
if(tresholds.includes("else")){ | |
return colorRule["else"]; | |
} | |
return config.defaultColor[instance]; | |
} | |
// Legt Footnote fest | |
function calcFootnote(groesserAls, kleinerAls, groesserText, kleinerText, sonstText, value){ | |
if (value > groesserAls){ | |
return(groesserText); | |
} | |
else if (value < kleinerAls){ | |
return(kleinerText); | |
} | |
else{ | |
return(sonstText); | |
} | |
} | |
// Gibt das entsprechende Color-Objekt zurück | |
function stringToColor(colorString, opacity){ | |
if(colorString == "red"){ | |
colorString = "FF453A"; | |
} | |
if(colorString == "yellow"){ | |
colorString = "FFD60A"; | |
} | |
if(colorString == "green"){ | |
colorString = "30D158"; | |
} | |
if(isHexColor(colorString)){ | |
return (new Color(colorString, opacity)); | |
} | |
return (new Color(config.defaultColor[instance], opacity)); | |
} | |
// Überprüft, ob der String eine Hex-Farbe darstellt | |
function isHexColor(hex){ | |
return typeof hex === 'string' | |
&& hex.length === 6 | |
&& !isNaN(Number('0x' + hex)) | |
} | |
// Erstellt Graph und liefert Bild davon zurück | |
function createChart(data, color){ | |
if(config.backgroundCharts[instance] == 1){ | |
let chart = new LineChart(97, 47, data).configure((ctx, path) => { | |
ctx.opaque = false; | |
ctx.setFillColor(color); | |
ctx.addPath(path); | |
ctx.fillPath(path); | |
}).getImage(); | |
return(chart); | |
} | |
} | |
// Holt CSV-Daten von oWB | |
async function getCSVFile(type, date){ | |
let df = new DateFormatter(); | |
switch(type){ | |
case "daily": | |
df.dateFormat = "YYYYMMdd"; | |
break; | |
case "monthly": | |
df.dateFormat = "YYYYMM"; | |
break; | |
} | |
let fdate = (df.string(date)); | |
let url = ("http://" + config.oWBip[instance] + "/openWB/web/logging/data/" + type + "/" + fdate + ".csv"); | |
let result = await stringRequest(url); | |
return(result); | |
} | |
// gibt zusammengesetzte CSV-Daten des aktuellen+vorherigen Tages als Object zurück | |
async function getDailyCSV(){ | |
let date = new Date(); | |
let csvToday = await getCSVFile("daily", date); | |
date.setDate(date.getDate()-1) | |
let csvYesterday = await getCSVFile("daily", date); | |
let csv = csvYesterday + csvToday; | |
let result = CSVToObject(csv, ["Zeit", "Bezug", "Einspeisung", "PV", "LP1", "LP2", "LP3", "LPGesamt", "Laden", "Entladen", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "SoC", "SoC1", "SoC2", "24", "25", "26", "Smarthome1", "Smarthome2", "Smarthome3", "Smarthome4", "Smarthome5", "Smarthome6", "Smarthome7", "Smarthome8", "Smarthome9", "36", "37", "38", "39"]); | |
return(result); | |
} | |
// gibt CSV-Daten des aktuellen Monats als Object zurück | |
async function getMonthlyCSV(){ | |
let date = new Date(); | |
let csv = await getCSVFile("monthly", date); | |
let result = CSVToObject(csv, ["1", "Bezug", "Einspeisung", "PV", "LP1", "LP2", "LP3", "LPGesamt", "9", "10", "11", "12", "13", "14", "15", "16", "17", "Laden", "Entladen", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29"]); | |
return(result); | |
} | |
// Kalkuliert die Differenz zwischen den Werten der letzten Stunde | |
async function calcCSVDifference(key){ | |
let result = new Array(); | |
if(config.backgroundCharts[instance] == 1){ | |
let csv = await dataHandler("csv", "daily"); | |
let resultVar = 11; | |
for(let i=0; i<12; i++){ | |
let a = Number(csv[csv.length-2-i][key]); | |
let b = Number(csv[csv.length-3-i][key]); | |
if(key != "SoC" && key != "SoC1" && key != "SoC2"){ | |
result[resultVar] = Number(a - b); | |
} | |
else{ | |
result[resultVar] = a; | |
} | |
resultVar --; | |
} | |
} | |
return(result); | |
} | |
// Konvertiert CSV zu einem Objekt | |
function CSVToObject(csv, headers){ | |
let lines = csv.split("\n"); | |
let result = []; | |
for(let i=0;i<lines.length;i++){ | |
let obj = {}; | |
let currentline=lines[i].split(","); | |
for(let j=0;j<headers.length;j++){ | |
obj[headers[j]] = currentline[j]; | |
} | |
result.push(obj); | |
} | |
return(result); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
iOS-Widget für openWB mit der Scriptable-App.
Mit dem Widget können eine Vielzahl an Werten der openWB angezeigt werden. Die Werte können je nach Bedarf frei auf dem Widget verteilt werden, und auch sonst stehen viele individuelle Konfigurationsmöglichkeiten zur Verfügung, so können z.B. alle verwendete Farben frei nach den eigenen Wünschen angepasst werden. Ein paar Beispiele gibt es hier:
Anleitung
1. Scriptable-App herunterladen
Die App ist hier im Appstore verfügbar.
2. Skript in der Scriptable-App einrichten
Dazu muss das Skript zuerst
Raw
geöffnet werden:Dann alles markieren (ja, das ist leider mühsam) und kopieren.
Jetzt die Scriptable-App öffnen, über das
+
oben rechts ein neues Skript erstellen und den zuvor kopierten Text einfügen.Anschließend unbedingt hinter
oWBip
die IP der openWB in Anführungszeichen eintragen und unterwidgetURL
die Internetseite, die beim Tippen auf das Widget geöffnet werden soll. Hier bietet sich die openWB-Startseite an.Alle anderen Parameter am Anfang des Skripts können nach Bedarf angepasst werden, die Optionen sollten mit den Erklärtexten eigentlich selbsterklärend sein.
Wichtig: Die Widgets unter iOS 14 stehen in drei verschiedenen Größen zur Verfügung. Damit das Widget richtig skaliert angezeigt wird, muss nach
widgetSize
die gewünschte Größe eingetragen werden. Standardmäßig ist die mittlere Größe eingestellt.Durch Tippen auf
Untitled Script
(ganz oben) kann noch der Name des Skripts geändert werden, danach durch Tippen aufDone
abspeichern.3. Widget auf dem Home-Bildschirm einrichten
Zuerst muss ein Scriptable-Widget zum Homescreen hinzugefügt werden. Dafür lange auf ein existierendes Widget oder eine App tippen, dann auf
Home-Bildschirm bearbeiten
tippen und dann mit dem+
oben links ein Scriptable-Widget in der gewünschten (und zuvor eingestellten!) Größe hinzufügen.Nachdem dieses Widget hinzugefügt wurde den Bearbeitungsmodus verlassen (Home-Button).
Danach lange auf das neue Scriptable-Widget tippen,
Widget bearbeiten
auswählen und unterScript
den zuvor verwendeten Namen auswählen. Alle anderen Werte können so gelassen werden.