initial working status (overview for each device)

This commit is contained in:
Aaska Black Wolf
2026-05-24 20:04:22 +02:00
parent cf77016f81
commit 10a03b645c
12 changed files with 628 additions and 1 deletions
Binary file not shown.
+72
View File
@@ -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();
}
?>
+155
View File
@@ -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"
}
+22
View File
@@ -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&auml;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"
}
+22
View File
@@ -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"
}
+10
View File
@@ -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
+29
View File
@@ -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">&#127968; 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>
+36
View File
@@ -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
View File
@@ -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)
}