UNPKG

iobroker.sbfspot

Version:
1,337 lines (923 loc) 47.5 kB
/* * sbfspot adapter für iobroker * * Created: 15.09.2016 21:31:28 * Author: Rene Copyright(C)[2016-2020][René Glaß] */ /* jshint -W097 */// jshint strict:false /*jslint node: true */ "use strict"; const utils = require("@iobroker/adapter-core"); //const { json } = require("stream/consumers"); const SunCalc = require("suncalc2"); const axios = require("axios"); const { promisify } = require('util'); const exec = promisify(require('child_process').exec) const os = require('os'); //const bluetooth_test = require("./lib/SMA_Bluetooth").test; const supportedVersion="3.9.12" let adapter; function startAdapter(options) { options = options || {}; Object.assign(options, { name: "sbfspot", ready: function () { try { adapter.log.debug("start"); main(); } catch (e) { adapter.log.error("exception catch after ready [" + e + "]"); } }, //####################################### // is called when adapter shuts down unload: function (callback) { try { if (intervalID != null) { clearInterval(intervalID); } if (updateTimerID != null) { clearTimeout(updateTimerID); } adapter && adapter.log && adapter.log.info && adapter.log.info("cleaned everything up..."); //to do stop intervall callback(); } catch (e) { callback(); } }, stateChange: async (id, state) => { //await HandleStateChange(id, state); }, //####################################### // message: async (obj) => { if (obj) { switch (obj.command) { case "Install": adapter.log.debug("message install called"); break; case "Update": adapter.log.debug("message update called"); break; case "checkInstallableversion": await CheckVersion("installable", obj); break; case "checkCurrentVersion": await CheckVersion("current", obj); break; case "checkSupportedVersion": await CheckVersion("supported", obj); break; default: adapter.log.error("unknown message " + obj.command); break; } } } //####################################### }); adapter = new utils.Adapter(options); return adapter; } let FirstValue4History; let FirstDate4History; //let numOfInverters; let longitude; let latitude; //---------- sqlite //https://www.npmjs.com/package/better-sqlite3 let sqlite_db; //---------- mySQL let mysql_connection; let killTimer; let intervalID = null; async function main() { let readInterval = 5; if (parseInt(adapter.config.readInterval) > 0) { readInterval = adapter.config.readInterval; } adapter.log.debug("read every " + readInterval + " minutes"); intervalID = setInterval(Do, readInterval * 60 * 1000); //read at adapterstart await Do(); } async function Do() { //bluetooth_test(); //todo check access righst for sqlite file and folder... if (adapter.config.databasetype === undefined) { adapter.log.error("databasetype not defined. check and update settings and save"); // adapter.terminate ? adapter.terminate(11) : process.exit(11); } //await CheckInverterVariables(); //killTimer = setTimeout(function () { // //adapter.stop(); // adapter.log.error("force terminate "); // adapter.terminate ? adapter.terminate(11) : process.exit(11); //}, 2*60*1000); let daylight = false; if (adapter.config.GetDataOnlyWhenDaylight) { await GetSystemDateformat(); const times = SunCalc.getTimes(new Date(), latitude, longitude); // format sunset/sunrise time from the Date object const sunsetStr = ("0" + times.sunset.getHours()).slice(-2) + ":" + ("0" + times.sunset.getMinutes()).slice(-2); const sunriseStr = ("0" + times.sunrise.getHours()).slice(-2) + ":" + ("0" + times.sunrise.getMinutes()).slice(-2); adapter.log.debug("sunrise " + sunriseStr + " sunset " + sunsetStr + " " + adapter.config.GetDataOnlyWhenDaylight); const now = new Date(); if ((now.getHours() > times.sunrise.getHours() || (now.getHours() == times.sunrise.getHours() && now.getMinutes() > times.sunrise.getMinutes())) && (now.getHours() < times.sunset.getHours() || (now.getHours() == times.sunset.getHours() && now.getMinutes() < times.sunset.getMinutes()))) { daylight = true; } } else { //always daylight = true; } if (daylight) { let connected = false; connected = await DB_Connect(); if (connected) { const rows = await DB_GetInverters(); adapter.log.debug("rows " + JSON.stringify(rows)); if (rows != null && rows.length > 0) { for (const i in rows) { const serial = rows[i].Serial; await GetInverter(i, rows); await DB_CheckLastUploads(serial); let rows1 = await DB_GetInvertersData(serial); adapter.log.debug("rows " + JSON.stringify(rows1)); if (rows1 != null) { await GetInverterData(0, rows1, serial); rows1 = await DB_CalcHistory_LastMonth(serial); if (rows1 != null) { await CalcHistory_LastMonth(0, rows1, serial); rows1 = await DB_CalcHistory_Prepare(serial); if (rows1 != null) { await CalcHistory_Prepare(0, rows1, serial); rows1 = await DB_CalcHistory_Today(serial); if (rows1 != null) { await CalcHistory_Today(0, rows1, serial); rows1 = await DB_CalcHistory_Years(serial); if (rows1 != null) { await CalcHistory_Years(0, rows1, serial); rows1 = await DB_CalcHistory_Months(serial); if (rows1 != null) { await CalcHistory_Months(0, rows1, serial); } } } } } } } } else { // adapter.log.error("no inverter data found, adding dummy data..."); await DB_AddDummyData(); } } DB_Disconnect(); } else { adapter.log.info("nothing to do, because no daylight ... "); } //if (killTimer) { // clearTimeout(killTimer); // adapter.log.debug("timer killed"); //} //adapter.terminate ? adapter.terminate(11) : process.exit(11); } async function GetSystemDateformat() { try { const ret = await adapter.getForeignObjectAsync("system.config"); if (ret !== undefined && ret != null) { //dateformat = ret.common.dateFormat; longitude = ret.common.longitude; latitude = ret.common.latitude; adapter.log.debug("system: longitude " + longitude + " latitude " + latitude); } else { adapter.log.error("system.config not available. longitude and latitude set to Berlin"); longitude = 52.520008; latitude = 13.404954; } } catch (e) { adapter.log.error("exception in GetSystemDateformat [" + e + "]"); } } async function AddInverterVariables(serial) { adapter.log.debug("AddInverterVariables for " + serial ); //This will be refused in future versions.Please report this to the developer. //The id 1100173807 has an invalid type!: Expected "string" or "object", received "number". //need a string instead of number as id await AddObject(serial.toString(), "channel", serial.toString(), "string", "", "", true, false); await AddObject(serial.toString() + ".Type", "state", "SMA inverter Serialnumber", "string", "value", "", true, false); await AddObject(serial.toString() + ".ETotal", "state", "SMA inverter Ertrag Total", "number", "value", "Wh", true, false); await AddObject(serial.toString() + ".EToday", "state", "SMA inverter Ertrag Today", "number", "value", "Wh", true, false); await AddObject(serial.toString() + ".SW_Version", "state", "SMA inverter SW Version", "string", "value", "", true, false); await AddObject(serial.toString() + ".TotalPac", "state", "SMA inverter Total P AC", "number", "value", "", true, false); await AddObject(serial.toString() + ".OperatingTime", "state", "SMA inverter Operating Time", "number", "value", "h", true, false); await AddObject(serial.toString() + ".FeedInTime", "state", "SMA inverter Feed In Time", "number", "value", "h", true, false); await AddObject(serial.toString() + ".Status", "state", "SMA inverter Status", "string", "value", "", true, false); await AddObject(serial.toString() + ".GridRelay", "state", "SMA inverter Grid Relay", "string", "value", "", true, false); await AddObject(serial.toString() + ".Temperature", "state", "SMA inverter Temperature", "number", "value.temperature", "°C", true, false); await AddObject(serial.toString() + ".Pdc1", "state", "SMA inverter Power DC 1", "number", "value", "W", true, false); await AddObject(serial.toString() + ".Pdc2", "state", "SMA inverter Power DC 2", "number", "value", "W", true, false); await AddObject(serial.toString() + ".Idc1", "state", "SMA inverter Current DC 1", "number", "value", "A", true, false); await AddObject(serial.toString() + ".Idc2", "state", "SMA inverter Current DC 2", "number", "value", "A", true, false); await AddObject(serial.toString() + ".Udc1", "state", "SMA inverter Voltage DC 1", "number", "value.voltage", "V", true, false); await AddObject(serial.toString() + ".Udc2", "state", "SMA inverter Voltage DC 2", "number", "value.voltage", "V", true, false); await AddObject(serial.toString() + ".Pac1", "state", "SMA inverter Power AC 1", "number", "value.voltage", "W", true, false); await AddObject(serial.toString() + ".Pac2", "state", "SMA inverter Power AC 2", "number", "value", "W", true, false); await AddObject(serial.toString() + ".Pac3", "state", "SMA inverter Power AC 3", "number", "value", "W", true, false); await AddObject(serial.toString() + ".Iac1", "state", "SMA inverter Current AC 1", "number", "value", "A", true, false); await AddObject(serial.toString() + ".Iac2", "state", "SMA inverter Current AC 2", "number", "value", "A", true, false); await AddObject(serial.toString() + ".Iac3", "state", "SMA inverter Current AC 3", "number", "value", "A", true, false); await AddObject(serial.toString() + ".Uac1", "state", "SMA inverter Voltage AC 1", "number", "value.voltage", "V", true, false); await AddObject(serial.toString() + ".Uac2", "state", "SMA inverter Voltage AC 2", "number", "value.voltage", "V", true, false); await AddObject(serial.toString() + ".Uac3", "state", "SMA inverter Voltage AC 3", "number", "value.voltage", "V", true, false); await AddObject(serial.toString() + ".Frequency", "state", "SMA inverter Frequency", "number", "value", "Hz", true, false); await AddObject(serial.toString() + ".BT_Signal", "state", "SMA inverter BT_Signal", "number", "value", "%", true, false); await AddObject(serial.toString() + ".timestamp", "state", "SMA inverter timestamp", "number", "value", "", true, false); await AddObject(serial.toString() + ".lastup", "state", "SMA inverter lastup", "string", "value", "", true, false); await AddObject(serial.toString() + ".error", "state", "SMA inverter error", "string", "value", "", true, false); await AddObject(serial.toString() + ".history.today", "state", "SMA inverter history today (JSON)", "string", "value", "", true, false); await AddObject(serial.toString() + ".history.last30Days", "state", "SMA inverter history last 30 days (JSON)", "string", "value", "", true, false); await AddObject(serial.toString() + ".history.last12Months", "state", "SMA inverter history last 12 Months (JSON)", "string", "value", "", true, false); await AddObject(serial.toString() + ".history.years", "state", "SMA inverter history years (JSON)", "string", "value", "", true, false); await AddObject(serial.toString() + ".sbfspot.LastUpload", "state", "date/time of last upload to sbfspot", "string", "value", "", true, false); await AddObject(serial.toString() + ".sbfspot.notUploaded", "state", "number of datapoints waiting for upload", "number", "value", "", true, false); adapter.log.debug("AddInverterVariables done " ); } async function CreateObject(key, obj) { const obj_new = await adapter.getObjectAsync(key); //adapter.log.warn("got object " + JSON.stringify(obj_new)); if (obj_new != null) { if ((obj_new.common.role != obj.common.role || obj_new.common.type != obj.common.type || (obj_new.common.unit != obj.common.unit && obj.common.unit != null) || obj_new.common.read != obj.common.read || obj_new.common.write != obj.common.write || obj_new.common.name != obj.common.name) && obj.type === "state" ) { adapter.log.warn("change object " + JSON.stringify(obj) + " " + JSON.stringify(obj_new)); await adapter.extendObject(key, { common: { name: obj.common.name, role: obj.common.role, type: obj.common.type, unit: obj.common.unit, read: obj.common.read, write: obj.common.write } }); } } else { await adapter.setObjectNotExistsAsync(key, obj); } } async function AddObject(key, type, common_name, common_type, common_role, common_unit, common_read, common_write) { adapter.log.debug("addObject " + key); const obj= { type: type, common: { name: common_name, type: common_type, role: common_role, unit: common_unit, read: common_read, write: common_write }, native: { location: key } }; await CreateObject(key, obj); } async function DB_Connect() { let ret = true; try { if (adapter.config.databasetype == "mySQL" || adapter.config.databasetype == "MariaDB") { //var express = require("express"); const mysql = require("mysql2/promise"); if (adapter.config.databasetype == "MariaDB") { adapter.log.info("start with MariaDB"); adapter.log.debug("--- connecting to " + adapter.config.sbfspotIP + " " + adapter.config.sbfspotPort + " " + adapter.config.sbfspotDatabasename); mysql_connection = await mysql.createConnection({ host: adapter.config.sbfspotIP, user: adapter.config.sbfspotUser, port: adapter.config.sbfspotPort, password: adapter.config.sbfspotPassword, database: adapter.config.sbfspotDatabasename }); } else { adapter.log.info("start with mySQL"); adapter.log.debug("--- connecting to " + adapter.config.sbfspotIP + " " + adapter.config.sbfspotDatabasename); mysql_connection = await mysql.createConnection({ host: adapter.config.sbfspotIP, user: adapter.config.sbfspotUser, password: adapter.config.sbfspotPassword, database: adapter.config.sbfspotDatabasename }); } mysql_connection.on("error", err => { adapter.log.error("Error on connection: " + err.message); // stop doing stuff with conn DB_Disconnect(); }); } else { adapter.log.info("start with sqlite"); const path = require("path"); const file_path = adapter.config.sqlite_path; const dbPath = path.resolve(__dirname, file_path.trim()); adapter.log.debug("--- connecting to " + dbPath); const sqlite3 = require("better-sqlite3"); sqlite_db = new sqlite3(dbPath, { fileMustExist: true }); adapter.log.debug("sqlite Database is connected ..."); //DB_GetInverters(); } } catch (e) { adapter.log.error("exception in DB_Connect [" + e + "]"); ret = false; } return ret; } async function DB_Query(query) { adapter.log.debug(query); let retRows; if (adapter.config.databasetype == "mySQL" || adapter.config.databasetype == "MariaDB") { const [rows, fields] = await mysql_connection.execute(query); retRows = rows; } else { const stmt = sqlite_db.prepare(query); const rows = stmt.all(); retRows = rows; } adapter.log.debug(JSON.stringify(retRows)); return retRows; } async function DB_CheckLastUploads(serial) { try { let query = "SELECT `TimeStamp` FROM `DayData` WHERE `PVoutput` is null "; let rows = await DB_Query(query); const notUploaded = rows.length; await adapter.setStateAsync(serial + ".sbfspot.notUploaded", { ack: true, val: notUploaded }); query = "SELECT `TimeStamp` FROM `DayData` WHERE `PVoutput` = 1 ORDER BY TimeStamp DESC LIMIT 1 "; rows = await DB_Query(query); if (rows.length > 0) { const updateTimestamp = rows[0].TimeStamp; const oDate = new Date(updateTimestamp * 1000); const oDateNow = new Date(); oDateNow.setDate(oDateNow.getDate() - 1); if (oDate < oDateNow) { adapter.log.error("no upload to sbfspot since " + oDate.toLocaleString()); } const lastUpload = oDate.toLocaleString(); await adapter.setStateAsync(serial + ".sbfspot.LastUpload", { ack: true, val: lastUpload }); } } catch (e) { adapter.log.error("exception in DB_CheckLastUploads [" + e + "]"); } } async function DB_GetInverters() { let retRows; try { const query = "SELECT * from Inverters"; retRows = await DB_Query(query); } catch (e) { adapter.log.error("exception in DB_GetInverters [" + e + "]"); } return retRows; } async function GetInverter(i, rows) { adapter.log.info("got data from " + rows[i].Type + " with ID " + rows[i].Serial); await AddInverterVariables(rows[i].Serial); await adapter.setStateAsync(rows[i].Serial + ".Type", { ack: true, val: rows[i].Type }); await adapter.setStateAsync(rows[i].Serial + ".SW_Version", { ack: true, val: rows[i].SW_Version }); await adapter.setStateAsync(rows[i].Serial + ".TotalPac", { ack: true, val: rows[i].TotalPac }); await adapter.setStateAsync(rows[i].Serial + ".OperatingTime", { ack: true, val: rows[i].OperatingTime }); await adapter.setStateAsync(rows[i].Serial + ".FeedInTime", { ack: true, val: rows[i].FeedInTime }); await adapter.setStateAsync(rows[i].Serial + ".Status", { ack: true, val: rows[i].Status }); await adapter.setStateAsync(rows[i].Serial + ".GridRelay", { ack: true, val: rows[i].GridRelay }); await adapter.setStateAsync(rows[i].Serial + ".Temperature", { ack: true, val: rows[i].Temperature }); await adapter.setStateAsync(rows[i].Serial + ".timestamp", { ack: true, val: rows[i].TimeStamp }); const oDate = new Date(rows[i].TimeStamp * 1000); const nDate = oDate.getDate(); const nMonth = oDate.getMonth() + 1; const nYear = oDate.getFullYear(); const nHours = oDate.getHours(); const nMinutes = oDate.getMinutes(); const nSeconds = oDate.getSeconds(); const sLastup = nDate + "." + nMonth + "." + nYear + " " + nHours + ":" + nMinutes + ":" + nSeconds; await adapter.setStateAsync(rows[i].Serial + ".lastup", { ack: true, val: sLastup }); const oToday = new Date(); let sError = "none"; if (Math.abs(oDate.getTime() - oToday.getTime()) > (24 * 60 * 60 * 1000)) { sError = "sbfspot no update since " + sLastup + " "; adapter.log.debug(sError); } await adapter.setStateAsync(rows[i].Serial + ".error", { ack: true, val: sError }); //numOfInverters++; // await DB_GetInvertersData(rows[i].Serial); adapter.log.debug("GetInverter done"); } async function DB_GetInvertersData(serial) { let retRows; adapter.log.debug("DB_GetInvertersData for " + serial); try { //SELECT * from SpotData where Serial ='2000562095' ORDER BY TimeStamp DESC LIMIT 1 const query = "SELECT * from SpotData where Serial =" + serial + " ORDER BY TimeStamp DESC LIMIT 1"; retRows = await DB_Query(query); } catch (e) { adapter.log.error("exception in DB_GetInvertersData [" + e + "]"); } adapter.log.debug("DB_GetInvertersData done "); return retRows; } async function GetInverterData(err, rows, serial) { adapter.log.debug("GetInverterData for " + serial); if (!err) { adapter.log.debug("rows " + JSON.stringify(rows)); for (const i in rows) { //must only be one row... // check if it is really today, otherwise set to zero const oDate = new Date(rows[i].TimeStamp * 1000); const nDay = oDate.getDate(); const nMonth = oDate.getMonth() + 1; const nYear = oDate.getFullYear(); const oDateToday = new Date(); const nDayToday = oDateToday.getDate(); const nMonthToday = oDateToday.getMonth() + 1; const nYearToday = oDateToday.getFullYear(); await adapter.setStateAsync(rows[i].Serial + ".Pdc1", { ack: true, val: rows[i].Pdc1 }); await adapter.setStateAsync(rows[i].Serial + ".Pdc2", { ack: true, val: rows[i].Pdc2 }); await adapter.setStateAsync(rows[i].Serial + ".Idc1", { ack: true, val: rows[i].Idc1 }); await adapter.setStateAsync(rows[i].Serial + ".Idc2", { ack: true, val: rows[i].Idc2 }); await adapter.setStateAsync(rows[i].Serial + ".Udc1", { ack: true, val: rows[i].Udc1 }); await adapter.setStateAsync(rows[i].Serial + ".Udc2", { ack: true, val: rows[i].Udc2 }); await adapter.setStateAsync(rows[i].Serial + ".Pac1", { ack: true, val: rows[i].Pac1 }); await adapter.setStateAsync(rows[i].Serial + ".Pac2", { ack: true, val: rows[i].Pac2 }); await adapter.setStateAsync(rows[i].Serial + ".Pac3", { ack: true, val: rows[i].Pac3 }); await adapter.setStateAsync(rows[i].Serial + ".Iac1", { ack: true, val: rows[i].Iac1 }); await adapter.setStateAsync(rows[i].Serial + ".Iac2", { ack: true, val: rows[i].Iac2 }); await adapter.setStateAsync(rows[i].Serial + ".Iac3", { ack: true, val: rows[i].Iac3 }); await adapter.setStateAsync(rows[i].Serial + ".Uac1", { ack: true, val: rows[i].Uac1 }); await adapter.setStateAsync(rows[i].Serial + ".Uac2", { ack: true, val: rows[i].Uac2 }); await adapter.setStateAsync(rows[i].Serial + ".Uac3", { ack: true, val: rows[i].Uac3 }); adapter.log.debug("### " + nDay + "." + nMonth + "." + nYear + " = " + nDayToday + "." + nMonthToday + "." + nYearToday); if (nDay == nDayToday && nMonth == nMonthToday && nYear == nYearToday) { await adapter.setStateAsync(rows[i].Serial + ".EToday", { ack: true, val: rows[i].EToday }); } else { await adapter.setStateAsync(rows[i].Serial + ".EToday", { ack: true, val: 0 }); } await adapter.setStateAsync(rows[i].Serial + ".ETotal", { ack: true, val: rows[i].ETotal }); await adapter.setStateAsync(rows[i].Serial + ".Frequency", { ack: true, val: rows[i].Frequency }); await adapter.setStateAsync(rows[i].Serial + ".BT_Signal", { ack: true, val: rows[i].BT_Signal }); } //await DB_CalcHistory_LastMonth(serial); } else { adapter.log.error("Error while performing Query in GetInverterData. " + err); } adapter.log.debug("GetInverterData done "); } async function DB_CalcHistory_LastMonth(serial) { adapter.log.debug("DB_CalcHistory_LastMonth for " + serial); let retRows; try { //täglich im aktuellen Monat //SELECT from_unixtime(TimeStamp, '%Y-%m-%d') as date, Max(`EToday`) as ertrag FROM `SpotData` WHERE `Serial` = '2000562095' AND TimeStamp>= 1501542000 AND TimeStamp<= 1504133999 Group By from_unixtime(TimeStamp, '%Y-%m-%d') //SELECT from_unixtime(TimeStamp, '%Y-%m-%d') as date, Max(`EToday`) as ertrag FROM `SpotData` WHERE `Serial` = '2000562095' AND TimeStamp>= 1500406746112 AND TimeStamp<= 1502998746112 Group By from_unixtime(TimeStamp, '%Y-%m-%d') //füy mySQL: //SELECT from_unixtime(TimeStamp, '%Y') as date, Max(`ETotal`) as ertrag FROM `SpotData` WHERE `Serial` = '2000562095' Group By from_unixtime(TimeStamp, '%Y') //für sqlite //SELECT strftime('%Y-%m-%d ', datetime(TimeStamp, 'unixepoch')) as date, Max(`EToday`) as ertrag FROM `SpotData` WHERE `Serial` = '2000562095' AND TimeStamp>= 1511859131.474 AND TimeStamp<= 1514451131.474 Group By strftime('%Y-%m-%d ', datetime(TimeStamp, 'unixepoch')) const dateto = new Date(); //today const datefrom = new Date(); datefrom.setDate(datefrom.getDate() - 30); //adapter.log.debug('from ' + datefrom.toDateString() + " to " + dateto.toDateString()); //gettime gives milliseconds!! let query = ""; if (adapter.config.databasetype == "mySQL" || adapter.config.databasetype == "MariaDB") { query = "SELECT from_unixtime(TimeStamp, '%Y-%m-%d') as date, Max(`EToday`) as ertrag FROM `SpotData` WHERE `Serial` = '" + serial + "' AND TimeStamp>= " + datefrom.getTime() / 1000 + " AND TimeStamp<= " + dateto.getTime() / 1000 + " Group By from_unixtime(TimeStamp, '%Y-%m-%d')"; } else { query = "SELECT strftime('%Y-%m-%d', datetime(TimeStamp, 'unixepoch')) as date, Max(`EToday`) as ertrag FROM `SpotData` WHERE `Serial` = '" + serial + "' AND TimeStamp>= " + datefrom.getTime() / 1000 + " AND TimeStamp<= " + dateto.getTime() / 1000 + " Group By strftime('%Y-%m-%d', datetime(TimeStamp, 'unixepoch'))"; } retRows = await DB_Query(query); } catch (e) { adapter.log.error("exception in DB_CalcHistory_LastMonth [" + e + "]"); } return retRows; } async function CalcHistory_LastMonth(err, rows, serial) { adapter.log.debug("CalcHistory_LastMonth for " + serial); if (!err) { adapter.log.debug("rows " + JSON.stringify(rows)); //rows[{ "date": "2017-07-19", "ertrag": 12259 }, { "date": "2017-07-20", "ertrag": 9905 }, { "date": "2017-07-21", "ertrag": 12991 }, { "date": "2017-07-22", "ertrag": 9292 }, { "date": "2017-07-23", "ertrag": 7730 }, { const oLastDays = []; //var daydata = {}; for (const i in rows) { const data = rows[i]; if (adapter.config.History4Vis2) { const oDate = new Date(data["date"]); oDate.setHours(12); oDate.setMinutes(0); //for vis-2 oLastDays.push( [ oDate, data["ertrag"] ]); /* [ ["2024-04-23T00:00:00.000Z",12551], ["2024-04-24T00:00:00.000Z",5898], ["2024-04-25T00:00:00.000Z",8373] ] */ } else { oLastDays.push({ "date": data["date"], "value": data["ertrag"] }); } //adapter.log.debug(JSON.stringify(oLastDays)); } await adapter.setStateAsync(serial + ".history.last30Days", { ack: true, val: JSON.stringify(oLastDays) }); //await DB_CalcHistory_Prepare(serial); } else { adapter.log.error("Error while performing Query in CalcHistory_LastMonth. " + err); } } async function DB_CalcHistory_Prepare(serial) { adapter.log.debug("DB_CalcHistory_Prepare for " + serial); let retRows; try { //var dateto = new Date(); //today //SELECT from_unixtime(TimeStamp, '%Y-%m-%d') as date, ETotal FROM `SpotData` ORDER by `TimeStamp` ASC LIMIT 1 let query = ""; if (adapter.config.databasetype == "mySQL" || adapter.config.databasetype == "MariaDB") { query = "SELECT from_unixtime(TimeStamp, '%Y-%m-%d') as date, ETotal FROM `SpotData` WHERE `Serial` = '" + serial + "' ORDER by `TimeStamp` ASC LIMIT 1"; } else { query = "SELECT strftime('%Y-%m-%d', datetime(TimeStamp, 'unixepoch')) as date, ETotal FROM `SpotData` WHERE `Serial` = '" + serial + "' ORDER by `TimeStamp` ASC LIMIT 1"; } retRows = await DB_Query(query); } catch (e) { adapter.log.error("exception in DB_CalcHistory_Prepare [" + e + "]"); } return retRows; } async function CalcHistory_Prepare(err, rows, serial) { adapter.log.debug("CalcHistory_Prepare for " + serial); if (!err) { adapter.log.debug("prepare: rows " + JSON.stringify(rows)); for (const i in rows) { const data = rows[i]; FirstValue4History = data["ETotal"]; FirstDate4History = data["date"]; adapter.log.debug(FirstDate4History + " " + FirstValue4History); } //await DB_CalcHistory_Today(serial); } else { adapter.log.error("Error while performing Query in CalcHistory_Prepare. " + err); } } async function DB_CalcHistory_Today(serial) { adapter.log.debug("DB_CalcHistory_Today for " + serial); let retRows; try { const dateto = new Date(); //today const datefrom = new Date(); datefrom.setHours(0); datefrom.setMinutes(0); //adapter.log.debug('from ' + datefrom.toDateString() + " to " + dateto.toDateString()); //gettime gives milliseconds!! let query = ""; if (adapter.config.databasetype == "mySQL" || adapter.config.databasetype == "MariaDB") { query = "SELECT from_unixtime(TimeStamp, '%H:%i') as time, Max(`EToday`) as ertrag FROM `SpotData` WHERE `Serial` = '" + serial + "' AND TimeStamp>= " + datefrom.getTime() / 1000 + " AND TimeStamp<= " + dateto.getTime() / 1000 + " Group By from_unixtime(TimeStamp, '%H:%i')"; } else { query = "SELECT strftime('%H:%m', datetime(TimeStamp, 'unixepoch')) as time, Max(`EToday`) as ertrag FROM `SpotData` WHERE `Serial` = '" + serial + "' AND TimeStamp>= " + datefrom.getTime() / 1000 + " AND TimeStamp<= " + dateto.getTime() / 1000 + " Group By strftime('%H-%m', datetime(TimeStamp, 'unixepoch'))"; } retRows = await DB_Query(query); } catch (e) { adapter.log.error("exception in DB_CalcHistory_Today [" + e + "]"); } return retRows; } async function CalcHistory_Today(err, rows, serial) { adapter.log.debug("CalcHistory_Today for " + serial); if (!err) { adapter.log.debug("rows " + JSON.stringify(rows)); const oLastDays = []; //var daydata = {}; for (const i in rows) { const data = rows[i]; if (adapter.config.History4Vis2) { const oDate = new Date(); const times = data["time"].split(":"); oDate.setHours(parseInt(times[0])); oDate.setMinutes(parseInt(times[1])); oDate.setSeconds(0); adapter.log.debug("### " + data["time"] + " " + JSON.stringify(times) + " " + oDate.toLocaleString()); //for vis-2 oLastDays.push( [ oDate, data["ertrag"] ]); /* [ ["2024-05-23T14:45:07.403Z",1], ["2024-05-23T14:45:07.403Z",3], ["2024-05-23T14:45:07.403Z",4], ["2024-05-23T14:45:07.403Z",6], ["2024-05-23T14:45:07.403Z",9], ["2024-05-23T14:45:07.403Z",12], ["2024-05-23T14:45:07.404Z",16], ["2024-05-23T14:45:07.404Z",20]] */ } else { oLastDays.push({ "time": data["time"], "value": data["ertrag"] }); } } await adapter.setStateAsync(serial + ".history.today", { ack: true, val: JSON.stringify(oLastDays) }); } else { adapter.log.error("Error while performing Query in CalcHistory_Today. " + err); } } async function DB_CalcHistory_Years(serial) { adapter.log.debug("DB_CalcHistory_Years for " + serial); let retRows; try { //SELECT from_unixtime(TimeStamp, '%Y') as date, Max(`ETotal`) as ertrag FROM `SpotData` WHERE `Serial` = '2000562095' Group By from_unixtime(TimeStamp, '%Y') let query = ""; if (adapter.config.databasetype == "mySQL" || adapter.config.databasetype == "MariaDB") { query = "SELECT from_unixtime(TimeStamp, '%Y') as date, Max(`ETotal`) as ertrag, Min(`ETotal`) as startertrag FROM `SpotData` WHERE `Serial` = '" + serial + "' Group By from_unixtime(TimeStamp, '%Y')"; } else { query = "SELECT strftime('%Y', datetime(TimeStamp, 'unixepoch')) as date, Max(`ETotal`) as ertrag, Min(`ETotal`) as startertrag FROM `SpotData` WHERE `Serial` = '" + serial + "' Group By strftime('%Y', datetime(TimeStamp, 'unixepoch'))"; } retRows = await DB_Query(query); } catch (e) { adapter.log.error("exception in DB_CalcHistory_Years [" + e + "]"); } return retRows; } async function CalcHistory_Years(err, rows, serial) { adapter.log.debug("CalcHistory_Years for " + serial); if (!err) { adapter.log.debug("rows " + JSON.stringify(rows)); const oLastYears = []; //var yeardata = {}; const installdate = new Date(adapter.config.install_date); const firstvaluedate = new Date(FirstDate4History); //adapter.log.debug("------ " + installdate.toDateString() + " " + firstvaluedate.toDateString()); //adapter.log.debug("------ " + installdate.getUTCFullYear() + " < " + firstvaluedate.getUTCFullYear()); const installyear = installdate.getUTCFullYear(); let firstyear = true; let yearvalue = 0; for (const i in rows) { const data = rows[i]; if (installdate.getUTCFullYear() < firstvaluedate.getUTCFullYear() && firstyear == true) { const diffyears = firstvaluedate.getUTCFullYear() - installdate.getUTCFullYear(); const monthoffirstyear = 12 - installdate.getUTCMonth(); const monthoflastyear = firstvaluedate.getUTCMonth(); const months = monthoffirstyear + monthoflastyear + (diffyears - 1) * 12; //adapter.log.debug("---- " + monthoffirstyear + " " + monthoflastyear); const valuepermonth = FirstValue4History / months; //adapter.log.debug("++++ yeardiff " + diffyears + " monthdiff " + months + " value per month " + valuepermonth); for (let n = 0; n <= diffyears; n++) { if (n == 0) { yearvalue += monthoffirstyear * valuepermonth; } else if (n == (diffyears)) { //yearvalue += monthoflastyear * valuepermonth + data["ertrag"] - data["startertrag"]; //yearvalue += monthoflastyear * valuepermonth; yearvalue = FirstValue4History; //???? 4 504880.3333333333 44558440 0 //adapter.log.debug("???? " + monthoflastyear + " " + valuepermonth + " " + data["ertrag"] + " " + data["startertrag"]); } else { yearvalue += 12 * valuepermonth; } adapter.log.debug("fillup " + (installyear + n) + " " + yearvalue); if (adapter.config.History4Vis2) { const year = installyear + n; const oDate = new Date(year); //for vis-2 oLastYears.push( [ oDate, parseInt(yearvalue) ]); /* [ ["2008-01-01T00:00:00.000Z",7000], ["2009-01-01T00:00:00.000Z",2309000], ["2010-01-01T00:00:00.000Z",4445000], ["2011-01-01T00:00:00.000Z",7019000],["2012-01-01T00:00:00.000Z",9371000],["2013-01-01T00:00:00.000Z",11393000],["2014-01-01T00:00:00.000Z",13666000],["2015-01-01T00:00:00.000Z",16034000],["2016-01-01T00:00:00.000Z",18052202],["2017-01-01T00:00:00.000Z",20172891],["2018-01-01T00:00:00.000Z",22476037],["2019-01-01T00:00:00.000Z",24617484],["2020-01-01T00:00:00.000Z",26802727],["2021-01-01T00:00:00.000Z",28673935],["2022-01-01T00:00:00.000Z",30710918],["2023-01-01T00:00:00.000Z",32662549],["2024-01-01T00:00:00.000Z",33370608]] */ } else { oLastYears.push({ "year": installyear + n, "value": parseInt(yearvalue) }); } } } else { yearvalue = data["ertrag"]; adapter.log.debug(data["date"] + " " + yearvalue); if (adapter.config.History4Vis2) { const oDate = new Date(data["date"]); //for vis-2 oLastYears.push( [ oDate, yearvalue ]); } else { oLastYears.push({ "year": data["date"], "value": yearvalue }); } } firstyear = false; } adapter.log.debug(JSON.stringify(oLastYears)); await adapter.setStateAsync(serial + ".history.years", { ack: true, val: JSON.stringify(oLastYears) }); } else { adapter.log.error("Error while performing Query in CalcHistory_Years. " + err); } } async function DB_CalcHistory_Months(serial) { adapter.log.debug("DB_CalcHistory_Months for " + serial); let retRows; try { const dateto = new Date(); //today const datefrom = new Date(); datefrom.setHours(0); datefrom.setMinutes(0); datefrom.setFullYear(dateto.getFullYear() - 1); datefrom.setDate(1); //adapter.log.debug('DB_CalcHistory_Months: from ' + datefrom.toDateString() + " to " + dateto.toDateString()); //SELECT from_unixtime(TimeStamp, '%Y-%m') as date, Max(`ETotal`) as ertrag FROM `SpotData` WHERE `Serial` = '2000562095' Group By from_unixtime(TimeStamp, '%Y-%m') let query = ""; if (adapter.config.databasetype == "mySQL" || adapter.config.databasetype == "MariaDB") { query = "SELECT from_unixtime(TimeStamp, '%Y-%m') as date, Max(`ETotal`) as ertrag FROM `SpotData` WHERE `Serial` = '" + serial + "' AND TimeStamp>= " + datefrom.getTime() / 1000 + " AND TimeStamp<= " + dateto.getTime() / 1000 + " Group By from_unixtime(TimeStamp, '%Y-%m')"; } else { query = "SELECT strftime('%Y-%m', datetime(TimeStamp, 'unixepoch')) as date, Max(`ETotal`) as ertrag FROM `SpotData` WHERE `Serial` = '" + serial + "' AND TimeStamp>= " + datefrom.getTime() / 1000 + " AND TimeStamp<= " + dateto.getTime() / 1000 + " Group By strftime('%Y-%m', datetime(TimeStamp, 'unixepoch'))"; } retRows = await DB_Query(query); } catch (e) { adapter.log.error("exception in DB_CalcHistory_Months [" + e + "]"); } return retRows; } async function CalcHistory_Months(err, rows, serial) { adapter.log.debug("CalcHistory_Months for " + serial); if (!err) { adapter.log.debug("rows " + JSON.stringify(rows)); const oLastMonth = []; for (const i in rows) { const data = rows[i]; if (adapter.config.History4Vis2) { const oDate = new Date(data["date"]); //for vis-2 oLastMonth.push( [ oDate, data["ertrag"] ]); /* [ ["2023-05-01T00:00:00.000Z",31422838], ["2023-06-01T00:00:00.000Z",31732951], ["2023-07-01T00:00:00.000Z",32009703], ["2023-08-01T00:00:00.000Z",32245904],["2023-09-01T00:00:00.000Z",32505135],["2023-10-01T00:00:00.000Z",32615574],["2023-11-01T00:00:00.000Z",32645852],["2023-12-01T00:00:00.000Z",32662549],["2024-01-01T00:00:00.000Z",32689084],["2024-02-01T00:00:00.000Z",32754174],["2024-03-01T00:00:00.000Z",32940065],["2024-04-01T00:00:00.000Z",33184421],["2024-05-01T00:00:00.000Z",33370608]] */ } else { oLastMonth.push({ "month": data["date"], "value": data["ertrag"] }); } } await adapter.setStateAsync(serial + ".history.last12Months", { ack: true, val: JSON.stringify(oLastMonth) }); } else { adapter.log.error("Error while performing Query in CalcHistory_Months. " + err); } } async function DB_AddDummyData() { try { adapter.log.debug("add dummy data"); //INSERT INTO`Inverters`(`Serial`, `Name`, `Type`, `SW_Version`, `TimeStamp`, `TotalPac`, `EToday`, `ETotal`, `OperatingTime`, `FeedInTime`, `Status`, `GridRelay`, `Temperature`) VALUES([value - 1], [value - 2], [value - 3], [value - 4], [value - 5], [value - 6], [value - 7], [value - 8], [value - 9], [value - 10], [value - 11], [value - 12], [value - 13]) let query = ""; query = "INSERT INTO`Inverters`(`Serial`, `Name`, `Type`, `SW_Version`, `TimeStamp`, `TotalPac`,"; query += " `EToday`, `ETotal`, `OperatingTime`, `FeedInTime`, `Status`, `GridRelay`, `Temperature`) VALUES("; query += " 12345678, `SN: 1234567`, `SB Dummy`, `0.0` , 1548776704 , 0 ,"; query += " 3, 3512, 50, 45, `okay`, `?`, 37 "; query += ")"; adapter.log.debug(query); if (adapter.config.databasetype == "mySQL" || adapter.config.databasetype == "MariaDB") { await mysql_connection.execute(query); } else { const stmt = sqlite_db.prepare(query); stmt.all(); } } catch (e) { adapter.log.error("exception in DB_AddDummyData [" + e + "]"); } } function DB_Disconnect() { adapter.log.debug("disconnect database"); if (adapter.config.databasetype == "mySQL" || adapter.config.databasetype == "MariaDB") { if (mysql_connection !== undefined && mysql_connection != null) { mysql_connection.end(); } } else { if (sqlite_db !== undefined && sqlite_db != null) { sqlite_db.close(); } } adapter.log.info("all done ... "); } async function CheckVersion(version, msg) { if (version == "installable") { const version = await GetLatestVersionGithub(); adapter.sendTo(msg.from, msg.command, version, msg.callback); } else if (version == "current") { const version = await GetInstalledVersion(); adapter.sendTo(msg.from, msg.command, version, msg.callback); } else if (version == "supported") { adapter.sendTo(msg.from, msg.command, supportedVersion, msg.callback); } } async function GetLatestVersionGithub() { let latestVersion = "unknown"; try { const url = " https://api.github.com/repos/SBFspot/SBFspot/releases/latest"; adapter.log.debug("call " + url); let result = await axios.get(url, { timeout: 5000 }); if (result != null && result.status == 200 && result.data != null) { adapter.log.info("installable version " + JSON.stringify(result.data.name)); latestVersion = result.data.name; } else { latestVersion = "unknown / no result"; } } catch (e) { adapter.log.error("exception in GetLatestVersionGithub [" + e + "]"); latestVersion = "unknown / error"; } return latestVersion; } async function GetInstalledVersion() { let Version = "unknown"; try { if (os.type() == "Linux") { const cmd = " /usr/local/bin/sbfspot.3/SBFspot -version"; adapter.log.debug("call " + cmd); let res = await exec(cmd); Version = res.stdout; adapter.log.info("result " + Version); } else { adapter.log.error("sbfspot version cannot be detected on " + os.type()); Version = "unknown on " + os.type(); } } catch (e) { adapter.log.error("exception in GetInstalledVersion [" + e + "]"); Version = "unknown / error" } return Version; } // If started as allInOne/compact mode => return function to create instance if (module && module.parent) { module.exports = startAdapter; } else { // or start the instance directly startAdapter(); }