Files
VE.Direct-webui/public/main.js
T
2026-05-31 14:52:38 +02:00

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)
}