UNPKG

iobroker.envertech-pv

Version:
745 lines (655 loc) 35.8 kB
/** * * envertech_pv adapter, * copyright CTJaeger 2020 - 2022, MIT * copyright McM1957 2023, MIT * */ /* jshint -W097 */ /* jshint strict: false */ /* jslint node: true */ 'use strict'; const utils = require('@iobroker/adapter-core'); const { iobInit, iobStates, iobTranslator } = require('@mcm1957/iobroker.library'); //const { iobInit, iobStates, iobTranslator } = require('e:/github/mcm1957/iobroker.library/library.js'); const EnvCloud = require('./lib/envertechCloud.js'); const STATEs = {}; /* prettier-ignore */ const STATES_CFG = { _Error_: {type: 'boolean', name: 'error flag', desc: 'descError', role: 'indicator.maintenance', unit: '', cvt: null }, _ErrorText_: {type: 'string', name: 'error text', desc: 'descErrorText', role: 'text', unit: '', cvt: null }, _LastData_: {type: 'string', name: 'last data', desc: 'descLastData', role: 'text', unit: '', cvt: null }, _LastUpdate_: {type: 'string', name: 'last update', desc: 'descLastUpdate', role: 'date', unit: '', cvt: null }, _Online_: {type: 'boolean', name: 'online flag', desc: 'descOnline', role: 'indicator.reachable', unit: '', cvt: null }, _MpptOnline_: {type: 'number', name: 'mppt online', desc: 'descMpptOnline', role: 'value', unit: '', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, // '0,9 kWp' _MpptOffline_:{type: 'number', name: 'mppt offline', desc: 'descMpptOffline', role: 'value', unit: '', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, // '0,9 kWp' _StationId_: {type: 'string', name: 'station id', desc: 'descStationId', role: 'info.serial', unit: '', cvt: null }, // '' UnitCapacity: {type: 'number', name: 'unit capacity', desc: 'descUnitCapacity', role: 'value.power', unit: 'kWp', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, // '0,9 kWp' UnitEToday: {type: 'number', name: 'unit earned today', desc: 'descUnitEToday', role: 'value.power.produced', unit: 'kWh', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, // '2.12 kWh' UnitEMonth: {type: 'number', name: 'unit earned month', desc: 'descUnitEMonth', role: 'value.power.produced', unit: 'kWh', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, // '6.99 kWh', UnitEYear: {type: 'number', name: 'unit earned year', desc: 'descUnitEYear', role: 'value.power.produced', unit: 'kWh', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, //'40.9 kWh', UnitETotal: {type: 'number', name: 'unit earned total', desc: 'descUnitETotal', role: 'value.power.produced', unit: 'kWh', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, //'353.49 kWh', Power: {type: 'number', name: 'power', desc: 'descUnitPower', role: 'value.power', unit: 'W', cvt: null }, // 0, PowerStr: {type: null, name: '', desc: '', role: '', unit: '', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, // '0 W', *ignore* Capacity: {type: null, name: '', desc: '', role: '', unit: '', cvt: null }, // 0.9, *ignore* StrCO2: {type: 'number', name: 'co2 saved', desc: 'descStrCO2', role: 'value', unit: 't', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, //'0.352 ton', StrTrees: {type: 'number', name: 'trees', desc: 'descStrTrees', role: 'value', unit: '', cvt: null }, // '1', StrIncome: {type: 'number', name: 'income', desc: 'descStrIncome', role: 'value', unit: '', cvt: /\S+\s+(?<val>\d+(\.\d+)?)/ }, //:'€ 0.00', PwImg: {type: null, name: '', desc: '', role: '', unit: '', cvt: null }, //'Default.jpg', *ignore* StationName: {type: 'string', name: 'station name', desc: 'descStationName', role: 'info.name', unit: '', cvt: null }, //'KTG6', InvModel1: {type: 'string', name: 'inverter #1 model', desc: 'descInvModel1', role: 'info.name', unit: '', cvt: null }, //null, InvModel2: {type: 'string', name: 'inverter #2 model', desc: 'descInvModel2', role: 'info.name', unit: '', cvt: null }, //null, Lat: {type: 'number', name: 'latitude', desc: 'descLat', role: 'value.gps.latitude', unit: '', cvt: null }, //48.208201762059105, Lng: {type: 'number', name: 'longitude', desc: 'descLng', role: 'value.gps.longitude', unit: '', cvt: null }, //16.371216773986816, TimeZone: {type: 'number', name: 'timezone', desc: 'descTimeZone', role: 'value', unit: '', cvt: null }, //1, StrPeakPower: {type: 'number', name: 'peak power', desc: 'descStrPeakPower', role: 'value.power', unit: 'W', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, //'616.42 W', Installer: {type: 'string', name: 'installer', desc: 'descInstaller', role: 'info.name', unit: '', cvt: null }, //null, CreateTime: {type: 'string', name: 'create time', desc: 'descCreateTime', role: 'value', unit: '', cvt: null }, //'21/12/2022 GMT +1', CreateYear: {type: 'number', name: 'create year', desc: 'descCreateYear', role: 'value', unit: '', cvt: null }, //2022, CreateMonth: {type: 'number', name: 'create month', desc: 'descCreateMonth', role: 'value', unit: '', cvt: null }, //12, Etoday: {type: 'number', name: 'earning today', desc: 'descEtoday', role: 'value.power.produced', unit: 'kWh', cvt: null }, //2.12, InvTotal: {type: 'number', name: 'total inverts', desc: 'descInvTotal', role: 'value', unit: '', cvt: null }, //2 GridPower: {type: 'number', name: 'power from grid', desc: 'descGridPower', role: 'value.power.consumed', unit: 'W', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, //'0 W' LoadPower: {type: 'number', name: 'load power', desc: 'descLoadPower', role: 'value.power.consumed', unit: 'W', cvt: /(?<val>\d+(\.\d+)?)\s+\w+/ }, //'0 W' GATEWAYALIAS: {type: 'string', name: 'gateway alias', desc: 'descGatewayAlias', role: 'info.name', unit: '', cvt: null }, //'EVB300_94001732', GATEWAYSN: {type: 'string', name: 'gateway sn', desc: 'descGatewaySn', role: 'info.serial', unit: '', cvt: null }, //'94001732', SNALIAS: {type: 'string', name: 'converter alias', desc: 'descConverterAlias',role: 'info.name', unit: '', cvt: null }, //'EVT west', SN: {type: 'string', name: 'convertser sn', desc: 'descConverterSn', role: 'info.serial', unit: '', cvt: null }, //'12866103', DCVOLTAGE: {type: 'number', name: 'dc voltage', desc: 'descDcVoltage', role: 'value.voltage', unit: 'V', cvt: null }, //'32.54', ACVOLTAGE: {type: 'number', name: 'ac voltage', desc: 'descAcVoltage', role: 'value.voltage', unit: 'V', cvt: null }, //'242.25', ACCURRENCY: {type: null, name: '', desc: 'descAccurency', role: '', unit: '', cvt: null }, //'0', POWER: {type: 'number', name: 'power', desc: 'descPower', role: 'value.power', unit: 'W', cvt: null }, //'14.19', FREQUENCY: {type: 'number', name: 'frequency', desc: 'descFrequency', role: 'value.frequency', unit: 'Hz', cvt: null }, //'50.02', DAYENERGY: {type: 'number', name: 'day energy', desc: 'descDayEnergy', role: 'value.power.produced',unit: 'kWh', cvt: null }, //'0.24', ENERGY: {type: 'number', name: 'energy', desc: 'descEnergy', role: 'value.power.produced',unit: 'kWh', cvt: null }, //'202.52', TEMPERATURE: {type: 'number', name: 'temperature', desc: 'descTemperature', role: 'value.temperature', unit: '°C', cvt: null }, //'15.4', SITETIME: {type: 'string', name: 'site datetime', desc: 'descSiteDateTime', role: 'value', unit: '', cvt: null }, //'3/5/2023 1:34:58 PM', STATIONID: {type: 'string', name: 'station id', desc: 'descStationId', role: 'value', unit: '', cvt: null }, //null, STATUS: {type: 'number', name: 'status', desc: 'descStatus', role: 'value', unit: '', cvt: null }, //'0', SNID: {type: 'string', name: 'sn id', desc: 'descSnId', role: 'value', unit: '', cvt: null }, //'9489A29C25BD4A468594E976AD2411E4' MGMERR: {type: null, name: '', desc: 'descMgmErr', role: '', unit: '', cvt: null }, //'200' MGSERR: {type: null, name: '', desc: 'descMgsErr', role: '', unit: '', cvt: null }, //'0' MGMVER: {type: null, name: '', desc: 'descMgmVer', role: '', unit: '', cvt: null }, //'66' MGSVER: {type: null, name: '', desc: 'descMgsVer', role: '', unit: '', cvt: null }, //'33' }; /** * main adapter class */ class envertech_pv extends utils.Adapter { constructor(options) { super({ ...options, name: 'envertech-pv', }); this.on('message', this.onMessage.bind(this)); this.on('ready', this.onReady.bind(this)); this.on('unload', this.onUnload.bind(this)); iobInit(this); } /** * onReady - will be called as soon as adapter is ready * */ async onReady() { this.log.debug('onReady triggered'); // check and setup config this.minDelayMs = (this.config?.expertMinDelay || 15) * 1000; this.cloudUrl = this.config?.expertUrl; this.cloudTmo = this.config?.expertTimeout || 30; this.stations = {}; await this.setStateAsync('info.connection', { val: false, ack: true, q: 0x00 }); // reset await this.resetStateObjects(); // start scanning loop let stationCnt = 0; if (this.config.stations) { for (const station of this.config.stations) { this.log.info(`[start] scanning station with id ${station.stationId}`); let pollIntvl = station.pollIntvl || 60; if (pollIntvl < 15) { pollIntvl = 15; } this.stations[station.stationId] = { envCloud: new EnvCloud(this, { url: this.config.expertUrl, timeout: this.cloudTmo, log: this.config.optLogReq, }), pollIntvlMs: pollIntvl * 1000, timer: null, }; setImmediate(this.doScan.bind(this), station.stationId); stationCnt++; this.delay(5000); } } if (stationCnt) { this.log.info(`scanning started for ${stationCnt} station(s)`); } else { this.log.warn(`no active station(s) detected - check config`); } return; } /** * onUnload - called for adapter shuts down * * @param callback callback function * @returns */ onUnload(callback) { this.log.debug('onUnload triggered'); this.setState('info.connection', { val: false, ack: true, q: 0x00 }); try { for (const station of this.stations) { if (station.timeout) { this.clearTimeout(station.timeout); } station.envCloud = null; } callback(); } catch { callback(); } } /** * onMessage - called if adapter receives a message * * @param pObj message object * @returns */ async onMessage(pObj) { if (pObj) { this.log.debug(`onMessage - ${JSON.stringify(pObj)}`); if (!pObj.callback) { return; } switch (pObj.command) { case 'getStationId': { if (pObj.message) { const username = pObj.message?.username; const password = pObj.message?.password; if (username.trim() === '' || password.trim() === '') { this.sendTo( pObj.from, pObj.command, { error: 'Username and password must not be empty' }, pObj.callback, ); return; } const envCloud = new EnvCloud(this, { url: this.config.expertUrl, timeout: this.cloudTmo, log: this.config.optLogReq, }); const result = await envCloud.login(username, password); if (result.status !== 0) { this.log.error(`[login] ${result.statustext}`); this.sendTo( pObj.from, pObj.command, { error: `Error retrieving station-id: ${result.statustext}` }, pObj.callback, ); return; } this.stationId = result.data.stationId; this.sendTo( pObj.from, pObj.command, { native: { cloudStationId: `${result.data.stationId}` } }, pObj.callback, ); return; } break; } } } } /** * updateInfoConnected - scan stations and set info.connected * * @returns nothing */ async updateInfoConnected() { this.log.debug(`updateInfoConnected triggered`); let online = false; for (const stationId in this.stations) { if (this.stations[stationId].online) { online = true; } } this.log.debug(`info.connection set to ${online}`); await this.setStateAsync('info.connection', { val: online, ack: true, q: 0x00 }); } /** * doScan - Scan station data * * @param pStationId * @returns nothing */ async doScan(pStationId) { this.log.debug(`doScan triggered (${pStationId})`); this.stations[pStationId].timeout = null; const start = Date.now(); // scan gateways await this.doQueryStation(pStationId); // scan gateways and converters await this.doQueryGateways(pStationId); // update global status await this.updateInfoConnected(); // start next scan let delayMs = this.stations[pStationId].pollIntvlMs + start - Date.now(); if (delayMs < this.minDelayMs) { delayMs = this.minDelayMs; } this.stations[pStationId].timeout = this.setTimeout(this.doScan.bind(this), delayMs, pStationId); } /** * doQueryGateways - Scan station data * * @param pStationId * @returns nothing */ async doQueryGateways(pStationId) { this.log.debug(`doQueryGateways (${pStationId}`); const stationId = `ST-${pStationId}`; const cvtOnline = {}; const cvtOffline = {}; let actPage = 0; // page index to read let lastPage = 1; // will be adapted by data retrieved at first page let totalCount = 0; // will be adapted by data retrieved at first page do { actPage++; const result = await this.stations[pStationId].envCloud.getGatewayInfo(pStationId, actPage); if (result.status < 0) { // error raised by axios await iobStates.setStatesAsync(`ST-${pStationId}.GW-*.info.online`, { val: false, ack: true, q: 0x00 }); await iobStates.setStatesAsync(`ST-${pStationId}.GW-*.info.error`, { val: true, ack: true, q: 0x00 }); await iobStates.setStatesAsync(`ST-${pStationId}.GW-*.info.error_text`, { val: result.error_text, ack: true, q: 0x00, }); this.stations[pStationId].online = false; return; // abort } else if (result.status == 1) { // await iobStates.setStatesAsync(`ST-${pStationId}.GW-*.info.online`, { val: false, ack: true, q: 0x00 }); await iobStates.setStatesAsync(`ST-${pStationId}.GW-*.info.error`, { val: true, ack: true, q: 0x00 }); await iobStates.setStatesAsync(`ST-${pStationId}.GW-*.info.error_text`, { val: result.error_text, ack: true, q: 0x00, }); this.stations[pStationId].online = false; return; // abort } else if (result.status >= 100) { // await iobStates.setStatesAsync(`ST-${pStationId}.GW-*.info.online`, { val: false, ack: true, q: 0x00 }); await iobStates.setStatesAsync(`ST-${pStationId}.GW-*.info.error`, { val: true, ack: true, q: 0x00 }); await iobStates.setStatesAsync(`ST-${pStationId}.GW-*.info.error_text`, { val: result.error_text, ack: true, q: 0x00, }); this.stations[pStationId].online = false; return; // abort } if (!result.data.QueryResults) { return; } if (result.data.PageNumber != actPage) { this.log.warn( `[gateway] unxpected data received - pageNumber:${result.data.PageNumber}, expected ${actPage}`, ); } lastPage = result.data.TotalPage; this.stations[pStationId].online = true; for (const row of result.data.QueryResults) { const gatewayAlias = row['GATEWAYALIAS']; const gatewaySn = row['GATEWAYSN']; const gatewayId = `ST-${pStationId}.GW-${gatewaySn}`; await this.initObject({ _id: `${gatewayId}`, type: 'device', common: { name: `gateway ${gatewayAlias}`, statusStates: { onlineId: `${this.name}.${this.instance}.${gatewayId}.info.online`, errorId: `${this.name}.${this.instance}.${gatewayId}.info.error`, }, }, native: {}, }); await this.initObject({ _id: `${gatewayId}.info`, type: 'folder', common: { name: `gateway ${gatewayAlias} info`, }, native: {}, }); await this.initStateObject(`${gatewayId}.info.error`, STATES_CFG['_Error_']); await this.initStateObject(`${gatewayId}.info.error_text`, STATES_CFG['_ErrorText_']); await this.initStateObject(`${gatewayId}.info.last_update`, STATES_CFG['_LastUpdate_']); await this.initStateObject(`${gatewayId}.info.online`, STATES_CFG['_Online_']); await this.initStateObject(`${gatewayId}.mppt_online`, STATES_CFG['_MpptOnline_']); await this.initStateObject(`${gatewayId}.mppt_offline`, STATES_CFG['_MpptOffline_']); if (typeof cvtOnline[stationId] === 'undefined') { cvtOnline[stationId] = 0; } if (typeof cvtOffline[stationId] === 'undefined') { cvtOffline[stationId] = 0; } if (typeof cvtOnline[gatewayId] === 'undefined') { cvtOnline[gatewayId] = 0; } if (typeof cvtOffline[gatewayId] === 'undefined') { cvtOffline[gatewayId] = 0; } /* prettier-ignore */ await this.setStateAsync(`${gatewayId}.info.last_update`, { val: new Date().toLocaleString(), ack: true, q: 0x00 }); await this.setStateAsync(`${gatewayId}.info.online`, { val: true, ack: true, q: 0x00 }); await this.setStateAsync(`${gatewayId}.info.error`, { val: false, ack: true, q: 0x00 }); await this.setStateAsync(`${gatewayId}.info.error_text`, { val: null, ack: true, q: 0x00 }); const snAlias = row['SNALIAS']; const sn = row['SN']; const rootId = `ST-${pStationId}.GW-${gatewaySn}.CVT-${sn}`; await this.initObject({ _id: `${rootId}`, type: 'channel', common: { name: `converter ${snAlias}`, statusStates: { onlineId: `${this.name}.${this.instance}.${rootId}.info.online`, errorId: `${this.name}.${this.instance}.${rootId}.info.error`, }, }, native: {}, }); await this.initObject({ _id: `${rootId}.info`, type: 'folder', common: { name: `converter ${snAlias} info`, }, native: {}, }); await this.initStateObject(`${rootId}.info.online`, STATES_CFG['_Online_']); for (const key in row) { this.log.debug(`[gatewayinfo] processing ${key}`); if (!STATES_CFG[key]) { if (this.config.optLogNew) { this.log.warn(`[gateway] object ${key} not configured - report to developer.`); } continue; } await this.initStateObject(`${rootId}.${key}`, STATES_CFG[key]); if (typeof STATEs[`${this.name}.${this.instance}.${rootId}.${key}`] === 'undefined') { continue; } // undesired object let val = row[key]; // process status field to set additions states if (key === 'STATUS') { if (val == 0) { cvtOnline[stationId] = cvtOnline[stationId] + 1; cvtOnline[gatewayId] = cvtOnline[gatewayId] + 1; await this.setStateAsync(`${rootId}.info.online`, { val: true, ack: true, q: 0x00 }); } else if (val == 1) { cvtOffline[stationId] = cvtOffline[stationId] + 1; cvtOffline[gatewayId] = cvtOffline[gatewayId] + 1; await this.setStateAsync(`${rootId}.info.online`, { val: false, ack: true, q: 0x00 }); } else if (val == 2) { /* unknown value - might be starting up */ cvtOffline[stationId] = cvtOffline[stationId] + 1; cvtOffline[gatewayId] = cvtOffline[gatewayId] + 1; await this.setStateAsync(`${rootId}.info.online`, { val: false, ack: true, q: 0x00 }); } else { this.log.warn(`[gateway] unexpected STATUS value '${val}'`); cvtOffline[stationId] = cvtOffline[stationId] + 1; cvtOffline[gatewayId] = cvtOffline[gatewayId] + 1; await this.setStateAsync(`${rootId}.info.online`, { val: false, ack: true, q: 0x00 }); } } if (STATES_CFG[key].cvt && typeof val === 'string') { const match = result.data[key].match(STATES_CFG[key].cvt); if (match) { val = match.groups.val; } else { if (this.config.optLogNew) { this.log.warn(`[gateway] unexpected data format for ${key} - ${val}`); } else { this.log.debug(`[gateway] unexpected data format for ${key} - ${val}`); } } } if (STATES_CFG[key].type === 'number') { val = Number(val); } await this.setStateAsync(`${rootId}.${key}`, { val: val, ack: true, q: 0x00 }); } } totalCount = result.data.TotalCount; // set total count for final check } while (actPage < lastPage); // data total count - total converters await this.setStateAsync(`${stationId}.mppt_online`, { val: cvtOnline[stationId], ack: true, q: 0x00 }); await this.setStateAsync(`${stationId}.mppt_offline`, { val: cvtOffline[stationId], ack: true, q: 0x00 }); if (totalCount != cvtOnline[stationId] + cvtOffline[stationId]) { this.log.warn(`[gateway] inconsistent counters detected - please report to developer`); this.log.warn( `[gateway] TotalCount: ${totalCount}, cvtOnline:${cvtOnline[stationId]}, cvtOffline:${cvtOffline[stationId]}`, ); } for (const gatewayId in cvtOnline) { const val = cvtOnline[gatewayId]; await this.setStateAsync(`${gatewayId}.mppt_online`, { val: val, ack: true, q: 0x00 }); } for (const gatewayId in cvtOffline) { const val = cvtOffline[gatewayId]; await this.setStateAsync(`${gatewayId}.mppt_offline`, { val: val, ack: true, q: 0x00 }); } } /** * doQueryStation - Scan station data * * @param pStationId * @returns nothing */ async doQueryStation(pStationId) { this.log.debug(`doQueryStation (${pStationId})`); const rootId = `ST-${pStationId}`; const result = await this.stations[pStationId].envCloud.getStationInfo(pStationId); if (result.status < 0) { // error raised by axios if (this.stationInitialized) { if (!result.statustext.match(/Error: connect ETIMEDOUT/)) { await this.setStateAsync(`${rootId}.info.error`, { val: true, ack: true, q: 0x00 }); } await this.setStateAsync(`${rootId}.info.error_text`, { val: result.statustext, ack: true, q: 0x00 }); await this.setStateAsync(`${rootId}.info.online`, { val: false, ack: true, q: 0x00 }); } this.stations[pStationId].online = false; return; // abort } else if (result.status == 1) { if (this.stationInitialized) { await this.setStateAsync(`${rootId}.info.error_text`, { val: result.statustext, ack: true, q: 0x00 }); await this.setStateAsync(`${rootId}.info.online`, { val: false, ack: true, q: 0x00 }); await this.setStateAsync(`${rootId}.info.error`, { val: true, ack: true, q: 0x00 }); } this.stations[pStationId].online = false; return; // abort } else if (result.status >= 100) { // http error if (this.stationInitialized) { if (!result.statustext.match(/Error: connect ETIMEDOUT/)) { await this.setStateAsync(`${rootId}.info.error`, { val: true, ack: true, q: 0x00 }); } await this.setStateAsync(`${rootId}.info.error_text`, { val: result.statustext, ack: true, q: 0x00 }); await this.setStateAsync(`${rootId}.info.online`, { val: false, ack: true, q: 0x00 }); } this.stations[pStationId].online = false; return; // abort } this.stations[pStationId].online = true; await this.initObject({ _id: `${rootId}`, type: 'folder', common: { name: `station ${result.data.StationName}`, statusStates: { onlineId: `${this.name}.${this.instance}.${rootId}.info.online`, errorId: `${this.name}.${this.instance}.${rootId}.info.error`, }, }, native: {}, }); await this.initObject({ _id: `${rootId}.info`, type: 'channel', common: { name: `station ${result.data.StationName} info`, }, native: {}, }); await this.initStateObject(`${rootId}.info.error`, STATES_CFG['_Error_']); await this.initStateObject(`${rootId}.info.error_text`, STATES_CFG['_ErrorText_']); //await this.initStateObject(`${rootId}.info.last_data`, STATES_CFG['_LastData_']); await this.initStateObject(`${rootId}.info.last_update`, STATES_CFG['_LastUpdate_']); await this.initStateObject(`${rootId}.info.online`, STATES_CFG['_Online_']); await this.initStateObject(`${rootId}.mppt_online`, STATES_CFG['_MpptOnline_']); await this.initStateObject(`${rootId}.mppt_offline`, STATES_CFG['_MpptOffline_']); await this.initStateObject(`${rootId}.station_id`, STATES_CFG['_StationId_']); this.stationInitialized = true; /* prettier-ignore */ await this.setStateAsync(`${rootId}.info.last_update`, { val: new Date().toLocaleString(), ack: true, q: 0x00 }); //await this.setStateAsync(`${rootId}.info.last_data`, { val: null, ack: true, q: 0x00 }); await this.setStateAsync(`${rootId}.info.online`, { val: true, ack: true, q: 0x00 }); await this.setStateAsync(`${rootId}.info.error`, { val: false, ack: true, q: 0x00 }); await this.setStateAsync(`${rootId}.info.error_text`, { val: null, ack: true, q: 0x00 }); await this.setStateAsync(`${rootId}.station_id`, { val: pStationId, ack: true, q: 0x00 }); for (const key in result.data) { this.log.debug(`[stationdatainfo] processing station info ${key}`); if (!STATES_CFG[key]) { if (this.config.optLogNew) { this.log.warn( `[station] object ${key} : ${result.data[key]} not configured - report to developer.`, ); } continue; } await this.initStateObject(`${rootId}.${key}`, STATES_CFG[key]); if (typeof STATEs[`${this.name}.${this.instance}.${rootId}.${key}`] === 'undefined') { continue; } // undesired object let val = result.data[key]; if (STATES_CFG[key].cvt && typeof val === 'string') { const match = result.data[key].match(STATES_CFG[key].cvt); if (match) { val = match.groups.val; } else { this.log.debug(`[station] unexpected data format for ${key} - ${val}`); } } if (STATES_CFG[key].type === 'number') { val = Number(val); } await this.setStateAsync(`${rootId}.${key}`, { val: val, ack: true, q: 0x00 }); } } /** * initObject - create or reconfigure single object * * creates object if it does not exist * overrides object data otherwise * * @param pObj objectstructure * @returns */ async initObject(pObj) { this.log.debug(`initobject [${pObj._id}]`); const fullId = `${this.name}.${this.instance}.${pObj._id}`; if (typeof STATEs[fullId] === 'undefined') { try { this.log.debug(`creating obj "${pObj._id}" with type ${pObj.type}`); await this.setObjectNotExistsAsync(pObj._id, pObj); await this.extendObjectAsync(pObj._id, pObj); STATEs[fullId] = 'X'; } catch (e) { this.log.error(`error initializing obj "${pObj._id}" ${e.message}`); } } } /** * initStateObject - create or reconfigure single state object * * creates object if it does not exist * overrides object data otherwise * * @param pId object id * @param pObj configuration object * @returns */ async initStateObject(pId, pObj) { this.log.debug(`initStateobject (${pId})`); if (!pObj.type) { return; } await this.initObject({ _id: pId, type: 'state', common: { name: pObj.name, desc: iobTranslator.getTranslations(pObj.desc), write: false, read: true, type: pObj.type, role: pObj.role, unit: pObj.unit, }, native: {}, }); } /** * resetStateObjects - reset state existing objects * * @returns */ async resetStateObjects() { this.log.debug(`resetStateobjects`); await iobStates.setStatesAsync('*', { ack: true, q: 0x02 }); await iobStates.setStatesAsync('*.info.online', { val: false, ack: true, q: 0x00 }); await iobStates.setStatesAsync('*.info.error', { val: false, ack: true, q: 0x00 }); } } /* end of adapter class */ // --------------------------------------------------------------------------------------------------------------------- /** * module export / startup */ console.log(`DEBUG : envertech_pv adapter initializing (${process.argv}) ...`); //logger not yet initialized /* if (process.argv) { for (let a = 1; a < process.argv.length; a++) { if (process.argv[a] === '--install') { doInstall = true; process.on('exit', function () { if (!didInstall) { console.log('WARNING: migration of config skipped - ioBroker might be stopped'); } }); } } } */ // @ts-expect-error parent is a valid property on module if (module.parent) { // Export the constructor in compact mode /** * @param [options] */ module.exports = options => new envertech_pv(options); } else { // otherwise start the instance directly new envertech_pv(); }