initial working status (overview for each device)
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/*
|
||||
* dbQuery.php
|
||||
*
|
||||
* This file is part of the VE.Direct web UI
|
||||
* https://git.schauaus.at/VE-Tools/VE.Direct-webui
|
||||
* It is licensed under the GPL3.0 or later.
|
||||
* You should have received a copy of the license with this software
|
||||
*/
|
||||
|
||||
$config = json_decode( file_get_contents('../config.json') );
|
||||
$dbPath = (str_starts_with($config->database, '/')) ? $config->database : dirname(__FILE__) . '/../' . $config->database;
|
||||
try {
|
||||
$db = new SQLite3($dbPath, SQLITE3_OPEN_READONLY);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
returnJson(['error'=>'Cannot conect to database! ' . $e->getMessage()], 400);
|
||||
}
|
||||
switch ($_REQUEST['q']) {
|
||||
case 'deviceData':
|
||||
getDeviceData();
|
||||
break;
|
||||
case 'devices':
|
||||
getDevices();
|
||||
break;
|
||||
case 'metadata':
|
||||
getMetaData();
|
||||
break;
|
||||
default:
|
||||
returnJson(['error'=>'No request provided!'], 400);
|
||||
}
|
||||
|
||||
function getDeviceData() {
|
||||
if(!isset($_REQUEST['device'])) returnJson(['error'=>'No device provided'], 400);
|
||||
$sql = "SELECT date, time, data FROM '" . $_REQUEST['device'] . "' ORDER BY date ASC, time ASC;";
|
||||
$data = queryDb($sql);
|
||||
if(!$data) returnJson(['error'=>'No data found'], 400);
|
||||
returnJson($data);
|
||||
}
|
||||
|
||||
function getDevices () {
|
||||
$sql = "SELECT name FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%' AND name <> 'meta' ORDER BY name ASC;";
|
||||
$data = queryDb($sql);
|
||||
if(!$data) returnJson(['error'=>'No devices found'], 400);
|
||||
returnJson($data);
|
||||
}
|
||||
|
||||
function getMetaData() {
|
||||
$sql = "SELECT * FROM meta;";
|
||||
$data = queryDb($sql);
|
||||
if(!$data) returnJson(['error'=>'No metadata found in the database!'], 400);
|
||||
returnJson($data);
|
||||
}
|
||||
|
||||
function queryDb($sql) {
|
||||
global $db;
|
||||
$res = $db->query($sql);
|
||||
if(!$res) return false;
|
||||
$data = [];
|
||||
while ( $resLine = $res->fetchArray(SQLITE3_NUM) )
|
||||
array_push($data, $resLine);
|
||||
if(count($data) == 0) return false;
|
||||
return $data;
|
||||
}
|
||||
|
||||
function returnJson($data, $status = 200) {
|
||||
http_response_code($status); //HTTP status code
|
||||
header('Content-Type: application/json; charset=utf-8'); //HTTP headers
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE); //send data
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"0x203":"BMV-700",
|
||||
"0x204":"BMV-702",
|
||||
"0x205":"BMV-700H",
|
||||
"0x0300":"BlueSolar MPPT 70|15",
|
||||
"0xA040":"BlueSolar MPPT 75|50",
|
||||
"0xA041":"BlueSolar MPPT 150|35",
|
||||
"0xA042":"BlueSolar MPPT 75|15",
|
||||
"0xA043":"BlueSolar MPPT 100|15",
|
||||
"0xA044":"BlueSolar MPPT 100|30",
|
||||
"0xA045":"BlueSolar MPPT 100|50",
|
||||
"0xA046":"BlueSolar MPPT 150|70",
|
||||
"0xA047":"BlueSolar MPPT 150|100",
|
||||
"0xA049":"BlueSolar MPPT 100|50 rev2",
|
||||
"0xA04A":"BlueSolar MPPT 100|30 rev2",
|
||||
"0xA04B":"BlueSolar MPPT 150|35 rev2",
|
||||
"0xA04C":"BlueSolar MPPT 75|10",
|
||||
"0xA04D":"BlueSolar MPPT 150|45",
|
||||
"0xA04E":"BlueSolar MPPT 150|60",
|
||||
"0xA04F":"BlueSolar MPPT 150|85",
|
||||
"0xA050":"SmartSolar MPPT 250|100",
|
||||
"0xA051":"SmartSolar MPPT 150|100",
|
||||
"0xA052":"SmartSolar MPPT 150|85",
|
||||
"0xA053":"SmartSolar MPPT 75|15",
|
||||
"0xA054":"SmartSolar MPPT 75|10",
|
||||
"0xA055":"SmartSolar MPPT 100|15",
|
||||
"0xA056":"SmartSolar MPPT 100|30",
|
||||
"0xA057":"SmartSolar MPPT 100|50",
|
||||
"0xA058":"SmartSolar MPPT 150|35",
|
||||
"0xA059":"SmartSolar MPPT 150|100 rev2",
|
||||
"0xA05A":"SmartSolar MPPT 150|85 rev2",
|
||||
"0xA05B":"SmartSolar MPPT 250|70",
|
||||
"0xA05C":"SmartSolar MPPT 250|85",
|
||||
"0xA05D":"SmartSolar MPPT 250|60",
|
||||
"0xA05E":"SmartSolar MPPT 250|45",
|
||||
"0xA05F":"SmartSolar MPPT 100|20",
|
||||
"0xA060":"SmartSolar MPPT 100|20 48V",
|
||||
"0xA061":"SmartSolar MPPT 150|45",
|
||||
"0xA062":"SmartSolar MPPT 150|60",
|
||||
"0xA063":"SmartSolar MPPT 150|70",
|
||||
"0xA064":"SmartSolar MPPT 250|85 rev2",
|
||||
"0xA065":"SmartSolar MPPT 250|100 rev2",
|
||||
"0xA066":"BlueSolar MPPT 100|20",
|
||||
"0xA067":"BlueSolar MPPT 100|20 48V",
|
||||
"0xA068":"SmartSolar MPPT 250|60 rev2",
|
||||
"0xA069":"SmartSolar MPPT 250|70 rev2",
|
||||
"0xA06A":"SmartSolar MPPT 150|45 rev2",
|
||||
"0xA06B":"SmartSolar MPPT 150|60 rev2",
|
||||
"0xA06C":"SmartSolar MPPT 150|70 rev2",
|
||||
"0xA06D":"SmartSolar MPPT 150|85 rev3",
|
||||
"0xA06E":"SmartSolar MPPT 150|100 rev3",
|
||||
"0xA06F":"BlueSolar MPPT 150|45 rev2",
|
||||
"0xA070":"BlueSolar MPPT 150|60 rev2",
|
||||
"0xA071":"BlueSolar MPPT 150|70 rev2",
|
||||
"0xA072":"BlueSolar MPPT 150/45 rev3",
|
||||
"0xA073":"SmartSolar MPPT 150/45 rev3",
|
||||
"0xA074":"SmartSolar MPPT 75 /10 rev2",
|
||||
"0xA075":"SmartSolar MPPT 75 /15 rev2",
|
||||
"0xA076":"BlueSolar MPPT 1 00/30 rev3",
|
||||
"0xA077":"BlueSolar MPPT 1 00/50 rev3",
|
||||
"0xA078":"BlueSolar MPPT 1 50/35 rev3",
|
||||
"0xA079":"BlueSolar MPPT 75 /10 rev2",
|
||||
"0xA07A":"BlueSolar MPPT 75 /15 rev2",
|
||||
"0xA07B":"BlueSolar MPPT 100 /15 rev2",
|
||||
"0xA07C":"BlueSolar MPPT 75 /10 rev3",
|
||||
"0xA07D":"BlueSolar MPPT 75 /15 rev3",
|
||||
"0xA07E":"SmartSolar MPPT 1 00/30 12V",
|
||||
"0xA07F":"All -In -1 SmartSolar MPPT 75/15 12V",
|
||||
"0xA080":"SmartSolar MPPT 250|60 rev3",
|
||||
"0xA081":"SmartSolar MPPT 250|70 rev3",
|
||||
"0xA102":"SmartSolar MPPT VE.Can 150/70",
|
||||
"0xA103":"SmartSolar MPPT VE.Can 150/45",
|
||||
"0xA104":"SmartSolar MPPT VE.Can 150/60",
|
||||
"0xA105":"SmartSolar MPPT VE.Can 150/85",
|
||||
"0xA106":"SmartSolar MPPT VE.Can 150/100",
|
||||
"0xA107":"SmartSolar MPPT VE.Can 250/45",
|
||||
"0xA108":"SmartSolar MPPT VE.Can 250/60",
|
||||
"0xA109":"SmartSolar MPPT VE.Can 250/70",
|
||||
"0xA10A":"SmartSolar MPPT VE.Can 250/85",
|
||||
"0xA10B":"SmartSolar MPPT VE.Can 250/100",
|
||||
"0xA10C":"SmartSolar MPPT VE.Can 150/70 rev2",
|
||||
"0xA10D":"SmartSolar MPPT VE.Can 150/85 rev2",
|
||||
"0xA10E":"SmartSolar MPPT VE.Can 150/100 rev2",
|
||||
"0xA10F":"BlueSolar MPPT VE.Can 150/100",
|
||||
"0xA112":"BlueSolar MPPT VE.Can 250/70",
|
||||
"0xA113":"BlueSolar MPPT VE.Can 250/100",
|
||||
"0xA114":"SmartSolar MPPT VE.Can 250/70 rev2",
|
||||
"0xA115":"SmartSolar MPPT VE.Can 250/100 rev2",
|
||||
"0xA116":"SmartSolar MPPT VE.Can 250/85 rev2",
|
||||
"0xA117":"BlueSolar MPPT VE.Can 150/100 rev2",
|
||||
"0xA201":"Phoenix Inverter 12V 250VA 230V",
|
||||
"0xA202":"Phoenix Inverter 24V 250VA 230V",
|
||||
"0xA204":"Phoenix Inverter 48V 250VA 230V",
|
||||
"0xA211":"Phoenix Inverter 12V 375VA 230V",
|
||||
"0xA212":"Phoenix Inverter 24V 375VA 230V",
|
||||
"0xA214":"Phoenix Inverter 48V 375VA 230V",
|
||||
"0xA221":"Phoenix Inverter 12V 500VA 230V",
|
||||
"0xA222":"Phoenix Inverter 24V 500VA 230V",
|
||||
"0xA224":"Phoenix Inverter 48V 500VA 230V",
|
||||
"0xA231":"Phoenix Inverter 12V 250VA 230V",
|
||||
"0xA232":"Phoenix Inverter 24V 250VA 230V",
|
||||
"0xA234":"Phoenix Inverter 48V 250VA 230V",
|
||||
"0xA239":"Phoenix Inverter 12V 250VA 120V",
|
||||
"0xA23A":"Phoenix Inverter 24V 250VA 120V",
|
||||
"0xA23C":"Phoenix Inverter 48V 250VA 120V",
|
||||
"0xA241":"Phoenix Inverter 12V 375VA 230V",
|
||||
"0xA242":"Phoenix Inverter 24V 375VA 230V",
|
||||
"0xA244":"Phoenix Inverter 48V 375VA 230V",
|
||||
"0xA249":"Phoenix Inverter 12V 375VA 120V",
|
||||
"0xA24A":"Phoenix Inverter 24V 375VA 120V",
|
||||
"0xA24C":"Phoenix Inverter 48V 375VA 120V",
|
||||
"0xA251":"Phoenix Inverter 12V 500VA 230V",
|
||||
"0xA252":"Phoenix Inverter 24V 500VA 230V",
|
||||
"0xA254":"Phoenix Inverter 48V 500VA 230V",
|
||||
"0xA259":"Phoenix Inverter 12V 500VA 120V",
|
||||
"0xA25A":"Phoenix Inverter 24V 500VA 120V",
|
||||
"0xA25C":"Phoenix Inverter 48V 500VA 120V",
|
||||
"0xA261":"Phoenix Inverter 12V 800VA 230V",
|
||||
"0xA262":"Phoenix Inverter 24V 800VA 230V",
|
||||
"0xA264":"Phoenix Inverter 48V 800VA 230V",
|
||||
"0xA269":"Phoenix Inverter 12V 800VA 120V",
|
||||
"0xA26A":"Phoenix Inverter 24V 800VA 120V",
|
||||
"0xA26C":"Phoenix Inverter 48V 800VA 120V",
|
||||
"0xA271":"Phoenix Inverter 12V 1200VA 230V",
|
||||
"0xA272":"Phoenix Inverter 24V 1200VA 230V",
|
||||
"0xA274":"Phoenix Inverter 48V 1200VA 230V",
|
||||
"0xA279":"Phoenix Inverter 12V 1200VA 120V",
|
||||
"0xA27A":"Phoenix Inverter 24V 1200VA 120V",
|
||||
"0xA27C":"Phoenix Inverter 48V 1200VA 120V",
|
||||
"0xA281":"Phoenix Inverter 12V 1600VA 230V",
|
||||
"0xA282":"Phoenix Inverter 24V 1600VA 230V",
|
||||
"0xA284":"Phoenix Inverter 48V 1600VA 230V",
|
||||
"0xA291":"Phoenix Inverter 12V 2000VA 230V",
|
||||
"0xA292":"Phoenix Inverter 24V 2000VA 230V",
|
||||
"0xA294":"Phoenix Inverter 48V 2000VA 230V",
|
||||
"0xA2A1":"Phoenix Inverter 12V 3000VA 230V",
|
||||
"0xA2A2":"Phoenix Inverter 24V 3000VA 230V",
|
||||
"0xA2A4":"Phoenix Inverter 48V 3000VA 230V",
|
||||
"0xA340":"Phoenix Smart IP43 Charger 12|50 (1+1)",
|
||||
"0xA341":"Phoenix Smart IP43 Charger 12|50 (3)",
|
||||
"0xA342":"Phoenix Smart IP43 Charger 24|25 (1+1)",
|
||||
"0xA343":"Phoenix Smart IP43 Charger 24|25 (3)",
|
||||
"0xA344":"Phoenix Smart IP43 Charger 12|30 (1+1)",
|
||||
"0xA345":"Phoenix Smart IP43 Charger 12|30 (3)",
|
||||
"0xA346":"Phoenix Smart IP43 Charger 24|16 (1+1)",
|
||||
"0xA347":"Phoenix Smart IP43 Charger 24|16 (3)",
|
||||
"0xA381":"BMV-712 Smart",
|
||||
"0xA382":"BMV-710H Smart",
|
||||
"0xA383":"BMV-712 Smart Rev2",
|
||||
"0xA389":"SmartShunt 500A/50mV",
|
||||
"0xA38A":"SmartShunt 1000A/50mV",
|
||||
"0xA38B":"SmartShunt 2000A/50mV",
|
||||
"0xA3F0":"Orion XS 12V/12V-50A",
|
||||
"0xA3F1":"Orion XS 1400"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"datafield_CS": "Systemstatus",
|
||||
"datafield_ERR": "Fehler",
|
||||
"datafield_FW": "Firmwareversion",
|
||||
"datafield_FWE": "Firmwareversion",
|
||||
"datafield_H19": "Gesamtertrag seit letztem Reset",
|
||||
"datafield_H20": "Ertrag aktuellster Tag",
|
||||
"datafield_H21": "max. Leistung aktuellster Tag",
|
||||
"datafield_H22": "Ertrag Vortag",
|
||||
"datafield_H23": "max. Leistung Vortag",
|
||||
"datafield_I": "Stromstärke Kanal 1",
|
||||
"datafield_LOAD": "Status Lastausgang",
|
||||
"datafield_MPPT": "Arbeitsmodus Tracker",
|
||||
"datafield_OR": "Abschaltgrund",
|
||||
"datafield_P": "Systemleistung",
|
||||
"datafield_PPV": "Panelleistung",
|
||||
"datafield_Relay": "Status Relais",
|
||||
"datafield_V": "Spannung Kanal 1",
|
||||
"datafield_VPV": "Panelspannung",
|
||||
"latestDataPoint": "letzter Datenpunkt",
|
||||
"serialNumber": "Seriennummer"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"datafield_CS": "state of operation",
|
||||
"datafield_ERR": "error",
|
||||
"datafield_FW": "firmware version",
|
||||
"datafield_FWE": "firmware version",
|
||||
"datafield_H19": "total yield since last reset",
|
||||
"datafield_H20": "yield latest day",
|
||||
"datafield_H21": "max. power latest day",
|
||||
"datafield_H22": "yield yesterday",
|
||||
"datafield_H23": "max. power yesterday",
|
||||
"datafield_I": "current channel 1",
|
||||
"datafield_LOAD": "status load output",
|
||||
"datafield_MPPT": "tracker operation mode",
|
||||
"datafield_OR": "off reason",
|
||||
"datafield_P": "system power",
|
||||
"datafield_PPV": "panel power",
|
||||
"datafield_Relay": "relay status",
|
||||
"datafield_V": "voltage channel 1",
|
||||
"datafield_VPV": "panel voltage",
|
||||
"latestDataPoint": "last data point",
|
||||
"serialNumber": "serial number"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Product images
|
||||
|
||||
Put any picture of your own products in this folder. It might be in JPEG or PNG format.
|
||||
|
||||
## Naming convention
|
||||
|
||||
Every picture needs to be named in a certain way to be automatically identified by the frontend.
|
||||
The naming shall follow this convention:
|
||||
ProductID.jpg or ProductID.png
|
||||
Let's say you have a SmartSolar MPPT VE.CAN150/70 rev. 2 (like the developer of this tool). The product ID would be 0xA10C. So any picture shall be named 0xA10C.jpg or 0xA10C.png
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>VE.Direct web-UI</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
</head>
|
||||
|
||||
<body onload="initialize()">
|
||||
<div class="container">
|
||||
<div class="productList" id="productList">
|
||||
<div class="padding bright" id="liDashboard">🏠 Dashboard</div>
|
||||
</div>
|
||||
|
||||
<div class="mainView dashboard" id="dashboard">
|
||||
<h1>Dashboard</h1>
|
||||
</div>
|
||||
<div class="mainView deviceDetails" id="deviceDetails">
|
||||
<img id="deviceDetailsProductImage">
|
||||
<h1>Details <span id="deviceDetailsName"></span></h1>
|
||||
<div id="deviceDetailsSN"></div>
|
||||
</div>
|
||||
</div><!-- end of container -->
|
||||
</bod>
|
||||
|
||||
<footer>
|
||||
<div>This is the <a href="https://git.schauaus.at/VE-Tools/VE.Direct-webui" target="_blank">VE.Direct web-UI</a> by SCHAUAUS GmbH.</div>
|
||||
</footer>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* main.css
|
||||
*
|
||||
* This file is part of the VE.Direct web UI
|
||||
* https://git.schauaus.at/VE-Tools/VE.Direct-webui
|
||||
* It is licensed under the GPL3.0 or later.
|
||||
* You should have received a copy of the license with this software
|
||||
*/
|
||||
a, a.visited { color: #aa0; font-weight: bold; }
|
||||
|
||||
body { font: normal 16px sans-serif; color: #ccc; background-color: #222; }
|
||||
.bright { background-color: #ddd; color: #444; }
|
||||
|
||||
.container { display: flex; width: 100%; }
|
||||
|
||||
.dashboard { display: inline-block; }
|
||||
.deviceDetails { display: none; }
|
||||
#deviceDetailsProductImage { max-width: 200px; max-height: 200px; }
|
||||
|
||||
.dark { background-color: #222; }
|
||||
|
||||
footer { font-size: 12px; text-align: center; clear: both; }
|
||||
|
||||
h1 { display: inline-block; }
|
||||
|
||||
img { float: left; padding: 0px; margin: 0px; margin-right: 5px; }
|
||||
|
||||
.mainView { min-width: 600px; background-color: #660; margin: 5px; flex-grow: 1; }
|
||||
|
||||
.productImageSmall { height: 50px; }
|
||||
.productList { width: 300px; overflow: auto; background-color: #00a; margin: 5px; }
|
||||
.padding { padding: 5px; }
|
||||
|
||||
table { border-collapse: collapse; border-width: 0px; }
|
||||
|
||||
[id^="li"] { cursor: pointer; }
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* main.js
|
||||
*
|
||||
* This file is part of the VE.Direct web UI
|
||||
* https://git.schauaus.at/VE-Tools/VE.Direct-webui
|
||||
* It is licensed under the GPL3.0 or later.
|
||||
* You should have received a copy of the license with this software
|
||||
*/
|
||||
|
||||
var deviceDataParsed = false
|
||||
var deviceDataSet = {}
|
||||
var deviceDataTimer;
|
||||
var deviceIds = {}
|
||||
var devices = [];
|
||||
var deviceTimer;
|
||||
var i18n = {}
|
||||
var lang = 'de'
|
||||
|
||||
function checkFile(url) {
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open('HEAD', url, false);
|
||||
xhr.send()
|
||||
return xhr.status !==404
|
||||
}
|
||||
|
||||
function fileReadJson(ReturnFunction, url, params = "") {
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.onload = function () {
|
||||
if(xhr.status == 200)
|
||||
ReturnFunction(xhr.response)
|
||||
else return false
|
||||
}
|
||||
xhr.onerror = function() { return false }
|
||||
xhr.open('POST', url, true)
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
xhr.responseType = 'json'
|
||||
xhr.send(params)
|
||||
}
|
||||
|
||||
function getDeviceData(device) {
|
||||
if(!device.startsWith('0x')) //check if device ID is valid
|
||||
return false
|
||||
fileReadJson(parseDeviceData, './dbQuery.php', 'q=deviceData&device=' + device)
|
||||
}
|
||||
|
||||
function getDeviceIds() {
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.onload = function() {
|
||||
if(xhr.status === 200)
|
||||
deviceIds = xhr.response
|
||||
}
|
||||
xhr.open('GET', './deviceIds.json', true)
|
||||
xhr.responseType = 'json'
|
||||
xhr.send()
|
||||
}
|
||||
|
||||
function getDevices() {
|
||||
if(Object.keys(deviceIds).length < 1) return
|
||||
if(Object.keys(i18n).lenght < 1) return
|
||||
clearInterval(deviceTimer)
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.onload = function() {
|
||||
if(xhr.status != 200) { // error handling
|
||||
devices = false
|
||||
}
|
||||
else { //process data
|
||||
devices = []
|
||||
for(let i=0; i<xhr.response.length; i++) {
|
||||
devices.push(xhr.response[i][0])
|
||||
}
|
||||
renderDeviceList()
|
||||
}
|
||||
}
|
||||
xhr.onerror = function() {
|
||||
devices = false
|
||||
}
|
||||
xhr.open("POST", "dbQuery.php", true);
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
xhr.responseType = 'json'
|
||||
xhr.send("q=devices");
|
||||
}
|
||||
|
||||
function getLang(i18n_data) {
|
||||
i18n[lang] = i18n_data
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
getDeviceIds()
|
||||
fileReadJson(getLang, './i18n/' + lang + '.json')
|
||||
deviceTimer = setInterval( getDevices, 100)
|
||||
document.getElementById('liDashboard').addEventListener("click", showDevice)
|
||||
}
|
||||
|
||||
function parseDeviceData(data) {
|
||||
deviceDataParsed = false
|
||||
deviceDataSet = {}
|
||||
for(let i=0; i < data.length; i++) {
|
||||
let date = data[i][0]
|
||||
let time = data[i][1]
|
||||
let dataPoint = data[i][2]
|
||||
|
||||
let year = date.split('-')[0]
|
||||
let month = date.split('-')[1]
|
||||
let day = date.split('-')[2]
|
||||
|
||||
if(!deviceDataSet[year])
|
||||
deviceDataSet[year] = {}
|
||||
if(!deviceDataSet[year][month])
|
||||
deviceDataSet[year][month] = {}
|
||||
if(!deviceDataSet[year][month][day])
|
||||
deviceDataSet[year][month][day] = {}
|
||||
deviceDataSet[year][month][day][time] = JSON.parse(dataPoint)
|
||||
}
|
||||
deviceDataParsed = true
|
||||
}
|
||||
|
||||
function renderDeviceDetails(device) {
|
||||
let deviceName = device.split('-')[0]
|
||||
let deviceSN = device.split('-')[1]
|
||||
|
||||
document.getElementById('deviceDetailsName').innerHTML = ""
|
||||
document.getElementById('deviceDetailsSN').innerHTML = ""
|
||||
document.getElementById('deviceDetailsProductImage').src = ""
|
||||
|
||||
//load product images
|
||||
let deviceImagePath = './images/' + deviceName
|
||||
if( checkFile(deviceImagePath + '.jpg') )
|
||||
deviceImagePath += '.jpg'
|
||||
else if( checkFile(deviceImagePath + '.png') )
|
||||
deviceImagePath += '.png'
|
||||
if( deviceImagePath.endsWith('.jpg') || deviceImagePath.endsWith('.png') )
|
||||
document.getElementById('deviceDetailsProductImage').src = deviceImagePath
|
||||
|
||||
//set basic information about the device
|
||||
document.getElementById('deviceDetailsName').innerHTML = deviceIds[deviceName]
|
||||
document.getElementById('deviceDetailsSN').innerHTML = i18n[lang]['serialNumber'] + ": " + deviceSN
|
||||
|
||||
//query device data from database and store it in global variable
|
||||
deviceDataParsed = false
|
||||
deviceDataSet = {}
|
||||
getDeviceData(device)
|
||||
|
||||
//view data on frontend
|
||||
deviceDataTimer = setInterval(showDeviceData, 100)
|
||||
}
|
||||
|
||||
function renderDeviceList() {
|
||||
for(let i=0; i < devices.length; i++) {
|
||||
let device = devices[i].split('-')[0]
|
||||
let sn = devices[i].split('-')[1]
|
||||
let classStyle = (i % 2) ? 'bright' : 'dark'
|
||||
|
||||
let block = document.createElement('div')
|
||||
block.classList.add('padding')
|
||||
block.classList.add(classStyle)
|
||||
block.id = 'li' + devices[i]
|
||||
|
||||
let productImage = document.createElement('img')
|
||||
let productImagePath = './images/' + device
|
||||
if( checkFile(productImagePath + '.jpg') )
|
||||
productImage.src = productImagePath + '.jpg'
|
||||
else if( checkFile(productImagePath + '.png') )
|
||||
productImage.src = productImagePath + '.png'
|
||||
|
||||
if(productImage.src) {
|
||||
productImage.classList.add('productImageSmall')
|
||||
block.appendChild(productImage)
|
||||
}
|
||||
document.getElementById('productList').appendChild(block)
|
||||
|
||||
block.append(deviceIds[device])
|
||||
block.innerHTML += "<br>\r\n<small>" + i18n[lang]['serialNumber'] + ': ' + sn + '<small>'
|
||||
block.addEventListener("click", showDevice)
|
||||
}
|
||||
}
|
||||
|
||||
function showDevice(event) {
|
||||
let deviceName = event.currentTarget.id.substr(2)
|
||||
if(deviceName == "Dashboard") {
|
||||
document.getElementById('deviceDetails').style.display = "none" //hide device details
|
||||
document.getElementById('dashboard').style.display = "inline-block"
|
||||
}
|
||||
else {
|
||||
document.getElementById('dashboard').style.display = "none" //hide dashboard
|
||||
|
||||
//cleanup device details
|
||||
let deviceDetailNode = document.getElementById('deviceDetails')
|
||||
while(deviceDetailNode.children.length > 3)
|
||||
deviceDetailNode.removeChild( deviceDetailNode.children[ deviceDetailNode.children.length - 1 ] )
|
||||
|
||||
//show device data block and fill in the details
|
||||
document.getElementById('deviceDetails').style.display = "inline-block"
|
||||
renderDeviceDetails(deviceName)
|
||||
}
|
||||
}
|
||||
|
||||
function showDeviceData() {
|
||||
if(!deviceDataParsed) return
|
||||
clearInterval(deviceDataTimer)
|
||||
|
||||
//parse years, months, days and timestamps
|
||||
let years = Object.keys(deviceDataSet)
|
||||
years.sort()
|
||||
let latestYear = years[years.length - 1]
|
||||
let months = Object.keys(deviceDataSet[latestYear])
|
||||
months.sort()
|
||||
let latestMonth = months[months.length - 1]
|
||||
let days = Object.keys(deviceDataSet[latestYear][latestMonth])
|
||||
days.sort()
|
||||
let latestDay = days[days.length - 1]
|
||||
let timestamps = Object.keys( deviceDataSet[latestYear][latestMonth][latestDay] )
|
||||
timestamps.sort()
|
||||
let latestTimestamp = timestamps[timestamps.length - 1]
|
||||
let latestDataPoint = document.createElement('div')
|
||||
latestDataPoint.innerHTML = i18n[lang]['latestDataPoint'] + ': ' + latestYear + '-' + latestMonth + '-' + latestDay + ' ' + latestTimestamp
|
||||
document.getElementById('deviceDetails').appendChild(latestDataPoint)
|
||||
|
||||
//parse latest datapoint regarding system power, system voltage, solar power, solar voltage
|
||||
let latestDataSet = deviceDataSet[latestYear][latestMonth][latestDay][latestTimestamp]
|
||||
let latestPowerData = document.createElement('table')
|
||||
if(latestDataSet['PPV'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_PPV'] + ': ' + latestDataSet['PPV'] + 'W' + '</td></tr>'
|
||||
if(latestDataSet['VPV'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_VPV'] + ': ' + parseInt(latestDataSet['VPV']) / 1000 + 'V' + '</td></tr>'
|
||||
if(latestDataSet['V'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_V'] + ': ' + parseInt(latestDataSet['V']) / 1000 + 'V' + '</td></tr>'
|
||||
if(latestDataSet['I'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_I'] + ': ' + parseInt(latestDataSet['I']) / 1000 + 'A' + '</td></tr>'
|
||||
if(latestDataSet['P'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_P'] + ': ' + latestDataSet['P'] + 'W' + '</td></tr>'
|
||||
if(latestDataSet['H20'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_H20'] + ': ' + parseInt(latestDataSet['H20']) * 0.01 + 'kWh' + '</td></tr>'
|
||||
if(latestDataSet['H22'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_H22'] + ': ' + parseInt(latestDataSet['H22']) / 100 + 'kWh' + '</td></tr>'
|
||||
if(latestDataSet['H19'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_H19'] + ': ' + parseInt(latestDataSet['H19']) / 100 + 'kWh' + '</td></tr>'
|
||||
if(latestDataSet['H21'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_H21'] + ': ' + parseInt(latestDataSet['H21']) + 'W' + '</td></tr>'
|
||||
if(latestDataSet['H23'])
|
||||
latestPowerData.innerHTML += '<tr><td>' + i18n[lang]['datafield_H23'] + ': ' + parseInt(latestDataSet['H23']) + 'W' + '</td></tr>'
|
||||
|
||||
document.getElementById('deviceDetails').appendChild(latestPowerData)
|
||||
}
|
||||
Reference in New Issue
Block a user