260 lines
8.8 KiB
JavaScript
260 lines
8.8 KiB
JavaScript
/*
|
|
* 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('productListItem')
|
|
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')
|
|
|
|
//solar panel data
|
|
latestPowerData.innerHTML += '<tr><td class="right">' +
|
|
i18n[lang]['datafield_CS'] + ':</td><td>' + ( (latestDataSet['CS']) ? i18n[lang]['operatingStatus_' + latestDataSet['CS']] : '---') +
|
|
'</td><td class="right">' +
|
|
i18n[lang]['datafield_VPV'] + ':</td><td>' + ( (latestDataSet['VPV']) ? parseInt(latestDataSet['VPV']) / 1000 : '---' ) + 'V' +
|
|
'</td><td class="right">' +
|
|
i18n[lang]['datafield_PPV'] + ':</td><td>' + ( (latestDataSet['PPV']) ? latestDataSet['PPV'] : '---' ) + 'W' +
|
|
'</td></tr>'
|
|
|
|
//battery / channel 1 data
|
|
latestPowerData.innerHTML += '<tr><td class="right">' +
|
|
i18n[lang]['datafield_I'] + ':</td><td>' + ( (latestDataSet['I']) ? parseInt(latestDataSet['I']) / 1000 : '---' ) + 'A' +
|
|
'</td><td class="right">' +
|
|
i18n[lang]['datafield_V'] + ':</td><td>' + ( (latestDataSet['V']) ? parseInt(latestDataSet['V']) / 1000 : '---' ) + 'V' +
|
|
'</td><td class="right">' +
|
|
i18n[lang]['datafield_P'] + ':</td><td>' + ( (latestDataSet['P']) ? latestDataSet['P'] : ( ( latestDataSet['I'] && latestDataSet['V'] ) ? parseInt(latestDataSet['I']) * parseInt(latestDataSet['V']) /1000000 : '---' ) ) + 'W' +
|
|
'</td></tr>'
|
|
|
|
//yield data
|
|
latestPowerData.innerHTML += '<tr><td class="right">' +
|
|
i18n[lang]['datafield_H20'] + ':</td><td>' + ( (latestDataSet['H20']) ? parseInt(latestDataSet['H20']) * 0.01 : '---' ) + 'kWh' +
|
|
'</td><td class="right">' +
|
|
i18n[lang]['datafield_H22'] + ':</td><td>' + ( (latestDataSet['H22']) ? parseInt(latestDataSet['H22']) / 100 : '---' ) + 'kWh' +
|
|
'</td><td class="right">' +
|
|
i18n[lang]['datafield_H19'] + ':</td><td>' + ( ( latestDataSet['H19'] ) ? parseInt(latestDataSet['H19']) / 100 : '---' ) + 'kWh' +
|
|
'</td></tr>'
|
|
|
|
//power data
|
|
latestPowerData.innerHTML += '<tr><td class="right">' +
|
|
i18n[lang]['datafield_H21'] + ':</td><td>' + parseInt(latestDataSet['H21']) + 'W' +
|
|
'</td><td class="right">' +
|
|
i18n[lang]['datafield_H23'] + ':</td><td> ' + parseInt(latestDataSet['H23']) + 'W' +
|
|
'</td></tr>'
|
|
|
|
|
|
document.getElementById('deviceDetails').appendChild(latestPowerData)
|
|
}
|