iobroker.sun2000
Version:
328 lines (284 loc) • 6.64 kB
JavaScript
'use strict';
const suncalc = require('suncalc2');
//const {storeType} = require(__dirname + '/types.js');
class Logging {
constructor(adapterInstance) {
this.adapter = adapterInstance;
this._quiet = false;
}
get quiet() {
return this._quiet;
}
beQuiet(quiet) {
this._quiet = quiet;
}
debug(msg) {
this.adapter.log.debug(msg);
}
info(msg) {
if (this._quiet) {
this.debug(`Info: ${msg}`);
} else {
this.adapter.log.info(msg);
}
}
silly(msg) {
if (this._quiet) {
this.debug(`Warn: ${msg}`);
} else {
this.adapter.log.silly(msg);
}
}
warn(msg) {
if (this._quiet) {
this.debug(`Warn: ${msg}`);
} else {
this.adapter.log.warn(msg);
}
}
error(msg) {
if (this._quiet) {
this.debug(`Error: ${msg}`);
} else {
this.adapter.log.error(msg);
}
}
}
class StateMap {
constructor() {
this.stateMap = new Map();
}
round(num, decimalPlaces = 0) {
var p = Math.pow(10, decimalPlaces);
var n = num * p * (1 + Number.EPSILON);
return Math.round(n) / p;
}
get(id) {
return this.stateMap.get(id);
}
set(id, value, options) {
if (options?.type == 'number' && isNaN(value)) {
return;
}
if (value !== null) {
if (options?.type == 'number') {
//value = Math.round((value + Number.EPSILON) * 1000) / 1000; //3rd behind
value = this.round(value, 3);
}
const existing = this.get(id); //existing entry
const mapOptions = {
id: id,
value: value,
stored: existing?.stored ? existing.stored : false,
};
if (options?.renew || existing?.value !== value) {
mapOptions.stored = false;
}
if (options?.stored) {
mapOptions.stored = options.stored;
}
this.stateMap.set(id, mapOptions);
}
}
values() {
return this.stateMap.values();
}
}
class RegisterMap {
constructor() {
this._map = new Map();
this._mapDefaults = new Map();
//only for inverter
this._defaults = [
{
reg: 37000, //battery.unit.1.runningStatus
value: 0,
len: 1,
},
{
reg: 37741, //battery.unit.2.runningStatus
value: 0,
len: 1,
},
{
reg: 37760, //SOC
value: 0,
len: 1,
},
{
reg: 37765, //Battery Charge And Discharge Power
value: 0,
len: 2,
},
];
for (const item of this._defaults) {
for (let i = 0; i < item.len; i++) {
this._mapDefaults.set(item.reg, item.value);
}
}
}
get(startAddr, length, useDefaults = false) {
const values = [];
for (let i = 0; i < length; i++) {
values[i] = this._map.get(startAddr + i);
if (useDefaults && values[i] == null) {
values[i] = this._mapDefaults.get(startAddr + i);
}
}
return values;
}
set(startAddr, values) {
for (let i = 0; i < values.length; i++) {
const value = values[i];
this._map.set(startAddr + i, value);
}
}
values() {
return this._map.values();
}
}
class RiemannSum {
constructor(autoResetAtMitnight = true) {
this._resetAtMitnight = autoResetAtMitnight;
this.reset();
}
get lastDate() {
return this._lastDate ? this._lastDate : new Date();
}
add(newValue) {
//Obersumme bilden
const now = new Date();
if (this._resetAtMitnight) {
const lastnight = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(), // today, ...
0,
0,
0, // ...at 00:00:00 hours
);
if (this.lastDate?.getTime() <= lastnight.getTime()) {
this.reset();
}
}
if (!isNaN(newValue)) {
this._sum += (newValue * (now.getTime() - this.lastDate.getTime())) / 3600 / 1000; //Hour/Sekunden
this._lastDate = now;
}
}
reset() {
this._sum = 0;
this._lastDate = new Date();
}
get sum() {
return this._sum;
}
setStart(sum, ts) {
this._sum = sum;
this._lastDate = new Date(ts);
}
}
//https://stackoverflow.com/questions/38802959/how-to-lock-on-object-which-shared-by-multiple-async-method-in-nodejs
const createAsyncLock = () => {
const queue = [];
let active = false;
return fn => {
let deferredResolve;
let deferredReject;
const deferred = new Promise((resolve, reject) => {
deferredResolve = resolve;
deferredReject = reject;
});
const exec = async () => {
await fn().then(deferredResolve, deferredReject);
if (queue.length > 0) {
queue.shift()();
} else {
active = false;
}
};
if (active) {
queue.push(exec);
} else {
active = true;
exec();
}
return deferred;
};
};
// Get longitude an latidude from system config
async function getSystemData(adapter) {
const state = await adapter.getForeignObjectAsync('system.config');
if (state) {
adapter.config.longitude = state.common.longitude;
adapter.config.latitude = state.common.latitude;
adapter.log.info(`system longitude ${adapter.config.longitude} latitude ${adapter.config.latitude}`);
}
}
function getAstroDate(adapter, pattern, date, offsetMinutes) {
if (date === undefined) {
date = new Date();
}
if (typeof date === 'number') {
date = new Date(date);
}
if ((!adapter.latitude && adapter.latitude !== 0) || (!adapter.longitude && adapter.longitude !== 0)) {
adapter.logger.warn('Longitude or latitude does not set. Cannot use astro.');
return;
}
// ensure events are calculated independent of current time
date.setHours(12, 0, 0, 0);
let ts = suncalc.getTimes(date, adapter.latitude, adapter.longitude)[pattern];
if (ts === undefined || ts.getTime().toString() === 'NaN') {
adapter.logger.warn(`Cannot calculate astro date "${pattern}" for ${adapter.latitude}, ${adapter.longitude}`);
}
adapter.logger.debug(`getAstroDate(pattern=${pattern}, date=${date}) => ${ts}`, 'info');
if (offsetMinutes !== undefined) {
ts = new Date(ts.getTime() + offsetMinutes * 60000);
}
return ts;
}
function isSunshine(adapter) {
if (adapter.settings.sunrise && adapter.settings.sunset) {
const now = new Date();
/*
const sunrise = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(), // today, ...
14, 18, 0 // ...at 00:00:00 hours
);
const sunset = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(), // today, ...
14, 16, 0 // ...at 00:00:00 hours
);
return (now.getTime() > sunrise.getTime() || now.getTime() < sunset.getTime());
*/
return now.getTime() > adapter.settings.sunrise.getTime() && now.getTime() < adapter.settings.sunset.getTime();
}
return true;
}
//contains a value in array
function contains(r, val) {
const len = r.length;
let i = 0;
for (; i < len; i++) {
if (r[i] === val) {
return i;
}
}
return -1;
}
module.exports = {
Logging,
StateMap,
RegisterMap,
RiemannSum,
createAsyncLock,
getSystemData,
getAstroDate,
isSunshine,
contains,
};