UNPKG

iobroker.ecoflow-mqtt

Version:
1,344 lines (1,309 loc) 75.3 kB
'use strict'; /* * Created with @iobroker/create-adapter v2.5.0 */ // 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 myutils = require('./lib/adapter_utils.js'); const ef = require('./lib/ecoflow_utils.js'); const ha = require('./lib/ha_utils.js'); const mqtt = require('mqtt'); // Load your modules here, e.g.: // const fs = require("fs"); let recon_timer = null; let lastQuotInterval = null; let haLoadInterval = null; const version = require('./io-package.json').common.version; class EcoflowMqtt extends utils.Adapter { /** * @param {Partial<utils.AdapterOptions>} [options={}] */ constructor(options) { super({ ...options, name: 'ecoflow-mqtt' }); this.mqttClient = null; this.msgCountPstream = 0; this.msgCountPlug = 0; this.msgCountPstation = 0; this.msgReconnects = 0; this.mqttUserId = ''; this.mqttUserName = ''; this.mqttPwd = ''; this.mqttClientId = ''; this.mqttPort = 8883; this.mqttProtocol = 'mqtts://'; this.mqttUrl = 'mqtt-e.ecoflow.com'; this.pdevices = {}; this.pdevicesStates = {}; this.pdevicesStatesDict = {}; this.pdevicesStates = {}; this.pdevicesCmd = {}; this.protoSource = {}; this.protoMsg = {}; this.storeProtoPayload = {}; this.prepareProtoCmd = {}; this.quotas = {}; this.haDevices = null; this.haCounter = 0; this.haCountMem = 0; 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)); } /** * Is called when databases are connected and adapter received configuration. */ async onReady() { // Initialize your adapter here this.log.info('adapter entered ready'); try { this.mqttUserId = this.config.mqttUserId; this.mqttUserName = this.config.mqttUserName; this.mqttPwd = this.config.mqttPwd; this.mqttClientId = this.config.mqttClientId; this.mqttPort = this.config.mqttPort || 8883; this.mqttProtocol = 'mqtts://'; this.mqttUrl = this.config.mqttUrl || 'mqtt-e.ecoflow.com'; //modify this.pstationStates this.log.info('your configration:'); this.log.info('powerstream -> ' + JSON.stringify(this.config.pstreams)); this.log.info('powerstation -> ' + JSON.stringify(this.config.pstations)); this.log.info('smartplug -> ' + JSON.stringify(this.config.plugs)); this.log.info('wave -> ' + JSON.stringify(this.config.waves)); this.log.info('glacier -> ' + JSON.stringify(this.config.glaciers)); //this.log.info('blade -> ' + JSON.stringify(this.config.blades)); this.log.info('generator -> ' + JSON.stringify(this.config.generators)); this.log.info('panel -> ' + JSON.stringify(this.config.panels)); this.log.info('shelly -> ' + JSON.stringify(this.config.shellies)); this.log.info('powerkit -> ' + JSON.stringify(this.config.powerkits)); this.log.info('powerocean -> ' + JSON.stringify(this.config.poweroceans)); this.log.info('alternator -> ' + JSON.stringify(this.config.alternators)); this.log.info('unknown -> ' + JSON.stringify(this.config.unknowns)); try { //loop durch alle Geräte const confdevices = [].concat( this.config.pstreams, this.config.plugs, this.config.pstations, this.config.waves, this.config.glaciers, this.config.generators, this.config.panels, this.config.shellies, this.config.powerkits, this.config.poweroceans, this.config.alternators, this.config.unknowns ); if (confdevices.length > 0) { //loop durch alle pstations for (let psta = 0; psta < confdevices.length; psta++) { let devtype = confdevices[psta]['devType']; if (devtype !== 'none' && devtype !== '') { const id = confdevices[psta]['devId']; const name = confdevices[psta]['devName']; let haEnable = false; //Testgeräte haben kein haEnable if (confdevices[psta]['haEnable']) { haEnable = confdevices[psta]['haEnable']; } const debugEnable = confdevices[psta]['debugEnable']; this.pdevices[id] = {}; this.pdevices[id]['devType'] = devtype; this.pdevices[id]['devName'] = name; this.pdevices[id]['haEnable'] = haEnable; this.pdevices[id]['debugEnable'] = debugEnable; if (confdevices[psta]['pstationsSlave1']) { this.pdevices[id]['pstationsSlave1'] = confdevices[psta]['pstationsSlave1']; } if (confdevices[psta]['pstationsSlave2']) { this.pdevices[id]['pstationsSlave2'] = confdevices[psta]['pstationsSlave2']; } let devStates = null; if (devtype === 'pstream600' || devtype === 'pstream800') { devStates = require('./lib/dict_data/ef_pstream_data.js').deviceStates; } else if ( devtype === 'powerkitbp2000' || devtype === 'powerkitbp5000' || devtype === 'powerkit' ) { devStates = require('./lib/dict_data/ef_powerkit_data.js').powerkitStates; } else if (devtype === 'plug') { devStates = require('./lib/dict_data/ef_plug_data.js').deviceStates; } else if (devtype === 'powerocean') { devStates = require('./lib/dict_data/ef_powerocean_data.js').deviceStates; } else if (devtype === 'panel') { devStates = require('./lib/dict_data/ef_panel_data.js').panelStates; } else if (devtype === 'panel2') { devStates = require('./lib/dict_data/ef_panel2_data.js').deviceStates; } else if (devtype === 'deltaproultra') { devStates = require('./lib/dict_data/ef_deltaproultra_data.js').deviceStates; } else if (devtype === 'alternator') { devStates = require('./lib/dict_data/ef_alternator_data.js').deviceStates; } else if (devtype === 'deltapro3') { devStates = require('./lib/dict_data/ef_deltapro3_data.js').deviceStates; } else if (devtype === 'delta3plus') { devStates = require('./lib/dict_data/ef_delta3plus_data.js').deviceStates; } else if (devtype === 'river3plus') { devStates = require('./lib/dict_data/ef_river3plus_data.js').deviceStates; } else { devStates = require('./lib/ecoflow_data.js').pstationStates; } if (devtype !== 'none' && devStates) { let devupd = null; if (devtype === 'pstream600' || devtype === 'pstream800') { devupd = require('./lib/dict_data/ef_pstream_data.js').deviceRanges[devtype]; } else if ( devtype === 'powerkitbp2000' || devtype === 'powerkitbp5000' || devtype === 'powerkit' ) { devupd = require('./lib/dict_data/ef_powerkit_data.js').powerkitRanges[devtype]; } else if (devtype === 'plug') { devupd = require('./lib/dict_data/ef_plug_data.js').deviceRanges[devtype]; } else if (devtype === 'powerocean') { devupd = require('./lib/dict_data/ef_powerocean_data.js').deviceRanges[devtype]; } else if (devtype === 'panel') { devupd = require('./lib/dict_data/ef_panel_data.js').panelRanges[devtype]; } else if (devtype === 'panel2') { devupd = require('./lib/dict_data/ef_panel2_data.js').deviceRanges[devtype]; } else if (devtype === 'deltaproultra') { devupd = require('./lib/dict_data/ef_deltaproultra_data.js').deviceRanges[devtype]; } else if (devtype === 'alternator') { devupd = require('./lib/dict_data/ef_alternator_data.js').deviceRanges[devtype]; } else if (devtype === 'delta3plus') { devupd = require('./lib/dict_data/ef_delta3plus_data.js').deviceRanges[devtype]; } else if (devtype === 'river3plus') { devupd = require('./lib/dict_data/ef_river3plus_data.js').deviceRanges[devtype]; } else if (devtype === 'deltapro3') { devupd = require('./lib/dict_data/ef_deltapro3_data.js').deviceRanges[devtype]; } else { devupd = require('./lib/ecoflow_data.js').pstationRanges[devtype]; } this.log.debug('device upd ' + JSON.stringify(devupd)); if (devupd) { if (Object.keys(devupd).length > 0) { for (let channel in devupd) { for (let type in devupd[channel]) { for (let state in devupd[channel][type]) { for (let value in devupd[channel][type][state]) { this.log.debug( 'manipulate: ' + channel + '/' + state + '(' + value + ') old--new ' + devStates[channel][type][state][value] + ' -- ' + devupd[channel][type][state][value] ); devStates[channel][type][state][value] = devupd[channel][type][state][value]; } } } } //we have to store the states individually, because same state can have different ranges, factors in different devices } else { this.log.error('device states upd not possible'); } } else { this.log.warn('did not get devupd for ' + devtype); } } else { this.log.error('devType not set -> ' + devtype + 'or no device states -> ' + devStates); } if (!devStates) { this.log.warn('no states for ' + devtype); } //devStates is now modfied and used for the object creation //create pdevices objects const origdevtype = devtype; if (devtype === 'pstream600' || devtype === 'pstream800') { devtype = 'pstream'; } if (devtype === 'powerkitbp2000' || devtype === 'powerkitbp5000') { devtype = 'powerkit'; } let pdevicesStatesDict = null; let pdevicesCmd = null; let protoSource = null; let protoMsg = null; let storeProtoPayload = null; let prepareProtoCmd = null; if (devtype === 'pstream') { pdevicesStatesDict = require('./lib/dict_data/ef_pstream_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_pstream_data.js').deviceCmd[origdevtype]; protoSource = require('./lib/dict_data/ef_pstream_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_pstream_data.js').protoMsg; storeProtoPayload = require('./lib/dict_data/ef_pstream_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_pstream_data.js').prepareProtoCmd; } else if (devtype === 'plug') { pdevicesStatesDict = require('./lib/dict_data/ef_plug_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_plug_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_plug_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_plug_data.js').protoMsg; storeProtoPayload = require('./lib/dict_data/ef_plug_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_plug_data.js').prepareProtoCmd; } else if (devtype === 'powerkit') { pdevicesStatesDict = require('./lib/dict_data/ef_powerkit_data.js').powerkitStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_powerkit_data.js').powerkitCmd[devtype]; } else if (devtype === 'powerocean') { pdevicesStatesDict = require('./lib/dict_data/ef_powerocean_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_powerocean_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_powerocean_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_powerocean_data.js').protoMsg; storeProtoPayload = require('./lib/dict_data/ef_powerocean_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_powerocean_data.js').prepareProtoCmd; } else if (devtype === 'panel') { pdevicesStatesDict = require('./lib/dict_data/ef_panel_data.js').panelStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_panel_data.js').panelCmd[devtype]; } else if (devtype === 'panel2') { pdevicesStatesDict = require('./lib/dict_data/ef_panel2_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_panel2_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_panel2_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_panel2_data.js').protoMsg; storeProtoPayload = require('./lib/dict_data/ef_panel2_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_panel2_data.js').prepareProtoCmd; } else if (devtype === 'deltaproultra') { pdevicesStatesDict = require('./lib/dict_data/ef_deltaproultra_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_deltaproultra_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_deltaproultra_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_deltaproultra_data.js').protoMsg; storeProtoPayload = require('./lib/dict_data/ef_deltaproultra_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_deltaproultra_data.js').prepareProtoCmd; } else if (devtype === 'alternator') { pdevicesStatesDict = require('./lib/dict_data/ef_alternator_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_alternator_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_alternator_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_alternator_data.js').protoMsg; storeProtoPayload = require('./lib/dict_data/ef_alternator_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_alternator_data.js').prepareProtoCmd; } else if (devtype === 'deltapro3') { pdevicesStatesDict = require('./lib/dict_data/ef_deltapro3_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_deltapro3_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_deltapro3_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_deltapro3_data.js').protoMsg; storeProtoPayload = require('./lib/dict_data/ef_deltapro3_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_deltapro3_data.js').prepareProtoCmd; } else if (devtype === 'delta3') { pdevicesStatesDict = require('./lib/dict_data/ef_delta3_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_delta3_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_delta3_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_delta3_data.js').protoMsg; //storeProtoPayload = require('./lib/dict_data/ef_delta3_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_delta3_data.js').prepareProtoCmd; } else if (devtype === 'delta3plus') { pdevicesStatesDict = require('./lib/dict_data/ef_delta3plus_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_delta3plus_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_delta3plus_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_delta3plus_data.js').protoMsg; storeProtoPayload = require('./lib/dict_data/ef_delta3plus_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_delta3plus_data.js').prepareProtoCmd; } else if (devtype === 'river3') { pdevicesStatesDict = require('./lib/dict_data/ef_river3_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_river3_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_river3_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_river3_data.js').protoMsg; //storeProtoPayload = require('./lib/dict_data/ef_river3_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_river3_data.js').prepareProtoCmd; } else if (devtype === 'river3plus') { pdevicesStatesDict = require('./lib/dict_data/ef_river3plus_data.js').deviceStatesDict[devtype]; pdevicesCmd = require('./lib/dict_data/ef_river3plus_data.js').deviceCmd[devtype]; protoSource = require('./lib/dict_data/ef_river3plus_data.js').protoSource; protoMsg = require('./lib/dict_data/ef_river3plus_data.js').protoMsg; storeProtoPayload = require('./lib/dict_data/ef_river3plus_data.js').storeProtoPayload; prepareProtoCmd = require('./lib/dict_data/ef_river3plus_data.js').prepareProtoCmd; } else { pdevicesStatesDict = require('./lib/ecoflow_data.js').pstationStatesDict[origdevtype]; pdevicesCmd = require('./lib/ecoflow_data.js').pstationCmd[origdevtype]; } if (!pdevicesStatesDict) { this.log.warn('no states dict for ' + devtype); } if (!pdevicesCmd) { this.log.warn('no CMD dict for ' + devtype); } //create device objects //we store only the dict from used components if (!this.pdevicesStatesDict[origdevtype]) { this.pdevicesStatesDict[origdevtype] = pdevicesStatesDict; } if (!this.pdevicesStates[origdevtype]) { this.pdevicesStates[origdevtype] = ef.statesFromDict(devStates, pdevicesStatesDict); } //if protobuf devices store additionally the protoSource and protoMsg if (protoSource) { if (!this.protoSource[devtype]) { this.protoSource[devtype] = protoSource; } } if (protoMsg) { if (!this.protoMsg[devtype]) { this.protoMsg[devtype] = protoMsg; } } // and storing the functions if (storeProtoPayload) { if (!this.storeProtoPayload[devtype]) { this.storeProtoPayload[devtype] = storeProtoPayload; } } if (prepareProtoCmd) { if (!this.prepareProtoCmd[devtype]) { this.prepareProtoCmd[devtype] = prepareProtoCmd; } } //we store only the cmd from used components if (!this.pdevicesCmd[origdevtype]) { this.pdevicesCmd[origdevtype] = pdevicesCmd; } if (devtype !== 'none' && devStates && pdevicesStatesDict) { this.log.info('start device state creation ->' + devtype + ' for Id ' + id); try { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create device ' + id); } await this.setObjectNotExistsAsync(id, { type: 'device', common: { name: name, role: 'device' }, native: {} }); for (let part in pdevicesStatesDict) { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + part); } await myutils.createMyChannel(this, id, part, part, 'channel'); for (let key in pdevicesStatesDict[part]) { let type = pdevicesStatesDict[part][key]['entity']; if (type !== 'icon') { if (devStates[part][type][key]) { if (this.config.msgStateCreation) { this.log.debug('state creation ' + key); } await myutils.createMyState( this, id, part, key, devStates[part][type][key] ); } else { this.log.info( 'not created/mismatch ->' + part + ' ' + key + ' ' + type ); } } } } if (devtype === 'pstream' || devtype === 'plug') { let part = 'time_task_config_post'; if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + part); } //createMyChannel(adapter, device, channel, name, role) await myutils.createMyChannel(this, id, part, part, 'channel'); for (let j = 1; j < 12; j++) { const task = 'task' + j; await myutils.createMyChannel(this, id + '.' + part, task, task, 'channel'); //taskIndex //createMyState(adapter, device, channel, datapoint, stateObj) await myutils.createMyState( this, id, part + '.' + task, 'taskIndex', devStates[part]['tasks']['taskIndex'] ); //type await myutils.createMyState( this, id, part + '.' + task, 'type', devStates[part]['tasks']['type'] ); //timerange await myutils.createMyChannel( this, id + '.' + part + '.' + task, 'timeRange', 'timeRange', 'channel' ); for (let key in devStates[part]['tasks']['timeRange']) { if (key === 'startTime' || key === 'stopTime') { for (let key2 in devStates[part]['tasks']['timeRange'][key]) { await myutils.createMyState( this, id, part + '.' + task + '.timeRange.' + key, key2, devStates[part]['tasks']['timeRange'][key][key2] ); } } else { await myutils.createMyState( this, id, part + '.' + task + '.timeRange', key, devStates[part]['tasks']['timeRange'][key] ); } } } } else if (devtype === 'deltamax') { let part = 'timeTask'; if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + part); } //createMyChannel(adapter, device, channel, name, role) await myutils.createMyChannel(this, id, part, part, 'channel'); //"totalTaskNum" await myutils.createMyState( this, id, part, 'totalTaskNum', devStates[part]['totalTaskNum'] ); for (let j = 0; j < 6; j++) { const task = 'task' + j; await myutils.createMyChannel(this, id + '.' + part, task, task, 'channel'); for (let type in devStates[part]) { if (type !== 'totalTaskNum') { for (let key in devStates[part][type]) { await myutils.createMyState( this, id, part + '.' + task, key, devStates[part][type][key] ); } } } } } else if (devtype === 'panel') { let part = 'timeTask'; if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + part); } //createMyChannel(adapter, device, channel, name, role) await myutils.createMyChannel(this, id, part, part, 'channel'); for (let j = 1; j < 21; j++) { const task = 'task' + j; await myutils.createMyChannel(this, id + '.' + part, task, task, 'channel'); //param await myutils.createMyChannel( this, id + '.' + part + '.' + task, 'param', 'param', 'channel' ); for (let key in devStates[part]['cfg']['param']) { await myutils.createMyState( this, id, part + '.' + task + '.param', key, devStates[part]['cfg']['param'][key] ); } //comCfg await myutils.createMyChannel( this, id + '.' + part + '.' + task, 'comCfg', 'comCfg', 'channel' ); for (let key in devStates[part]['cfg']['comCfg']) { if (key === 'setTime') { //setTime await myutils.createMyChannel( this, id + '.' + part + '.' + task + '.comCfg', 'setTime', 'setTime', 'channel' ); for (let key2 in devStates[part]['cfg']['comCfg'][key]) { await myutils.createMyState( this, id, part + '.' + task + '.comCfg.' + key, key2, devStates[part]['cfg']['comCfg'][key][key2] ); } } else if (key === 'timeRange') { //timerange await myutils.createMyChannel( this, id + '.' + part + '.' + task + '.comCfg', 'timeRange', 'timeRange', 'channel' ); for (let key2 in devStates[part]['cfg']['comCfg'][key]) { if (key2 === 'startTime' || key2 === 'endTime') { for (let key3 in devStates[part]['cfg']['comCfg'][key][ key2 ]) { await myutils.createMyState( this, id, part + '.' + task + '.comCfg.' + key + '.' + key2, key3, devStates[part]['cfg']['comCfg'][key][key2][key3] ); } } else { await myutils.createMyState( this, id, part + '.' + task + '.comCfg.' + key, key2, devStates[part]['cfg']['comCfg'][key][key2] ); } } } else { await myutils.createMyState( this, id, part + '.' + task + '.comCfg', key, devStates[part]['cfg']['comCfg'][key] ); } } } } this.log.info('pdevices states created for ' + id + ' / ' + devtype + ' / ' + name); //first additional battery if (confdevices[psta]['pstationsSlave1']) { if (devtype == 'powerkit') { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + 'bp2'); } await myutils.createMyChannel(this, id, 'bp2', 'bp2', 'channel'); for (let key in pdevicesStatesDict['bp1']) { if (this.config.msgStateCreation) { this.log.debug('state creation ' + key); } let type = pdevicesStatesDict['bp1'][key]['entity']; if (type !== 'icon') { if (devStates['bp1'][type][key]) { await myutils.createMyState( this, id, 'bp2', key, devStates['bp1'][type][key] ); } else { this.log.info( 'not created/mismatch ' + ' bp2 ->' + ' ' + key + ' ' + type ); } } } this.log.info('powerkit add battery #1 states created'); } else if (devtype == 'powerocean') { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + 'statusReportBattery2'); } await myutils.createMyChannel( this, id, 'statusReportBattery2', 'statusReportBattery2', 'channel' ); for (let key in pdevicesStatesDict['statusReportBattery1']) { if (this.config.msgStateCreation) { this.log.debug('state creation ' + key); } let type = pdevicesStatesDict['statusReportBattery1'][key]['entity']; if (type !== 'icon') { if (devStates['statusReportBattery1'][type][key]) { await myutils.createMyState( this, id, 'statusReportBattery2', key, devStates['statusReportBattery1'][type][key] ); } else { this.log.info( 'not created/mismatch ' + ' statusReportBattery2 ->' + ' ' + key + ' ' + type ); } } } this.log.info('powerocean add battery #1 states created'); } else if (devtype == 'deltaproultra') { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + 'BPInfo2'); } await myutils.createMyChannel(this, id, 'BPInfo2', 'BPInfo2', 'channel'); for (let key in pdevicesStatesDict['BPInfo']) { if (this.config.msgStateCreation) { this.log.debug('state creation ' + key); } let type = pdevicesStatesDict['BPInfo'][key]['entity']; if (type !== 'icon') { if (devStates['BPInfo'][type][key]) { await myutils.createMyState( this, id, 'BPInfo2', key, devStates['BPInfo'][type][key] ); } else { this.log.info( 'not created/mismatch ' + ' BPInfo2 ->' + ' ' + key + ' ' + type ); } } } this.log.info('DPU add battery (#2) states created'); } else { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + 'bmsSlave1'); } await myutils.createMyChannel( this, id, 'bmsSlave1', 'bmsSlave1', 'channel' ); for (let key in pdevicesStatesDict['bmsMaster']) { if (this.config.msgStateCreation) { this.log.debug('state creation ' + key); } let type = pdevicesStatesDict['bmsMaster'][key]['entity']; if (type !== 'icon') { if (devStates['bmsMaster'][type][key]) { await myutils.createMyState( this, id, 'bmsSlave1', key, devStates['bmsMaster'][type][key] ); } else { this.log.info( 'not created/mismatch ' + ' bmsSlave1 ->' + ' ' + key + ' ' + type ); } } } this.log.info('pstation add battery #1 states created'); } } //second additional battery if (confdevices[psta]['pstationsSlave2']) { if (devtype == 'powerkit') { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + 'bp3'); } await myutils.createMyChannel(this, id, 'bp3', 'bp3', 'channel'); for (let key in pdevicesStatesDict['bp1']) { if (this.config.msgStateCreation) { this.log.debug('state creation ' + key); } let type = pdevicesStatesDict['bp1'][key]['entity']; if (type !== 'icon') { if (devStates['bp1'][type][key]) { await myutils.createMyState( this, id, 'bp3', key, devStates['bp1'][type][key] ); } else { this.log.info( 'not created/mismatch ' + 'bp3 ->' + ' ' + key + ' ' + type ); } } } this.log.info('powerkit add battery #2 states created'); } else if (devtype == 'powerocean') { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + 'statusReportBattery3'); } await myutils.createMyChannel( this, id, 'statusReportBattery3', 'statusReportBattery3', 'channel' ); for (let key in pdevicesStatesDict['statusReportBattery1']) { if (this.config.msgStateCreation) { this.log.debug('state creation ' + key); } let type = pdevicesStatesDict['statusReportBattery1'][key]['entity']; if (type !== 'icon') { if (devStates['statusReportBattery1'][type][key]) { await myutils.createMyState( this, id, 'statusReportBattery3', key, devStates['statusReportBattery1'][type][key] ); } else { this.log.info( 'not created/mismatch ' + ' statusReportBattery3 ->' + ' ' + key + ' ' + type ); } } } this.log.info('powerocean add battery #1 states created'); } else if (devtype == 'deltaproultra') { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + 'BPInfo3'); } await myutils.createMyChannel(this, id, 'BPInfo3', 'BPInfo3', 'channel'); for (let key in pdevicesStatesDict['BPInfo']) { if (this.config.msgStateCreation) { this.log.debug('state creation ' + key); } let type = pdevicesStatesDict['BPInfo'][key]['entity']; if (type !== 'icon') { if (devStates['BPInfo'][type][key]) { await myutils.createMyState( this, id, 'BPInfo3', key, devStates['BPInfo'][type][key] ); } else { this.log.info( 'not created/mismatch ' + ' BPInfo3 ->' + ' ' + key + ' ' + type ); } } } this.log.info('DPU add battery (#3) states created'); } else { if (this.config.msgStateCreation) { this.log.debug('____________________________________________'); this.log.debug('create channel ' + 'bmsSlave2'); } await myutils.createMyChannel( this, id, 'bmsSlave2', 'bmsSlave2', 'channel' ); for (let key in pdevicesStatesDict['bmsMaster']) { if (this.config.msgStateCreation) { this.log.debug('state creation ' + key); } let type = pdevicesStatesDict['bmsMaster'][key]['entity']; if (type !== 'icon') { if (devStates['bmsMaster'][type][key]) { await myutils.createMyState( this, id, 'bmsSlave2', key, devStates['bmsMaster'][type][key] ); } else { this.log.info( 'not created/mismatch ' + 'bmsSlave2 ->' + ' ' + key + ' ' + type ); } } } this.log.info('pstation add battery #2 states created'); } } } catch (error) { this.log.error('create states ' + error); } } else { this.log.error( 'something empty ID->' + id + ' states -> ' + devStates + ' dict -> ' + pdevicesStatesDict + ' type -> ' + devtype ); } } } } } catch (error) { this.log.error('modification or state creation went wrong ->' + error); } } catch (error) { this.log.error('read config ' + error); } //Homeassistant connection if (this.config.haMqttEnable) { this.haDevices = ha.defineHaDevices(this.pdevices); this.log.info('HA communication:'); this.log.info('devices -> ' + JSON.stringify(this.config.pstreams)); } await myutils.createInfoStates(this, this.config.haMqttEnable); //create subscription topics let topics = []; if (this.mqttUserId.length > 0) { topics = topics.concat(ef.createSubscribeTopics(this.mqttUserId, this.pdevices)); } // this.log.debug('subscription topics EF ' + JSON.stringify(topics)); //connect to Ecoflow const optionsMqtt = { port: this.mqttPort || 8883, clientId: this.mqttClientId, username: this.mqttUserName, password: this.mqttPwd, keepalive: 60, reconnectPeriod: 5000, clean: true //manualConnect: true }; if (optionsMqtt.clientId.length > 18 && optionsMqtt.username.length > 18 && optionsMqtt.password.length > 18) { try { this.log.info('[EF] ' + 'going to connect to mqtt broker'); this.log.debug('[EF] ' + 'your mqtt configration:'); //this.log.debug('[EF] ' + 'user -> ' + this.mqttUserId); //this.log.debug('[EF] ' + 'name -> ' + this.mqttUserName); //this.log.debug('[EF] ' + 'client -> ' + this.mqttClientId); this.log.debug('[EF] ' + 'port -> ' + this.mqttPort); this.log.debug('[EF] ' + 'url -> ' + this.mqttUrl); this.log.debug('[EF] ' + 'protocol -> ' + this.mqttProtocol); this.client = mqtt.connect(this.mqttUrl + ':' + this.mqttPort, optionsMqtt); this.client.on('connect', async () => { this.log.info('EF connected'); if (topics.length > 0) { if (this.client) { this.client.subscribe(topics, async (err) => { if (!err) { this.log.debug('subscribed the topics EF'); //initial and interval for requesting last quotas await ef.getLastProtobufQuotas(this, this.pdevices); await ef.getLastJSONQuotas(this, this.pdevices); lastQuotInterval = this.setInterval(async () => { await ef.getLastProtobufQuotas(this, this.pdevices); await ef.getLastJSONQuotas(this, this.pdevices); }, 300 * 1000); // lastQuot every 5min } else { this.log.warn('could not subscribe to topics ' + err); } }); } } else { this.log.debug('no topics for subscription'); } this.setState('info.connection', true, true); }); this.client.on('message', async (topic, message) => { // message is Buffer // this.log.debug(topic + ' got ' + message.toString()); const msgtop = ef.getIdFromTopic(topic, this.mqttUserId); const msgtype = msgtop.msg; //this topic only contains the id of device topic = msgtop.topic; let devtype = ''; let logged = false; let devicelogged = false; if (this.pdevices) { if (this.pdevices[topic]) { devtype = this.pdevices[topic]['devType']; if (this.pdevices[topic]['debugEnable'] === true) { devicelogged = true; if (this.config.msgUpdate && msgtype === 'update') { logged = true; } else if ( this.config.msgSetGet && msgtype !== 'update' && msgtype !== 'unknown msgtype' ) { logged = true; } if (msgtype == 'unknown msgtype') { logged = true; } } } else { this.log.debug(topic + ' not part of configured devices'); } } //protobuf msg const origdevtype = devtype; if (devtype === 'pstream600' || devtype === 'pstream800') { devtype = 'pstream'; } if ( devtype === 'pstream' || devtype === 'plug' || devtype === 'deltaproultra' || devtype === 'powerocean' || devtype === 'panel2' || devtype === 'alternator' || devtype === 'deltapro3' || devtype === 'delta3' || devtype === 'delta3plus' || devtype === 'river3' || devtype === 'river3plus' ) { if (this.pdevicesStatesDict && this.pdevicesStates) { let msgdecode = null; if (devtype === 'delta3' || devtype === 'river3') { this.log.debug( '[PROTOBUF unknown] ' + topic + ' [' + devtype + '/' + msgtype + '] raw (hex): ' + message.toString('hex') ); } else { try { msgdecode = ef.pstreamDecode( this, message, '', topic, msgtype, this.protoSource[devtype], this.protoMsg[devtype], logged ); } catch (error) { this.log.debug('pstreamDecode call ->' + error); } if (msgtype === 'update' || msgtype === 'get_reply' || msgtype === 'set_reply' || msgtype === 'set') { if (msgdecode !== null && typeof msgdecode === 'object') { if (Object.keys(msgdecode).length > 0) { //storeStreamPayload handles multiple objects const haupdate = await this.storeProtoPayload[devtype]( this, this.pdevicesStatesDict[origdevtype], this.pdevicesStates[origdevtype], topic, msgdecode, devtype, this.pdevices[topic]['haEnable'], logged ); if (haupdate.length > 0) { for (let i = 0; i < haupdate.length; i++) { if (haupdate[i]) { if (typeof haupdate[i].payload === 'string') { ha.publish( this, topic, haupdate[i].topic, haupdate[i].payload, { qos: 1 }, devicelogged && this.config.msgHaOutgoing, 'HA EF PB UPDATE RCV' ); } else { this.log.warn( 'not a string! : ' + haupdate[i].topic + ' ' + haupdate[i].payload ); } } } } } } } else { //ef.pstreamDecode() } } } if (devtype === 'plug') { this.msgCountPlug++; await this.setStateAsync('info.msgCountPlug', { val: this.msgCountPlug, ack: true }); } else if ( devtype === 'pstream' || devtype === 'deltaproultra' || devtype === 'powerocean' || devtype === 'panel2' || devtype === 'alternator' || devtype === 'deltapro3' || devtype === 'delta3' || devtype === 'delta3plus' || devtype === 'river3' || devtype === 'river3plus' ) { this.msgCountPstream++; await this.setStateAsync('info.msgCountPstream', { val: this.msgCountPstream, ack: true }); } } else { // JSON msg switch (msgtype) { case 'set': case 'set_reply': if (logged === true) { let setmsg = JSON.parse(message.toString()); if (setmsg.params) { let key = setmsg.params.id; switch (key) { case 40: case 68: case 72: //Lebenszeichen der APP? break; default: this.log.debug( '[JSON] ' + topic + ' [' + msgtype + '] -> ' + ' key:' + key + ' ' + JSON.stringify(setmsg) ); break; } } else { this.log.debug( '[JSON] ' + topic + ' [' + msgtype + '] -> set w/o params' + JSON.stringify(setmsg) ); } } break; default: if (logged === true) { this.log.debug('[JSON] ' + topic + ' [' + msgtype + '] -> ' + message.toString()); } break; } if ( this.pdevicesStatesDict && this.pdevicesStates && (msgtype === 'get_reply' || msgtype === 'update') ) { const dict = this.pdevicesStatesDict[devtype]; let haupdate = []; switch (devtype) { case 'panel': haupdate = await ef.storeSHPpayload( this, dict, this.pdevicesStates[devtype], topic, JSON.parse(message.toString()), this.pdevices[topic]['haEnable'], logged ); break; case 'powerkitbp2000': case 'powerkitbp5000': haupdate = await ef.storePowerkitPayload( this, dict, this.pdevicesStates[devtype], topic, JSON.parse(message.toString()), this.pdevices[topic]['haEnable'], logged ); break; case 'shelly3em': haupdate = await ef.storeSHELLYpayload( this, dict, this.pdevicesStates[devtype], topic, JSON.parse(message.toString()), this.pdevices[topic]['haEnable'], logged ); break; default: haupdate = await ef.storeStationPayload( this, dict, this.pdevicesStates[devtype], topic, JSON.parse(message.toString()), this.pdevices[topic]['haEnable'], logged ); break; } if (haupdate.length > 0) { for (let i = 0; i < haupdate.length; i++) { if (typeof haupdate[i].payload === 'string') { ha.publish( this, topic, haupdate[i].topic, haupdate[i].payload, { qos: 1 }, devicelogged && this.config.msgHaOutgoing, 'HA EF JSON UPDATE RCV' ); } else { this.log.warn( 'not a string! : ' + haupdate[i].topic + ' ' + haupdate[i].payload ); } } } } this.msgCountPstation++; await this.setStateAsync('info.msgCountPstation', { val: this.msgCountPstation, ack: true }); } //reconnection trial if (this.config.enableMqttReconnect) { if (recon_timer) this.clearTimeout(recon_timer); // online check recon_timer = this.setTimeout(async () => { this.log.debug('no telegrams from devices within 10min'); this.msgReconnects++; await this.setStateAsync('info.reconnects', { val: this.msgReconnects, ack: true }); }, 600 * 1000); // retrigger time } }); this.client.on('close', () => { this.setState('info.connection', false, true); //all info/status auf offline setzen this.log.info('ecoflow connection closed'); }); this.client.on('error', (error) => { this.setState('info.connection', false, true); this.log.error('Error inconnection to Ecoflow MQTT-Broker:' + error); }); this.client.on('reconnect', async () => { this.log.debug('Reconnecting to Ecoflow MQTT broker...'); }); } catch (error) { this.log.error('create mqtt client handling ' + error); } } else { this.log.warn('check your mqtt credentials, they seem too short'); } //connect to Homeassistant if (this.config.haMqttEnable) { try { this.log.info('[HA] ' + 'going to connect to HA mqtt broker'); this.log.debug('[HA] ' + 'your HA mqtt configration:'); this.log.debug('[HA] ' + 'topic -> ' + this.config.haTopic); this.log.debug('[HA] ' + 'user -> ' + this.config.haMqttUserId); this.log.debug('[HA] ' + 'port -> ' + this.config.haMqttPort); this.log.debug('[HA] ' + 'url -> ' + this.config.haMqttUrl); this.log.debug('[HA] ' + 'ptotocol -> ' + this.config.haMqttProtocol); this.log.debug('[HA] ' + 'devices -> ' + JSON.stringify(this.haDevices)); const optionsHaMqtt = { port: this.config.haMqttPort || 1883, username: this.config.haMqttUserId, password: this.config.haMqttUserPWd, keepAlive: 60, reconnectPeriod: 5000, will: { topic: this.config.haTopic + '/iob/info/status', payload: 'offline', qos: 1, retain: false } }; this.haClient = mqtt.connect( this.config.haMqttProtocol + this.config.haMqttUrl + ':' + this.config.haMqttPort, optionsHaMqtt ); this.haClient.on('connect', async () => { await this.setStateAsync('info.haConnection', { val: 'online', ack: true }); await this.setStateAsync('info.haConnAvgLoad', { val: 0, ack: true }); this.log.debug('[HA] ' + 'haConnAvgLoad interval started'); haLoadInterval = this.setInterval(async () => { const msgcnt = this.haCounter - this.