UNPKG

iobroker.sun2000

Version:
961 lines (947 loc) 29.1 kB
const { deviceType, driverClasses, dataRefreshRate, dataType } = require(`${__dirname}/../types.js`); const DriverBase = require(`${__dirname}/driver_base.js`); const ServiceQueueMap = require(`${__dirname}/../controls/emma_service_queue.js`); class Emma extends DriverBase { constructor(stateInstance, device, options) { super(stateInstance, device, { name: 'emma', driverClass: driverClasses.emma, ...options, }); this.control = new ServiceQueueMap(this.adapter, this.deviceInfo); //emma service queue //https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/stateroles.md const newFields = [ /*{ address : 30000, length : 50, info : 'Emma Characteristic data', states: [{ state: {id: 'emma.offeringName', name: 'offering name', type: 'string', unit: '', role: 'info.display', desc: 'reg:30000, len:15'}, register: {reg: 30000, type: dataType.string, length: 8} }, { state: {id: 'emma.SN', name: 'serial number', type: 'string', unit: '', role: 'info.serial', desc: 'reg:30015, len:10'}, register: {reg: 30015, type: dataType.string, length: 5} }, { state: {id: 'emma.softwareVersion', name: 'Software version', type: 'string', unit: '', role: 'info.firmware', desc: 'reg:30035, len:15'}, register: {reg: 30035, type: dataType.string, length: 8} } ] },*/ { address: 30035, length: 15, info: 'Emma Characteristic data', states: [ { state: { id: 'emma.softwareVersion', name: 'Software version', type: 'string', unit: '', role: 'info.firmware', desc: 'reg:30035, len:15', }, register: { reg: 30035, type: dataType.string, length: 8 }, }, ], readErrorHook: (err, reg) => { reg.lastread = this._newNowTime(); //try it once }, }, { address: 30222, length: 20, info: 'Emma Characteristic data', states: [ { state: { id: 'emma.model', name: 'model', type: 'string', unit: '', role: 'info.name', desc: 'reg:30222, len:20' }, register: { reg: 30222, type: dataType.string, length: 10 }, }, ], readErrorHook: (err, reg) => { reg.lastread = this._newNowTime(); //try it once }, }, { address: 30302, length: 50, info: 'Emma sampled data 1', refresh: dataRefreshRate.low, states: [ { state: { id: 'emma.inverterTotalAbsorbedEnergy', name: 'Inverter total absorbed energy', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30302, len:4', }, register: { reg: 30302, type: dataType.uint64, gain: 100 }, }, { state: { id: 'emma.energyChargedToday', name: 'Energy charged today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30306, len:2', }, register: { reg: 30306, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.totalChargedEnergy', name: 'Total charged energy', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30308, len:4', }, register: { reg: 30308, type: dataType.uint64, gain: 100 }, }, { state: { id: 'emma.energyDischargedToday', name: 'nergy discharged today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30312, len:2', }, register: { reg: 30312, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.totalDischargedEnergy', name: 'Total discharged energy', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30314, len:4', }, register: { reg: 30314, type: dataType.uint64, gain: 100 }, }, { state: { id: 'emma.ESSchargeableEnergy', name: 'ESS chargeable energy', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30318, len:2', }, register: { reg: 30318, type: dataType.uint32, gain: 1000 }, }, { state: { id: 'emma.ESSdischargeableEnergy', name: 'ESS dischargeable energy', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30320, len:2', }, register: { reg: 30320, type: dataType.uint32, gain: 1000 }, }, { state: { id: 'emma.ratedESScapacity', name: 'Rated ESS capacity', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30322, len:2', }, register: { reg: 30322, type: dataType.uint32, gain: 1000 }, }, { state: { id: 'emma.consumptionToday', name: 'Consumption today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30324, len:2', }, register: { reg: 30324, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.totalEnergyConsumption', name: 'Total energy consumption', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30326, len:4', }, register: { reg: 30326, type: dataType.uint64, gain: 100 }, }, { state: { id: 'emma.feed-inToGridToday', name: 'Feed-in to grid today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30330, len:2', }, register: { reg: 30330, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.totalFeed-inToGrid', name: 'Total feed-in to grid', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30332, len:4', }, register: { reg: 30332, type: dataType.uint64, gain: 100 }, }, { state: { id: 'emma.supplyFromGridToday', name: 'Supply from grid today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30336, len:2', }, register: { reg: 30336, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.totalSupplyFromGrid', name: 'Total supply from grid', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30338, len:4', }, register: { reg: 30338, type: dataType.uint64, gain: 100 }, }, { state: { id: 'emma.inverterEnergyYieldToday', name: 'Inverter energy yield today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30342, len:2', }, register: { reg: 30342, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.inverterTotalEnergyYield', name: 'Inverter total energy yieldy', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30344, len:2', }, register: { reg: 30344, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.PVyieldToday', name: 'PV yield today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30346, len:2', }, register: { reg: 30346, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.totalPVenergyYield', name: 'Total PV energy yield', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30348, len:4', }, register: { reg: 30348, type: dataType.uint64, gain: 100 }, }, ], postHook: path => { if (this.isTestMode()) { const value = 18000099; this.log.info(`### TestMode ### set Emma inverterTotalAbsorbedEnergy 30302 - set to ${value} kWh`); this.stateCache.set(`${path}emma.inverterTotalAbsorbedEnergy`, value, { type: 'number' }); this.addHoldingRegisters(30302, dataType.numToArray(value * 100, dataType.uint64)); //write also to the modbus read cache } }, }, { address: 30354, length: 12, info: 'Emma sampled data 2', refresh: dataRefreshRate.high, states: [ { state: { id: 'emma.PVoutputPower', name: 'PV output power', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:30354, len:2' }, register: { reg: 30354, type: dataType.uint32, gain: 1000 }, }, { state: { id: 'emma.loadPower', name: 'Load power', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:30356, len:2' }, register: { reg: 30356, type: dataType.uint32, gain: 1000 }, }, { state: { id: 'emma.feed-inPower', name: 'Feed-in power', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:30358, len:2' }, register: { reg: 30358, type: dataType.int32, gain: 1000 }, }, { state: { id: 'emma.batteryChargeDischargePower', name: 'Battery charge/discharge power', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:30360, len:2', }, register: { reg: 30360, type: dataType.int32, gain: 1000 }, }, { state: { id: 'emma.inverterRatedPower', name: 'Battery charge/discharge power', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:30362, len:2', }, register: { reg: 30362, type: dataType.uint32, gain: 1000 }, }, { state: { id: 'emma.inverterActivePower', name: 'Inverter active power', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:30364, len:2', }, register: { reg: 30364, type: dataType.int32, gain: 1000 }, }, ], }, { address: 30368, length: 6, info: 'Emma sampled data 3', refresh: dataRefreshRate.low, states: [ { state: { id: 'emma.SOC', name: 'SOC', type: 'number', unit: '%', role: 'value.battery', desc: 'reg:30368, len:1' }, register: { reg: 30368, type: dataType.uint16, gain: 100 }, }, { state: { id: 'emma.ESSchargeableCapacity', name: 'ESS chargeable capacity', type: 'number', unit: 'kWh', role: 'value.power.capacity', desc: 'reg:30369, len:2', }, register: { reg: 30369, type: dataType.uint32, gain: 1000 }, }, { state: { id: 'emma.ESSdischargeableCapacity', name: 'ESS dischargeable capacity', type: 'number', unit: 'kWh', role: 'value.power.capacity', desc: 'reg:30371, len:2', }, register: { reg: 30371, type: dataType.uint32, gain: 1000 }, }, { state: { id: 'emma.BackupPowerSOC', name: 'Backup power SOC', type: 'number', unit: '%', role: 'value.battery', desc: 'reg:30373, len:1', }, register: { reg: 30373, type: dataType.uint16, gain: 100 }, }, ], }, { address: 30380, length: 31, info: 'Emma sampled data 4', refresh: dataRefreshRate.low, states: [ { state: { id: 'emma.yieldThisMonth', name: 'Yield this month', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30380, len:2', }, register: { reg: 30380, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.monthlyEnergyConsumption', name: 'Monthly energy consumption', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30382, len:2', }, register: { reg: 30382, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.monthlyFeed-inToGrid', name: 'Monthly feed-in to grid', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30384, len:2', }, register: { reg: 30384, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.yieldThisYear', name: 'Yield this year', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30386, len:2', }, register: { reg: 30386, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.annualEnergyConsumption', name: 'Annual energy consumption', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30388, len:2', }, register: { reg: 30388, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.yearlyFeed-inToGrid', name: 'Yearly feed-in to grid', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30390, len:2', }, register: { reg: 30390, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.monthlySupplyFromGrid', name: 'Monthly supply from grid', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30394, len:2', }, register: { reg: 30394, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.yearlySupplyFromGrid', name: 'Yearly supply from grid', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30396, len:2', }, register: { reg: 30396, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.backupTimeNotificationThreshold', name: 'Backup time notification threshold', type: 'number', unit: 'min', role: 'value', desc: 'reg:30406, len:1', }, register: { reg: 30406, type: dataType.uint16 }, }, { state: { id: 'emma.energyChargedThisMonth', name: 'Energy charged this month', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30407, len:2', }, register: { reg: 30407, type: dataType.uint32, gain: 100 }, }, { state: { id: 'emma.energyDischargedThisMonth', name: 'Energy discharged this month', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30409, len:2', }, register: { reg: 30409, type: dataType.uint32, gain: 100 }, }, ], }, /* { address : 30407, length : 4, info : 'Emma sampled data 5', refresh : dataRefreshRate.low, states: [{ state: {id: 'emma.energyChargedThisMonth', name: 'Energy charged this month', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30407, len:2'}, register: {reg: 30407, type: dataType.uint32, gain: 100} }, { state: {id: 'emma.energyDischargedThisMonth', name: 'Energy discharged this month', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30409, len:2'}, register: {reg: 30409, type: dataType.uint32, gain: 100} }] }, */ { address: 31002, length: 1, info: 'Emma DST State', refresh: dataRefreshRate.low, states: [ { state: { id: 'emma.DSTState', name: 'DST state', type: 'number', unit: '', role: 'value', desc: 'reg:31002, len:1' }, register: { reg: 31002, type: dataType.uint16 }, }, ], }, { address: 30801, length: 4, info: 'Emma running Devices', states: [ { state: { id: 'emma.numberOfInverters', name: 'Number of inverters found', type: 'number', unit: '', role: 'value', desc: 'reg:30801, len:1', }, register: { reg: 30801, type: dataType.uint16 }, }, { state: { id: 'emma.numberOfChargers', name: 'Number of chargers found', type: 'number', unit: '', role: 'value', desc: 'reg:30804, len:1', }, register: { reg: 30804, type: dataType.uint16 }, }, ], postHook: path => { const numberOfChargers = this.stateCache.get(`${path}emma.numberOfChargers`)?.value ?? 0; this.log.debug(`Number of chargers ${numberOfChargers}`); this.log.debug('### PostHook for Emma - identify Subdevices charger'); this.identifySubdevices('charger', this.modbusId) .then(ret => { this.log.debug(`### PostHook identification for Emma chargers - ret: ${JSON.stringify(ret)}`); for (const [i, charger] of ret.entries()) { const device = { index: i, duration: 0, modbusId: charger.slave_id, driverClass: driverClasses.emmaCharger, }; this.adapter.devices.push(device); this.adapter.initDevicePath(device); } }) .catch(err => { this.log.warn(`PostHook for identification EmmaChargers - err: ${err}`); }); this.identifySubdevices('sun2000', this.modbusId) .then(ret => { this.log.debug(`### PostHook indentification for Inverter Sun2000 - ret: ${JSON.stringify(ret)}`); //check if inverter sun2000 already exists const inverterExist = this.adapter.devices.findIndex(x => x.driverClass === driverClasses.inverter); for (const [i, inverter] of ret.entries()) { this.log.info(`${this._name} identify subdevice an inverter sun2000: OID=${inverter.obj_id}, modbus id: ${inverter.slave_id}`); //unless inverters are configured, add them if (inverterExist === -1) { const device = { index: i, duration: 5000, modbusId: inverter.slave_id, driverClass: driverClasses.inverter, meter: false, }; this.adapter.devices.push(device); this.adapter.initDevicePath(device); this.log.info(`Inverter sun2000 with modbus id ${inverter.slave_id} added.`); } } }) .catch(err => { this.log.warn(`### PostHook for identification Sun2000 - err: ${err}`); }); }, }, { address: 31639, length: 52, info: 'Emma build-in energy sensor', refresh: dataRefreshRate.high, type: deviceType.meter, states: [ { state: { id: 'meter.voltageL1', name: 'Phase 1 voltage', type: 'number', unit: 'V', role: 'value.voltage', desc: 'reg:31639, len:2' }, register: { reg: 31639, type: dataType.uint32, gain: 100 }, }, { state: { id: 'meter.voltageL2', name: 'Phase 2 voltage', type: 'number', unit: 'V', role: 'value.voltage', desc: 'reg:31641, len:2' }, register: { reg: 31641, type: dataType.uint32, gain: 100 }, }, { state: { id: 'meter.voltageL3', name: 'Phase 3 voltage', type: 'number', unit: 'V', role: 'value.voltage', desc: 'reg:31643, len:2' }, register: { reg: 31643, type: dataType.uint32, gain: 100 }, }, { state: { id: 'meter.currentL1', name: 'Phase 1 current', type: 'number', unit: 'A', role: 'value.current', desc: 'reg:31651, len:2' }, register: { reg: 31651, type: dataType.int32, gain: 10 }, }, { state: { id: 'meter.currentL2', name: 'Phase 2 current', type: 'number', unit: 'A', role: 'value.current', desc: 'reg:31653, len:2' }, register: { reg: 31653, type: dataType.int32, gain: 10 }, }, { state: { id: 'meter.currentL3', name: 'Phase 3 current', type: 'number', unit: 'A', role: 'value.current', desc: 'reg:31655, len:2' }, register: { reg: 31655, type: dataType.int32, gain: 10 }, }, { state: { id: 'meter.activePower', name: 'Active power', type: 'number', unit: 'kW', role: 'value.power.active', desc: 'reg:31657, len:2 (<0: feed-in to grid. >0: supply from grid.)', }, register: { reg: 31657, type: dataType.int32, gain: 1000 }, }, { state: { id: 'meter.derived.signConventionForPowerFeed-in', name: 'Sign convention for power feed-in', type: 'number', unit: '', role: 'value', desc: '1 : positive value indicates that energy is being supplied to the grid, -1 : positive value indicates that energy is being consumed from the grid', }, }, { state: { id: 'meter.derived.feed-inPower', name: 'feed-in power', type: 'number', unit: 'kW', role: 'value.power.active', desc: 'Power to grid', }, }, { state: { id: 'meter.powerFactor', name: 'Power factor', type: 'number', unit: '', role: 'value', desc: 'reg:31661, len:1' }, register: { reg: 31661, type: dataType.int16, gain: 1000 }, }, { state: { id: 'meter.voltageL1-L2', name: 'Voltage L1-L2', type: 'number', unit: 'V', role: 'value.voltage', desc: 'reg:31645 , len:2' }, register: { reg: 31645, type: dataType.uint32, gain: 100 }, }, { state: { id: 'meter.voltageL2-L3', name: 'Voltage L2-L3', type: 'number', unit: 'V', role: 'value.voltage', desc: 'reg:31647 , len:2', }, register: { reg: 31647, type: dataType.uint32, gain: 100 }, }, { state: { id: 'meter.voltageL3-L1', name: 'Voltage L3-L1', type: 'number', unit: 'V', role: 'value.voltage', desc: 'reg:31649, len:2' }, register: { reg: 31649, type: dataType.uint32, gain: 100 }, }, { state: { id: 'meter.activePowerL1', name: 'Active power L1', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:31651, len:2', }, register: { reg: 31651, type: dataType.int32, gain: 1000 }, }, { state: { id: 'meter.activePowerL2', name: 'Active power L2', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:31653, len:2', }, register: { reg: 31653, type: dataType.int32, gain: 1000 }, }, { state: { id: 'meter.activePowerL3', name: 'Active power L3', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:31655, len:2', }, register: { reg: 31655, type: dataType.int32, gain: 1000 }, }, { state: { id: 'meter.reverseActiveEnergy', name: 'Reverse active energy', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:31679, len:4', }, register: { reg: 31679, type: dataType.int64, gain: 100 }, }, { state: { id: 'meter.positiveActiveEnergy', name: 'Positive active energy', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:31687, len:4', }, register: { reg: 31687, type: dataType.int64, gain: 100 }, }, ], postHook: () => { this.stateCache.set(`meter.derived.signConventionForPowerFeed-in`, -1, { type: 'number' }); //Emma build-in energy sensor const activePower = this.stateCache.get('meter.activePower')?.value ?? 0; this.stateCache.set('meter.derived.feed-inPower', -activePower, { type: 'number' }); }, }, { address: 40470, length: 2, info: 'Emma time management', refresh: dataRefreshRate.low, states: [ { state: { id: 'emma.systemTime', name: 'System time', type: 'number', role: 'value', desc: 'reg:40470, len:2' }, register: { reg: 40470, type: dataType.uint32 }, }, { state: { id: 'emma.derived.systemTime', name: 'System time', type: 'number', unit: '', role: 'value.time', desc: 'fixed system time' }, }, ], postHook: path => { function fixTime(value) { return value * 1000; } const systemTime = this.stateCache.get(`${path}emma.systemTime`)?.value; this.stateCache.set(`${path}emma.derived.systemTime`, fixTime(systemTime), { type: 'number' }); }, }, ]; this.registerFields.push.apply(this.registerFields, newFields); } // Override /** * Checks if the device is in test mode * @returns {boolean} True if the device is in test mode, false otherwise * A device is considered in test mode if its modbus ID is not equal to 0. */ isTestMode() { return this.modbusId !== 0; } } class EmmaCharger extends DriverBase { constructor(stateInstance, device, options) { super(stateInstance, device, { name: 'emmaCharger', driverClass: driverClasses.emmaCharger, ...options, }); const newFields = [ { address: 30000, length: 47, info: 'Emma Scharger info', states: [ { state: { id: 'offeringName', name: 'Offering name', type: 'string', role: 'value', desc: 'reg:30000, len:15' }, register: { reg: 30000, type: dataType.string, length: 8 }, }, { state: { id: 'esn', name: 'ESN', type: 'string', role: 'value', desc: 'reg:30015, len:16' }, register: { reg: 30015, type: dataType.string, length: 10 }, }, { state: { id: 'softwareVersion', name: 'Software version', type: 'string', role: 'value', desc: 'reg:30031, len:16' }, register: { reg: 30031, type: dataType.string, length: 10 }, }, ], }, { address: 30076, length: 34, info: 'Emma Scharger info 2', states: [ { state: { id: 'ratedPower', name: 'Rated power', type: 'number', unit: 'kW', role: 'value.power', desc: 'reg:30076, len:2', }, register: { reg: 30076, type: dataType.uint32, gain: 10 }, }, { state: { id: 'chargerModel', name: 'charger model', type: 'string', role: 'value', desc: 'reg:30078 len:14' }, register: { reg: 30078, type: dataType.string, length: 8 }, }, { state: { id: 'bluetoothName', name: 'bluetooth name', type: 'string', role: 'value', desc: 'reg:30094, len:16' }, register: { reg: 30094, type: dataType.string, length: 10 }, }, ], }, { address: 30500, length: 10, info: 'Emma Scharger data', refresh: dataRefreshRate.high, states: [ { state: { id: 'voltageL1', name: 'Phase A voltage', type: 'number', unit: 'V', role: 'value.voltage', desc: 'reg:30500, len:2', }, register: { reg: 30500, type: dataType.uint32, gain: 10 }, }, { state: { id: 'voltageL2', name: 'Phase B voltage', type: 'number', unit: 'V', role: 'value.voltage', desc: 'reg:30502, len:2', }, register: { reg: 30502, type: dataType.uint32, gain: 10 }, }, { state: { id: 'voltageL3', name: 'Phase C voltage', type: 'number', unit: 'V', role: 'value.voltage', desc: 'reg:30504, len:2', }, register: { reg: 30504, type: dataType.uint32, gain: 10 }, }, { state: { id: 'totalEnergyCharged', name: 'total energy charged', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'reg:30506, len:2', }, register: { reg: 30506, type: dataType.uint32, gain: 1000 }, }, { state: { id: 'chargerTemperature', name: 'charger temperature', type: 'number', unit: '°C', role: 'value.temperature', desc: 'reg:30508, len:2', }, register: { reg: 30508, type: dataType.int32, gain: 10 }, }, { state: { id: 'derived.outputPower', name: 'total output power', type: 'number', unit: 'kW', role: 'value.power', }, }, ], postHook: path => { //this.log.debug('#### POSTHOOK CHARGER ####'); const now = new Date(); const totalCharged = this.stateCache.get(`${path}totalEnergyCharged`)?.value ?? 0; const smoothingFactor = 0.5; // between 0 (slow) and 1 (fast), tune as needed if (this._lastTime === undefined) { this._lastTime = now; this._lastEnergy = totalCharged; this.stateCache.set(`${path}derived.outputPower`, 0, { type: 'number' }); //this.stateCache.set(`${path}derived.smoothedOutputPower`, 0, { type: 'number' }); this._smoothedPower = 0; return; } const timeDiff = now.getTime() - this._lastTime.getTime(); if (timeDiff < 1000 * 30) return; // ignore changes less than 30 seconds const energyDiff = totalCharged - this._lastEnergy; const power = (energyDiff / timeDiff) * 1000 * 3600; // Exponential Moving Average this._smoothedPower = (this._smoothedPower ?? 0) * (1 - smoothingFactor) + power * smoothingFactor; this.stateCache.set(`${path}derived.outputPower`, this._smoothedPower, { type: 'number' }); //this.stateCache.set(`${path}derived.smoothedOutputPower`, this._smoothedPower, { type: 'number' }); this._lastTime = now; this._lastEnergy = totalCharged; }, }, ]; this.registerFields.push.apply(this.registerFields, newFields); } } module.exports = { Emma, EmmaCharger };