iobroker.ecoflow-mqtt
Version:
connects to ecoflow products
1,344 lines (1,309 loc) • 75.3 kB
JavaScript
'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.