diff --git a/ajax/AutoAction.php b/ajax/AutoAction.php index 3af2ca6..c88ce48 100644 --- a/ajax/AutoAction.php +++ b/ajax/AutoAction.php @@ -2,15 +2,6 @@ require_once("../helper.php"); $close = 0; -function getSSLPage($url) { - $curlSession = curl_init(); - curl_setopt($curlSession, CURLOPT_URL, $url); - curl_setopt($curlSession, CURLOPT_BINARYTRANSFER, true); - curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, true); - $jsonData = json_decode(curl_exec($curlSession),true); - curl_close($curlSession); - return $jsonData; -} if (checkLogin()) { if (isset($_POST["sensorSelect1"])) { //if form war sent add an action @@ -127,13 +118,6 @@ if (checkLogin()) { } if (!$close) { echo << -input[type='range']::-webkit-slider-runnable-track { - background: linear-gradient(to right, #00788F, #00788F), #D7D7D7; - background-size: var(--background-size, 0%) 100%; - background-repeat: no-repeat; -} -
diff --git a/ajax/carEG.php b/ajax/carEG.php index cdc46fd..65f70b5 100644 --- a/ajax/carEG.php +++ b/ajax/carEG.php @@ -55,87 +55,6 @@ if (checkLogin()) { $close = 1; } if (!$close) { - echo << -input[type='range']::-webkit-slider-runnable-track { - background: linear-gradient(to right, #00788F, #00788F), #D7D7D7; - background-size: var(--background-size, 0%) 100%; - background-repeat: no-repeat; + $formId = "carEG_form"; + include __DIR__ . "/carForm.php"; } - -
- -
- - -
-
-
Modus
-
- -
-
-
- - -
-
- - -
-
- - -
-
-
-
-
-
-
Max. Ladestrom
-
-
-
-
- - -
-
- - -
-
- - -
-
-
-
-
-
-
Ladeplanung
-
-
-
-
- - - -
- 1% - 100% -
- -
-
-
-
- - -
-
-
-
- -ENDE; -} \ No newline at end of file diff --git a/ajax/carForm.php b/ajax/carForm.php new file mode 100644 index 0000000..c973f89 --- /dev/null +++ b/ajax/carForm.php @@ -0,0 +1,81 @@ + +
+ +
+ +
+
+
+
Modus
+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
Max. Ladestrom
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
Ladeplanung
+
+
+
+
+ + + +
+ 1% + 100% +
+ +
+
+
+
+ + +
+
+
+
+
diff --git a/ajax/carOG.php b/ajax/carOG.php index 696c0b2..a5c644a 100644 --- a/ajax/carOG.php +++ b/ajax/carOG.php @@ -2,25 +2,16 @@ require_once("../helper.php"); -function getSSLPage($url) { - $curlSession = curl_init(); - curl_setopt($curlSession, CURLOPT_URL, $url); - curl_setopt($curlSession, CURLOPT_BINARYTRANSFER, true); - curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, true); - $jsonData = json_decode(curl_exec($curlSession),true); - curl_close($curlSession); - return $jsonData; -} if (checkLogin()) { $close = 0; if (isset($_POST["evStart/Stop"])) { if ($_POST["evStart/Stop"] == "Laden starten") { $close = 1; - getSSLPage("http://192.168.179.122/api/set?frc=2"); + httpGetJson("http://192.168.179.122/api/set?frc=2"); } else if ($_POST["evStart/Stop"] == "Laden stoppen") { $close = 1; - getSSLPage("http://192.168.179.122/api/set?frc=1"); + httpGetJson("http://192.168.179.122/api/set?frc=1"); } $close = 1; } else if (isset($_POST["evMode"])) { @@ -37,23 +28,23 @@ if (checkLogin()) { if ($_POST["evMode"] == "eco") { $close = 1; if ($_POST["ftt"] && $_POST["fte"] > 0) { - getSSLPage("http://192.168.179.122/api/set?ate=" . $_POST["fte"] . "&att=" . $seconds . "&=" . $_POST["evAmp"] . "&fup=true&lmo=4"); + httpGetJson("http://192.168.179.122/api/set?ate=" . $_POST["fte"] . "&att=" . $seconds . "&=" . $_POST["evAmp"] . "&fup=true&lmo=4"); } else { - getSSLPage("http://192.168.179.122/api/set?amp=" . $_POST["evAmp"] . "&fup=true&lmo=4"); + httpGetJson("http://192.168.179.122/api/set?amp=" . $_POST["evAmp"] . "&fup=true&lmo=4"); } } else if ($_POST["evMode"] == "default") { $close = 1; if ($_POST["ftt"] && $_POST["fte"] > 0) { - getSSLPage("http://192.168.179.122/api/set?ate=" . $_POST["fte"] . "&att=" . $seconds . "&=" . $_POST["evAmp"] . "&fup=true&lmo=3"); + httpGetJson("http://192.168.179.122/api/set?ate=" . $_POST["fte"] . "&att=" . $seconds . "&=" . $_POST["evAmp"] . "&fup=true&lmo=3"); } else { - getSSLPage("http://192.168.179.122/api/set?amp=" . $_POST["evAmp"] . "&fup=true&lmo=3"); + httpGetJson("http://192.168.179.122/api/set?amp=" . $_POST["evAmp"] . "&fup=true&lmo=3"); } } else if ($_POST["evMode"] == "NextTrip") { $close = 1; if ($_POST["ftt"] && $_POST["fte"] > 0) { - getSSLPage("http://192.168.179.122/api/set?ate=" . $_POST["fte"] . "&att=" . $seconds . "&=" . $_POST["evAmp"] . "&fup=true&lmo=5"); + httpGetJson("http://192.168.179.122/api/set?ate=" . $_POST["fte"] . "&att=" . $seconds . "&=" . $_POST["evAmp"] . "&fup=true&lmo=5"); } else { - getSSLPage("http://192.168.179.122/api/set?amp=" . $_POST["evAmp"] . "&fup=true&lmo=5"); + httpGetJson("http://192.168.179.122/api/set?amp=" . $_POST["evAmp"] . "&fup=true&lmo=5"); } } $close = 1; @@ -62,87 +53,6 @@ if (checkLogin()) { $close = 1; } if (!$close) { - echo << -input[type='range']::-webkit-slider-runnable-track { - background: linear-gradient(to right, #00788F, #00788F), #D7D7D7; - background-size: var(--background-size, 0%) 100%; - background-repeat: no-repeat; -} - -
- -
- -
-
-
-
Modus
-
- -
-
-
- - -
-
- - -
-
- - -
-
-
-
-
-
-
Max. Ladestrom
-
-
-
-
- - -
-
- - -
-
- - -
-
-
-
-
-
-
Ladeplanung
-
-
-
-
- - - -
- 1% - 100% -
- -
-
-
-
- - -
-
-
-
-
-ENDE; + $formId = "carOG_form"; + include __DIR__ . "/carForm.php"; } diff --git a/ajax/chartData.php b/ajax/chartData.php new file mode 100644 index 0000000..12bdca2 --- /dev/null +++ b/ajax/chartData.php @@ -0,0 +1,93 @@ +label = $name; + $dataset->data = []; + $dataset->borderColor = $color; + $dataset->backgroundColor = $color . "55"; + $dataset->borderWidth = 1; + $dataset->pointRadius = 0; + $dataset->pointHoverRadius = 5; + $dataset->tension = 0.2; + + if ($name == "Solarleistung") { + $dataset->stack = "SolarPwr"; + $dataset->fill = "none"; + $dataset->yAxisID = 'y'; + } else if ($name == "Ladestand") { + $dataset->stack = "Charge"; + $dataset->fill = "none"; + $dataset->yAxisID = 'y1'; + } else { + $dataset->stack = "Consumers"; + if (!$filled) { + $filled = true; + $dataset->fill = "origin"; + } else { + $dataset->fill = "-1"; + } + $dataset->yAxisID = 'y'; + } + + return $dataset; +} + +/** + * Führt $query aus und baut daraus das Chart.js-Objekt. + * $labelFactor multipliziert die Zeitspalte (1 für Textbeschriftungen, + * 1000 für Unix-Zeitstempel, die Chart.js in Millisekunden erwartet). + */ +function buildChartData($mysql, $query, $linecolors, $labelFactor = 1) +{ + $obj = (object)[]; + $obj->labels = []; + $obj->datasets = []; + + $result = mysqli_query($mysql, $query); + if (!$result) { + // Nicht ins Ergebnis schreiben - der Aufrufer liefert JSON aus. + error_log("chartData: " . mysqli_error($mysql)); + return $obj; + } + if ($result->num_rows < 2) { + return $obj; + } + + $column = []; // Spaltenname -> Position in $obj->datasets + $filled = false; + foreach ($result->fetch_fields() as $field) { + if ($field->name == "time") { + continue; + } + $column[$field->name] = count($obj->datasets); + $obj->datasets[] = chartDataset($field->name, $linecolors, $filled); + } + + while ($row = $result->fetch_assoc()) { + foreach ($row as $name => $value) { + if ($name == "time") { + $obj->labels[] = $labelFactor == 1 ? $value : $value * $labelFactor; + } else { + $obj->datasets[$column[$name]]->data[] = $value; + } + } + } + + return $obj; +} diff --git a/ajax/energyHistory.php b/ajax/energyHistory.php new file mode 100644 index 0000000..c9bf534 --- /dev/null +++ b/ajax/energyHistory.php @@ -0,0 +1,74 @@ + [Format der Beschriftung, Filter auf datetime] +$ranges = [ + "month" => ["'%d.%b.'", "DATE_FORMAT(datetime,'%Y%m%d') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 1 MONTH),'%Y%m%d')"], + "year" => ["'%b. %y'", "DATE_FORMAT(datetime,'%Y%m') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 13 MONTH),'%Y%m')"], + "decade" => ["'%Y'", "DATE_FORMAT(datetime,'%Y') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 10 YEAR),'%Y')"], +]; + +// Datenreihen je Diagramm. Beide teilen sich Solarertrag und Heizstab und +// unterscheiden sich nur darin, wohin die Energie fließt (prod) bzw. woher +// sie kommt (cons). +$series = [ + "prod" => "SUM(pvP/12 - IF(gridP < 0, -gridP/12,0) - IF(battP < 0, -battP/12,0) - heaterPwr/12) AS Direktverbrauch, + SUM(heaterPwr/12) AS Heizstab, + SUM(IF(battP < 0, -battP/12,0)) AS Batterieladung, + SUM(gridPfeed/12) AS Einspeisung", + "cons" => "SUM(pvP/12 - IF(gridP < 0, -gridP/12,0) - IF(battP < 0, -battP/12,0) - heaterPwr/12) AS Solarverbrauch, + SUM(heaterPwr/12) AS Heizstab, + SUM(IF(battP > 0, battP/12,0)) AS Batteriebezug, + SUM(gridPcons/12) AS Netzbezug", +]; + +$linecolors["Direktverbrauch"] = "#FFFF00"; +$linecolors["Solarverbrauch"] = "#FFFF00"; +$linecolors["UG"] = "#FFaa00"; +$linecolors["OG"] = "#FF4400"; +$linecolors["Auto UG"] = "#00aaFF"; +$linecolors["Auto OG"] = "#0044FF"; +$linecolors["Heizstab"] = "#FF6600"; +$linecolors["Batterieladung"] = "#00aa00"; +$linecolors["Batteriebezug"] = "#00aa00"; +$linecolors["Einspeisung"] = "#FF0000"; +$linecolors["Netzbezug"] = "#FF0000"; +$linecolors["Ladestand"] = "#00aa00"; +$linecolors["Vorhersage"] = "#2222FF"; + +header('Content-Type: application/json; charset=utf-8'); + +$rangeKey = isset($_GET["range"]) ? $_GET["range"] : ""; +$seriesKey = isset($_GET["series"]) ? $_GET["series"] : ""; +if (!isset($ranges[$rangeKey]) || !isset($series[$seriesKey])) { + http_response_code(400); + echo json_encode(["error" => "Unbekannter Zeitraum oder Datenreihe."]); + exit; +} +list($labelFormat, $filter) = $ranges[$rangeKey]; + +$obj = (object)[]; +$obj->labels = []; +$obj->datasets = []; + +if (checkLogin()) { + $mysql = new mysqli($mysql_server, $mysql_solarUser, $mysql_solarPass, $mysql_solarDB); + $query = "SELECT + DATE_FORMAT(datetime, $labelFormat) AS 'time', + " . $series[$seriesKey] . " +FROM EnergyFlow + WHERE $filter + GROUP BY DATE_FORMAT(datetime, $labelFormat) +ORDER BY datetime ; "; + $obj = buildChartData($mysql, $query, $linecolors); +} + +echo json_encode($obj); diff --git a/ajax/getConsData_decade.php b/ajax/getConsData_decade.php deleted file mode 100644 index 0a5233e..0000000 --- a/ajax/getConsData_decade.php +++ /dev/null @@ -1,98 +0,0 @@ - 0, battP/12,0)) AS Batteriebezug, - SUM(gridPcons/12) AS Netzbezug -FROM EnergyFlow - WHERE DATE_FORMAT(datetime,'%Y') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 10 YEAR),'%Y') - GROUP BY time -ORDER BY datetime ; "; - -$linecolors["Solarverbrauch"] = "#FFFF00"; -$linecolors["UG"] = "#FFaa00"; -$linecolors["OG"] = "#FF4400"; -$linecolors["Auto UG"] = "#00aaFF"; -$linecolors["Auto OG"] = "#0044FF"; -$linecolors["Heizstab"] = "#FF6600"; -$linecolors["Batteriebezug"] = "#00aa00"; -$linecolors["Netzbezug"] = "#FF0000"; -$linecolors["Ladestand"] = "#00aa00"; -$linecolors["Vorhersage"] = "#2222FF"; - - - -if (checkLogin()) { - $mysql = new mysqli($mysql_server, $mysql_solarUser, $mysql_solarPass, $mysql_solarDB); - $result = mysqli_query($mysql, $consQuery); - if(!$result){ - echo "Error:
".mysqli_error($mysql)."
"; - } - $obj = (object)[]; // Cast empty array to object - $obj->labels = []; - $obj->datasets = []; - $i = 0; - $filled = 0; - if ($result->num_rows > 1) { - $ii = 0; - $row = $result->fetch_assoc(); - foreach ($row as $name => $value) { - $dataset = (object)[]; - if ($name != "time") { - $dataset->borderColor = $linecolors[$name]; - $dataset->backgroundColor = $linecolors[$name]."55"; - $dataset->borderWidth=1; - $dataset->pointRadius= 0; - $dataset->pointHoverRadius= 5; - $dataset->tension=0.2; - if ($name == "Solarleistung") { - $dataset->stack = "SolarPwr"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y'; - } else if ($name == "Ladestand") { - $dataset->stack = "Charge"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y1'; - } else { - $dataset->stack = "Consumers"; - if ($filled == 0) { - $filled = 1; - $dataset->fill = "origin"; - } else { - $dataset->fill = "-1"; - } - $dataset->yAxisID = 'y'; - } - $dataset->label = $name; - - $dataset->data[] = $value; - $obj->datasets[] = clone $dataset; - $ii++; - } else { - $obj->labels[] = $value; - } - } - while ($row = $result->fetch_assoc()) { - $ii = 0; - foreach ($row as $name => $value) { - if ($name != "time") { - $obj->datasets[$ii]->data[] = $value; - $ii++; - } else { - $obj->labels[] = $value; - } - } - } - } - - -} - - -//header('Content-Type: application/json'); -echo json_encode($obj); -//echo '{"labels":[1761322682000,1761322782000,1761322882000,1761322982000,1761323082000,1761323182000,1761323282000],"datasets":[{"stack": "Stack 0","cubicInterpolationMode":"monotone","fill":"origin","label":"Acquisitions by year","data":[10,20,50,20,10,5,70]},{"fill": "false","stack": "Stack 1","cubicInterpolationMode": "monotone","label": "Acquisitions by year","data": [10,20,50,20,10,5,70]}]}'; diff --git a/ajax/getConsData_month.php b/ajax/getConsData_month.php deleted file mode 100644 index 31d9906..0000000 --- a/ajax/getConsData_month.php +++ /dev/null @@ -1,98 +0,0 @@ - 0, battP/12,0)) AS Batteriebezug, - SUM(gridPcons/12) AS Netzbezug -FROM EnergyFlow - WHERE DATE_FORMAT(datetime,'%Y%m%d') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 1 MONTH),'%Y%m%d') - GROUP BY DATE_FORMAT(datetime, '%d.%b.') -ORDER BY datetime ; "; - -$linecolors["Solarverbrauch"] = "#FFFF00"; -$linecolors["UG"] = "#FFaa00"; -$linecolors["OG"] = "#FF4400"; -$linecolors["Auto UG"] = "#00aaFF"; -$linecolors["Auto OG"] = "#0044FF"; -$linecolors["Heizstab"] = "#FF6600"; -$linecolors["Batteriebezug"] = "#00aa00"; -$linecolors["Netzbezug"] = "#FF0000"; -$linecolors["Ladestand"] = "#00aa00"; -$linecolors["Vorhersage"] = "#2222FF"; - - - -if (checkLogin()) { - $mysql = new mysqli($mysql_server, $mysql_solarUser, $mysql_solarPass, $mysql_solarDB); - $result = mysqli_query($mysql, $consQuery); - if(!$result){ - echo "Error:
".mysqli_error($mysql)."
"; - } - $obj = (object)[]; // Cast empty array to object - $obj->labels = []; - $obj->datasets = []; - $i = 0; - $filled = 0; - if ($result->num_rows > 1) { - $ii = 0; - $row = $result->fetch_assoc(); - foreach ($row as $name => $value) { - $dataset = (object)[]; - if ($name != "time") { - $dataset->borderColor = $linecolors[$name]; - $dataset->backgroundColor = $linecolors[$name]."55"; - $dataset->borderWidth=1; - $dataset->pointRadius= 0; - $dataset->pointHoverRadius= 5; - $dataset->tension=0.2; - if ($name == "Solarleistung") { - $dataset->stack = "SolarPwr"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y'; - } else if ($name == "Ladestand") { - $dataset->stack = "Charge"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y1'; - } else { - $dataset->stack = "Consumers"; - if ($filled == 0) { - $filled = 1; - $dataset->fill = "origin"; - } else { - $dataset->fill = "-1"; - } - $dataset->yAxisID = 'y'; - } - $dataset->label = $name; - - $dataset->data[] = $value; - $obj->datasets[] = clone $dataset; - $ii++; - } else { - $obj->labels[] = $value; - } - } - while ($row = $result->fetch_assoc()) { - $ii = 0; - foreach ($row as $name => $value) { - if ($name != "time") { - $obj->datasets[$ii]->data[] = $value; - $ii++; - } else { - $obj->labels[] = $value; - } - } - } - } - - -} - - -//header('Content-Type: application/json'); -echo json_encode($obj); -//echo '{"labels":[1761322682000,1761322782000,1761322882000,1761322982000,1761323082000,1761323182000,1761323282000],"datasets":[{"stack": "Stack 0","cubicInterpolationMode":"monotone","fill":"origin","label":"Acquisitions by year","data":[10,20,50,20,10,5,70]},{"fill": "false","stack": "Stack 1","cubicInterpolationMode": "monotone","label": "Acquisitions by year","data": [10,20,50,20,10,5,70]}]}'; diff --git a/ajax/getConsData_year.php b/ajax/getConsData_year.php deleted file mode 100644 index 5564ad0..0000000 --- a/ajax/getConsData_year.php +++ /dev/null @@ -1,98 +0,0 @@ - 0, battP/12,0)) AS Batteriebezug, - SUM(gridPcons/12) AS Netzbezug -FROM EnergyFlow - WHERE DATE_FORMAT(datetime,'%Y%m') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 13 MONTH),'%Y%m') - GROUP BY DATE_FORMAT(datetime, '%b. %y') -ORDER BY datetime ; "; - -$linecolors["Solarverbrauch"] = "#FFFF00"; -$linecolors["UG"] = "#FFaa00"; -$linecolors["OG"] = "#FF4400"; -$linecolors["Auto UG"] = "#00aaFF"; -$linecolors["Auto OG"] = "#0044FF"; -$linecolors["Heizstab"] = "#FF6600"; -$linecolors["Batteriebezug"] = "#00aa00"; -$linecolors["Netzbezug"] = "#FF0000"; -$linecolors["Ladestand"] = "#00aa00"; -$linecolors["Vorhersage"] = "#2222FF"; - - - -if (checkLogin()) { - $mysql = new mysqli($mysql_server, $mysql_solarUser, $mysql_solarPass, $mysql_solarDB); - $result = mysqli_query($mysql, $consQuery); - if(!$result){ - echo "Error:
".mysqli_error($mysql)."
"; - } - $obj = (object)[]; // Cast empty array to object - $obj->labels = []; - $obj->datasets = []; - $i = 0; - $filled = 0; - if ($result->num_rows > 1) { - $ii = 0; - $row = $result->fetch_assoc(); - foreach ($row as $name => $value) { - $dataset = (object)[]; - if ($name != "time") { - $dataset->borderColor = $linecolors[$name]; - $dataset->backgroundColor = $linecolors[$name]."55"; - $dataset->borderWidth=1; - $dataset->pointRadius= 0; - $dataset->pointHoverRadius= 5; - $dataset->tension=0.2; - if ($name == "Solarleistung") { - $dataset->stack = "SolarPwr"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y'; - } else if ($name == "Ladestand") { - $dataset->stack = "Charge"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y1'; - } else { - $dataset->stack = "Consumers"; - if ($filled == 0) { - $filled = 1; - $dataset->fill = "origin"; - } else { - $dataset->fill = "-1"; - } - $dataset->yAxisID = 'y'; - } - $dataset->label = $name; - - $dataset->data[] = $value; - $obj->datasets[] = clone $dataset; - $ii++; - } else { - $obj->labels[] = $value; - } - } - while ($row = $result->fetch_assoc()) { - $ii = 0; - foreach ($row as $name => $value) { - if ($name != "time") { - $obj->datasets[$ii]->data[] = $value; - $ii++; - } else { - $obj->labels[] = $value; - } - } - } - } - - -} - - -//header('Content-Type: application/json'); -echo json_encode($obj); -//echo '{"labels":[1761322682000,1761322782000,1761322882000,1761322982000,1761323082000,1761323182000,1761323282000],"datasets":[{"stack": "Stack 0","cubicInterpolationMode":"monotone","fill":"origin","label":"Acquisitions by year","data":[10,20,50,20,10,5,70]},{"fill": "false","stack": "Stack 1","cubicInterpolationMode": "monotone","label": "Acquisitions by year","data": [10,20,50,20,10,5,70]}]}'; diff --git a/ajax/getProdData_decade.php b/ajax/getProdData_decade.php deleted file mode 100644 index d396659..0000000 --- a/ajax/getProdData_decade.php +++ /dev/null @@ -1,98 +0,0 @@ -= DATE_FORMAT(DATE_SUB(now(),INTERVAL 10 YEAR),'%Y') - GROUP BY DATE_FORMAT(datetime, '%Y') -ORDER BY datetime ; "; - -$linecolors["Direktverbrauch"] = "#FFFF00"; -$linecolors["UG"] = "#FFaa00"; -$linecolors["OG"] = "#FF4400"; -$linecolors["Auto UG"] = "#00aaFF"; -$linecolors["Auto OG"] = "#0044FF"; -$linecolors["Heizstab"] = "#FF6600"; -$linecolors["Batterieladung"] = "#00aa00"; -$linecolors["Einspeisung"] = "#FF0000"; -$linecolors["Ladestand"] = "#00aa00"; -$linecolors["Vorhersage"] = "#2222FF"; - - - -if (checkLogin()) { - $mysql = new mysqli($mysql_server, $mysql_solarUser, $mysql_solarPass, $mysql_solarDB); - $result = mysqli_query($mysql, $consQuery); - if(!$result){ - echo "Error:
".mysqli_error($mysql)."
"; - } - $obj = (object)[]; // Cast empty array to object - $obj->labels = []; - $obj->datasets = []; - $i = 0; - $filled = 0; - if ($result->num_rows > 1) { - $ii = 0; - $row = $result->fetch_assoc(); - foreach ($row as $name => $value) { - $dataset = (object)[]; - if ($name != "time") { - $dataset->borderColor = $linecolors[$name]; - $dataset->backgroundColor = $linecolors[$name]."55"; - $dataset->borderWidth=1; - $dataset->pointRadius= 0; - $dataset->pointHoverRadius= 5; - $dataset->tension=0.2; - if ($name == "Solarleistung") { - $dataset->stack = "SolarPwr"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y'; - } else if ($name == "Ladestand") { - $dataset->stack = "Charge"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y1'; - } else { - $dataset->stack = "Consumers"; - if ($filled == 0) { - $filled = 1; - $dataset->fill = "origin"; - } else { - $dataset->fill = "-1"; - } - $dataset->yAxisID = 'y'; - } - $dataset->label = $name; - - $dataset->data[] = $value; - $obj->datasets[] = clone $dataset; - $ii++; - } else { - $obj->labels[] = $value; - } - } - while ($row = $result->fetch_assoc()) { - $ii = 0; - foreach ($row as $name => $value) { - if ($name != "time") { - $obj->datasets[$ii]->data[] = $value; - $ii++; - } else { - $obj->labels[] = $value; - } - } - } - } - - -} - - -//header('Content-Type: application/json'); -echo json_encode($obj); -//echo '{"labels":[1761322682000,1761322782000,1761322882000,1761322982000,1761323082000,1761323182000,1761323282000],"datasets":[{"stack": "Stack 0","cubicInterpolationMode":"monotone","fill":"origin","label":"Acquisitions by year","data":[10,20,50,20,10,5,70]},{"fill": "false","stack": "Stack 1","cubicInterpolationMode": "monotone","label": "Acquisitions by year","data": [10,20,50,20,10,5,70]}]}'; diff --git a/ajax/getProdData_month.php b/ajax/getProdData_month.php deleted file mode 100644 index 3055622..0000000 --- a/ajax/getProdData_month.php +++ /dev/null @@ -1,98 +0,0 @@ -= DATE_FORMAT(DATE_SUB(now(),INTERVAL 1 MONTH),'%Y%m%d') - GROUP BY DATE_FORMAT(datetime, '%d.%b.') -ORDER BY datetime ; "; - -$linecolors["Direktverbrauch"] = "#FFFF00"; -$linecolors["UG"] = "#FFaa00"; -$linecolors["OG"] = "#FF4400"; -$linecolors["Auto UG"] = "#00aaFF"; -$linecolors["Auto OG"] = "#0044FF"; -$linecolors["Heizstab"] = "#FF6600"; -$linecolors["Batterieladung"] = "#00aa00"; -$linecolors["Einspeisung"] = "#FF0000"; -$linecolors["Ladestand"] = "#00aa00"; -$linecolors["Vorhersage"] = "#2222FF"; - - - -if (checkLogin()) { - $mysql = new mysqli($mysql_server, $mysql_solarUser, $mysql_solarPass, $mysql_solarDB); - $result = mysqli_query($mysql, $consQuery); - if(!$result){ - echo "Error:
".mysqli_error($mysql)."
"; - } - $obj = (object)[]; // Cast empty array to object - $obj->labels = []; - $obj->datasets = []; - $i = 0; - $filled = 0; - if ($result->num_rows > 1) { - $ii = 0; - $row = $result->fetch_assoc(); - foreach ($row as $name => $value) { - $dataset = (object)[]; - if ($name != "time") { - $dataset->borderColor = $linecolors[$name]; - $dataset->backgroundColor = $linecolors[$name]."55"; - $dataset->borderWidth=1; - $dataset->pointRadius= 0; - $dataset->pointHoverRadius= 5; - $dataset->tension=0.2; - if ($name == "Solarleistung") { - $dataset->stack = "SolarPwr"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y'; - } else if ($name == "Ladestand") { - $dataset->stack = "Charge"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y1'; - } else { - $dataset->stack = "Consumers"; - if ($filled == 0) { - $filled = 1; - $dataset->fill = "origin"; - } else { - $dataset->fill = "-1"; - } - $dataset->yAxisID = 'y'; - } - $dataset->label = $name; - - $dataset->data[] = $value; - $obj->datasets[] = clone $dataset; - $ii++; - } else { - $obj->labels[] = $value; - } - } - while ($row = $result->fetch_assoc()) { - $ii = 0; - foreach ($row as $name => $value) { - if ($name != "time") { - $obj->datasets[$ii]->data[] = $value; - $ii++; - } else { - $obj->labels[] = $value; - } - } - } - } - - -} - - -//header('Content-Type: application/json'); -echo json_encode($obj); -//echo '{"labels":[1761322682000,1761322782000,1761322882000,1761322982000,1761323082000,1761323182000,1761323282000],"datasets":[{"stack": "Stack 0","cubicInterpolationMode":"monotone","fill":"origin","label":"Acquisitions by year","data":[10,20,50,20,10,5,70]},{"fill": "false","stack": "Stack 1","cubicInterpolationMode": "monotone","label": "Acquisitions by year","data": [10,20,50,20,10,5,70]}]}'; diff --git a/ajax/getProdData_year.php b/ajax/getProdData_year.php deleted file mode 100644 index b47fef4..0000000 --- a/ajax/getProdData_year.php +++ /dev/null @@ -1,98 +0,0 @@ -= DATE_FORMAT(DATE_SUB(now(),INTERVAL 13 MONTH),'%Y%m') - GROUP BY DATE_FORMAT(datetime, '%b. %y') -ORDER BY datetime ; "; - -$linecolors["Direktverbrauch"] = "#FFFF00"; -$linecolors["UG"] = "#FFaa00"; -$linecolors["OG"] = "#FF4400"; -$linecolors["Auto UG"] = "#00aaFF"; -$linecolors["Auto OG"] = "#0044FF"; -$linecolors["Heizstab"] = "#FF6600"; -$linecolors["Batterieladung"] = "#00aa00"; -$linecolors["Einspeisung"] = "#FF0000"; -$linecolors["Ladestand"] = "#00aa00"; -$linecolors["Vorhersage"] = "#2222FF"; - - - -if (checkLogin()) { - $mysql = new mysqli($mysql_server, $mysql_solarUser, $mysql_solarPass, $mysql_solarDB); - $result = mysqli_query($mysql, $consQuery); - if(!$result){ - echo "Error:
".mysqli_error($mysql)."
"; - } - $obj = (object)[]; // Cast empty array to object - $obj->labels = []; - $obj->datasets = []; - $i = 0; - $filled = 0; - if ($result->num_rows > 1) { - $ii = 0; - $row = $result->fetch_assoc(); - foreach ($row as $name => $value) { - $dataset = (object)[]; - if ($name != "time") { - $dataset->borderColor = $linecolors[$name]; - $dataset->backgroundColor = $linecolors[$name]."55"; - $dataset->borderWidth=1; - $dataset->pointRadius= 0; - $dataset->pointHoverRadius= 5; - $dataset->tension=0.2; - if ($name == "Solarleistung") { - $dataset->stack = "SolarPwr"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y'; - } else if ($name == "Ladestand") { - $dataset->stack = "Charge"; - $dataset->fill = "none"; - $dataset->yAxisID = 'y1'; - } else { - $dataset->stack = "Consumers"; - if ($filled == 0) { - $filled = 1; - $dataset->fill = "origin"; - } else { - $dataset->fill = "-1"; - } - $dataset->yAxisID = 'y'; - } - $dataset->label = $name; - - $dataset->data[] = $value; - $obj->datasets[] = clone $dataset; - $ii++; - } else { - $obj->labels[] = $value; - } - } - while ($row = $result->fetch_assoc()) { - $ii = 0; - foreach ($row as $name => $value) { - if ($name != "time") { - $obj->datasets[$ii]->data[] = $value; - $ii++; - } else { - $obj->labels[] = $value; - } - } - } - } - - -} - - -//header('Content-Type: application/json'); -echo json_encode($obj); -//echo '{"labels":[1761322682000,1761322782000,1761322882000,1761322982000,1761323082000,1761323182000,1761323282000],"datasets":[{"stack": "Stack 0","cubicInterpolationMode":"monotone","fill":"origin","label":"Acquisitions by year","data":[10,20,50,20,10,5,70]},{"fill": "false","stack": "Stack 1","cubicInterpolationMode": "monotone","label": "Acquisitions by year","data": [10,20,50,20,10,5,70]}]}'; diff --git a/ajax/heater.php b/ajax/heater.php index 75e9302..c060248 100644 --- a/ajax/heater.php +++ b/ajax/heater.php @@ -2,15 +2,6 @@ require_once("../helper.php"); -function getSSLPage($url) { - $curlSession = curl_init(); - curl_setopt($curlSession, CURLOPT_URL, $url); - curl_setopt($curlSession, CURLOPT_BINARYTRANSFER, true); - curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, true); - $jsonData = json_decode(curl_exec($curlSession),true); - curl_close($curlSession); - return $jsonData; -} if (checkLogin()) { $close = 0; @@ -48,13 +39,6 @@ if (checkLogin()) { } if (!$close) { echo << -input[type='range']::-webkit-slider-runnable-track { - background: linear-gradient(to right, #00788F, #00788F), #D7D7D7; - background-size: var(--background-size, 0%) 100%; - background-repeat: no-repeat; -} -
diff --git a/ajax/roomtemp.php b/ajax/roomtemp.php index 6a4bf67..4eb29e6 100644 --- a/ajax/roomtemp.php +++ b/ajax/roomtemp.php @@ -30,13 +30,6 @@ if (checkLogin()) { } if (!$close) { echo << -input[type='range']::-webkit-slider-runnable-track { - background: linear-gradient(to right, #00788F, #00788F), #D7D7D7; - background-size: var(--background-size, 0%) 100%; - background-repeat: no-repeat; -} -
diff --git a/assets/img/1UG.png b/assets/img/1UG.png deleted file mode 100644 index 8308ea0..0000000 Binary files a/assets/img/1UG.png and /dev/null differ diff --git a/assets/img/2EG.png b/assets/img/2EG.png deleted file mode 100644 index c0a303a..0000000 Binary files a/assets/img/2EG.png and /dev/null differ diff --git a/assets/img/3OG.png b/assets/img/3OG.png deleted file mode 100644 index 41ae59d..0000000 Binary files a/assets/img/3OG.png and /dev/null differ diff --git a/assets/img/AdminLTEFullLogo.png b/assets/img/AdminLTEFullLogo.png deleted file mode 100644 index 99a098c..0000000 Binary files a/assets/img/AdminLTEFullLogo.png and /dev/null differ diff --git a/assets/img/boxed-bg.jpg b/assets/img/boxed-bg.jpg deleted file mode 100644 index 4bda80d..0000000 Binary files a/assets/img/boxed-bg.jpg and /dev/null differ diff --git a/assets/img/boxed-bg.png b/assets/img/boxed-bg.png deleted file mode 100644 index 9a10544..0000000 Binary files a/assets/img/boxed-bg.png and /dev/null differ diff --git a/assets/img/credit/american-express.png b/assets/img/credit/american-express.png deleted file mode 100644 index 24657b5..0000000 Binary files a/assets/img/credit/american-express.png and /dev/null differ diff --git a/assets/img/credit/cirrus.png b/assets/img/credit/cirrus.png deleted file mode 100644 index 219ec18..0000000 Binary files a/assets/img/credit/cirrus.png and /dev/null differ diff --git a/assets/img/credit/mastercard.png b/assets/img/credit/mastercard.png deleted file mode 100644 index 75ff0e1..0000000 Binary files a/assets/img/credit/mastercard.png and /dev/null differ diff --git a/assets/img/credit/paypal.png b/assets/img/credit/paypal.png deleted file mode 100644 index 8c6f493..0000000 Binary files a/assets/img/credit/paypal.png and /dev/null differ diff --git a/assets/img/credit/paypal2.png b/assets/img/credit/paypal2.png deleted file mode 100644 index be6260b..0000000 Binary files a/assets/img/credit/paypal2.png and /dev/null differ diff --git a/assets/img/credit/visa.png b/assets/img/credit/visa.png deleted file mode 100644 index e04715d..0000000 Binary files a/assets/img/credit/visa.png and /dev/null differ diff --git a/assets/img/default-150x150.png b/assets/img/default-150x150.png deleted file mode 100644 index e5b2551..0000000 Binary files a/assets/img/default-150x150.png and /dev/null differ diff --git a/assets/img/favicon2.png b/assets/img/favicon2.png deleted file mode 100644 index 64e156f..0000000 Binary files a/assets/img/favicon2.png and /dev/null differ diff --git a/assets/img/icons.png b/assets/img/icons.png deleted file mode 100644 index fae5a10..0000000 Binary files a/assets/img/icons.png and /dev/null differ diff --git a/assets/img/pvDetail.svg b/assets/img/pvDetail.svg deleted file mode 100644 index ab88b43..0000000 --- a/assets/img/pvDetail.svg +++ /dev/null @@ -1,475 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --.-- kW - --.-- kW - --.-- kW - --W - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -.-- kW - - - - - - - - - - - - -.-- kW - - - #19 - - #19 - - #18 - - #17 - - #16 - - #15 - - #14 - - #13 - - #12 - - #11 - - #10 - - #9 - - #8 - - #7 - - #6 - - #5 - - #1 - - #2 - - #3 - - #4 - - - - W - - - W - - - - W - - - W - - - - W - - - W - - - - W - - - W - - - - W - - - W - - - - W - - - - W - - - - - - \ No newline at end of file diff --git a/assets/img/realtime.svg b/assets/img/realtime.svg index cea4d20..6bdb8f5 100644 --- a/assets/img/realtime.svg +++ b/assets/img/realtime.svg @@ -1,21 +1,30 @@
`)[0],o=r.firstChild,s=r.lastChild,i=On;i&&(s.nonce=i);const[l,,c]=ve(),[a,u]=e({o:t(r,o),i:nt},st(t,r,o,!0)),[d]=u(),p=(t=>{let e=!1;const n=Ct(t,Oe);try{e="none"===Zt(t,"scrollbar-width")||"none"===Zt(t,"display","::-webkit-scrollbar")}catch(r){}return n(),e})(r),y={x:0===d.x,y:0===d.y},h={elements:{host:null,padding:!p,viewport:t=>p&&kt(t)&&t,content:!1},scrollbars:{slot:!0},cancel:{nativeScrollbarsOverlaid:!1,body:null}},f=ut({},mn),x=st(ut,{},f),b=st(ut,{},h),g={N:d,T:y,P:p,J:!!v,K:st(l,"r"),Z:b,tt:t=>ut(h,t)&&b(),nt:x,ot:t=>ut(f,t)&&x(),st:ut({},h),et:ut({},f)};if(bt(r,"style"),Et(r),se(n,"resize",(()=>{c("r",[])})),$(n.matchMedia)&&!p&&(!y.x||!y.y)){const t=e=>{const r=n.matchMedia(`(resolution: ${n.devicePixelRatio}dppx)`);se(r,"change",(()=>{e(),t(e)}),{A:!0})};t((()=>{const[t,e]=a();ut(g.N,t),c("r",[e])}))}return g})()),Mn),Dn=(t,e,n,r)=>{let o=!1;const{ct:s,rt:i,lt:l,it:c,ut:a,_t:u}=r||{},d=lt((()=>o&&n(!0)),{_:33,v:99}),[p,h]=((t,e,n)=>{let r=!1;const o=!!n&&new WeakMap,s=s=>{if(o&&n){const i=n.map((e=>{const[n,r]=e||[];return[r&&n?(s||Dt)(n,t):[],r]}));H(i,(n=>H(n[0],(s=>{const i=n[1],l=o.get(s)||[];if(t.contains(s)&&i){const t=se(s,i,(n=>{r?(t(),o.delete(s)):e(n)}));o.set(s,E(l,t))}else _(l),o.delete(s)}))))}};return s(),[()=>{r=!0},s]})(t,d,l),f=i||[],v=L(s||[],f),x=(o,s)=>{if(!z(s)){const i=a||ot,l=u||ot,d=[],p=[];let y=!1,v=!1;if(H(s,(n=>{const{attributeName:o,target:s,type:a,oldValue:u,addedNodes:h,removedNodes:x}=n,b="attributes"===a,g="childList"===a,m=t===s,$=b&&o,S=$&&ft(s,o||""),O=w(S)?S:null,M=$&&u!==O,C=A(f,o)&&M;if(e&&(g||!m)){const e=b&&M,a=e&&c&&Tt(s,c),p=(a?!i(s,o,u,O):!b||e)&&!l(n,!!a,t,r);H(h,(t=>E(d,t))),H(x,(t=>E(d,t))),v=v||p}!e&&m&&M&&!i(s,o,u,O)&&(E(p,o),y=y||C)})),h((t=>I(d).reduce(((e,n)=>(E(e,Dt(t,n)),Tt(n,t)?E(e,n):e)),[]))),e)return!o&&v&&n(!1),[!1];if(!z(p)||y){const t=[I(p),y];return!o&&n.apply(0,t),t}}},b=new y(st(x,!1));return[()=>(b.observe(t,{attributes:!0,attributeOldValue:!0,attributeFilter:v,subtree:e,childList:e,characterData:e}),o=!0,()=>{o&&(p(),b.disconnect(),o=!1)}),()=>{if(o)return d.m(),x(!0,b.takeRecords())}]},Tn=(t,n,r)=>{const{dt:o}=r||{},s=we(un),[i]=e({o:!1,u:!0});return()=>{const e=[],r=It(`
`)[0],l=r.firstChild,c=t=>{let e=!1,r=!1;if(t instanceof ResizeObserverEntry){const[n,,o]=i(t.contentRect),s=ne(n);r=re(n,o),e=!r&&!s}else r=!0===t;e||n({ft:!0,dt:r})};if(f){const t=new f((t=>c(t.pop())));t.observe(l),E(e,(()=>{t.disconnect()}))}else{if(!s)return ot;{const[t,n]=s(l,c,o);E(e,L([Ct(r,Ne),se(r,"animationstart",t)],n))}}return st(_,E(e,Rt(t,r)))}},kn=(t,n)=>{let r;const o=zt(We),[s]=e({o:!1}),i=(t,e)=>{if(t){const r=s((t=>0===t.h||t.isIntersecting||t.intersectionRatio>0)(t)),[,o]=r;return o&&!e&&n(r)&&[r]}},l=(t,e)=>i(e.pop(),t);return[()=>{const e=[];if(h)r=new h(st(l,!1),{root:t}),r.observe(o),E(e,(()=>{r.disconnect()}));else{const t=()=>{const t=Jt(o);i(t)};E(e,Tn(o,t)()),t()}return st(_,E(e,Rt(t,o)))},()=>r&&l(!0,r.takeRecords())]},Pn=(t,n,r,o)=>{let s,i,l,c,a,u;const d=`[${Te}]`,p=`[${Pe}]`,y=["id","class","style","open","wrap","cols","rows"],{vt:h,ht:v,U:x,gt:b,bt:w,L:m,yt:O,wt:M,St:C,Ot:D}=t,k=t=>"rtl"===Zt(t,"direction"),P={$t:!1,B:k(h)},H=Cn(),A=we(fn),[E]=e({i:et,o:{w:0,h:0}},(()=>{const e=A&&A.V(t,n,P,H,r).W,o=!(O&&m)&&St(v,Te,Ce),s=!m&&M(Le),i=s&&ue(b),l=i&&D(),c=C(Ee,o),a=s&&e&&e()[0],u=Qt(x),d=te(x);return a&&a(),ae(b,i),l&&l(),o&&c(),{w:u.w+d.w,h:u.h+d.h}})),R=lt(o,{_:()=>s,v:()=>i,S(t,e){const[n]=t,[r]=e;return[L(at(n),at(r)).reduce(((t,e)=>(t[e]=n[e]||r[e],t)),{})]}}),U=t=>{const e=k(h);ut(t,{Ct:u!==e}),ut(P,{B:e}),u=e},z=(t,e)=>{const[n,r]=t,s={xt:r};return ut(P,{$t:n}),!e&&o(s),s},I=({ft:t,dt:e})=>{const n=t&&!e||!H.P?o:R,r={ft:t||e,dt:e};U(r),n(r)},_=(t,e)=>{const[,n]=E(),r={Ht:n};return U(r),n&&!e&&(t?o:R)(r),r},j=(t,e,n)=>{const r={Et:e};return U(r),e&&!n&&R(r),r},[N,V]=w?kn(v,z):[],B=!m&&Tn(v,I,{dt:!0}),[F,Z]=Dn(v,!1,j,{rt:y,ct:y}),W=m&&f&&new f((t=>{const e=t[t.length-1].contentRect;I({ft:!0,dt:re(e,a)}),a=e})),q=lt((()=>{const[,t]=E();o({Ht:t})}),{_:222,p:!0});return[()=>{W&&W.observe(v);const t=B&&B(),e=N&&N(),n=F(),r=H.K((t=>{t?R({zt:t}):q()}));return()=>{W&&W.disconnect(),t&&t(),e&&e(),c&&c(),n(),r()}},({It:t,At:e,Dt:n})=>{const r={},[o]=t("update.ignoreMutation"),[a,u]=t("update.attributes"),[h,f]=t("update.elementEvents"),[v,b]=t("update.debounce"),O=e||n;if(f||u){l&&l(),c&&c();const[t,e]=Dn(w||x,!0,_,{ct:L(y,a||[]),lt:h,it:d,_t:(t,e)=>{const{target:n,attributeName:r}=t;return!(e||!r||m)&&((t,e,n)=>{const r=At(t,e),o=t&&((t,e)=>{const n=e?T(e)&&e:document;return n&&n.querySelector(t)})(n,r),s=At(o,e)===r;return!(!r||!o)&&(r===t||o===t||s&&At(At(t,n),e)!==r)})(n,d,p)||!!At(n,`.${Ye}`)||!!(t=>$(o)&&o(t))(t)}});c=t(),l=e}if(b)if(R.m(),S(v)){const t=v[0],e=v[1];s=g(t)&&t,i=g(e)&&e}else g(v)?(s=v,i=!1):(s=!1,i=!1);if(O){const t=Z(),e=V&&V(),n=l&&l();t&&ut(r,j(t[0],t[1],O)),e&&ut(r,z(e[0],O)),n&&ut(r,_(n[0],O))}return U(r),r},P]},Hn=(t,e)=>$(e)?e.apply(0,t):e,An=(t,e,n,r)=>{const o=x(r)?n:r;return Hn(t,o)||e.apply(0,t)},Ln=(t,e,n,r)=>{const o=x(r)?n:r,s=Hn(t,o);return!!s&&(D(s)?s:e.apply(0,t))},En=(t,e,n,r)=>{const o="--os-viewport-percent",s="--os-scroll-percent",i="--os-scroll-direction",{Z:l}=Cn(),{scrollbars:c}=l(),{slot:a}=c,{vt:u,ht:d,U:p,Mt:y,gt:h,yt:f,L:x}=e,{scrollbars:b}=y?{}:t,{slot:g}=b||{},w=[],$=[],S=[],O=Ln([u,d,p],(()=>x&&f?u:d),a,g),M=t=>{if(v){let e=null,r=[];const o=new v({source:h,axis:t}),s=()=>{e&&e.cancel(),e=null},i=i=>{const{Tt:l}=n,c=pe(l)[t],a="x"===t,u=[qt(0,a),qt(`calc(100cq${a?"w":"h"} + -100%)`,a)],d=c?u:u.reverse();return r[0]===d[0]&&r[1]===d[1]||(s(),r=d,e=i.kt.animate({clear:["left"],transform:d},{timeline:o})),s};return{Rt:i}}},C={x:M("x"),y:M("y")},D=(t,e,n)=>{const r=n?Ct:Mt;H(t,(t=>{r(t.Ut,e)}))},T=(t,e)=>{H(t,(t=>{const[n,r]=e(t);Ft(n,r)}))},k=(t,e,n)=>{const r=m(n),o=!r||!n;(!r||n)&&D($,t,e),o&&D(S,t,e)},P=t=>{const e=t?"x":"y",n=zt(`${Ye} ${t?Ge:Je}`),o=zt(Ke),s=zt(Qe),i={Ut:n,Pt:o,kt:s},l=C[e];return E(t?$:S,i),E(w,[Rt(n,o),Rt(o,s),st(Et,n),l&&l.Rt(i),r(i,k,t)]),i},A=st(P,!0),L=st(P,!1);return A(),L(),[{Nt:()=>{const t=(()=>{const{Vt:t,Lt:e}=n,r=(t,e)=>yt(0,1,t/(t+e)||0);return{x:r(e.x,t.x),y:r(e.y,t.y)}})(),e=t=>e=>[e.Ut,{[o]:Vt(t)+""}];T($,e(t.x)),T(S,e(t.y))},qt:()=>{if(!v){const{Tt:t}=n,e=ye(t,ue(h)),r=t=>e=>[e.Ut,{[s]:Vt(t)+""}];T($,r(e.x)),T(S,r(e.y))}},jt:()=>{const{Tt:t}=n,e=pe(t),r=t=>e=>[e.Ut,{[i]:t?"0":"1"}];T($,r(e.x)),T(S,r(e.y)),v&&($.forEach(C.x.Rt),S.forEach(C.y.Rt))},Bt:()=>{if(x&&!f){const{Vt:t,Tt:e}=n,r=pe(e),o=ye(e,ue(h)),s=e=>{const{Ut:n}=e,s=Ht(n)===p&&n,i=(t,e,n)=>{const r=e*t;return Bt(n?r:-r)};return[s,s&&{transform:qt({x:i(o.x,t.x,r.x),y:i(o.y,t.y,r.y)})}]};T($,s),T(S,s)}},Ft:k,Xt:{Yt:$,Wt:A,Gt:st(T,$)},Jt:{Yt:S,Wt:L,Gt:st(T,S)}},()=>(Rt(O,$[0].Ut),Rt(O,S[0].Ut),st(_,w))]},Rn=(t,e,n,r)=>(o,l,c)=>{const{ht:a,U:d,L:p,gt:y,Kt:h,Ot:f}=e,{Ut:v,Pt:x,kt:b}=o,[g,w]=it(333),[m,S]=it(444),O=t=>{$(y.scrollBy)&&y.scrollBy({behavior:"smooth",left:t.x,top:t.y})};let M=!0;return st(_,[se(b,"pointermove pointerleave",r),se(v,"pointerenter",(()=>{l(nn,!0)})),se(v,"pointerleave pointercancel",(()=>{l(nn,!1)})),!p&&se(v,"mousedown",(()=>{const t=Lt();(vt(t,Pe)||vt(t,Te)||t===document.body)&&u(st(he,d),25)})),se(v,"wheel",(t=>{const{deltaX:e,deltaY:n,deltaMode:r}=t;M&&0===r&&Ht(v)===a&&O({x:e,y:n}),M=!1,l(ln,!0),g((()=>{M=!0,l(ln)})),le(t)}),{H:!1,I:!0}),se(v,"pointerdown",st(se,h,"click",ce,{A:!0,I:!0,H:!1}),{I:!0}),(()=>{const e="pointerup pointercancel lostpointercapture",r=`client${c?"X":"Y"}`,o=c?X:G,l=c?"left":"top",a=c?"w":"h",u=c?"x":"y",d=(t,e)=>r=>{const{Vt:o}=n,s=Jt(x)[a]-Jt(b)[a],i=e*r/s*o[u];ae(y,{[u]:t+i})},p=[];return se(x,"pointerdown",(n=>{const c=At(n.target,`.${Qe}`)===b,v=c?b:x,g=t.scrollbars,w=g[c?"dragScroll":"clickScroll"],{button:$,isPrimary:M,pointerType:C}=n,{pointers:D}=g;if(0===$&&M&&w&&(D||[]).includes(C)){_(p),S();const t=!c&&(n.shiftKey||"instant"===w),g=st(ee,b),$=st(ee,x),M=(t,e)=>(t||g())[l]-(e||$())[l],C=s(ee(y)[o])/Jt(y)[a]||1,D=d(ue(y)[u],1/C),T=n[r],k=g(),P=$(),H=k[o],A=M(k,P)+H/2,L=T-P[l],R=c?0:L-A,U=t=>{_(j),v.releasePointerCapture(t.pointerId)},z=c||t,I=f(),j=[se(h,e,U),se(h,"selectstart",(t=>le(t)),{H:!1}),se(x,e,U),z&&se(x,"pointermove",(t=>D(R+(t[r]-T)))),z&&(()=>{const t=ue(y);I();const e=ue(y),n={x:e.x-t.x,y:e.y-t.y};(i(n.x)>3||i(n.y)>3)&&(f(),ae(y,t),O(n),m(I))})];if(v.setPointerCapture(n.pointerId),t)D(R);else if(!c){const t=we(xn);if(t){const e=t(D,R,H,(t=>{t?I():E(j,I)}));E(j,e),E(p,st(e,!0))}}}}))})(),w,S])},Un=t=>{const e=Cn(),{Z:r,P:o}=e,{elements:s}=r(),{padding:i,viewport:l,content:c}=s,a=D(t),u=a?{}:t,{elements:d}=u,{padding:p,viewport:y,content:h}=d||{},f=a?t:u.target,v=kt(f),x=f.ownerDocument,b=x.documentElement,g=()=>x.defaultView||n,w=st(An,[f]),m=st(Ln,[f]),$=st(zt,""),S=st(w,$,l),O=st(m,$,c),M=S(y),C=M===f,T=C&&v,k=!C&&O(h),P=!C&&M===k,H=T?b:M,L=T?H:f,R=!C&&m($,i,p),U=!P&&k,z=[U,H,R,L].map((t=>D(t)&&!Ht(t)&&t)),I=t=>t&&A(z,t),j=!I(H)&&(t=>{const e=Jt(t),n=Qt(t),r=Zt(t,q),o=Zt(t,Y);return n.w-e.w>0&&!yn(r)||n.h-e.h>0&&!yn(o)})(H)?H:f,N=T?b:H,V={vt:f,ht:L,U:H,ln:R,bt:U,gt:N,Qt:T?x:H,an:v?b:j,Kt:x,yt:v,Mt:a,L:C,un:g,wt:t=>St(H,Pe,t),St:(t,e)=>$t(H,Pe,t,e),Ot:()=>$t(N,Pe,Re,!0)},{vt:B,ht:F,ln:Z,U:W,bt:X}=V,G=[()=>{bt(F,[Te,Me]),bt(B,Me),v&&bt(b,[Me,Te])}];let J=Pt([X,W,Z,F,B].find((t=>t&&!I(t))));const K=T?B:X||W,Q=st(_,G);return[V,()=>{const t=g(),e=Lt(),n=t=>{Rt(Ht(t),Pt(t)),Et(t)},r=t=>se(t,"focusin focusout focus blur",ce,{I:!0,H:!1}),s="tabindex",i=ft(W,s),l=r(e);return xt(F,Te,C?"":ke),xt(Z,Ie,""),xt(W,Pe,""),xt(X,_e,""),C||(xt(W,s,i||"-1"),v&&xt(b,De,"")),Rt(K,J),Rt(F,Z),Rt(Z||F,!C&&W),Rt(W,X),E(G,[l,()=>{const t=Lt(),e=I(W),o=e&&t===W?B:t,l=r(o);bt(Z,Ie),bt(X,_e),bt(W,Pe),v&&bt(b,De),i?xt(W,s,i):bt(W,s),I(X)&&n(X),e&&n(W),I(Z)&&n(Z),he(o),l()}]),o&&!C&&(mt(W,Pe,Ue),E(G,st(bt,W,Pe))),he(!C&&v&&e===B&&t.top===t?W:e),l(),J=0,Q},Q]},zn=({bt:t})=>({Zt:e,_n:n,Dt:r})=>{const{xt:o}=e||{},{$t:s}=n;t&&(o||r)&&Ft(t,{[G]:s&&"100%"})},In=({ht:t,ln:n,U:r,L:o},s)=>{const[i,l]=e({i:rt,o:Wt()},st(Wt,t,"padding",""));return({It:t,Zt:e,_n:c,Dt:a})=>{let[u,d]=l(a);const{P:p}=Cn(),{ft:y,Ht:h,Ct:f}=e||{},{B:v}=c,[x,b]=t("paddingAbsolute");(y||d||a||h)&&([u,d]=i(a));const g=!o&&(b||f||d);if(g){const t=!x||!n&&!p,e=u.r+u.l,o=u.t+u.b,i={[Z]:t&&!v?-e:0,[W]:t?-o:0,[F]:t&&v?-e:0,top:t?-u.t:0,right:t?v?-u.r:"auto":0,left:t?v?"auto":-u.l:0,[X]:t&&`calc(100% + ${e}px)`},l={[j]:t?u.t:0,[N]:t?u.r:0,[B]:t?u.b:0,[V]:t?u.l:0};Ft(n||r,i),Ft(r,l),ut(s,{ln:u,dn:!t,F:n?l:ut({},i,l)})}return{fn:g}}},_n=(t,o)=>{const s=Cn(),{ht:i,ln:l,U:c,L:u,Qt:d,gt:p,yt:y,St:h,un:f}=t,{P:v}=s,x=y&&u,b=st(r,0),g={display:()=>!1,direction:t=>"ltr"!==t,flexDirection:t=>t.endsWith("-reverse"),writingMode:t=>"horizontal-tb"!==t},w=at(g),m={i:et,o:{w:0,h:0}},$={i:nt,o:{}},S=t=>{h(Ee,!x&&t)},O=t=>{const e=w.some((e=>{const n=t[e];return n&&g[e](n)}));if(!e)return{D:{x:0,y:0},M:{x:1,y:1}};S(!0);const n=ue(p),r=h(ze,!0),o=se(d,Q,(t=>{const e=ue(p);t.isTrusted&&e.x===n.x&&e.y===n.y&&ie(t)}),{I:!0,A:!0});ae(p,{x:0,y:0}),r();const s=ue(p),i=Qt(p);ae(p,{x:i.w,y:i.h});const l=ue(p);ae(p,{x:l.x-s.x<1&&-i.w,y:l.y-s.y<1&&-i.h});const c=ue(p);return ae(p,n),a((()=>o())),{D:s,M:c}},M=(t,e)=>{const r=n.devicePixelRatio%1!=0?1:0,o={w:b(t.w-e.w),h:b(t.h-e.h)};return{w:o.w>r?o.w:0,h:o.h>r?o.h:0}},[C,D]=e(m,st(te,c)),[T,k]=e(m,st(Qt,c)),[P,H]=e(m),[A]=e($),[L,E]=e(m),[R]=e($),[U]=e({i:(t,e)=>tt(t,e,w),o:{}},(()=>(t=>!!t&&(t=>!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length))(t))(c)?Zt(c,w):{})),[z,I]=e({i:(t,e)=>nt(t.D,e.D)&&nt(t.M,e.M),o:{D:{x:0,y:0},M:{x:0,y:0}}}),_=we(fn),j=(t,e)=>`${e?He:Ae}${(t=>{const e=String(t||"");return e?e[0].toUpperCase()+e.slice(1):""})(t)}`,N=t=>{const e=t=>[J,K,Q].map((e=>j(e,t))),n=e(!0).concat(e()).join(" ");h(n),h(at(t).map((e=>j(t[e],"x"===e))).join(" "),!0)};return({It:e,Zt:n,_n:r,Dt:a},{fn:u})=>{const{ft:d,Ht:p,Ct:y,dt:g,zt:w}=n||{},m=_&&_.V(t,o,r,s,e),{Y:$,W:j,G:V}=m||{},[B,F]=pn(e,s),[Z,W]=e("overflow"),q=yn(Z.x),Y=yn(Z.y);let X=D(a),G=k(a),J=H(a),K=E(a);F&&v&&h(Ue,!B);{St(i,Te,Ce)&&S(!0);const[t]=j?j():[],[e]=X=C(a),[n]=G=T(a),r=Kt(c),o=x&&Gt(f()),s={w:b(n.w+e.w),h:b(n.h+e.h)},l={w:b((o?o.w:r.w+b(r.w-n.w))+e.w),h:b((o?o.h:r.h+b(r.h-n.h))+e.h)};t&&t(),K=L(l),J=P(M(s,l),a)}const[Q,tt]=K,[et,nt]=J,[rt,ot]=G,[st,it]=X,[lt,ct]=A({x:et.w>0,y:et.h>0}),at=q&&Y&&(lt.x||lt.y)||q&<.x&&!lt.y||Y&<.y&&!lt.x,dt=u||y||w||it||ot||tt||nt||W||F||!0,pt=hn(lt,Z),[yt,ht]=R(pt.k),[ft,vt]=U(a),xt=y||g||vt||ct||a,[bt,gt]=xt?z(O(ft),a):I();return dt&&(ht&&N(pt.k),V&&$&&Ft(c,V(pt,r,$(pt,rt,st)))),S(!1),$t(i,Te,Ce,at),$t(l,Ie,Ce,at),ut(o,{k:yt,Lt:{x:Q.w,y:Q.h},Vt:{x:et.w,y:et.h},rn:lt,Tt:de(bt,et)}),{en:ht,nn:tt,sn:nt,cn:gt||nt,vn:xt}}},jn=t=>{const[e,n,r]=Un(t),o={ln:{t:0,r:0,b:0,l:0},dn:!1,F:{[Z]:0,[W]:0,[F]:0,[j]:0,[N]:0,[B]:0,[V]:0},Lt:{x:0,y:0},Vt:{x:0,y:0},k:{x:K,y:K},rn:{x:!1,y:!1},Tt:{D:{x:0,y:0},M:{x:0,y:0}}},{vt:s,gt:i,L:l,Ot:c}=e,{P:a,T:u}=Cn(),d=!a&&(u.x||u.y),p=[zn(e),In(e,o),_n(e,o)];return[n,t=>{const e={},n=d&&ue(i),r=n&&c();return H(p,(n=>{ut(e,n(t,e)||{})})),ae(i,n),r&&r(),!l&&ae(s,0),e},o,e,r]},Nn=new WeakMap,Vn=t=>Nn.get(t),Bn=(t,e,n)=>{const{nt:r}=Cn(),o=D(t),s=o?t:t.target,i=Vn(s);if(e&&!i){let i=!1;const l=[],c={},a=t=>{const e=dt(t),n=we(me);return n?n(e,!0):e},u=ut({},r(),a(e)),[d,p,y]=ve(),[h,f,v]=ve(n),g=(t,e)=>{v(t,e),y(t,e)},[w,m,$,S,O]=((t,e,n,r)=>{let o=!1;const s=Sn(e,{}),[i,l,c,a,u]=jn(t),[d,p,y]=Pn(a,c,s,(t=>{b({},t)})),[h,f,,v]=((t,e,n,r,o,s)=>{let i,l,c,a,u,d=ot,p=0;const y=["mouse","pen"],h=t=>y.includes(t.pointerType),[f,v]=it(),[x,b]=it(100),[g,w]=it(100),[m,$]=it((()=>p)),[S,O]=En(t,o,r,Rn(e,o,r,(t=>h(t)&&R()))),{ht:M,Qt:C,yt:D}=o,{Ft:T,Nt:k,qt:P,jt:H,Bt:A}=S,L=(t,e)=>{if($(),t)T(sn);else{const t=st(T,sn,!0);p>0&&!e?m(t):t()}},R=()=>{(c?i:a)||(L(!0),x((()=>{L(!1)})))},U=t=>{T(on,t,!0),T(on,t,!1)},z=t=>{h(t)&&(i=c,c&&L(!0))},I=[$,b,w,v,()=>d(),se(M,"pointerover",z,{A:!0}),se(M,"pointerenter",z),se(M,"pointerleave",(t=>{h(t)&&(i=!1,c&&L(!1))})),se(M,"pointermove",(t=>{h(t)&&l&&R()})),se(C,"scroll",(t=>{f((()=>{P(),R()})),s(t),A()}))];return[()=>st(_,E(I,O())),({It:t,Dt:e,Zt:o,tn:s})=>{const{nn:i,sn:y,en:h,cn:f}=s||{},{Ct:v,dt:x}=o||{},{B:b}=n,{T:w}=Cn(),{k:m,rn:$}=r,[S,O]=t("showNativeOverlaidScrollbars"),[M,E]=t("scrollbars.theme"),[R,z]=t("scrollbars.visibility"),[I,_]=t("scrollbars.autoHide"),[j,N]=t("scrollbars.autoHideSuspend"),[V]=t("scrollbars.autoHideDelay"),[B,F]=t("scrollbars.dragScroll"),[Z,W]=t("scrollbars.clickScroll"),[q,Y]=t("overflow"),X=x&&!e,G=$.x||$.y,K=i||y||f||v||e,tt=h||z||Y,et=S&&w.x&&w.y,nt=(t,e,n)=>{const r=t.includes(Q)&&(R===J||"auto"===R&&e===Q);return T(tn,r,n),r};if(p=V,X&&(j&&G?(U(!1),d(),g((()=>{d=se(C,"scroll",st(U,!0),{A:!0})}))):U(!0)),O&&T(qe,et),E&&(T(u),T(M,!0),u=M),N&&!j&&U(!0),_&&(l="move"===I,c="leave"===I,a="never"===I,L(a,!0)),F&&T(an,B),W&&T(cn,!!Z),tt){const t=nt(q.x,m.x,!0),e=nt(q.y,m.y,!1);T(en,!(t&&e))}K&&(P(),k(),A(),f&&H(),T(rn,!$.x,!0),T(rn,!$.y,!1),T(Xe,b&&!D))},{},S]})(t,e,y,c,a,(t=>g("scroll",[C,t]))),x=t=>at(t).some((e=>!!t[e])),b=(t,s)=>{if(n())return!1;const{pn:i,Dt:c,At:a,hn:u}=t,d=i||{},h=!!c||!o,v={It:Sn(e,d,h),pn:d,Dt:h};if(u)return f(v),!1;const b=s||p(ut({},v,{At:a})),g=l(ut({},v,{_n:y,Zt:b}));f(ut({},v,{Zt:b,tn:g}));const w=x(b),m=x(g),$=w||m||!pt(d)||h;return o=!0,$&&r(t,{Zt:b,tn:g}),$};return[()=>{const{an:t,gt:e,Ot:n}=a,r=ue(t),o=[d(),i(),h()],s=n();return ae(e,r),s(),st(_,o)},b,()=>({gn:y,bn:c}),{yn:a,wn:v},u]})(t,u,(()=>i),(({pn:t,Dt:e},{Zt:n,tn:r})=>{const{ft:o,Ct:s,xt:i,Ht:l,Et:c,dt:a}=n,{nn:u,sn:d,en:p,cn:y}=r;g("updated",[C,{updateHints:{sizeChanged:!!o,directionChanged:!!s,heightIntrinsicChanged:!!i,overflowEdgeChanged:!!u,overflowAmountChanged:!!d,overflowStyleChanged:!!p,scrollCoordinatesChanged:!!y,contentMutation:!!l,hostMutation:!!c,appear:!!a},changedOptions:t||{},force:!!e}])})),M=t=>{(t=>{Nn.delete(t)})(s),_(l),i=!0,g("destroyed",[C,t]),p(),f()},C={options(t,e){if(t){const n=e?r():{},o=$n(u,ut(n,a(t)));pt(o)||(ut(u,o),m({pn:o}))}return ut({},u)},on:h,off:(t,e)=>{t&&e&&f(t,e)},state(){const{gn:t,bn:e}=$(),{B:n}=t,{Lt:r,Vt:o,k:s,rn:l,ln:c,dn:a,Tt:u}=e;return ut({},{overflowEdge:r,overflowAmount:o,overflowStyle:s,hasOverflow:l,scrollCoordinates:{start:u.D,end:u.M},padding:c,paddingAbsolute:a,directionRTL:n,destroyed:i})},elements(){const{vt:t,ht:e,ln:n,U:r,bt:o,gt:s,Qt:i}=S.yn,{Xt:l,Jt:c}=S.wn,a=t=>{const{kt:e,Pt:n,Ut:r}=t;return{scrollbar:r,track:n,handle:e}},u=t=>{const{Yt:e,Wt:n}=t,r=a(e[0]);return ut({},r,{clone:()=>{const t=a(n());return m({hn:!0}),t}})};return ut({},{target:t,host:e,padding:n||r,viewport:r,content:o||r,scrollOffsetElement:s,scrollEventElement:i,scrollbarHorizontal:u(l),scrollbarVertical:u(c)})},update:t=>m({Dt:t,At:!0}),destroy:st(M,!1),plugin:t=>c[at(t)[0]]};return E(l,[O]),((t,e)=>{Nn.set(t,e)})(s,C),ge(xe,Bn,[C,d,c]),((t,e)=>{const{nativeScrollbarsOverlaid:n,body:r}=e||{},{T:o,P:s,Z:i}=Cn(),{nativeScrollbarsOverlaid:l,body:c}=i().cancel,a=null!=n?n:l,u=x(r)?c:r,d=(o.x||o.y)&&a,p=t&&(b(u)?!s:u);return!!d||!!p})(S.yn.yt,!o&&t.cancel)?(M(!0),C):(E(l,w()),g("initialized",[C]),C.update(),C)}return i};return Bn.plugin=t=>{const e=S(t),n=e?t:[t],r=n.map((t=>ge(t,Bn)[0]));return(t=>{H(t,(t=>H(t,((e,n)=>{xe[n]=t[n]}))))})(n),e?r:r[0]},Bn.valid=t=>{const e=t&&t.elements,n=$(e)&&e();return C(n)&&!!Vn(n.target)},Bn.env=()=>{const{N:t,T:e,P:n,J:r,st:o,et:s,Z:i,tt:l,nt:c,ot:a}=Cn();return ut({},{scrollbarsSize:t,scrollbarsOverlaid:e,scrollbarsHiding:n,scrollTimeline:r,staticDefaultInitialization:o,staticDefaultOptions:s,getDefaultInitialization:i,setDefaultInitialization:l,getDefaultOptions:c,setDefaultOptions:a})},Bn.nonce=t=>{On=t},Bn.trustedTypePolicy=t=>{Ut=t},t.ClickScrollPlugin=bn,t.OverlayScrollbars=Bn,t.ScrollbarsHidingPlugin=vn,t.SizeObserverPlugin=dn,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),t}({}); \ No newline at end of file diff --git a/js/solar/common.js b/js/solar/common.js new file mode 100644 index 0000000..3d8dc5f --- /dev/null +++ b/js/solar/common.js @@ -0,0 +1,182 @@ +/* + * Gemeinsame Bausteine der Dashboard-Seiten. + * Wird vor solarMQTT.js / homeMQTT.js / heatMQTT.js / historyMQTT.js geladen. + */ + +/** Sekunden als "HH:MM". */ +String.prototype.toHHMM = function () { + var sec_num = parseInt(this, 10); + var hours = Math.floor(sec_num / 3600); + var minutes = Math.floor((sec_num - (hours * 3600)) / 60); + if (hours < 10) { hours = "0" + hours; } + if (minutes < 10) { minutes = "0" + minutes; } + return hours + ':' + minutes; +}; + +function powerToString(power) { + if (Math.abs(power) > 999) { + power = power / 1000; + return power.toPrecision(3) + " kW"; + } else { + return Math.round(power) + " W"; + } +} + +function loadingHTML(msg) { + return "
Loading...
   " + msg + "
"; +} + +/** + * Diagrammdaten holen und eintragen. + * params Funktion, die {FROM, TO} liefert (Zeitraum aendert sich zur Laufzeit) + * sunrise true -> Sonnenstands-Markierungen fuer denselben Zeitraum + * Objekt {FROM, TO} -> fester Zeitraum + * autoUpdate alle 5 Minuten neu laden + * updateMode an chart.update() durchgereicht ("none" = ohne Animation) + */ +async function getData(chart, url, options = {}) { + const { + params = null, + sunrise = false, + autoUpdate = true, + updateMode = undefined, + refreshMs = 5 * 60 * 1000 + } = options; + + const range = params ? params() : null; + const query = range ? "?FROM=" + range.FROM + "&TO=" + range.TO : ""; + + try { + chart.options.events = []; // waehrend des Ladens keine Tooltips + const response = await fetch(url + query); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + chart.data = await response.json(); + + if (sunrise) { + const sunRange = sunrise === true ? range : sunrise; + const response2 = await fetch("ajax/getSunrise.php?FROM=" + sunRange.FROM + "&TO=" + sunRange.TO); + if (!response2.ok) { + throw new Error(`Response status: ${response2.status}`); + } + chart.options.plugins.annotation.annotations = await response2.json(); + } + chart.options.events = ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove']; + chart.update(updateMode); + } catch (error) { + console.log("getData " + url + ": " + error.message); + } + + if (autoUpdate) { + setTimeout(function () { getData(chart, url, options); }, refreshMs); + } +} + +/** Vorgerendertes HTML-Schnipsel in ein Element laden. */ +async function getStats(elem_id, url) { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + document.getElementById(elem_id).innerHTML = await response.text(); + } catch (error) { + console.log("getStats " + url + ": " + error.message); + } +} + +/** + * Formular des Modals per POST abschicken. + * Der Start/Stop-Knopf der Ladesteuerung schickt nur sich selbst. + */ +function submitFormAjax(event) { + let xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function () { + if (this.readyState === 4 && this.status === 200) { + setTimeout(function () { modalEV.hide(); }, 700); + if (xmlhttp.responseText) { + alert(xmlhttp.responseText); + } + } + }; + + let post = ""; + if (event.currentTarget.id == "evStart/Stop") { + post = "evStart/Stop=" + event.currentTarget.innerHTML; + } else { + const form = document.getElementById('modalEV').querySelector('form'); + for (const element of Array.from(form.elements)) { + if (!element.name) continue; + if (element.type == "radio" || element.type == "checkbox") { + if (element.checked) { + post += element.name + "=" + encodeURIComponent(element.value) + "&"; + } + } else if (element.type == "button") { + if (!element.classList.contains("accordion-button")) { + post += element.name + "=" + encodeURIComponent(element.innerHTML) + "&"; + } + } else { + post += element.name + "=" + encodeURIComponent(element.value) + "&"; + } + } + } + + xmlhttp.open("POST", event.currentTarget.contentURL, true); + xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + document.getElementById('modalEV').querySelector('.modal-body').innerHTML = loadingHTML("Änderungen werden übernommen..."); + xmlhttp.send(post); + return false; +} + +/** + * Schieberegler im Modal mit seiner Beschriftung verbinden. + * label(wert) liefert den anzuzeigenden Text. + */ +function bindModalSlider(slider, span, label) { + slider.oninput = function () { + const min = Number(slider.min); + const max = Number(slider.max); + const percent = (Number(slider.value) - min) * 100 / (max - min); + slider.style.setProperty("--background-size", `${percent}%`); + + // Beschriftung mitlaufen lassen, an den Raendern etwas gebremst + let margin = percent; + if (margin < 10) { + margin = margin - 0.4 * margin; + } else if (margin < 85) { + margin = margin - 4; + } else { + margin = margin - 4 - 0.5 * (margin - 85); + } + span.setAttribute('style', 'margin-left:' + margin + '%;'); + span.innerHTML = label(slider.value); + }; +} + +/** Meteoblue-Meteogramm einhängen, sofern die Seite einen Platz dafür hat. */ +function mountMeteogram() { + const target = document.getElementById("meteogram"); + if (!target) return; + // loading="lazy": das Meteogramm ist ein vollstaendiges Fremd-Dokument und + // liegt unterhalb der Falz - es wird erst geladen, wenn man hinscrollt. + target.innerHTML = ""; +} + +/** + * Wetterkacheln aktualisieren (restricted/weather_cards.html). + * value(name) liefert den Wert zu weatherStation/ - die Seiten legen + * die MQTT-Werte unterschiedlich ab, deshalb der Zugriff über eine Funktion. + */ +function updateWeatherCards(value) { + const windDir = document.getElementById("windDir"); + if (!windDir) return; // Seite ohne Wetterkacheln + windDir.style.transform = "rotate(" + (90 + Number(value("windDeg"))) + "deg)"; + document.getElementById("windSpd").innerHTML = value("avgWindspeed"); + document.getElementById("humidity").innerHTML = value("hum"); + document.getElementById("temp").innerHTML = value("tempAmb"); + document.getElementById("windDirGust").style.transform = "rotate(" + (90 + Number(value("gustDeg"))) + "deg)"; + document.getElementById("windGust").innerHTML = value("maxgust"); + document.getElementById("ambPress").innerHTML = Math.round(value("qff") * 10) / 10; +} diff --git a/js/solar/heatMQTT.js b/js/solar/heatMQTT.js index 56ef647..5929059 100644 --- a/js/solar/heatMQTT.js +++ b/js/solar/heatMQTT.js @@ -54,7 +54,9 @@ const solarMQTT = { function messageReceived(topic, message) { setNestedProp(mqttData, topic, message); if (topic == "solarManager/P_Load") { - setTimeout(function () { solarSVG.updateValuesMQTT(mqttData) }, 200); //give the object tree some time to build up and receive all values + setTimeout(function () { solarSVG.updateValuesMQTT(mqttData) }, 200); //give the object tree some time to build up and receive all values + } else if (topic == "weatherStation/windDeg") { + setTimeout(function () { updateWeatherCards(name => mqttData["weatherStation"]?.[name]) }, 200); } } } @@ -185,52 +187,7 @@ var chartSettings = { document.addEventListener('readystatechange', function () { if (event.target.readyState === "complete") { solarMQTT.getMQTT(); - getData(heatChart, 'ajax/getHeaterData.php'); - getData(waterChart, 'ajax/getWaterData.php'); + getData(heatChart, 'ajax/getHeaterData.php', { sunrise: { FROM: -24, TO: 0 } }); + getData(waterChart, 'ajax/getWaterData.php', { sunrise: { FROM: -24, TO: 0 } }); } }); - - String.prototype.toHHMM = function () { - var sec_num = parseInt(this, 10); // don't forget the second param - var hours = Math.floor(sec_num / 3600); - var minutes = Math.floor((sec_num - (hours * 3600)) / 60); - var seconds = sec_num - (hours * 3600) - (minutes * 60); - - if (hours < 10) { hours = "0" + hours; } - if (minutes < 10) { minutes = "0" + minutes; } - return hours + ':' + minutes; - } - - async function getData(chart, url, sunrise=true) { - try { - console.log("fetching"); - const response = await fetch(url); - if (!response.ok) { - console.log("err"); - throw new Error(`Response status: ${response.status}`); - } - chart.data = await response.json(); - if(sunrise){ - const response2 = await fetch("ajax/getSunrise.php?FROM=-24&TO=0"); - if (!response2.ok) { - console.log("err"); - throw new Error(`Response status: ${response2.status}`); - } - chart.options.plugins.annotation.annotations = await response2.json(); - } - chart.update(); - } catch (error) { - console.log(error.message); - } - setTimeout(function () { getData(chart, url, sunrise) }, 5 * 60 * 1000); //renew data every 5 min. - } - - function powerToString(power) { - if (Math.abs(power) > 999) { - power = power / 1000 - return power.toPrecision(3) + " kW" - } else { - return Math.round(power) + " W" - } - - } \ No newline at end of file diff --git a/js/solar/historyMQTT.js b/js/solar/historyMQTT.js index 2980f70..dc51248 100644 --- a/js/solar/historyMQTT.js +++ b/js/solar/historyMQTT.js @@ -139,61 +139,14 @@ Chart.defaults.plugins.legend.labels.generateLabels = legendLabels; ); document.addEventListener('readystatechange', function () { if (event.target.readyState === "complete") { - getData(prodChart, 'ajax/getProdData_month.php'); - getData(consChart, 'ajax/getConsData_month.php'); - getData(prodChartYear, 'ajax/getProdData_year.php'); - getData(consChartYear, 'ajax/getConsData_year.php'); - getData(prodChartDecade, 'ajax/getProdData_decade.php'); - getData(consChartDecade, 'ajax/getConsData_decade.php'); - //getData(prodChart, 'ajax/getProdData.php'); - //getData(foreChart,'ajax/getForecastData.php', false); + getData(prodChart, 'ajax/energyHistory.php?series=prod&range=month'); + getData(consChart, 'ajax/energyHistory.php?series=cons&range=month'); + getData(prodChartYear, 'ajax/energyHistory.php?series=prod&range=year'); + getData(consChartYear, 'ajax/energyHistory.php?series=cons&range=year'); + getData(prodChartDecade, 'ajax/energyHistory.php?series=prod&range=decade'); + getData(consChartDecade, 'ajax/energyHistory.php?series=cons&range=decade'); getStats("Stats-Year","ajax/getStats.php?type=ThisYear"); getStats("Stats-Lastyear","ajax/getStats.php?type=LastYear"); getStats("Stats-Prelastyear","ajax/getStats.php?type=PreLastYear"); } }); - - async function getData(chart, url, sunrise=true) { - try { - console.log("fetching"+chart); - const response = await fetch(url); - if (!response.ok) { - console.log("err"); - throw new Error(`Response status: ${response.status}`); - } - chart.data = await response.json(); - - - chart.update(); - //chart.options.scales.x.min = chart.data.labels[0]-(chart.data.labels[1]-chart.data.labels[0])/2; - //chart.options.scales.x.max = chart.data.labels[chart.data.labels.length-1]+(chart.data.labels[1]-chart.data.labels[0])/2; - chart.update(); - } catch (error) { - console.log(error.message); - } - setTimeout(function () { getData(chart, url, sunrise) }, 5 * 60 * 1000); //renew data every 5 min. - } - async function getStats(elem_id, url) { - try { - console.log("fetching"); - const response = await fetch(url); - if (!response.ok) { - console.log("err"); - throw new Error(`Response status: ${response.status}`); - } - document.getElementById(elem_id).innerHTML = await response.text(); - } catch (error) { - console.log(error.message); - } - setTimeout(function () { getData(chart, url, sunrise) }, 5 * 60 * 1000); //renew data every 5 min. - } - - function powerToString(power) { - if (Math.abs(power) > 999) { - power = power / 1000 - return power.toPrecision(3) + " kW" - } else { - return Math.round(power) + " W" - } - - } \ No newline at end of file diff --git a/js/solar/homeMQTT.js b/js/solar/homeMQTT.js index 3fa3ead..70a5e03 100644 --- a/js/solar/homeMQTT.js +++ b/js/solar/homeMQTT.js @@ -51,205 +51,26 @@ const homeSVG = { fillElementArray: function () { }, - updateValuesMQTT: function (mqttData) { - var htmlNode = document -//OG - if(typeof(mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["Heating"] == "false") htmlNode.getElementById('OG_wozi_heater').setAttribute("display", "none"); - else htmlNode.getElementById('OG_wozi_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["mode"] == "Overheating") htmlNode.getElementById('OG_wozi_buffer').setAttribute("display", ""); - else htmlNode.getElementById('OG_wozi_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_pres_wozi').innerHTML = Math.floor(mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_tmp_wozi').innerHTML = mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('OG_hum_wozi').innerHTML = mqttData["Raumtemp"]["OG"]["Wohnzimmer"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["OG"]["Bad"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["Bad"]["Heating"] == "false") htmlNode.getElementById('OG_bad_heater').setAttribute("display", "none"); - else htmlNode.getElementById('OG_bad_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["OG"]["Bad"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["Bad"]["mode"] == "Overheating") htmlNode.getElementById('OG_bad_buffer').setAttribute("display", ""); - else htmlNode.getElementById('OG_bad_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["OG"]["Bad"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_pres_bad').innerHTML = Math.floor(mqttData["Raumtemp"]["OG"]["Bad"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["Bad"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_tmp_bad').innerHTML = mqttData["Raumtemp"]["OG"]["Bad"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["Bad"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('OG_hum_bad').innerHTML = mqttData["Raumtemp"]["OG"]["Bad"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["Heating"] == "false") htmlNode.getElementById('OG_schlafen_heater').setAttribute("display", "none"); - else htmlNode.getElementById('OG_schlafen_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["mode"] == "Overheating") htmlNode.getElementById('OG_schlafen_buffer').setAttribute("display", ""); - else htmlNode.getElementById('OG_schlafen_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_pres_schlafen').innerHTML = Math.floor(mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_tmp_schlafen').innerHTML = mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('OG_hum_schlafen').innerHTML = mqttData["Raumtemp"]["OG"]["Schlafzimmer"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["OG"]["KiZi"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["KiZi"]["Heating"] == "false") htmlNode.getElementById('OG_kizi_heater').setAttribute("display", "none"); - else htmlNode.getElementById('OG_kizi_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["OG"]["KiZi"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["KiZi"]["mode"] == "Overheating") htmlNode.getElementById('OG_kizi_buffer').setAttribute("display", ""); - else htmlNode.getElementById('OG_kizi_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["OG"]["KiZi"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_pres_kizi').innerHTML = Math.floor(mqttData["Raumtemp"]["OG"]["KiZi"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["KiZi"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_tmp_kizi').innerHTML = mqttData["Raumtemp"]["OG"]["KiZi"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["KiZi"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('OG_hum_kizi').innerHTML = mqttData["Raumtemp"]["OG"]["KiZi"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["OG"]["Buero"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["Buero"]["Heating"] == "false") htmlNode.getElementById('OG_buero_heater').setAttribute("display", "none"); - else htmlNode.getElementById('OG_buero_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["OG"]["Buero"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["OG"]["Buero"]["mode"] == "Overheating") htmlNode.getElementById('OG_buero_buffer').setAttribute("display", ""); - else htmlNode.getElementById('OG_buero_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["OG"]["Buero"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_pres_buero').innerHTML = Math.floor(mqttData["Raumtemp"]["OG"]["Buero"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["Buero"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('OG_tmp_buero').innerHTML = mqttData["Raumtemp"]["OG"]["Buero"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["OG"]["Buero"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('OG_hum_buero').innerHTML = mqttData["Raumtemp"]["OG"]["Buero"]["rHum[%]"]+" %rF"; - -//EG - if(typeof(mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["Heating"] == "false") htmlNode.getElementById('EG_wozi_heater').setAttribute("display", "none"); - else htmlNode.getElementById('EG_wozi_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["mode"] == "Overheating") htmlNode.getElementById('EG_wozi_buffer').setAttribute("display", ""); - else htmlNode.getElementById('EG_wozi_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_pres_wozi').innerHTML = Math.floor(mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_tmp_wozi').innerHTML = mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('EG_hum_wozi').innerHTML = mqttData["Raumtemp"]["EG"]["Wohnzimmer"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["EG"]["Bad"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Bad"]["Heating"] == "false") htmlNode.getElementById('EG_bad_heater').setAttribute("display", "none"); - else htmlNode.getElementById('EG_bad_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Bad"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Bad"]["mode"] == "Overheating") htmlNode.getElementById('EG_bad_buffer').setAttribute("display", ""); - else htmlNode.getElementById('EG_bad_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Bad"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_pres_bad').innerHTML = Math.floor(mqttData["Raumtemp"]["EG"]["Bad"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Bad"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_tmp_bad').innerHTML = mqttData["Raumtemp"]["EG"]["Bad"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Bad"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('EG_hum_bad').innerHTML = mqttData["Raumtemp"]["EG"]["Bad"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["EG"]["Schlafen"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Schlafen"]["Heating"] == "false") htmlNode.getElementById('EG_schlafen_heater').setAttribute("display", "none"); - else htmlNode.getElementById('EG_schlafen_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Schlafen"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Schlafen"]["mode"] == "Overheating") htmlNode.getElementById('EG_schlafen_buffer').setAttribute("display", ""); - else htmlNode.getElementById('EG_schlafen_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Schlafen"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_pres_schlafen').innerHTML = Math.floor(mqttData["Raumtemp"]["EG"]["Schlafen"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Schlafen"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_tmp_schlafen').innerHTML = mqttData["Raumtemp"]["EG"]["Schlafen"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Schlafen"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('EG_hum_schlafen').innerHTML = mqttData["Raumtemp"]["EG"]["Schlafen"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["EG"]["Florian"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Florian"]["Heating"] == "false") htmlNode.getElementById('EG_kizi_heater').setAttribute("display", "none"); - else htmlNode.getElementById('EG_kizi_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Florian"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Florian"]["mode"] == "Overheating") htmlNode.getElementById('EG_kizi_buffer').setAttribute("display", ""); - else htmlNode.getElementById('EG_kizi_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Florian"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_pres_kizi').innerHTML = Math.floor(mqttData["Raumtemp"]["EG"]["Florian"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Florian"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_tmp_kizi').innerHTML = mqttData["Raumtemp"]["EG"]["Florian"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Florian"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('EG_hum_kizi').innerHTML = mqttData["Raumtemp"]["EG"]["Florian"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["EG"]["Magdalena"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Magdalena"]["Heating"] == "false") htmlNode.getElementById('EG_buero_heater').setAttribute("display", "none"); - else htmlNode.getElementById('EG_buero_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Magdalena"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["EG"]["Magdalena"]["mode"] == "Overheating") htmlNode.getElementById('EG_buero_buffer').setAttribute("display", ""); - else htmlNode.getElementById('EG_buero_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["EG"]["Magdalena"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_pres_buero').innerHTML = Math.floor(mqttData["Raumtemp"]["EG"]["Magdalena"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Magdalena"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('EG_tmp_buero').innerHTML = mqttData["Raumtemp"]["EG"]["Magdalena"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["EG"]["Magdalena"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('EG_hum_buero').innerHTML = mqttData["Raumtemp"]["EG"]["Magdalena"]["rHum[%]"]+" %rF"; -//UG - if(typeof(mqttData["Raumtemp"]["UG"]["Kueche"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["UG"]["Kueche"]["Heating"] == "false") htmlNode.getElementById('UG_kueche_heater').setAttribute("display", "none"); - else htmlNode.getElementById('UG_kueche_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["UG"]["Kueche"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["UG"]["Kueche"]["mode"] == "Overheating") htmlNode.getElementById('UG_kueche_buffer').setAttribute("display", ""); - else htmlNode.getElementById('UG_kueche_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["UG"]["Kueche"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('UG_pres_kueche').innerHTML = Math.floor(mqttData["Raumtemp"]["UG"]["Kueche"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["UG"]["Kueche"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('UG_tmp_kueche').innerHTML = mqttData["Raumtemp"]["UG"]["Kueche"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["UG"]["Kueche"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('UG_hum_kueche').innerHTML = mqttData["Raumtemp"]["UG"]["Kueche"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["UG"]["Buero"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["UG"]["Buero"]["Heating"] == "false") htmlNode.getElementById('UG_buero_heater').setAttribute("display", "none"); - else htmlNode.getElementById('UG_buero_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["UG"]["Buero"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["UG"]["Buero"]["mode"] == "Overheating") htmlNode.getElementById('UG_buero_buffer').setAttribute("display", ""); - else htmlNode.getElementById('UG_buero_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["UG"]["Buero"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('UG_pres_buero').innerHTML = Math.floor(mqttData["Raumtemp"]["UG"]["Buero"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["UG"]["Buero"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('UG_tmp_buero').innerHTML = mqttData["Raumtemp"]["UG"]["Buero"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["UG"]["Buero"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('UG_hum_buero').innerHTML = mqttData["Raumtemp"]["UG"]["Buero"]["rHum[%]"]+" %rF"; - - if(typeof(mqttData["Raumtemp"]["UG"]["Bad"]["Heating"]) != "undefined"){ - if(mqttData["Raumtemp"]["UG"]["Bad"]["Heating"] == "false") htmlNode.getElementById('UG_bad_heater').setAttribute("display", "none"); - else htmlNode.getElementById('UG_bad_heater').setAttribute("display", ""); - } - if(typeof(mqttData["Raumtemp"]["UG"]["Bad"]["mode"]) != "undefined"){ - if(mqttData["Raumtemp"]["UG"]["Bad"]["mode"] == "Overheating") htmlNode.getElementById('UG_bad_buffer').setAttribute("display", ""); - else htmlNode.getElementById('UG_bad_buffer').setAttribute("display", "none"); - } - if(typeof(mqttData["Raumtemp"]["UG"]["Bad"]["Set Temp[degC]"]) != "undefined") - htmlNode.getElementById('UG_pres_bad').innerHTML = Math.floor(mqttData["Raumtemp"]["UG"]["Bad"]["Set Temp[degC]"])+" °C"; - if(typeof(mqttData["Raumtemp"]["UG"]["Bad"]["Temp[degC]"]) != "undefined") - htmlNode.getElementById('UG_tmp_bad').innerHTML = mqttData["Raumtemp"]["UG"]["Bad"]["Temp[degC]"]+" °C"; - if(typeof(mqttData["Raumtemp"]["UG"]["Bad"]["rHum[%]"]) != "undefined") - htmlNode.getElementById('UG_hum_bad').innerHTML = mqttData["Raumtemp"]["UG"]["Bad"]["rHum[%]"]+" %rF"; - + updateValuesMQTT: function (mqttData) { + // homeRooms liefert restricted/rooms.php (in home.php eingebettet). + // Jeder Raum bekommt die gleiche Behandlung - Raeume kommen dort dazu, + // nicht hier. + homeRooms.forEach(function (room) { + const values = mqttData["Raumtemp"]?.[room.floor]?.[room.mqtt]; + if (!values) return; // Topic noch nicht empfangen + const el = (part) => document.getElementById(room.floor + "_" + part); + + if (typeof (values["Heating"]) != "undefined") + el(room.id + "_heater").setAttribute("display", values["Heating"] == "false" ? "none" : ""); + if (typeof (values["mode"]) != "undefined") + el(room.id + "_buffer").setAttribute("display", values["mode"] == "Overheating" ? "" : "none"); + if (typeof (values["Set Temp[degC]"]) != "undefined") + el("pres_" + room.id).innerHTML = Math.floor(values["Set Temp[degC]"]) + "\u2009\u00b0C"; + if (typeof (values["Temp[degC]"]) != "undefined") + el("tmp_" + room.id).innerHTML = values["Temp[degC]"] + "\u2009\u00b0C"; + if (typeof (values["rHum[%]"]) != "undefined") + el("hum_" + room.id).innerHTML = values["rHum[%]"] + "\u2009%rF"; + }); } } @@ -336,13 +157,10 @@ function switchFloor(floor){ document.addEventListener('readystatechange', function () { if (event.target.readyState === "complete") { homeMQTT.getMQTT(); - this.getElementById("meteogram").innerHTML = ""; + mountMeteogram(); } }); - function loadingHTML(msg) { - return "
Loading...
   " + msg + "
" - } function openModal(heater) { var contentURL = "./ajax/roomtemp.php?heater="+heater; @@ -365,21 +183,7 @@ function switchFloor(floor){ modalBodyElement.innerHTML = html; var slider = document.getElementById("modal-slider"); var span = document.getElementById("modal-slider-label"); - slider.oninput = function () { - sliderPrcnt=(slider.value-10)*5; - slider.style.setProperty("--background-size", `${sliderPrcnt}%`); - marginValue = sliderPrcnt; - if (marginValue < 10) { - marginValue = marginValue - 0.4 * marginValue; - } else if (marginValue < 85) { - marginValue = marginValue - 4; - } - else { - marginValue = marginValue - 4 - 0.5 * (marginValue - 85); - } - span.setAttribute('style', 'margin-left:' + marginValue + '%;'); - span.innerHTML = this.value + " °C"; - } + bindModalSlider(slider, span, value => value + " °C"); if(typeof(mqttData["Raumtemp"][heaterMQTT[0]][heaterMQTT[1]]["Set Temp[degC]"])!= "undefined") slider.setAttribute("value",mqttData["Raumtemp"][heaterMQTT[0]][heaterMQTT[1]]["Set Temp[degC]"]); @@ -391,39 +195,3 @@ function switchFloor(floor){ modalBodyElement.innerHTML += error.message; }); } - - function submitFormAjax(event) { - let xmlhttp = window.XMLHttpRequest ? - new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); - - xmlhttp.onreadystatechange = function () { - if (this.readyState === 4 && this.status === 200){ - setTimeout(function () { modalEV.hide() }, 700); - if(xmlhttp.responseText){ - alert(xmlhttp.responseText); - } - } - } - const form = document.getElementById('modalEV').querySelector('form'); - post = ""; - // Get interesting form elements - const formElements = Array.from(form.elements); - for (let i = 0; i < formElements.length; i++) { - if(formElements[i].name){ - if (formElements[i].type == "radio" && formElements[i].checked) { - post += formElements[i].name + "=" + encodeURIComponent(formElements[i].value) + "&"; - } else if(formElements[i].type == "checkbox" && formElements[i].checked){ - post += formElements[i].name + "=" + encodeURIComponent(formElements[i].value) + "&"; - } else if(formElements[i].type == "button" && !formElements[i].classList.contains("accordion-button")){ - post += formElements[i].name + "=" + encodeURIComponent(formElements[i].innerHTML) + "&"; - }else if(formElements[i].type != "checkbox" && formElements[i].type != "radio") { - post += formElements[i].name + "=" + encodeURIComponent(formElements[i].value) + "&"; - } - } - } - xmlhttp.open("POST", event.currentTarget.contentURL, true); - xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - document.getElementById('modalEV').querySelector('.modal-body').innerHTML = loadingHTML("Änderungen werden übernommen..."); - xmlhttp.send(post); - return false; - } \ No newline at end of file diff --git a/js/solar/solarMQTT.js b/js/solar/solarMQTT.js index 09bbc8c..7757e6f 100644 --- a/js/solar/solarMQTT.js +++ b/js/solar/solarMQTT.js @@ -77,7 +77,7 @@ const solarMQTT = { }, 1000); } }else if(topic == "weatherStation/windDeg"){ - setTimeout(function () { updateValuesWeather() }, 200); //give the object tree some time to build up and receive all values + setTimeout(function () { updateWeatherCards(name => mqttData["weatherStation/" + name]) }, 200); //give the object tree some time to build up and receive all values } } }, @@ -89,10 +89,54 @@ const solarMQTT = { } } +// Die Knoten im eingebetteten Realtime-SVG bleiben ueber die Lebensdauer der +// Seite bestehen. updateValuesMQTT fasst 104 davon an - bis zu einmal pro +// Sekunde -, deshalb werden sie einmal nachgeschlagen und dann behalten. +const svgElements = {}; +function svgElement(id) { + let node = svgElements[id]; + if (node === undefined) { + node = svgElements[id] = document.getElementById(id); + } + return node; +} + +// Nur schreiben, wenn sich der Wert wirklich geaendert hat. Jeder Schreibzugriff +// macht Style und Paint des Elements ungueltig - bei einem gefilterten Element +// wird dabei sein Filter neu gerastert. Gemessen waren rund 71 % der Zugriffe +// pro Update reine Wiederholungen desselben Werts; die entfallen damit. +const letzterWert = new Map(); +function schreibe(schluessel, wert, anwenden) { + const s = String(wert); + if (letzterWert.get(schluessel) === s) return; + letzterWert.set(schluessel, s); + anwenden(s); +} +// textContent statt innerHTML: setzt einen Textknoten, ohne den HTML-Parser zu bemuehen. +function setText(id, wert) { schreibe("t:" + id, wert, s => { svgElement(id).textContent = s; }); } +function setHTML(id, markup) { schreibe("h:" + id, markup, s => { svgElement(id).innerHTML = s; }); } +function setAttr(id, name, wert) { schreibe("a:" + id + ":" + name, wert, s => { svgElement(id).setAttribute(name, s); }); } +function setDisplay(id, wert) { schreibe("d:" + id, wert, s => { svgElement(id).style.display = s; }); } + + + +// Punktgroesse einer Flusslinie setzen und die Animation anhalten, wenn kein +// Fluss anliegt. --w ist der Durchmesser, die CSS-Regel macht daraus den Radius +// aller Punkte der Gruppe. will-change nur solange die Gruppe laeuft: es fordert +// eine eigene Compositor-Ebene an, und die kostet Speicher. +function setFlowWidth(id, width) { + const flow = svgElement(id); + const sichtbar = Math.abs(width) >= 0.5; + flow.style.setProperty("--w", (sichtbar ? Math.abs(width) : 0) + "px"); + flow.style.animationPlayState = sichtbar ? "running" : "paused"; + flow.style.willChange = sichtbar ? "transform" : "auto"; +} + const solarSVG = { updateCnt: 99, + /** Cache verwerfen - noetig, falls das SVG neu gezeichnet wird. */ fillElementArray: function () { - + for (const id in svgElements) delete svgElements[id]; }, updateValuesMQTT: function (mqttData) { @@ -143,16 +187,15 @@ const solarSVG = { common = 0; } - var htmlNode = document - htmlNode.getElementById('consumerArc').setAttribute("d", describeArc(100, 100, 95, 0, Math.round(-cons / angleFactor))); + setAttr('consumerArc', "d", describeArc(100, 100, 95, 0, Math.round(-cons / angleFactor))); - htmlNode.getElementById("ogArc").setAttribute("d", describeArc(100, 100, 95, 0, Math.round(-og / angleFactor))); - htmlNode.getElementById("consumerText").innerHTML = powerToString(-cons); + setAttr("ogArc", "d", describeArc(100, 100, 95, 0, Math.round(-og / angleFactor))); + setText("consumerText", powerToString(-cons)); - htmlNode.getElementById("consumerTextAllg").innerHTML = "Gemein: "+powerToString(common); + setText("consumerTextAllg", "Gemein: "+powerToString(common)); if (evPower > 0) { - htmlNode.getElementById("evCharge").style.display = ""; + setDisplay("evCharge", ""); if (carRemChrg > -1) { var today = new Date(); today.setMinutes(today.getMinutes() + carRemChrg); @@ -162,31 +205,31 @@ const solarSVG = { h = "0" + h; if (m < 10) m = "0" + m; - htmlNode.getElementById("evRemText").innerHTML = h + ":" + m + setText("evRemText", h + ":" + m); } } else { - htmlNode.getElementById("evRemText").innerHTML = ""; + setText("evRemText", ""); } - htmlNode.getElementById("ogText").innerHTML = powerToString(-og); - htmlNode.getElementById("egText").innerHTML = powerToString(eg); - htmlNode.getElementById("ugText").innerHTML = powerToString(ug); - htmlNode.getElementById("pvText").innerHTML = powerToString(pv); - htmlNode.getElementById("pv1txt").innerHTML = powerToString(pv1); - htmlNode.getElementById("pv2txt").innerHTML = powerToString(pv2); - htmlNode.getElementById("pv3txt").innerHTML = powerToString(pv3); - htmlNode.getElementById("il1txt").innerHTML = Math.round(i_l1evu * 10) / 10 + " A" - htmlNode.getElementById("il2txt").innerHTML = Math.round(i_l2evu * 10) / 10 + " A" - htmlNode.getElementById("il3txt").innerHTML = Math.round(i_l3evu * 10) / 10 + " A" - htmlNode.getElementById("batText").innerHTML = powerToString(pbatt); - htmlNode.getElementById("heatText").innerHTML = powerToString(Pheat); - htmlNode.getElementById("evText").innerHTML = powerToString(evPower * 1000); - htmlNode.getElementById("evTextOG").innerHTML = powerToString(evPowerOG * 1000); - htmlNode.getElementById("genText").innerHTML = "Autarkie: " + Math.round(aut) + " %"; - htmlNode.getElementById("genInfoText").innerHTML = "Wirkungsgrad: " + Math.round(eff) + " %";// powerToString(p_wr); - htmlNode.getElementById("gridText").innerHTML = powerToString(grid); + setText("ogText", powerToString(-og)); + setText("egText", powerToString(eg)); + setText("ugText", powerToString(ug)); + setText("pvText", powerToString(pv)); + setText("pv1txt", powerToString(pv1)); + setText("pv2txt", powerToString(pv2)); + setText("pv3txt", powerToString(pv3)); + setText("il1txt", Math.round(i_l1evu * 10) / 10 + " A"); + setText("il2txt", Math.round(i_l2evu * 10) / 10 + " A"); + setText("il3txt", Math.round(i_l3evu * 10) / 10 + " A"); + setText("batText", powerToString(pbatt)); + setText("heatText", powerToString(Pheat)); + setText("evText", powerToString(evPower * 1000)); + setText("evTextOG", powerToString(evPowerOG * 1000)); + setText("genText", "Autarkie: " + Math.round(aut) + " %"); + setText("genInfoText", "Wirkungsgrad: " + Math.round(eff) + " %");// powerToString(p_wr); + setText("gridText", powerToString(grid)); for (i = 0; i < pvn.length; i++) { - htmlNode.getElementById("det_pv" + i + "P").innerHTML = powerToString(pvn[i]); + setText("det_pv" + i + "P", powerToString(pvn[i])); } tttext = "" @@ -204,127 +247,126 @@ const solarSVG = { i++; } - htmlNode.getElementById("invList").innerHTML = tttext - htmlNode.getElementById("egArc").setAttribute("d", describeArc(100, 100, 95, 0, Math.round(eg / angleFactor))); - htmlNode.getElementById("ugArc").setAttribute("d", describeArc(100, 100, 95, 0, Math.round(ug / angleFactor))); - htmlNode.getElementById("pvArc").setAttribute("d", describeArc(100, 100, 95, 0, Math.round(pv1 / angleFactor))); - htmlNode.getElementById("pv2Arc").setAttribute("d", describeArc(100, 100, 95, Math.round(pv1 / angleFactor), Math.round(pv1 / angleFactor) + Math.round(pv2 / angleFactor))); - htmlNode.getElementById("pv3Arc").setAttribute("d", describeArc(100, 100, 95, Math.round(pv1 / angleFactor) + Math.round(pv2 / angleFactor), Math.round(pv1 / angleFactor) + Math.round(pv2 / angleFactor) + Math.round(pv3 / angleFactor))); + setHTML("invList", tttext); + setAttr("egArc", "d", describeArc(100, 100, 95, 0, Math.round(eg / angleFactor))); + setAttr("ugArc", "d", describeArc(100, 100, 95, 0, Math.round(ug / angleFactor))); + setAttr("pvArc", "d", describeArc(100, 100, 95, 0, Math.round(pv1 / angleFactor))); + setAttr("pv2Arc", "d", describeArc(100, 100, 95, Math.round(pv1 / angleFactor), Math.round(pv1 / angleFactor) + Math.round(pv2 / angleFactor))); + setAttr("pv3Arc", "d", describeArc(100, 100, 95, Math.round(pv1 / angleFactor) + Math.round(pv2 / angleFactor), Math.round(pv1 / angleFactor) + Math.round(pv2 / angleFactor) + Math.round(pv3 / angleFactor))); if (pbatt < 0) { if (limbatt < 100) { - htmlNode.getElementById("charge").style.display = "none"; - htmlNode.getElementById("batLim").style.display = ""; - htmlNode.getElementById("batLimText").innerHTML = Math.round(limbatt) + "%"; + setDisplay("charge", "none"); + setDisplay("batLim", ""); + setText("batLimText", Math.round(limbatt) + "%"); } else { - htmlNode.getElementById("charge").style.display = ""; - htmlNode.getElementById("batLim").style.display = "none"; + setDisplay("charge", ""); + setDisplay("batLim", "none"); } - htmlNode.getElementById("batArc").setAttribute("d", describeArc(100, 100, 95, 0, Math.round(-pbatt / angleFactor))); - htmlNode.getElementById("battani").setAttribute("class", "stream-rev"); - htmlNode.getElementById("battani").setAttribute("stroke-width", 55 * (1 - Math.exp(pbatt / 2000))); + setAttr("batArc", "d", describeArc(100, 100, 95, 0, Math.round(-pbatt / angleFactor))); + setAttr("battani", "class", "stream-rev"); + setFlowWidth("battani", 55 * (1 - Math.exp(pbatt / 2000))); } else { - htmlNode.getElementById("charge").style.display = "none"; - htmlNode.getElementById("batLim").style.display = "none"; - htmlNode.getElementById("batArc").setAttribute("d", describeArc(100, 100, 95, 0, Math.round(pbatt / angleFactor))); - htmlNode.getElementById("battani").setAttribute("class", "stream"); - htmlNode.getElementById("battani").setAttribute("stroke-width", 55 * (1 - Math.exp(-pbatt / 2000))); + setDisplay("charge", "none"); + setDisplay("batLim", "none"); + setAttr("batArc", "d", describeArc(100, 100, 95, 0, Math.round(pbatt / angleFactor))); + setAttr("battani", "class", "stream"); + setFlowWidth("battani", 55 * (1 - Math.exp(-pbatt / 2000))); } - htmlNode.getElementById("batChargeState").setAttribute("y", (100 - soc) + "%"); - //htmlNode.getElementById("heatChargeState").setAttribute("y", 100 - ((((puffO + puffM) / 2) - 30) * 1.66) + "%"); - htmlNode.getElementById('tmpH').setAttribute("stop-color", assignColor("#2389BA", "#BA3B23", 20, 80, puffO)); - htmlNode.getElementById('tmpM').setAttribute("stop-color", assignColor("#2389BA", "#BA3B23", 20, 80, puffM)); - htmlNode.getElementById('tmpL').setAttribute("stop-color", assignColor("#2389BA", "#BA3B23", 20, 80, puffU)); - htmlNode.getElementById("batSOC").innerHTML = soc + " %"; - htmlNode.getElementById("evArc").setAttribute("d", describeArc(100, 100, 95, 0, Math.round(evPower * 2000 / angleFactor))); - htmlNode.getElementById("evChargeState").setAttribute("y", (100 - evsoc) + "%"); - htmlNode.getElementById("evSOC").innerHTML = evsoc + " %"; - htmlNode.getElementById("evFuel").innerHTML = fuelev + " %"; + setAttr("batChargeState", "y", (100 - soc) + "%"); + //setAttr("heatChargeState", "y", 100 - ((((puffO + puffM) / 2) - 30) * 1.66) + "%"); + setAttr('tmpH', "stop-color", assignColor("#2389BA", "#BA3B23", 20, 80, puffO)); + setAttr('tmpM', "stop-color", assignColor("#2389BA", "#BA3B23", 20, 80, puffM)); + setAttr('tmpL', "stop-color", assignColor("#2389BA", "#BA3B23", 20, 80, puffU)); + setText("batSOC", soc + " %"); + setAttr("evArc", "d", describeArc(100, 100, 95, 0, Math.round(evPower * 2000 / angleFactor))); + setAttr("evChargeState", "y", (100 - evsoc) + "%"); + setText("evSOC", evsoc + " %"); + setText("evFuel", fuelev + " %"); - htmlNode.getElementById("heatArc").setAttribute("d", describeArc(100, 100, 95, 0, Math.round(Pheat / sixkWangleFactor))); - htmlNode.getElementById("heatUp").innerHTML = Math.round(puffO * 10) / 10 + " °C"; - htmlNode.getElementById("heatDown").innerHTML = Math.round(puffM * 10) / 10 + " °C"; + setAttr("heatArc", "d", describeArc(100, 100, 95, 0, Math.round(Pheat / sixkWangleFactor))); + setText("heatUp", Math.round(puffO * 10) / 10 + " °C"); + setText("heatDown", Math.round(puffM * 10) / 10 + " °C"); if (evlock == false) { - htmlNode.getElementById("evLock").style.display = "none"; + setDisplay("evLock", "none"); } else { - htmlNode.getElementById("evLock").style.display = ""; + setDisplay("evLock", ""); } if (heatMode == "eco") { - htmlNode.getElementById("heatEco").style.display = ""; - htmlNode.getElementById("heatDefault").style.display = "none"; + setDisplay("heatEco", ""); + setDisplay("heatDefault", "none"); } else { - htmlNode.getElementById("heatEco").style.display = "none"; - htmlNode.getElementById("heatDefault").style.display = ""; + setDisplay("heatEco", "none"); + setDisplay("heatDefault", ""); } if (evMode == "Eco") { - htmlNode.getElementById("evEco").style.display = ""; - htmlNode.getElementById("evNextTrip").style.display = "none"; - htmlNode.getElementById("evDefault").style.display = "none"; + setDisplay("evEco", ""); + setDisplay("evNextTrip", "none"); + setDisplay("evDefault", "none"); } else if (evMode == "Next Trip") { - htmlNode.getElementById("evEco").style.display = "none"; - htmlNode.getElementById("evNextTrip").style.display = ""; - htmlNode.getElementById("evDefault").style.display = "none"; + setDisplay("evEco", "none"); + setDisplay("evNextTrip", ""); + setDisplay("evDefault", "none"); } else { - htmlNode.getElementById("evEco").style.display = "none"; - htmlNode.getElementById("evNextTrip").style.display = "none"; - htmlNode.getElementById("evDefault").style.display = ""; + setDisplay("evEco", "none"); + setDisplay("evNextTrip", "none"); + setDisplay("evDefault", ""); } if (heatOG) { - htmlNode.getElementById("heatOG").style.display = ""; + setDisplay("heatOG", ""); } else { - htmlNode.getElementById("heatOG").style.display = "none"; + setDisplay("heatOG", "none"); } if (heatEG) { - htmlNode.getElementById("heatEG").style.display = ""; + setDisplay("heatEG", ""); } else { - htmlNode.getElementById("heatEG").style.display = "none"; + setDisplay("heatEG", "none"); } if (plugev != "no car") { - htmlNode.getElementById("evPlug").style.display = ""; + setDisplay("evPlug", ""); } else { - htmlNode.getElementById("evPlug").style.display = "none"; + setDisplay("evPlug", "none"); } if (evModeOG == "Eco") { - htmlNode.getElementById("evEcoOG").style.display = ""; - htmlNode.getElementById("evNextTripOG").style.display = "none"; - htmlNode.getElementById("evDefaultOG").style.display = "none"; + setDisplay("evEcoOG", ""); + setDisplay("evNextTripOG", "none"); + setDisplay("evDefaultOG", "none"); } else if (evModeOG == "Next Trip") { - htmlNode.getElementById("evEcoOG").style.display = "none"; - htmlNode.getElementById("evNextTripOG").style.display = ""; - htmlNode.getElementById("evDefaultOG").style.display = "none"; + setDisplay("evEcoOG", "none"); + setDisplay("evNextTripOG", ""); + setDisplay("evDefaultOG", "none"); } else { - htmlNode.getElementById("evEcoOG").style.display = "none"; - htmlNode.getElementById("evNextTripOG").style.display = "none"; - htmlNode.getElementById("evDefaultOG").style.display = ""; + setDisplay("evEcoOG", "none"); + setDisplay("evNextTripOG", "none"); + setDisplay("evDefaultOG", ""); } if (evPlugOG > 0) { - htmlNode.getElementById("evPlugOG").style.display = ""; + setDisplay("evPlugOG", ""); } else { - htmlNode.getElementById("evPlugOG").style.display = "none"; + setDisplay("evPlugOG", "none"); } if (grid < 0) { - htmlNode.getElementById("gridArc").setAttribute("d", describeArc(100, 100, 95, 0, -grid / angleFactor)); - htmlNode.getElementById("consGridani").setAttribute("class", "stream-rev"); - htmlNode.getElementById("consGridani").setAttribute("stroke-width", 55 * (1 - Math.exp(grid / 2000))); + setAttr("gridArc", "d", describeArc(100, 100, 95, 0, -grid / angleFactor)); + setAttr("consGridani", "class", "stream-rev"); + setFlowWidth("consGridani", 55 * (1 - Math.exp(grid / 2000))); } else { - htmlNode.getElementById("gridArc").setAttribute("d", describeArc(100, 100, 95, 0, grid / angleFactor)); - htmlNode.getElementById("consGridani").setAttribute("class", "stream"); - htmlNode.getElementById("consGridani").setAttribute("stroke-width", 55 * (1 - Math.exp(-grid / 2000))); + setAttr("gridArc", "d", describeArc(100, 100, 95, 0, grid / angleFactor)); + setAttr("consGridani", "class", "stream"); + setFlowWidth("consGridani", 55 * (1 - Math.exp(-grid / 2000))); } - htmlNode.getElementById("consEVani").setAttribute("stroke-width", 55 * (1 - Math.exp(-evPower))); - htmlNode.getElementById("consEVOGani").setAttribute("stroke-width", 55 * (1 - Math.exp(-evPowerOG))); - htmlNode.getElementById("consOGani").setAttribute("stroke-width", 55 * (1 - Math.exp(og / 2000))); + setFlowWidth("consEVani", 55 * (1 - Math.exp(-evPower))); + setFlowWidth("consEVOGani", 55 * (1 - Math.exp(-evPowerOG))); + setFlowWidth("consOGani", 55 * (1 - Math.exp(og / 2000))); - htmlNode.getElementById("consUGani").setAttribute("stroke-width", 55 * (1 - Math.exp( -ug / 2000))); + setFlowWidth("consUGani", 55 * (1 - Math.exp( -ug / 2000))); - htmlNode.getElementById("consHeatAni").setAttribute("stroke-width", 55 * (1 - Math.exp(-Pheat / 2000))); - htmlNode.getElementById("consAni").setAttribute("stroke-width", 55 * (1 - Math.exp(cons / 2000))); - htmlNode.getElementById("consEGani").setAttribute("stroke-width", 55 * (1 - Math.exp(-eg / 2000))); - htmlNode.getElementById("pvani").setAttribute("stroke-width", 55 * (1 - Math.exp(-pv / 2000))); - htmlNode.getElementById("pvani"); - } + setFlowWidth("consHeatAni", 55 * (1 - Math.exp(-Pheat / 2000))); + setFlowWidth("consAni", 55 * (1 - Math.exp(cons / 2000))); + setFlowWidth("consEGani", 55 * (1 - Math.exp(-eg / 2000))); + setFlowWidth("pvani", 55 * (1 - Math.exp(-pv / 2000))); +} } @@ -472,6 +514,7 @@ var chartSettings = { var mqttData = {}; var timeFrom = -24; var timeTo = 12; + const dayRange = () => ({ FROM: timeFrom, TO: timeTo }); const consChart = new Chart( document.querySelector('#consumption-chart'), Object.assign({}, chartSettings) @@ -487,8 +530,8 @@ var chartSettings = { ); function updateCharts(){ - getData(consChart, 'ajax/getConsData.php',true,false,false); - getData(prodChart, 'ajax/getProdData.php',true,false,false); + getData(consChart, 'ajax/getConsData.php', { params: dayRange, sunrise: true, autoUpdate: false, updateMode: "none" }); + getData(prodChart, 'ajax/getProdData.php', { params: dayRange, sunrise: true, autoUpdate: false, updateMode: "none" }); } function prevDay(){ @@ -531,29 +574,16 @@ var chartSettings = { document.addEventListener('readystatechange', function () { if (event.target.readyState === "complete") { solarMQTT.getMQTT(); - getData(consChart, 'ajax/getConsData.php'); - getData(prodChart, 'ajax/getProdData.php'); - getData(foreChart,'ajax/getForecastData.php', false); + getData(consChart, 'ajax/getConsData.php', { params: dayRange, sunrise: true, updateMode: "none" }); + getData(prodChart, 'ajax/getProdData.php', { params: dayRange, sunrise: true, updateMode: "none" }); + getData(foreChart, 'ajax/getForecastData.php', { params: dayRange, updateMode: "none" }); getStats("Stats-Year","ajax/getStats.php?type=ThisYear"); solarSVG.fillElementArray(); - this.getElementById("meteogram").innerHTML = ""; + mountMeteogram(); } }); - String.prototype.toHHMM = function () { - var sec_num = parseInt(this, 10); // don't forget the second param - var hours = Math.floor(sec_num / 3600); - var minutes = Math.floor((sec_num - (hours * 3600)) / 60); - //var seconds = sec_num - (hours * 3600) - (minutes * 60); - if (hours < 10) { hours = "0" + hours; } - if (minutes < 10) { minutes = "0" + minutes; } - return hours + ':' + minutes; - } - - function loadingHTML(msg) { - return "
Loading...
   " + msg + "
" - } function openModal(type) { document.getElementById("modalSaveBtn").style.display = type === "watering" ? "none" : ""; @@ -600,20 +630,7 @@ var chartSettings = { var span = document.getElementById("modal-slider-label"); switch(type){ case "CarEG": - slider.oninput = function () { - marginValue = parseInt(this.value); - slider.style.setProperty("--background-size", `${marginValue}%`); - if (marginValue < 10) { - marginValue = marginValue - 0.4 * marginValue; - } else if (marginValue < 85) { - marginValue = marginValue - 4; - } - else { - marginValue = marginValue - 4 - 0.5 * (marginValue - 85); - } - span.setAttribute('style', 'margin-left:' + marginValue + '%;'); - span.innerHTML = this.value + "%"; - } + bindModalSlider(slider, span, value => value + "%"); document.getElementById("evStart/Stop").addEventListener("click", submitFormAjax); document.getElementById("evStart/Stop").contentURL = contentURL; mode = new TextDecoder().decode(mqttData["wattpilot/properties/lmo/state"]); @@ -636,20 +653,7 @@ var chartSettings = { else if (amp == "6") { document.getElementById("EV_6A").click(); } break; case "CarOG": - slider.oninput = function () { - marginValue = parseInt((this.value-this.min)*100/this.max); - slider.style.setProperty("--background-size", `${marginValue}%`); - if (marginValue < 10) { - marginValue = marginValue - 0.4 * marginValue; - } else if (marginValue < 85) { - marginValue = marginValue - 4; - } - else { - marginValue = marginValue - 4 - 0.5 * (marginValue - 85); - } - span.setAttribute('style', 'margin-left:' + marginValue + '%;'); - span.innerHTML = this.value + "%"; - } + bindModalSlider(slider, span, value => value + "%"); document.getElementById("evStart/Stop").addEventListener("click", submitFormAjax); document.getElementById("evStart/Stop").contentURL = contentURL; mode = mqttData["go-eCharger/270003/lmo"]; @@ -671,24 +675,7 @@ var chartSettings = { else if (amp == "6") { document.getElementById("EV_6A").click(); } break; case "heater": - slider.oninput = function () { - marginValue = parseInt((this.value-this.min)*100/this.max); - slider.style.setProperty("--background-size", `${marginValue}%`); - if (marginValue < 10) { - marginValue = marginValue - 0.4 * marginValue; - } else if (marginValue < 85) { - marginValue = marginValue - 4; - } - else { - marginValue = marginValue - 4 - 0.5 * (marginValue - 85); - } - span.setAttribute('style', 'margin-left:' + marginValue + '%;'); - if(this.value == 0){ - span.innerHTML = "Automatik"; - }else{ - span.innerHTML = (this.value/10) + " kW"; - } - } + bindModalSlider(slider, span, value => value == 0 ? "Automatik" : (value / 10) + " kW"); if(mqttData["solarManager/heatMode"] == "eco"){ document.getElementById("modal-slider").value = 0; }else{ @@ -744,7 +731,8 @@ var chartSettings = { const updateButton = () => { const zone = wateringZones[button.dataset.wateringZone]; const state = wateringLabel(zone.topic, zone.valve); - button.textContent = state === "aus" ? "Ein" : "Aus"; + const beschriftung = state === "aus" ? "Ein" : "Aus"; + if (button.textContent !== beschriftung) button.textContent = beschriftung; button.classList.toggle("btn-success", state === "aus"); button.classList.toggle("btn-danger", state !== "aus"); }; @@ -774,14 +762,27 @@ var chartSettings = { updateWateringStatus(); } + // Die Anzeige laeuft im Sekundentakt, die Nutzlasten aendern sich aber nur, + // wenn eine neue MQTT-Nachricht kommt. messageReceived legt dabei jedes Mal + // ein neues Buffer-Objekt ab - der Identitaetsvergleich reicht also als + // Gueltigkeitspruefung, ein Invalidieren von Hand braucht es nicht. + const wateringJsonCache = {}; + const wateringDecoder = new TextDecoder(); + function wateringJson(topic) { const value = mqttData[topic]; if (!value) return null; + const cached = wateringJsonCache[topic]; + if (cached && cached.raw === value) return cached.parsed; + + let parsed = null; try { - return JSON.parse(typeof value === "string" ? value : new TextDecoder().decode(value)); + parsed = JSON.parse(typeof value === "string" ? value : wateringDecoder.decode(value)); } catch (error) { - return null; + parsed = null; } + wateringJsonCache[topic] = { raw: value, parsed: parsed }; + return parsed; } function wateringHistory(topic, valve) { @@ -872,15 +873,24 @@ var chartSettings = { return !!(status && status.would_water && !status.watered_recently); } + // Konstante Tabellen - stehen ausserhalb, damit der Sekundentakt sie nicht + // jedes Mal neu aufbaut. + const wateringIndicators = [ + ["wateringFrontStatus", "wateringFrontIcon", "Vorn", "Gartenwasser/vorn", 6, "garten_vorn"], + ["wateringTroughStatus", "wateringTroughIcon", "Tröge", "Gartenwasser/vorn", 1, "troege"], + ["wateringBedsStatus", "wateringBedsIcon", "Beete", "Gartenwasser/hinten", 6, "hochbeete"] + ]; + const wateringHistoryRows = [ + ["troughs", "Gartenwasser/vorn", 1], + ["frontGarden", "Gartenwasser/vorn", 6], + ["raisedBeds", "Gartenwasser/hinten", 6] + ]; + function updateWateringStatus() { - const indicators = [ - ["wateringFrontStatus", "wateringFrontIcon", "Vorn", "Gartenwasser/vorn", 6, "garten_vorn"], - ["wateringTroughStatus", "wateringTroughIcon", "Tröge", "Gartenwasser/vorn", 1, "troege"], - ["wateringBedsStatus", "wateringBedsIcon", "Beete", "Gartenwasser/hinten", 6, "hochbeete"] - ]; - indicators.forEach(([statusId, iconId, name, topic, valve, autoZoneKey]) => { - const status = document.getElementById(statusId); - const icon = document.getElementById(iconId); + // Die Anzeigen liegen im Realtime-SVG und bleiben bestehen -> svgElement. + wateringIndicators.forEach(([statusId, iconId, name, topic, valve, autoZoneKey]) => { + const status = svgElement(statusId); + const icon = svgElement(iconId); if (!status || !icon) return; const state = wateringLabel(topic, valve); const planned = state === "aus" && wateringAutoPlanned(autoZoneKey); @@ -889,16 +899,14 @@ var chartSettings = { document.querySelectorAll("[data-watering-toggle]").forEach((button) => { const zone = wateringZones[button.dataset.wateringZone]; const state = wateringLabel(zone.topic, zone.valve); - button.textContent = state === "aus" ? "Ein" : "Aus"; + const beschriftung = state === "aus" ? "Ein" : "Aus"; + if (button.textContent !== beschriftung) button.textContent = beschriftung; button.classList.toggle("btn-success", state === "aus"); button.classList.toggle("btn-danger", state !== "aus"); }); document.querySelectorAll("[data-watering-auto-toggle]").forEach(updateWateringAutoButton); - [ - ["troughs", "Gartenwasser/vorn", 1], - ["frontGarden", "Gartenwasser/vorn", 6], - ["raisedBeds", "Gartenwasser/hinten", 6] - ].forEach(([zone, topic, valve]) => { + // Die Modal-Elemente werden bei jedem Oeffnen neu erzeugt -> nicht cachen. + wateringHistoryRows.forEach(([zone, topic, valve]) => { const history = wateringHistory(topic, valve); const dateElement = document.querySelector(`[data-watering-last='${zone}']`); const durationElement = document.querySelector(`[data-watering-duration='${zone}']`); @@ -923,132 +931,29 @@ var chartSettings = { // "Aus" bleibt bei nicht geplant stehen; bei geplant ersetzt die // Stoppuhr (U+23F1, ohne Emoji-Variationsselektor -> einfarbig, folgt // der Icon-Farbe) den Text, um Platz auf der textPath zu sparen. - status.textContent = state === "aus" ? (planned ? "⏱" : "Aus") : state; + // Laeuft im Sekundentakt, aendert sich aber selten - ohne Pruefung + // wuerde jede Sekunde derselbe Wert geschrieben und das Icon samt + // Filter neu gezeichnet. + const text = state === "aus" ? (planned ? "⏱" : "Aus") : state; + if (status.textContent !== text) status.textContent = text; // Geplant-Blau ("#3A6292") entspricht dem Wassertropfen-Icon (id="water"). const color = state !== "aus" ? (paused ? "#ffc107" : "#20c997") : planned ? "#3A6292" : "#6c757d"; - icon.setAttribute("color", color); - icon.setAttribute("fill", color); - icon.style.color = color; - icon.querySelector("title").textContent = name + ": " + (state === "aus" && planned ? "Aus (Automatik heute noch geplant)" : state); + if (icon.getAttribute("color") !== color) { + icon.setAttribute("color", color); + icon.setAttribute("fill", color); + icon.style.color = color; + } + const titel = name + ": " + (state === "aus" && planned ? "Aus (Automatik heute noch geplant)" : state); + const titelKnoten = icon.querySelector("title"); + if (titelKnoten.textContent !== titel) titelKnoten.textContent = titel; } window.setInterval(updateWateringStatus, 1000); - function submitFormAjax(event) { - let xmlhttp = window.XMLHttpRequest ? - new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); - xmlhttp.onreadystatechange = function () { - if (this.readyState === 4 && this.status === 200) - modalEV.hide(); - } - const form = document.getElementById('modalEV').querySelector('form'); - post = ""; - // ✅ Get interesting form elements - if (event.currentTarget.id == "evStart/Stop") { - post = "evStart/Stop=" + event.currentTarget.innerHTML; - } else { - const formElements = Array.from(form.elements); - for (let i = 0; i < formElements.length; i++) { - if(formElements[i].name){ - if (formElements[i].type == "radio" && formElements[i].checked) { - post += formElements[i].name + "=" + encodeURIComponent(formElements[i].value) + "&"; - } else if(formElements[i].type == "checkbox" && formElements[i].checked){ - post += formElements[i].name + "=" + encodeURIComponent(formElements[i].value) + "&"; - } else if(formElements[i].type == "button" && !formElements[i].classList.contains("accordion-button")){ - post += formElements[i].name + "=" + encodeURIComponent(formElements[i].innerHTML) + "&"; - }else if(formElements[i].type != "checkbox" && formElements[i].type != "radio") { - post += formElements[i].name + "=" + encodeURIComponent(formElements[i].value) + "&"; - } - } - } - } - xmlhttp.open("POST", event.currentTarget.contentURL, true); - xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - document.getElementById('modalEV').querySelector('.modal-body').innerHTML = loadingHTML("Änderungen werden übernommen..."); - xmlhttp.send(post); - return false; - } - async function getData(chrt, url, sunrise=true, autoUpdate=true, animation=true) { - try { - chrt.options.events = []; - console.log("fetching "+url+"?FROM="+timeFrom+"&TO="+timeTo); - const response = await fetch(url+"?FROM="+timeFrom+"&TO="+timeTo); - if (!response.ok) { - console.log("err"); - throw new Error(`Response status: ${response.status}`); - } - chrt.data = await response.json(); - if(sunrise){ - const response2 = await fetch("ajax/getSunrise.php?FROM="+timeFrom+"&TO="+timeTo); - if (!response2.ok) { - console.log("err"); - throw new Error(`Response status: ${response2.status}`); - } - chrt.options.plugins.annotation.annotations = await response2.json(); - } - chrt.options.events = ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove']; - chrt.update("none"); - } catch (error) { - console.log(error.message); - } - if(autoUpdate){ - setTimeout(function () { getData(chrt, url, sunrise,autoUpdate,animation) }, 5 * 60 * 1000); //renew data every 5 min. - } - } - async function getDataOnly(chart, url, sunrise=true, autoUpdate=true, animation=true) { - - try { - console.log("fetching "+url+"?FROM="+timeFrom+"&TO="+timeTo); - const response = await fetch(url+"?FROM="+timeFrom+"&TO="+timeTo); - if (!response.ok) { - console.log("err"); - throw new Error(`Response status: ${response.status}`); - } - - chart.data = await response.json(); - if(sunrise){ - const response2 = await fetch("ajax/getSunrise.php?FROM="+timeFrom+"&TO="+timeTo); - if (!response2.ok) { - console.log("err"); - throw new Error(`Response status: ${response2.status}`); - } - chart.options.plugins.annotation.annotations = await response2.json(); - } - } catch (error) { - console.log(error.message); - } - if(autoUpdate){ - setTimeout(function () { getData(chart, url, sunrise,autoUpdate,animation) }, 5 * 60 * 1000); //renew data every 5 min. - } - } - - async function getStats(elem_id, url) { - try { - console.log("fetching"); - const response = await fetch(url); - if (!response.ok) { - console.log("err"); - throw new Error(`Response status: ${response.status}`); - } - document.getElementById(elem_id).innerHTML = await response.text(); - } catch (error) { - console.log(error.message); - } - } - - function powerToString(power) { - if (Math.abs(power) > 999) { - power = power / 1000 - return power.toPrecision(3) + " kW" - } else { - return Math.round(power) + " W" - } - - } function polarToCartesian(centerX, centerY, radius, angleInDegrees) { var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0; @@ -1090,29 +995,23 @@ var chartSettings = { return color; } - function describeArc(x, y, radius, startAngle, endAngle) { - if (endAngle > 288) { - endAngle = 288; + function describeArc(x, y, radius, startAngle, endAngle) { + if (endAngle > 288) { + endAngle = 288; + } + // Auf volle Grad runden: bei Radius 95 ist ein Grad rund 1,7 Einheiten, + // feiner sieht niemand. Dafuer bleibt das d-Attribut bei kleinen + // Schwankungen unveraendert und setAttr unterdrueckt den Schreibvorgang - + // sonst laesst jeder Zappler den Bogen samt Filter neu zeichnen. + startAngle = Math.round(startAngle); + endAngle = Math.round(endAngle); + var start = polarToCartesian(x, y, radius, endAngle); + var end = polarToCartesian(x, y, radius, startAngle); + var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1"; + // zwei Nachkommastellen genuegen und halten das Attribut kurz + var rnd = (v) => Math.round(v * 100) / 100; + return [ + "M", rnd(start.x), rnd(start.y), + "A", radius, radius, 0, largeArcFlag, 0, rnd(end.x), rnd(end.y) + ].join(" "); } - var start = polarToCartesian(x, y, radius, endAngle); - var end = polarToCartesian(x, y, radius, startAngle); - var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1"; - var d = [ - "M", start.x, start.y, - "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y - ].join(" "); - return d; - } - - - - function updateValuesWeather(){ - var div=document.getElementById("windDir"); - div.style.transform = "rotate("+(90+Number(mqttData["weatherStation/windDeg"]))+"deg)"; - document.getElementById("windSpd").innerHTML = mqttData["weatherStation/avgWindspeed"]; - document.getElementById("humidity").innerHTML = mqttData["weatherStation/hum"]; - document.getElementById("temp").innerHTML = mqttData["weatherStation/tempAmb"]; - document.getElementById("windDirGust").style.transform = "rotate("+(90+Number(mqttData["weatherStation/gustDeg"]))+"deg)"; - document.getElementById("windGust").innerHTML = mqttData["weatherStation/maxgust"]; - document.getElementById("ambPress").innerHTML = Math.round(mqttData["weatherStation/qff"]*10)/10; - } \ No newline at end of file diff --git a/js/solar/weatherMQTT.js b/js/solar/weatherMQTT.js new file mode 100644 index 0000000..ae362bf --- /dev/null +++ b/js/solar/weatherMQTT.js @@ -0,0 +1,33 @@ +/* + * Wetterseite: Messwerte der eigenen Wetterstation und das Meteogramm. + * Die Anzeige selbst steckt in updateWeatherCards() / mountMeteogram() + * in common.js, hier bleibt nur die MQTT-Anbindung. + */ +var mqttData = {}; + +const weatherMQTT = { + getMQTT: function () { + const client = mqtt.connect("wss://mqtt.nas.el-wa.org:443", { rejectUnauthorized: false }); + + client.on("connect", function () { + client.subscribe("weatherStation/#"); + }); + client.on("message", function (topic, message) { + mqttData[topic] = message; + if (topic == "weatherStation/windDeg") { + // etwas warten, bis die uebrigen Werte eingetroffen sind + setTimeout(function () { updateWeatherCards(name => mqttData["weatherStation/" + name]) }, 200); + } + }); + client.on("error", function (error) { + console.log("MQTT: " + error.message); + }); + } +}; + +document.addEventListener('readystatechange', function () { + if (event.target.readyState === "complete") { + weatherMQTT.getMQTT(); + mountMeteogram(); + } +}); diff --git a/restricted/charts.html b/restricted/charts.html new file mode 100644 index 0000000..90e7652 --- /dev/null +++ b/restricted/charts.html @@ -0,0 +1,64 @@ + + + + + + + + diff --git a/restricted/footer.html b/restricted/footer.html deleted file mode 100644 index 1c09d56..0000000 --- a/restricted/footer.html +++ /dev/null @@ -1,174 +0,0 @@ - -
- -
Free simple code
- - - - No © 2025  by - m0 - - All rights don't care. - -
- - - -
-
-
Offcanvas with backdrop
- -
-
-

.....

-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/restricted/footer.php b/restricted/footer.php new file mode 100644 index 0000000..06f0082 --- /dev/null +++ b/restricted/footer.php @@ -0,0 +1,74 @@ + +
+ +
Free simple code
+ + + + No © 2025  by + m0 + + All rights don't care. + +
+ + + +
+
+
Offcanvas with backdrop
+ +
+
+

.....

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/restricted/header.php b/restricted/header.php index d90823a..3db275b 100644 --- a/restricted/header.php +++ b/restricted/header.php @@ -2,10 +2,9 @@ - @@ -29,24 +28,20 @@ if(!isset($_GET["floor"])) - - + + - - - - + @@ -124,7 +119,7 @@ if(!isset($_GET["floor"]))