iobroker.janitza-gridvis
Version:
Read out data from Energymanagementsystem Janitza® GridVis®
1,105 lines (1,024 loc) • 87.5 kB
JavaScript
'use strict';
/*
* Created with @iobroker/create-adapter v2.1.1
*/
// The adapter-core module gives you access to the core ioBroker functions
// you need to create an adapter
const utils = require('@iobroker/adapter-core');
const schedule = require('node-schedule');
const assignHandlerClass = require('./lib/modules/assignHandler');
const objectStoreClass = require('./lib/modules/objectStore');
const GridVisDeviceManagement = require('./lib/modules/deviceManager/deviceManager');
// Load your modules here, e.g.:
// const fs = require("node:fs");
class JanitzaGridvis extends utils.Adapter {
/**
* @param [options] options of the adapter
*/
constructor(options) {
super({
...options,
name: 'janitza-gridvis',
});
this.on('ready', this.onReady.bind(this));
this.on('stateChange', this.onStateChange.bind(this));
// this.on("objectChange", this.onObjectChange.bind(this));
this.on('message', this.onMessage.bind(this));
this.on('unload', this.onUnload.bind(this));
this.AdapterObjectsAtStart = {};
// Init message Queue
this.messageQueue = Promise.resolve();
this.devices = {};
// cron Jobs
this.cronJobs = {};
this.cronJobIds = {
getAdditionalDeviceInformations: 'getAdditionalDeviceInformations',
refreshOnlineCronJob: 'refreshOnlineCronJob',
refreshHistoricCronJob: 'refreshHistoricCronJob',
};
this.cronJobValues = {
getAdditionalDeviceInformations: '* * * * *',
};
// Timeouts
this.timeouts = {};
this.timeoutIds = {
connectionTimeout: 'connectionTimeout',
};
this.timeoutValues = {
connectionTimeout: 10000,
};
this.internalIds = {
onlineDevices: 'onlineDevices',
onlineValues: 'onlineValues',
historicDevices: 'historicDevices',
historicValues: 'historicValues',
devices: 'devices',
readValuesTrigger: 'readValuesTrigger',
globalValue: 'GlobalValue',
userDefined: 'UserDefined',
reconnectCount: 'reconnectCount',
info: 'info',
serialNumber: 'serialNumber',
firmware: 'firmware',
hardware: 'hardware',
statusMsg: 'statusMsg',
status: 'status',
type: 'type',
gridVisVersion: 'gridVisVersion',
numberOfDevicesInProject: 'numberOfDevicesInProject',
connectedProject: 'connectedProject',
historicTimestamps: 'historicTimestamps',
startTimestamp1: 'startTimestamp1',
endTimestamp1: 'endTimestamp1',
startTimestamp2: 'startTimestamp2',
endTimestamp2: 'endTimestamp2',
startTimestamp3: 'startTimestamp3',
endTimestamp3: 'endTimestamp3',
startTimestamp4: 'startTimestamp4',
endTimestamp4: 'endTimestamp4',
startTimestamp5: 'startTimestamp5',
endTimestamp5: 'endTimestamp5',
startTimestamp6: 'startTimestamp6',
endTimestamp6: 'endTimestamp6',
hourX: 'hourX',
};
this.communicationStrings = {
connectedToGridVisVersion: 'Connected to GridVis-Version',
numberOfDevices: 'number of devices',
ready: 'Ready',
noCommunication: 'The configured project does not respond. Please check the basic settings.',
communicationOk: 'Data exchange with REST API successful.',
noCommunicationSelect: 'No connection',
noCommunicationSelectString: 'No active connection to GridVis',
noProjectSelect: 'No project',
noProjectSelectString: 'No project selected',
noValidDeviceSelectedSelectString: 'No valid device selected',
lastCommunicationError: 'last communication error',
checkOnlineValues:
'ATTENTION: Please check config of onlinevalues - maybe certain values cause an error and stop reading.',
checkHistoricValues:
'ATTENTION: Please check config of historicvalues - maybe certain values cause an error and stop reading.',
checkDeviceconfig:
'ATTENTION: Please check config of devices - maybe certain values cause an error and stop reading additional values.',
};
this.translationIds = {
onlineValues: 'online values',
historicValues: 'historic values',
};
// later defined (after translation is loaded)
this.definedObjects = {};
this.timeBases = {
today: {
namestring: 'Today',
startstring: 'NAMED_Today',
endstring: 'NAMED_Today',
anchorstring: '',
},
todayMinus1Year: {
namestring: 'Today-1Year',
startstring: 'NAMED_Today',
endstring: 'NAMED_Today',
anchorstring: 'RELATIVE_-1YEAR',
},
yesterday: {
namestring: 'Yesterday',
startstring: 'NAMED_Yesterday',
endstring: 'NAMED_Yesterday',
anchorstring: '',
},
yesterdayMinus1Year: {
namestring: 'Yesterday-1Year',
startstring: 'NAMED_Yesterday',
endstring: 'NAMED_Yesterday',
anchorstring: 'RELATIVE_-1YEAR',
},
thisWeek: {
namestring: 'ThisWeek',
startstring: 'NAMED_ThisWeek',
endstring: 'NAMED_ThisWeek',
anchorstring: '',
},
thisWeekMinus1Year: {
namestring: 'ThisWeek-1Year',
startstring: 'NAMED_ThisWeek',
endstring: 'NAMED_Today',
anchorstring: 'RELATIVE_-1YEAR',
},
lastWeek: {
namestring: 'LastWeek',
startstring: 'NAMED_LastWeek',
endstring: 'NAMED_LastWeek',
anchorstring: '',
},
lastWeekMinus1Year: {
namestring: 'LastWeek-1Year',
startstring: 'NAMED_LastWeek',
endstring: 'NAMED_LastWeek',
anchorstring: 'RELATIVE_-1YEAR',
},
thisMonth: {
namestring: 'ThisMonth',
startstring: 'NAMED_ThisMonth',
endstring: 'NAMED_ThisMonth',
anchorstring: '',
},
thisMonthMinus1Year: {
namestring: 'ThisMonth-1Year',
startstring: 'NAMED_ThisMonth',
endstring: 'NAMED_Today',
anchorstring: 'RELATIVE_-1YEAR',
},
lastMonth: {
namestring: 'LastMonth',
startstring: 'NAMED_LastMonth',
endstring: 'NAMED_LastMonth',
anchorstring: '',
},
lastMonthMinus1Year: {
namestring: 'LastMonth-1Year',
startstring: 'NAMED_LastMonth',
endstring: 'NAMED_LastMonth',
anchorstring: 'RELATIVE_-1YEAR',
},
thisQuarter: {
namestring: 'ThisQuarter',
startstring: 'NAMED_ThisQuarter',
endstring: 'NAMED_ThisQuarter',
anchorstring: '',
},
thisQuarterMinus1Year: {
namestring: 'ThisQuarter-1Year',
startstring: 'NAMED_ThisQuarter',
endstring: 'NAMED_Today',
anchorstring: 'RELATIVE_-1YEAR',
},
lastQuarter: {
namestring: 'LastQuarter',
startstring: 'NAMED_LastQuarter',
endstring: 'NAMED_LastQuarter',
anchorstring: '',
},
lastQuarterMinus1Year: {
namestring: 'LastQuarter-1Year',
startstring: 'NAMED_LastQuarter',
endstring: 'NAMED_LastQuarter',
anchorstring: 'RELATIVE_-1YEAR',
},
thisYear: {
namestring: 'ThisYear',
startstring: 'NAMED_ThisYear',
endstring: 'NAMED_ThisYear',
anchorstring: '',
},
thisYearMinus1Year: {
namestring: 'ThisYear-1Year',
startstring: 'NAMED_ThisYear',
endstring: 'NAMED_Today',
anchorstring: 'RELATIVE_-1YEAR',
},
lastYear: {
namestring: 'LastYear',
startstring: 'NAMED_LastYear',
endstring: 'NAMED_LastYear',
anchorstring: '',
},
lastYearMinus1Year: {
namestring: 'LastYear-1Year',
startstring: 'NAMED_LastYear',
endstring: 'NAMED_LastYear',
anchorstring: 'RELATIVE_-1YEAR',
},
hour1: {
namestring: 'Last_1_Hour',
startstring: 'RELATIVE_-1HOUR',
endstring: 'NAMED_Today',
anchorstring: '',
},
hour2: {
namestring: 'Last_2_Hours',
startstring: 'RELATIVE_-2HOUR',
endstring: 'NAMED_Today',
anchorstring: '',
},
hour3: {
namestring: 'Last_3_Hours',
startstring: 'RELATIVE_-3HOUR',
endstring: 'NAMED_Today',
anchorstring: '',
},
hour4: {
namestring: 'Last_4_Hours',
startstring: 'RELATIVE_-4HOUR',
endstring: 'NAMED_Today',
anchorstring: '',
},
hour5: {
namestring: 'Last_5_Hours',
startstring: 'RELATIVE_-5HOUR',
endstring: 'NAMED_Today',
anchorstring: '',
},
hour6: {
namestring: 'Last_6_Hours',
startstring: 'RELATIVE_-6HOUR',
endstring: 'NAMED_Today',
anchorstring: '',
},
hourX: {
namestring: 'Last_X_Hours',
startstring: 'RELATIVE_-12HOUR',
endstring: 'NAMED_Today',
anchorstring: '',
},
flexibleTime1: {
namestring: 'FlexibleTime1',
startstring: 'UTC_1681682400000', // 17.04.2023 00:00
endstring: 'UTC_1681743600000', // 17.04.2023 17:00
anchorstring: '',
},
flexibleTime2: {
namestring: 'FlexibleTime2',
startstring: 'UTC_1681596000000', // 16.04.2023 00:00
endstring: 'UTC_1681657200000', // 16.04.2023 17:00
anchorstring: '',
},
flexibleTime3: {
namestring: 'FlexibleTime3',
startstring: 'UTC_1715075100006', // 07.05.2024 11:45
endstring: 'UTC_1715082300006', // 07.05.2024 13:45
anchorstring: '',
},
flexibleTime4: {
namestring: 'FlexibleTime4',
startstring: 'UTC_1715071500006', // 07.05.2024 10:45
endstring: 'UTC_1715082300006', // 07.05.2024 13:45
anchorstring: '',
},
flexibleTime5: {
namestring: 'FlexibleTime5',
startstring: 'UTC_1715067900006', // 07.05.2024 09:45
endstring: 'UTC_1715082300006', // 07.05.2024 13:45
anchorstring: '',
},
flexibleTime6: {
namestring: 'FlexibleTime6',
startstring: 'UTC_1715064300006', // 07.05.2024 08:45
endstring: 'UTC_1715082300006', // 07.05.2024 13:45
anchorstring: '',
},
};
// internal connection state to block timeout errors
this.internalConnectionState = false;
// reconnect counter for counting reconnects before lor warning
this.reconnectErrorString = '';
this.reconnectCounter = 0;
// Transalation for some channel names
this.i18nTranslation = {};
// object of icons in folde admin/icons
this.presentIcons = {};
this.defaultIcon = 'default.png';
this.devicesWithSerialNumber = undefined;
this.supportedHistoricalUnits = {
Wh: 'Wh',
'm³': 'm³',
};
// Icons struct with created icons
this.createdDeviceicons = {};
// Timestamp to deside if the timeout of a request will be observed
this.lastConnectionStateTimestamp = Date.now();
this.lastProjectTimestamp = Date.now();
// States for don´t start twice reading online or historic Data
this.readSate = {
online: false,
historic: false,
};
// Create unload state to deactivate writing in state after unload Adapter
this.unloaded = false;
// Cache for API Calls in config
this._apiCache = new Map();
this._apiCacheMaxSize = 200;
// System Config
this.sysConfig = null;
}
async fetchJson(url, { timeout = 5000, allow204 = false } = {}) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
signal: controller.signal,
headers: { 'Accept-Language': this.sysConfig?.common.language || 'en' },
});
if (response.status === 204) {
if (allow204) {
return null;
}
const err = new Error(`204 No Content: ${url}`);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
err.status = response.status;
throw err;
}
if (!response.ok) {
const err = new Error(`HTTP ${response.status}: ${url}`);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
err.status = response.status;
throw err;
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
const err = new Error(`Timeout after ${timeout}ms: ${url}`);
throw err;
}
throw error;
} finally {
clearTimeout(timer);
}
}
/**
* Is called when databases are connected and adapter received configuration.
*/
async onReady() {
// Init assignhandler
this.assignHander = new assignHandlerClass(this);
// read system translation out of i18n translation
this.i18nTranslation = await this.geti18nTranslation();
// read out present icons
const dirInfo = await this.readDirAsync(`${this.name}.admin`, '/icons/');
dirInfo.forEach(info => {
this.presentIcons[info.file.substring(0, info.file.indexOf('.'))] = info.file;
});
// definition der internen Objecte (mit Übersetzung)
this.definedObjects = {
noCommunication: {
label: this.i18nTranslation[this.communicationStrings.noCommunicationSelectString],
value: this.i18nTranslation[this.communicationStrings.noCommunicationSelect],
},
noProject: {
label: this.i18nTranslation[this.communicationStrings.noProjectSelectString],
value: this.i18nTranslation[this.communicationStrings.noProjectSelectString],
},
noValidDeviceSelected: {
label: this.i18nTranslation[this.communicationStrings.noValidDeviceSelectedSelectString],
value: this.i18nTranslation[this.communicationStrings.noValidDeviceSelectedSelectString],
},
};
// Init internal states & device states
await this.initInternalValues();
// start connection to GridVis
this.connectToGridVis();
// Generate Object Store
this.objectStore = new objectStoreClass(this);
await this.objectStore.generateStoreObjects();
// Device Manager
this.deviceManagement = new GridVisDeviceManagement(this);
}
async geti18nTranslation() {
const systemConfig = await this.getForeignObjectAsync('system.config');
if (systemConfig) {
let lang = systemConfig.common.language;
if (!lang) {
lang = 'en';
}
const translationsPath = `./admin/i18n/${lang}/translations.json`;
this.sysConfig = systemConfig;
return require(translationsPath);
}
return {};
}
checkHistoricTimeBase() {
for (const key in this.config.historicTimeBase) {
if (!this.config.historicTimeBase[key]) {
delete this.timeBases[key];
}
}
}
async connectToGridVis() {
// Reset ConnectionTimeout
this.clearConnectionTimeout();
this.clearAllSchedules();
this.clearAllReadstate();
// increment the reconnect counter
if (this.reconnectCounter > 0 || this.internalConnectionState) {
this.reconnectCounter += 1;
}
if (this.reconnectCounter === 1 && this.config.reconnectCount === 0) {
this.log.warn(`${this.communicationStrings.lastCommunicationError}: ${this.reconnectErrorString}`);
}
// Reset the connection indicator
this.internalConnectionState = false;
await this.setState('info.connection', false, true);
// Check the configed connection settings
// in case there is no connection to GridVis possible
// the adapter will not work
const projectInfo = await this.checkConnectionToRestApi(
this.config.address,
this.config.port,
this.config.projectname,
);
if (projectInfo) {
// set recoonectionCounter (for better debug)
await this.setState(`info.${this.internalIds.reconnectCount}`, this.reconnectCounter, true);
// log just if the reconnect counter is bigger than the configed number before warning (or at startup)
if (this.reconnectCounter > this.config.reconnectCount || this.reconnectCounter === 0) {
this.log.info(
`${this.communicationStrings.connectedToGridVisVersion}: ${projectInfo.version} - ${this.communicationStrings.numberOfDevices}: ${projectInfo.numberOfDevices}`,
);
this.setState(`info.${this.internalIds.gridVisVersion}`, projectInfo.version, true);
this.setState(`info.${this.internalIds.numberOfDevicesInProject}`, projectInfo.numberOfDevices, true);
this.setState(`info.${this.internalIds.connectedProject}`, this.config.projectname, true);
} else {
this.log.debug(
`${this.communicationStrings.connectedToGridVisVersion}: ${projectInfo.version} - ${this.communicationStrings.numberOfDevices}: ${projectInfo.numberOfDevices}`,
);
}
// Set connection established
await this.setState('info.connection', true, true);
this.reconnectCounter = 0;
this.internalConnectionState = true;
this.setState(`info.${this.internalIds.reconnectCount}`, this.reconnectCounter, true);
this.StartCommunicationToGridVis();
} else {
if (this.reconnectCounter === this.config.reconnectCount && this.reconnectCounter !== 0) {
// Abfrage auf ungleich 0, da erst eine Verbindung aufgebaut sein musste bevor diese Warnung ausgegeben wird.
this.log.warn(`${this.communicationStrings.lastCommunicationError}: ${this.reconnectErrorString}`);
}
if (this.reconnectCounter >= this.config.reconnectCount || this.reconnectCounter === 0) {
// Abfrage auf 0, da solange keine Verbindung aufgebaut wurde immer die Warnung ausgegeben wird.
this.log.warn(this.communicationStrings.noCommunicationSelectString);
}
this.setState(`info.${this.internalIds.reconnectCount}`, this.reconnectCounter, true);
this.timeouts[this.timeoutIds.connectionTimeout] = this.setTimeout(
this.connectToGridVis.bind(this),
this.timeoutValues[this.timeoutIds.connectionTimeout],
);
}
}
async initInternalValues() {
this.checkHistoricTimeBase();
await this.createInternalStates();
await this.delNotConfiguredStates();
}
async getAdditionalDeviceInformations() {
let myUrl = '';
try {
// Assign just one time at startup adapter
if (this.devicesWithSerialNumber === undefined) {
this.devicesWithSerialNumber = JSON.parse(JSON.stringify(this.devices));
}
for (const device in this.devicesWithSerialNumber) {
myUrl = `http://${this.config.address}:${this.config.port}/rest/1/projects/${this.config.projectname}/devices/${device}/connectiontest.json`;
let result = null;
try {
result = await this.fetchJson(myUrl, {
timeout: this.config.timeout,
});
} catch (error) {
this.log.error(`error at fetching url: ${myUrl}. Error: ${error}`);
continue;
}
// create info-folder
await this.extendObject(`${this.internalIds.devices}.${device}.${this.internalIds.info}`, {
type: 'channel',
common: {
name: 'Information',
},
native: {},
});
// create state serialnumber
let id = `${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.serialNumber}`;
await this.extendObject(id, {
type: 'state',
common: {
name: 'serialnumber',
type: 'string',
role: 'value',
read: true,
write: false,
def: result.serialNumber,
},
native: {},
});
this.setState(id, result.serialNumber, true);
// create state firmware
id = `${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.firmware}`;
await this.extendObject(id, {
type: 'state',
common: {
name: 'firmware',
type: 'string',
role: 'value',
read: true,
write: false,
def: result.firmware,
},
native: {},
});
this.setState(id, result.firmware, true);
// create state hardware
id = `${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.hardware}`;
await this.extendObject(id, {
type: 'state',
common: {
name: 'hardware',
type: 'string',
role: 'value',
read: true,
write: false,
def: result.hardware,
},
native: {},
});
this.setState(id, result.hardware, true);
// create state status
id = `${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.status}`;
await this.extendObject(id, {
type: 'state',
common: {
name: 'status',
type: 'string',
role: 'value',
read: true,
write: false,
def: result.status,
},
native: {},
});
this.setState(id, result.status, true);
// create state statusMsg
id = `${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.statusMsg}`;
await this.extendObject(id, {
type: 'state',
common: {
name: 'statusMsg',
type: 'string',
role: 'value',
read: true,
write: false,
def: result.statusMsg,
},
native: {},
});
this.setState(id, result.statusMsg, true);
// create state type
id = `${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.type}`;
await this.extendObject(id, {
type: 'state',
common: {
name: 'type',
type: 'string',
role: 'value',
read: true,
write: false,
def: this.devices[device].type,
},
native: {},
});
this.setState(id, this.devices[device].type, true);
//}
/**else{
delete this.devicesWithSerialNumber[device];
}**/
}
} catch (error) {
if (this.internalConnectionState) {
this.log.debug(`${error} after sending ${myUrl}`);
this.reconnectErrorString = `${error} after sending ${myUrl}`;
if (
error.status &&
(error.status === 400 || // bad request
error.status === 404)
) {
// not found
this.log.warn(`${this.communicationStrings.checkDeviceconfig} --- ${this.reconnectErrorString}`);
return;
}
this.connectToGridVis();
}
}
}
clearConnectionTimeout() {
if (this.timeouts[this.timeoutIds.connectionTimeout]) {
this.clearTimeout(this.timeouts[this.timeoutIds.connectionTimeout]);
delete this.timeouts[this.timeoutIds.connectionTimeout];
}
}
// Clear all schedules, if there are some
clearAllSchedules() {
for (const cronJob in this.cronJobs) {
schedule.cancelJob(this.cronJobs[cronJob]);
delete this.cronJobs[cronJob];
}
}
clearAllReadstate() {
for (const readstate in this.readSate) {
this.readSate[readstate] = false;
}
}
// Clear all Timeouts, if there are some
clearAllTimeouts() {
for (const myTimeout in this.timeouts) {
this.clearTimeout(this.timeouts[myTimeout]);
delete this.timeouts[myTimeout];
}
}
StartCommunicationToGridVis() {
// create schedulejobs and do initalize reading
this.createScheduleJobs();
this.getAdditionalDeviceInformations();
this.readOnlineValues();
this.readHistoricValues();
}
// creates internal states
async createInternalStates() {
// Parse and asign online values
for (const index in this.config.onlineDeviceTable) {
if (
this.config.onlineDeviceTable[index][this.internalIds.onlineDevices] !==
this.i18nTranslation[this.communicationStrings.noCommunicationSelect] &&
this.config.onlineDeviceTable[index][this.internalIds.onlineValues] !==
this.i18nTranslation[this.communicationStrings.noValidDeviceSelectedSelectString]
) {
const configedOnlineDevices = JSON.parse(
this.config.onlineDeviceTable[index][this.internalIds.onlineDevices],
);
const configedOnlineValues = JSON.parse(
this.config.onlineDeviceTable[index][this.internalIds.onlineValues],
);
if (configedOnlineDevices && configedOnlineValues) {
const deviceId = configedOnlineDevices.id;
if (!this.devices[deviceId]) {
this.devices[deviceId] = {};
this.devices[deviceId].deviceName = configedOnlineDevices.deviceName;
this.devices[deviceId].type = configedOnlineDevices.type;
if (configedOnlineDevices.parentType) {
this.devices[deviceId].parentType = configedOnlineDevices.parentType;
}
}
// Create online Values structure (in case if its not created or device is created in historicValues)
if (!this.devices[deviceId].onlineValues) {
this.devices[deviceId].onlineValues = {};
}
if (!this.devices[deviceId].onlineValues[configedOnlineValues.value]) {
this.devices[deviceId].onlineValues[configedOnlineValues.value] = {};
this.devices[deviceId].onlineValues[configedOnlineValues.value].valueName =
configedOnlineValues.valueName;
this.devices[deviceId].onlineValues[configedOnlineValues.value].type = {};
}
this.devices[deviceId].onlineValues[configedOnlineValues.value].type[configedOnlineValues.type] = {
typeName: configedOnlineValues.typeName,
unit: configedOnlineValues.unit,
};
}
}
}
// Create onlinevalue structur
for (const device in this.devices) {
if (this.devices[device].onlineValues) {
// Create device folder (SetObject used in case the Devicetype changes with changing project)
let iconPath = this.defaultIcon;
// Check for parent device
if (this.devices[device].parentType && this.presentIcons[this.devices[device].parentType]) {
iconPath = this.presentIcons[this.devices[device].parentType];
} else if (this.presentIcons[this.devices[device].type]) {
// Define icon in case there is no parent device / parent type
iconPath = this.presentIcons[this.devices[device].type];
}
await this.setObjectAsync(`${this.internalIds.devices}.${device}`, {
type: 'device',
common: {
name: this.devices[device].deviceName,
icon: `icons/${iconPath}`,
},
native: {},
});
this.createdDeviceicons[device] = {};
// create onlinevalue folder
await this.extendObject(`${this.internalIds.devices}.${device}.${this.internalIds.onlineValues}`, {
type: 'channel',
common: {
name: this.i18nTranslation[this.translationIds.onlineValues],
},
native: {},
});
for (const value in this.devices[device].onlineValues) {
// create value channel
let channelName = this.devices[device].onlineValues[value].valueName;
if (value === this.internalIds.globalValue) {
channelName = this.internalIds.globalValue;
} else if (value === this.internalIds.userDefined) {
channelName = this.internalIds.userDefined;
}
await this.extendObject(
`${this.internalIds.devices}.${device}.${this.internalIds.onlineValues}.${value}`,
{
type: 'channel',
common: {
name: channelName,
},
native: {},
},
);
for (const type in this.devices[device].onlineValues[value].type) {
// create value state
await this.extendObject(
`${this.internalIds.devices}.${device}.${this.internalIds.onlineValues}.${value}.${type}`,
{
type: 'state',
common: {
name: this.devices[device].onlineValues[value].type[type].typeName
? this.devices[device].onlineValues[value].type[type].typeName
: type,
type: 'number',
role: this.assignHander ? this.assignHander.getRole(value) : 'state',
read: true,
write: false,
unit: this.devices[device].onlineValues[value].type[type].unit,
def: 0,
},
native: {},
},
);
}
}
}
}
// Parse and asign historic values
for (const index in this.config.historicDeviceTable) {
if (
this.config.historicDeviceTable[index][this.internalIds.historicDevices] !==
this.i18nTranslation[this.communicationStrings.noCommunicationSelect] &&
this.config.historicDeviceTable[index][this.internalIds.historicValues] !==
this.i18nTranslation[this.communicationStrings.noValidDeviceSelectedSelectString]
) {
const configedHistoricDevices = JSON.parse(
this.config.historicDeviceTable[index][this.internalIds.historicDevices],
);
const configedHistoricValues = JSON.parse(
this.config.historicDeviceTable[index][this.internalIds.historicValues],
);
if (configedHistoricDevices && configedHistoricValues) {
const deviceId = configedHistoricDevices.id;
if (!this.devices[deviceId]) {
this.devices[deviceId] = {};
this.devices[deviceId].deviceName = configedHistoricDevices.deviceName;
this.devices[deviceId].type = configedHistoricDevices.type;
if (configedHistoricDevices.parentType) {
this.devices[deviceId].parentType = configedHistoricDevices.parentType;
}
}
// Create historic Values structure (in case if its not created or device is created in onlineValues)
if (!this.devices[deviceId].historicValues) {
this.devices[deviceId].historicValues = {};
}
if (!this.devices[deviceId].historicValues[configedHistoricValues.value]) {
this.devices[deviceId].historicValues[configedHistoricValues.value] = {};
this.devices[deviceId].historicValues[configedHistoricValues.value].valueName =
configedHistoricValues.valueName;
this.devices[deviceId].historicValues[configedHistoricValues.value].type = {};
}
this.devices[deviceId].historicValues[configedHistoricValues.value].type[
configedHistoricValues.type
] = {
typeName: configedHistoricValues.typeName,
unit: configedHistoricValues.unit,
id: configedHistoricValues.id, // special parameter (in reality above the value)
};
}
}
}
// Create historic value structure
for (const device in this.devices) {
if (this.devices[device].historicValues) {
// Create device folder (SetObject used in case the Devicetype changes with changing project)
// Query, if device was created in onlinevalueloop
if (!this.createdDeviceicons[device]) {
let iconPath = this.defaultIcon;
// Check for parent device
if (this.devices[device].parentType) {
iconPath = this.presentIcons[this.devices[device].parentType];
} else if (this.presentIcons[this.devices[device].type]) {
// Define icon in case there is no parent device / parent type
iconPath = this.presentIcons[this.devices[device].type];
}
await this.setObjectAsync(`${this.internalIds.devices}.${device}`, {
type: 'device',
common: {
name: this.devices[device].deviceName,
icon: `icons/${iconPath}`,
},
native: {},
});
}
// create historic value folder
await this.extendObject(`${this.internalIds.devices}.${device}.${this.internalIds.historicValues}`, {
type: 'channel',
common: {
name: this.i18nTranslation[this.translationIds.historicValues],
},
native: {},
});
for (const value in this.devices[device].historicValues) {
// create historic value channel
let channelName = this.devices[device].historicValues[value].valueName;
if (value === this.internalIds.globalValue) {
channelName = this.internalIds.globalValue;
} else if (value === this.internalIds.userDefined) {
channelName = this.internalIds.userDefined;
}
await this.extendObject(
`${this.internalIds.devices}.${device}.${this.internalIds.historicValues}.${value}`,
{
type: 'channel',
common: {
name: channelName,
},
native: {},
},
);
for (const type in this.devices[device].historicValues[value].type) {
// create value state
for (const timeBase of Object.values(this.timeBases)) {
await this.extendObject(
`${this.internalIds.devices}.${device}.${this.internalIds.historicValues}.${value}.${type}_${timeBase.namestring}`,
{
type: 'state',
common: {
name: this.devices[device].historicValues[value].type[type].typeName
? this.devices[device].historicValues[value].type[type].typeName
: type,
type: 'number',
role: this.assignHander ? this.assignHander.getRole(value) : 'state',
read: true,
write: false,
unit: this.devices[device].historicValues[value].type[type].unit,
def: 0,
},
native: {},
},
);
}
}
}
}
}
// Subscribe trigger state
this.subscribeStatesAsync(`${this.internalIds.devices}.${this.internalIds.readValuesTrigger}`);
// Subscribe timestamps for start and end of flexible timebase states
if (this.timeBases.flexibleTime1) {
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.startTimestamp1}`);
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.endTimestamp1}`);
}
if (this.timeBases.flexibleTime2) {
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.startTimestamp2}`);
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.endTimestamp2}`);
}
if (this.timeBases.flexibleTime3) {
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.startTimestamp3}`);
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.endTimestamp3}`);
}
if (this.timeBases.flexibleTime4) {
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.startTimestamp4}`);
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.endTimestamp4}`);
}
if (this.timeBases.flexibleTime5) {
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.startTimestamp5}`);
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.endTimestamp5}`);
}
if (this.timeBases.flexibleTime6) {
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.startTimestamp6}`);
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.endTimestamp6}`);
}
if (this.timeBases.hourX) {
this.subscribeStatesAsync(`${this.internalIds.historicTimestamps}.${this.internalIds.hourX}`);
}
}
// deletes not configured states
async delNotConfiguredStates() {
// Get all objects in the adapter (later)
this.AdapterObjectsAtStart = await this.getAdapterObjectsAsync();
let activeString = '';
for (const device in this.devices) {
// folder devices
activeString = `${this.namespace}.${this.internalIds.devices}.${device}`;
delete this.AdapterObjectsAtStart[activeString];
// all informations
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.info}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.serialNumber}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.firmware}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.hardware}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.status}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.statusMsg}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.info}.${this.internalIds.type}`;
delete this.AdapterObjectsAtStart[activeString];
// all online configed states
if (this.devices[device].onlineValues) {
for (const value in this.devices[device].onlineValues) {
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.onlineValues}.${value}`;
delete this.AdapterObjectsAtStart[activeString];
for (const type in this.devices[device].onlineValues[value].type) {
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.onlineValues}.${value}.${type}`;
delete this.AdapterObjectsAtStart[activeString];
}
}
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.onlineValues}`;
delete this.AdapterObjectsAtStart[activeString];
}
// all historic configed states
if (this.devices[device].historicValues) {
for (const value in this.devices[device].historicValues) {
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.historicValues}.${value}`;
delete this.AdapterObjectsAtStart[activeString];
for (const type in this.devices[device].historicValues[value].type) {
for (const timeBase of Object.values(this.timeBases)) {
// remove all timeBase values out of the structure
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.historicValues}.${value}.${type}_${timeBase.namestring}`;
delete this.AdapterObjectsAtStart[activeString];
}
}
}
activeString = `${this.namespace}.${this.internalIds.devices}.${device}.${this.internalIds.historicValues}`;
delete this.AdapterObjectsAtStart[activeString];
}
}
// delete the general states from the array
activeString = `${this.namespace}.${this.internalIds.devices}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.info`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.info.connection`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.info.${this.internalIds.reconnectCount}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.info.${this.internalIds.connectedProject}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.info.${this.internalIds.numberOfDevicesInProject}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.info.${this.internalIds.gridVisVersion}`;
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.${this.internalIds.devices}.${this.internalIds.readValuesTrigger}`;
delete this.AdapterObjectsAtStart[activeString];
// Delete timestamp state in case of selection is deactivted
if (this.timeBases.flexibleTime1) {
activeString = `${this.namespace}.${this.internalIds.historicTimestamps}`; // Delete folder from array
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.${this.internalIds.historicTimestamps}.${this.internalIds.startTimestamp1}`;
delete this.AdapterObjectsAtStart[activeString];
this.asignTimestamps(activeString, await this.getStateAsync(activeString));
activeString = `${this.namespace}.${this.internalIds.historicTimestamps}.${this.internalIds.endTimestamp1}`;
delete this.AdapterObjectsAtStart[activeString];
this.asignTimestamps(activeString, await this.getStateAsync(activeString));
}
if (this.timeBases.flexibleTime2) {
activeString = `${this.namespace}.${this.internalIds.historicTimestamps}`; // Delete folder from array
delete this.AdapterObjectsAtStart[activeString];
activeString = `${this.namespace}.${this.internalIds.historicTimestamps}.${this.internalIds.startTimestamp2}`;
delete this.AdapterObjectsAtStart[activeString];
this.asignTimestamps(activeString, await this.getSt