Skip to content

Commit

Permalink
Merge pull request #17 from bolliy/main
Browse files Browse the repository at this point in the history
merge dev
  • Loading branch information
bolliy authored Jan 8, 2024
2 parents 2a837f4 + 8737c3d commit bdbc70c
Show file tree
Hide file tree
Showing 7 changed files with 1,805 additions and 3,726 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ If you use two inverters, then connect to the second inverter and read the commu
* `port`: Inverter modbus port (default: 502)
* `modbusId`: Primary Modbus inverter id (default: 1)
* `modbusId2`: Secondary Modbus inverter id (default: 0)
* `updateInterval`: Fast update interval (default: 30 sec)
* `updateInterval`: Fast update interval (default: 20 sec)

## Changelog
<!--
Expand All @@ -55,15 +55,16 @@ If you use two inverters, then connect to the second inverter and read the commu
-->

### **WORK IN PROGRESS**
* (bolliy) Increase stability
* (bolliy) severeals states
prepare collected values more precisely

### 0.0.3 (2024-01-01)
### 0.1.1 (2024-01-07)
* fix some collected values

### 0.1.0 (2024-01-06)
* watchdog implemented #11
* state values are cached - only changed data should be stored
* derived and collected values for example `inputPowerEffective` or `inputYield`
* more states

* deploy more register
### 0.0.2 (2023-12-19)
Dependency and configuration updates

Expand Down
2 changes: 1 addition & 1 deletion admin/jsonConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"updateInterval": {
"type": "number",
"label": "Update interval (s)",
"default": 30,
"default": 20,
"newLine": true,
"tooltip": "Update interval to update the values from the inverters"
}
Expand Down
32 changes: 5 additions & 27 deletions io-package.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,11 @@
{
"common": {
"name": "sun2000",
"version": "0.0.2",
"version": "0.1.1",
"news": {
"0.0.1": {
"en": "initial release",
"de": "Erstveröffentlichung",
"ru": "Начальная версия",
"pt": "lançamento inicial",
"nl": "Eerste uitgave",
"fr": "Première version",
"it": "Versione iniziale",
"es": "Versión inicial",
"pl": "Pierwsze wydanie",
"uk": "початковий випуск",
"zh-cn": "初始发行"
},
"0.0.2": {
"en": "Dependency and configuration updates",
"de": "Abhängigkeits- und Konfigurationsupdates",
"ru": "В зависимости и обновления конфигурации",
"pt": "Atualizações de dependência e configuração",
"nl": "Afhankelijkheid en configuratie updates",
"fr": "Mise à jour de dépendance et de configuration",
"it": "Dipendenza e aggiornamenti di configurazione",
"es": "Actualizaciones de dependencia y configuración",
"pl": "Zależność i aktualizacja konfiguracji",
"uk": "Залежність та оновлення конфігурації",
"zh-cn": "B. 扶养和配置的最新情况"
"0.1.1": {
"en": "fix some collected values",
"de": "Korrektur einiger der gesammelten Werte (collected values)"
}
},
"titleLang": {
Expand Down Expand Up @@ -209,7 +187,7 @@
"type": "number",
"role": "value",
"read": true,
"write": true,
"write": false,
"desc": "modbus update interval",
"unit": "sec"
}
Expand Down
112 changes: 73 additions & 39 deletions lib/register.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ class StateMap {
return this.stateMap.get(id);
}

set(id, value, renew = false) {
set(id, value, options) {
if (options?.type == 'number' && isNaN(value)) return;
if (value !== null) {
if (renew || this.get(id)?.value !== value) {
this.stateMap.set(id, {id: id, value: value});
if (options?.renew || this.get(id)?.value !== value) {
if (options?.stored ) {
this.stateMap.set(id, {id: id, value: value, stored: options.stored});
} else {
this.stateMap.set(id, {id: id, value: value});
}
}
}
}
Expand All @@ -30,7 +35,7 @@ class Registers {
this.adapter = adapterInstance;
this.stateCache = new StateMap();

this.loadStates();
this._loadStates();

//https://www.iobroker.net/#de/documentation/basics/roles.md
this.registerFields = [
Expand All @@ -54,7 +59,7 @@ class Registers {
states : [{
state: {id: 'activePower', name: 'Active power', type: 'number', unit: 'kW', role: 'value.power', desc: 'Power currently used'},
register: {reg: 32080, type: dataType.int32, gain:1000},
storeAlways: true
storeAlways: false
}]
},
{
Expand All @@ -66,7 +71,7 @@ class Registers {
states : [{
state: {id: 'inputPower', name: 'Input power' , type: 'number', unit: 'kW', role: 'value.power', desc: 'Power from solar'},
register: {reg: 32064, type: dataType.int32, gain:1000},
storeAlways: true
storeAlways: false
},
{
state: {id: 'derived.inputPowerWithEfficiencyLoss', name: 'input power with efficiency loss', type: 'number', unit: 'kW', role: 'value.power', desc: ''}
Expand All @@ -90,7 +95,7 @@ class Registers {
inPowerEff *= 0.98;
}
//inputPower_with_efficiency_loss
this.stateCache.set(path+'derived.inputPowerWithEfficiencyLoss', inPowerEff);
this.stateCache.set(path+'derived.inputPowerWithEfficiencyLoss', inPowerEff, {type: 'number'});
}
},
{
Expand Down Expand Up @@ -456,12 +461,12 @@ class Registers {
this.postUpdateHooks = [
{
refresh : dataRefreshRate.low,
state: {id: 'derived.dailyPortalYield', name: 'Input Yield Today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'daily yield displayed on the portal'},
state: {id: 'derived.dailyInputYield', name: 'Input Yield Today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'daily yield displayed on the portal'},
fn : (path) => {
const disCharge = this.stateCache.get(path+'battery.currentDayDischargeCapacity')?.value;
const charge = this.stateCache.get(path+'battery.currentDayChargeCapacity')?.value;
const inputYield = Math.round((this.stateCache.get(path+'dailyEnergyYield')?.value + charge - disCharge)*100)/100;
!isNaN(inputYield) && this.stateCache.set(path+'derived.dailyPortalYield', inputYield);
this.stateCache.set(path+'derived.dailyInputYield', inputYield, {type: 'number'});
}
}
];
Expand All @@ -483,51 +488,80 @@ class Registers {
inPower += this.stateCache.get(inverter.path+'.inputPower')?.value;
inPowerEff += this.stateCache.get(inverter.path+'.derived.inputPowerWithEfficiencyLoss')?.value;
}
!isNaN(inPower) && this.stateCache.set('collected.inputPower',inPower,true);
!isNaN(inPowerEff) && this.stateCache.set('collected.inputPowerWithEfficiencyLoss',inPowerEff,true);
!isNaN(sum) && this.stateCache.set('collected.activePower',sum,true);
this.stateCache.set('collected.inputPower',inPower,{type: 'number', renew : true});
this.stateCache.set('collected.inputPowerWithEfficiencyLoss',inPowerEff,{type: 'number', renew : true});
this.stateCache.set('collected.activePower',sum,{type: 'number', renew : true});
sum -= this.stateCache.get('meter.activePower')?.value;
!isNaN(sum) && this.stateCache.set('collected.houseConsumption',sum,true);
this.stateCache.set('collected.houseConsumption',sum,{type: 'number', renew : true});
}

},
{
refresh : dataRefreshRate.low,
states : [
{id: 'collected.dailyEnergyYield', name: 'Daily Energy Yield', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'daily output yield of the inverters'},
{id: 'collected.dailyPortalYield', name: 'Input Yield Today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'daily yield displayed on the portal'},
{id: 'collected.dailyInputYield', name: 'Input Yield Today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'daily yield displayed on the portal'},
{id: 'collected.accumulatedEnergyYield', name: 'Accumulated Energy Yield', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.consumptionSum', name: 'Consumption Sum', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.gridExportSum', name: 'Grid Export Sum', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.gridImportSum', name: 'Grid Export Sum', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.consumptionStart', name: 'Consumption Start', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.gridExportStart', name: 'Grid Export Start Today', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.gridImportStart', name: 'Grid Export Start Today', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.consumptionStart', name: 'Consumption Start Today', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.gridExportToday', name: 'Grid Export Today', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.gridImportToday', name: 'Grid Import Today', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.consumptionToday', name: 'Consumption Today', type: 'number', unit: 'kWh', role: 'value.power.consumption'}
{id: 'collected.gridImportToday', name: 'Grid Import Today', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.consumptionToday', name: 'Consumption Today', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.totalCharge', name: 'Total Charge of Battery', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.totalDischarge', name: 'Total Discharge of Battery', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
{id: 'collected.currentDayChargeCapacity', name: 'Current Day Charge Capacity of Battery', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
{id: 'collected.currentDayDischargeCapacity', name: 'Current Day Discharge Capacity of Battery', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'TBD' },
{id: 'collected.SOC', name: 'State of battery capacity', type: 'number', unit: '%', role: 'value.battery', desc: 'SOC'},
{id: 'collected.ratedCapacity', name: 'Rated of battery Capacity', type: 'number', unit: 'Wh', role: 'value.capacity'}
],
fn : (inverters) => {
let inYield = 0;
let outYield = 0;
let enYield = 0;
let charge = 0;
let disCharge = 0;
let totalDisCharge = 0;
let totalCharge = 0;
let ratedCap = 0;
let load = 0;
for (const inverter of inverters) {
outYield += this.stateCache.get(inverter.path+'.dailyEnergyYield')?.value;
inYield += this.stateCache.get(inverter.path+'.derived.dailyPortalYield')?.value;
inYield += this.stateCache.get(inverter.path+'.derived.dailyInputYield')?.value;
enYield += this.stateCache.get(inverter.path+'.accumulatedEnergyYield')?.value;
charge += this.stateCache.get(inverter.path+'.battery.currentDayChargeCapacity')?.value;
disCharge += this.stateCache.get(inverter.path+'.battery.currentDayDischargeCapacity')?.value;
totalCharge += this.stateCache.get(inverter.path+'.battery.totalCharge')?.value;
totalDisCharge += this.stateCache.get(inverter.path+'.battery.totalDischarge')?.value;
if (this.stateCache.get(inverter.path+'.battery.ratedCapacity')?.value > 0) {
load += this.stateCache.get(inverter.path+'.battery.ratedCapacity')?.value * this.stateCache.get(inverter.path+'.battery.SOC')?.value;
ratedCap += this.stateCache.get(inverter.path+'.battery.ratedCapacity')?.value;
}
}
!isNaN(outYield) && this.stateCache.set('collected.dailyEnergyYield',outYield);
!isNaN(inYield) && this.stateCache.set('collected.dailyPortalYield',inYield);
!isNaN(enYield) && this.stateCache.set('collected.accumulatedEnergyYield',enYield);
this.stateCache.set('collected.dailyEnergyYield',outYield, {type: 'number'});
this.stateCache.set('collected.dailyInputYield',inYield, {type: 'number'});
this.stateCache.set('collected.accumulatedEnergyYield',enYield, {type: 'number'});
const conSum = enYield + this.stateCache.get('meter.reverseActiveEnergy')?.value - this.stateCache.get('meter.positiveActiveEnergy')?.value;
!isNaN(conSum) && this.stateCache.set('collected.consumptionSum',conSum);
this.stateCache.set('collected.consumptionSum',conSum, {type: 'number'});
// compute export and import today
this.stateCache.set('collected.gridExportToday',this.stateCache.get('meter.positiveActiveEnergy')?.value - this.stateCache.get('collected.gridExportSum')?.value);
this.stateCache.set('collected.gridImportToday',this.stateCache.get('meter.reverseActiveEnergy')?.value - this.stateCache.get('collected.gridImportSum')?.value);
this.stateCache.set('collected.gridExportToday',this.stateCache.get('meter.positiveActiveEnergy')?.value -
this.stateCache.get('collected.gridExportStart')?.value, {type: 'number'});
this.stateCache.set('collected.gridImportToday',this.stateCache.get('meter.reverseActiveEnergy')?.value -
this.stateCache.get('collected.gridImportStart')?.value, {type: 'number'});
// compute consumption today
this.stateCache.set('collected.consumptionSum',this.stateCache.get('collected.accumulatedEnergyYield')?.value +
this.stateCache.get('meter.reverseActiveEnergy')?.value -
this.stateCache.get('meter.positiveActiveEnergy')?.value);
this.stateCache.get('meter.positiveActiveEnergy')?.value, {type: 'number'});
this.stateCache.set('collected.consumptionToday', this.stateCache.get('collected.consumptionSum')?.value -
this.stateCache.get('collected.consumptionStart')?.value);
this.stateCache.get('collected.consumptionStart')?.value, {type: 'number'});
//compute battery
this.stateCache.set('collected.totalCharge',totalCharge, {type: 'number'});
this.stateCache.set('collected.totalDischarge',totalDisCharge, {type: 'number'});
this.stateCache.set('collected.currentDayChargeCapacity',charge, {type: 'number'});
this.stateCache.set('collected.currentDayDischargeCapacity',disCharge, {type: 'number'});
this.stateCache.set('collected.ratedCapacity',ratedCap, {type: 'number'});
this.stateCache.set('collected.SOC',Math.round(load/ratedCap), {type: 'number'});
}


Expand All @@ -537,7 +571,7 @@ class Registers {


async _initState(path, state) {
await this.adapter.setObjectAsync(path+state.id, {
await this.adapter.setObjectNotExistsAsync(path+state.id, {
type: 'state',
common: {
name: state.name,
Expand Down Expand Up @@ -598,7 +632,7 @@ class Registers {
if (field.mapper) {
value = await field.mapper(value);
}
this.stateCache.set(path+state.id, value, field?.storeAlways);
this.stateCache.set(path+state.id, value, { stored : field?.storeAlways} );
}
}
}
Expand Down Expand Up @@ -683,22 +717,22 @@ class Registers {
this.storeStates(); //fire and forget
}

async loadStates() {
let value = await this.adapter.getStateAsync('collected.gridExportSum');
this.stateCache.set('collected.gridExportSum',value?.val);
value = await this.adapter.getStateAsync('collected.gridImportSum');
this.stateCache.set('collected.gridImportSum',value?.val);
async _loadStates() {
let value = await this.adapter.getStateAsync('collected.gridExportStart');
this.stateCache.set('collected.gridExportStart',value?.val, {type : 'number', stored : true });
value = await this.adapter.getStateAsync('collected.gridImportStart');
this.stateCache.set('collected.gridImportStart',value?.val, {type : 'number', stored : true });
value = await this.adapter.getStateAsync('collected.consumptionStart');
this.stateCache.set('collected.consumptionStart',value?.val);
this.stateCache.set('collected.consumptionStart',value?.val, {type : 'number', stored : true });
}

// one minute before midnight - perform housekeeping actions
async mitnightProcess () {
// copy current export/import kWh - used to compute daily import/export in kWh
this.stateCache.set('collected.gridExportSum',this.stateCache.get('meter.positiveActiveEnergy')?.value);
this.stateCache.set('collected.gridImportSum',this.stateCache.get('meter.reverseActiveEnergy')?.value);
this.stateCache.set('collected.gridExportStart',this.stateCache.get('meter.positiveActiveEnergy')?.value, {type : 'number'});
this.stateCache.set('collected.gridImportStart',this.stateCache.get('meter.reverseActiveEnergy')?.value, {type : 'number'});
// copy consumption Sum to Start for the next day
this.stateCache.set('collected.consumptionStart',this.stateCache.get('collected.consumptionSum')?.value);
this.stateCache.set('collected.consumptionStart',this.stateCache.get('collected.consumptionSum')?.value, {type : 'number'});
this.storeStates(); //fire and forget
}

Expand Down
3 changes: 0 additions & 3 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ const Registers = require(__dirname + '/lib/register.js');
const ModbusConnect = require(__dirname + '/lib/modbus_connect.js');
const {dataRefreshRate} = require(__dirname + '/lib/types.js');


// Load your modules here, e.g.:
// const fs = require("fs");

class Sun2000 extends utils.Adapter {

Expand Down Expand Up @@ -42,7 +40,6 @@ class Sun2000 extends utils.Adapter {
// this.on('objectChange', this.onObjectChange.bind(this));
// this.on('message', this.onMessage.bind(this));
this.on('unload', this.onUnload.bind(this));
//this.runWatchDog();
}

getInverterInfo(id) {
Expand Down
Loading

0 comments on commit bdbc70c

Please sign in to comment.