iobroker.sun2000 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -65,6 +65,18 @@ browse in the [wiki](https://github.com/bolliy/ioBroker.sun2000/wiki)
65
65
  Placeholder for the next version (at the beginning of the line):
66
66
  ### **WORK IN PROGRESS**
67
67
  -->
68
+ ### 2.2.0 (2025-10-05)
69
+ * dependency and configuration updates
70
+ * new state `meter.derived.signConventionForPowerFeed-in` sign of meter.activePower that is currently being fed into the power grid
71
+ * new state `meter.derived.feed-inPower` electric power that is supplied to a grid ("fed in")
72
+
73
+ ### 2.1.1 (2025-09-24)
74
+ * dependency and configuration updates
75
+ * fix: adjust event value limits based on usableSurplus parameters
76
+ * fix: swap register values for power consumption in Emma driver #190
77
+ * emma: improve power calculation with exponential moving average in EmmaCharger
78
+ * update surplus power state definitions and deprecate old identifiers
79
+
68
80
  ### 2.1.0 (2025-07-06)
69
81
  * emma: system time is being determined [#179](https://github.com/bolliy/ioBroker.sun2000/issues/179)
70
82
  * control: add grid power scheduling functionality to inverter driver [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "sun2000",
4
- "version": "2.1.0",
4
+ "version": "2.2.0",
5
5
  "news": {
6
+ "2.2.0": {
7
+ "en": "dependency and configuration updates\nnew state `meter.derived.signConventionForPowerFeed-in` sign of meter.activePower that is currently being fed into the power grid\nnew state `meter.derived.feed-inPower` electric power that is supplied to a grid (\"fed in\")",
8
+ "de": "abhängigkeits- und konfigurationsupdates\nneuer Zustand `meter.derived.signConventionForPowerFeed-in` Zeichen von meter.active Strom, der derzeit in das Stromnetz eingespeist wird\nneue Zustand `meter.derived.feed-inPower` elektrische Leistung, die einem Netz zugeführt wird (\"fed in\")",
9
+ "ru": "обновления зависимостей и конфигурации\nновый государственный знак «meter.derived.signConventionForPowerFeed-in» Мощность, которая в настоящее время подается в электросеть\nэлектрическая мощность нового состояния «meter.derived.feed-inPower», которая подается в сеть («питается в»)",
10
+ "pt": "atualizações de dependência e configuração\nnovo estado `meter.derived.signConventionForPowerFeed-in` sinal do medidor.active Energia que está sendo alimentada na rede de energia\nnovo estado `meter.derived.feed-inPower` energia elétrica que é fornecida a uma grade (\"feed in\")",
11
+ "nl": "afhankelijkheid en configuratie-updates\nnieuwe staat Energie die momenteel wordt gevoed in het elektriciteitsnet\nnieuwe toestand ",
12
+ "fr": "mises à jour de la dépendance et de la configuration\nnouvel état `meter.derived.signConventionForPowerFeed-in` signe de meter.active Puissance qui est actuellement introduite dans le réseau électrique\nnouvel état `meter.derived.feed-inPower` électricité qui est fourni à un réseau (\"feed in\")",
13
+ "it": "aggiornamenti di dipendenza e configurazione\nnuovo stato `meter.derived.signConventionForPowerFeed-in` segno di metro.active Potenza che sta attualmente alimentando nella rete elettrica\nnuovo stato `meter.derived.feed-inPower` potere elettrico che viene fornito a una griglia (\"fed in\")",
14
+ "es": "actualizaciones de dependencia y configuración\nnuevo estado `meter.derived.signConventionForPowerFeed-in` signo de medidor.active Potencia que actualmente está siendo alimentada en la red eléctrica\nnuevo estado `meter.derived.feed-inPower` energía eléctrica que se suministra a una red (\"fed in\")",
15
+ "pl": "aktualizacje zależności i konfiguracji\nnowy stan 'meter.derived.signConventionForPowerFeed- in' znak meter.active Energia zasilana obecnie do sieci energetycznej\nnowy stan 'meter.derived.feed-inPower' energia elektryczna, która jest dostarczana do sieci (\"karmiona w\")",
16
+ "uk": "оновлення залежності та конфігурації\nновий стан `meter.derived.signConventionForPowerFeed-in` знак лічильника.active Потужність, яка в даний час вдається в електромережу\nновий стан `meter.derived.feed-inPower` електрична потужність, яка подається в сітку (\"fed in\")",
17
+ "zh-cn": "依赖和配置更新\n新的状态“ 公尺” 。 生成。 签署 Convention ForPowerFeed- in' sign of meter. 活动 目前输入电网的电力\n新状态“ met. entered. feed-inpower” 供电给电网(“ feeded in”)"
18
+ },
19
+ "2.1.1": {
20
+ "en": "dependency and configuration updates\nfix: adjust event value limits based on usableSurplus parameters\nfix: swap register values for power consumption in Emma driver #190\nemma: improve power calculation with exponential moving average in EmmaCharger\nupdate surplus power state definitions and deprecate old identifiers",
21
+ "de": "abhängigkeits- und konfigurationsupdates\nfix: Event-Wert-Grenze basierend auf nutzbar Überschüssige Parameter\nfix: Swap-Registerwerte für den Stromverbrauch in Emma Treiber #190\nemma: Leistungsberechnung mit exponentiellem Bewegungsmittel in EmmaCharger verbessern\naktualisierung der überschussstrom-zustandsdefinitionen und abschreibung alter kennungen",
22
+ "ru": "обновления зависимостей и конфигурации\nисправление: корректировка предельных значений событий на основе пригодных для использования Дополнительные параметры\nисправление: значения регистра свопов для энергопотребления в драйвере Emma #190\nemma: улучшите расчет мощности с экспоненциальной скользящей средней в EmmaCharger\nобновить определения состояния избыточной мощности и обесценить старые идентификаторы",
23
+ "pt": "atualizações de dependência e configuração\ncorreção: ajustar os limites do valor do evento com base no utilizável Parâmetros do excedente\ncorreção: valores de registro de swap para consumo de energia em Emma driver #190\nemma: melhorar o cálculo de potência com média móvel exponencial em EmmaCharger\natualizar definições de estado de potência excedente e depreciar identificadores antigos",
24
+ "nl": "afhankelijkheid en configuratie-updates\nfix: aanpassen gebeurtenis waarde limieten op basis van bruikbaar Overschotparameters\nfix: swap register waarden voor energieverbruik in Emma driver #190\nemma: verbeteren van de stroomberekening met exponentieel bewegend gemiddelde in EmmaCharger\nde definitie van overtollige vermogenstoestand bij te werken en oude identificatienummers te schrappen",
25
+ "fr": "mises à jour de la dépendance et de la configuration\nfix: ajuster les limites de valeur d'événement en fonction de la valeur utilisable Paramètres excédentaires\nfix: valeurs de registre d'échange pour la consommation d'énergie en Emma driver #190\nemma: améliorer le calcul de la puissance avec la moyenne mobile exponentielle en EmmaCharger\nmettre à jour les définitions de l'état de puissance excédentaire et supprimer les anciens identifiants",
26
+ "it": "aggiornamenti di dipendenza e configurazione\ncorrezione: regolare i limiti del valore dell'evento in base all'utilizzabile Parametri in eccesso\ncorrezione: swap valori di registro per il consumo di energia in Emma driver #190\nemma: migliorare il calcolo di potenza con una media mobile esponenziale in EmmaCharger\naggiornare le definizioni di stato di potenza eccedente e deprecare vecchi identificatori",
27
+ "es": "actualizaciones de dependencia y configuración\nfijación: ajustar los límites de valor de evento basados en utilizables Parámetros adicionales\nfijado: cambiar los valores de registro para el consumo de energía en el controlador Emma #190\nemma: mejorar el cálculo de potencia con promedio de movimiento exponencial en EmmaCharger\nactualizar las definiciones de estado de excedente de energía y depreparar identificadores antiguos",
28
+ "pl": "aktualizacje zależności i konfiguracji\nfix: dostosować wartości graniczne dla zdarzeń w oparciu o wartość użytkową Parametry nadwyżki\nfix: wartości rejestru swap dla zużycia energii w sterowniku Emma # 190\nemma: ulepszenie obliczania mocy przy zastosowaniu wykładniczej średniej ruchomej w EmmaCharger\naktualizacja definicji nadwyżek energii elektrycznej i deprecjacja starych identyfikatorów",
29
+ "uk": "оновлення залежності та конфігурації\nвиправити: регулювати ліміти значення події на основі Параметри Surplus\nвиправити: значення swap для споживання енергії в машині Емма #190\nемма: поліпшення розрахунку потужності з постійним рухомим середнім в ЕммаКхаргер\nоновлення надлишок визначення стану живлення та депресувати старі ідентифікатори",
30
+ "zh-cn": "依赖和配置更新\n固定:根据可用值调整事件值限制 盈余参数\n修补: Emma 驱动器中用电量的交换记录值 # 190\nemma: 改进电量计算, 以 Emmacourer 中的指数移动平均值\n更新剩余电源状态定义并折旧旧标识符"
31
+ },
6
32
  "2.1.0": {
7
33
  "en": "emma: system time is being determined [#179](https://github.com/bolliy/ioBroker.sun2000/issues/179)\ncontrol: add grid power scheduling functionality to inverter driver [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)",
8
34
  "de": "emma: Die Systemzeit wird bestimmt [#179](https://github.com/bolliy/ioBroker.sun2000/issues/179)\nsteuerung: Netz-Leistungs-Scheduling-Funktionalität zum Wechselrichtertreiber hinzufügen [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)",
@@ -67,32 +93,6 @@
67
93
  "pl": "usableSurplusPower: nowy stan kontroli 'Sun2000.0.control.usableSurplus.allowNegativeValue'",
68
94
  "uk": "english, Українська, Français... й",
69
95
  "zh-cn": "可用SurpusPower: 新的控制状态 `sun 2000.control.usurplus.allow NegativeValue ' `"
70
- },
71
- "1.2.2": {
72
- "en": "deploy 1.2",
73
- "de": "bereitstellung 1.2",
74
- "ru": "развернуть 1.2",
75
- "pt": "implementação 1.2",
76
- "nl": "1.2 inzetten",
77
- "fr": "déploiement 1,2",
78
- "it": "distribuzione 1.2",
79
- "es": "despliegue 1.2",
80
- "pl": "rozmieszczenie 1.2",
81
- "uk": "розгортання 1.2",
82
- "zh-cn": "部署"
83
- },
84
- "1.2.2-alpha.0": {
85
- "en": "test release",
86
- "de": "prüfbericht",
87
- "ru": "испытательный выпуск",
88
- "pt": "liberação de teste",
89
- "nl": "loslaten van de test",
90
- "fr": "libération d'essai",
91
- "it": "rilascio del test",
92
- "es": "prueba de liberación",
93
- "pl": "uwalnianie testowe",
94
- "uk": "тестовий реліз",
95
- "zh-cn": "测试发布"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -221,7 +221,7 @@
221
221
  ],
222
222
  "globalDependencies": [
223
223
  {
224
- "admin": ">=7.4.10"
224
+ "admin": ">=7.6.17"
225
225
  }
226
226
  ],
227
227
  "plugins": {
@@ -26,6 +26,12 @@ class ConfigMap {
26
26
  if (event.value < 0) {
27
27
  event.value = 0;
28
28
  }
29
+ const bufferSoc = this.get('usableSurplus.bufferSoc')?.value ?? 0;
30
+
31
+ if (bufferSoc > 0 && event.value > bufferSoc) {
32
+ event.value = bufferSoc;
33
+ }
34
+
29
35
  event.value = Math.round(event.value);
30
36
  return true;
31
37
  },
@@ -46,6 +52,11 @@ class ConfigMap {
46
52
  if (event.value < 0) {
47
53
  event.value = 0;
48
54
  }
55
+ const minSoc = this.get('usableSurplus.minSoc')?.value ?? 0;
56
+
57
+ if (event.value > 0 && event.value < minSoc) {
58
+ event.value = minSoc;
59
+ }
49
60
  event.value = Math.round(event.value);
50
61
  return true;
51
62
  },
@@ -9,6 +9,8 @@ class Emma extends DriverBase {
9
9
  ...options,
10
10
  });
11
11
 
12
+ //this.stateCache.set(`meter.derived.directionOfPowerFlow`, 1, { type: 'number' }); //Emma build-in energy sensor
13
+
12
14
  //https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/stateroles.md
13
15
  const newFields = [
14
16
  /*{
@@ -116,7 +118,7 @@ class Emma extends DriverBase {
116
118
  },
117
119
  {
118
120
  state: {
119
- id: 'emma.TotalDischargedEnergy',
121
+ id: 'emma.totalDischargedEnergy',
120
122
  name: 'Total discharged energy',
121
123
  type: 'number',
122
124
  unit: 'kWh',
@@ -312,7 +314,7 @@ class Emma extends DriverBase {
312
314
  },
313
315
  {
314
316
  state: {
315
- id: 'emma.Inverter active power',
317
+ id: 'emma.inverterActivePower',
316
318
  name: 'Inverter active power',
317
319
  type: 'number',
318
320
  unit: 'kW',
@@ -464,7 +466,7 @@ class Emma extends DriverBase {
464
466
  },
465
467
  {
466
468
  state: {
467
- id: 'emma.BackupTimeNotificationThreshold',
469
+ id: 'emma.backupTimeNotificationThreshold',
468
470
  name: 'Backup time notification threshold',
469
471
  type: 'number',
470
472
  unit: 'min',
@@ -556,7 +558,7 @@ class Emma extends DriverBase {
556
558
  postHook: path => {
557
559
  const numberOfChargers = this.stateCache.get(`${path}emma.numberOfChargers`)?.value ?? 0;
558
560
  this.log.debug(`Number of chargers ${numberOfChargers}`);
559
- this.log.debug('### PostHook for InverterSun2000');
561
+ this.log.debug('### PostHook for Emma - identify Subdevices charger');
560
562
  this.identifySubdevices('charger', this.modbusId)
561
563
  .then(ret => {
562
564
  this.log.debug(`### PostHook for Emma - ret: ${JSON.stringify(ret)}`);
@@ -574,6 +576,16 @@ class Emma extends DriverBase {
574
576
  .catch(err => {
575
577
  this.log.warn(`PostHook for identification EmmaChargers - err: ${err}`);
576
578
  });
579
+ this.identifySubdevices('sun2000', this.modbusId)
580
+ .then(ret => {
581
+ this.log.debug(`### PostHook for InverterSun2000 - ret: ${JSON.stringify(ret)}`);
582
+ for (const [i, inverter] of ret.entries()) {
583
+ this.log.info(`inverter ${i} sun2000 found - modbus id: ${inverter.slave_id}`);
584
+ }
585
+ })
586
+ .catch(err => {
587
+ this.log.warn(`### PostHook for identification Sun2000 - err: ${err}`);
588
+ });
577
589
  },
578
590
  },
579
591
  {
@@ -614,9 +626,29 @@ class Emma extends DriverBase {
614
626
  type: 'number',
615
627
  unit: 'kW',
616
628
  role: 'value.power.active',
617
- desc: 'reg:31657, len:2 (>0: feed-in to grid. <0: supply from grid.)',
629
+ desc: 'reg:31657, len:2 (<0: feed-in to grid. >0: supply from grid.)',
630
+ },
631
+ register: { reg: 31657, type: dataType.int32, gain: 1000 },
632
+ },
633
+ {
634
+ state: {
635
+ id: 'meter.derived.signConventionForPowerFeed-in',
636
+ name: 'Sign convention for power feed-in',
637
+ type: 'number',
638
+ unit: '',
639
+ role: 'value',
640
+ 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',
641
+ },
642
+ },
643
+ {
644
+ state: {
645
+ id: 'meter.derived.feed-inPower',
646
+ name: 'feed-in power',
647
+ type: 'number',
648
+ unit: 'kW',
649
+ role: 'value.power.active',
650
+ desc: 'Power to grid',
618
651
  },
619
- register: { reg: 31657, type: dataType.int32, gain: -1000 },
620
652
  },
621
653
  {
622
654
  state: { id: 'meter.powerFactor', name: 'Power factor', type: 'number', unit: '', role: 'value', desc: 'reg:31661, len:1' },
@@ -697,6 +729,11 @@ class Emma extends DriverBase {
697
729
  register: { reg: 31687, type: dataType.int64, gain: 100 },
698
730
  },
699
731
  ],
732
+ postHook: () => {
733
+ this.stateCache.set(`meter.derived.signConventionForPowerFeed-in`, -1, { type: 'number' }); //Emma build-in energy sensor
734
+ const activePower = this.stateCache.get('meter.activePower')?.value ?? 0;
735
+ this.stateCache.set('meter.derived.feed-inPower', -activePower, { type: 'number' });
736
+ },
700
737
  },
701
738
  {
702
739
  address: 40470,
@@ -852,24 +889,30 @@ class EmmaCharger extends DriverBase {
852
889
  },
853
890
  ],
854
891
  postHook: path => {
855
- const now = new Date();
856
892
  //this.log.debug('#### POSTHOOK CHARGER ####');
893
+ const now = new Date();
857
894
  const totalCharged = this.stateCache.get(`${path}totalEnergyCharged`)?.value ?? 0;
858
- //const totalCharged = this.stateCache.get(`collected.dailySolarYield`)?.value ?? 0;
859
- //const test = this.stateCache.get(`collected.inputPower`)?.value ?? 0;
895
+ const smoothingFactor = 0.5; // between 0 (slow) and 1 (fast), tune as needed
896
+
860
897
  if (this._lastTime === undefined) {
861
898
  this._lastTime = now;
862
899
  this._lastEnergy = totalCharged;
863
900
  this.stateCache.set(`${path}derived.outputPower`, 0, { type: 'number' });
901
+ //this.stateCache.set(`${path}derived.smoothedOutputPower`, 0, { type: 'number' });
902
+ this._smoothedPower = 0;
864
903
  return;
865
904
  }
866
905
  const timeDiff = now.getTime() - this._lastTime.getTime();
867
- if (timeDiff < 1000 * 20) return; // ignore changes less than 20 seconds
906
+ if (timeDiff < 1000 * 30) return; // ignore changes less than 30 seconds
868
907
  const energyDiff = totalCharged - this._lastEnergy;
869
908
  const power = (energyDiff / timeDiff) * 1000 * 3600;
870
909
 
871
- this.stateCache.set(`${path}derived.outputPower`, power, { type: 'number' });
872
- //this.stateCache.set(`${path}derived.testpower`, test, { type: 'number' });
910
+ // Exponential Moving Average
911
+ this._smoothedPower = (this._smoothedPower ?? 0) * (1 - smoothingFactor) + power * smoothingFactor;
912
+
913
+ this.stateCache.set(`${path}derived.outputPower`, this._smoothedPower, { type: 'number' });
914
+ //this.stateCache.set(`${path}derived.smoothedOutputPower`, this._smoothedPower, { type: 'number' });
915
+
873
916
  this._lastTime = now;
874
917
  this._lastEnergy = totalCharged;
875
918
  },
@@ -262,30 +262,6 @@ class InverterSun2000 extends DriverBase {
262
262
  this.solarSum.add(inPowerEff); //riemann Sum
263
263
  },
264
264
  },
265
- /*
266
- {
267
- address: 37113,
268
- length: 2,
269
- info: 'meter activePower',
270
- refresh: dataRefreshRate.high,
271
- //standby : true,
272
- type: deviceType.meter,
273
- states: [
274
- {
275
- state: {
276
- id: 'meter.activePower',
277
- name: 'Active power',
278
- type: 'number',
279
- unit: 'kW',
280
- role: 'value.power.active',
281
- desc: 'reg:37113, len:2 (>0: feed-in to grid. <0: supply from grid.)',
282
- },
283
- register: { reg: 37113, type: dataType.int32, gain: 1000 },
284
- },
285
-
286
- ],
287
- },
288
- */
289
265
  {
290
266
  address: 37052,
291
267
  length: 10,
@@ -1293,6 +1269,26 @@ class InverterSun2000 extends DriverBase {
1293
1269
  },
1294
1270
  register: { reg: 37113, type: dataType.int32, gain: 1000 },
1295
1271
  },
1272
+ {
1273
+ state: {
1274
+ id: 'meter.derived.signConventionForPowerFeed-in',
1275
+ name: 'Sign convention for power feed-in',
1276
+ type: 'number',
1277
+ unit: '',
1278
+ role: 'value',
1279
+ 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',
1280
+ },
1281
+ },
1282
+ {
1283
+ state: {
1284
+ id: 'meter.derived.feed-inPower',
1285
+ name: 'feed-in power',
1286
+ type: 'number',
1287
+ unit: 'kW',
1288
+ role: 'value.power.active',
1289
+ desc: 'Power to grid',
1290
+ },
1291
+ },
1296
1292
  {
1297
1293
  state: {
1298
1294
  id: 'meter.reactivePower',
@@ -1399,6 +1395,11 @@ class InverterSun2000 extends DriverBase {
1399
1395
  register: { reg: 37136, type: dataType.int32 },
1400
1396
  },
1401
1397
  ],
1398
+ postHook: () => {
1399
+ this.stateCache.set(`meter.derived.signConventionForPowerFeed-in`, 1, { type: 'number' });
1400
+ const activePower = this.stateCache.get('meter.activePower')?.value ?? 0;
1401
+ this.stateCache.set('meter.derived.feed-inPower', activePower, { type: 'number' });
1402
+ },
1402
1403
  },
1403
1404
  {
1404
1405
  //https://photomate.zendesk.com/hc/en-gb/articles/5701625507485-Export-limitation-for-SUN2000-inverters-via-FusionSolar-App
@@ -80,6 +80,7 @@ class SmartLoggerMeter extends DriverBase {
80
80
  });
81
81
 
82
82
  this._testMode = false;
83
+
83
84
  //this._testMode = (this._modbusId == 1);
84
85
 
85
86
  //https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/stateroles.md
@@ -126,6 +127,26 @@ class SmartLoggerMeter extends DriverBase {
126
127
  },
127
128
  register: { reg: 32278, type: dataType.int32, gain: 1000 },
128
129
  },
130
+ {
131
+ state: {
132
+ id: 'meter.derived.signConventionForPowerFeed-in',
133
+ name: 'Sign convention for power feed-in',
134
+ type: 'number',
135
+ unit: '',
136
+ role: 'value',
137
+ 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',
138
+ },
139
+ },
140
+ {
141
+ state: {
142
+ id: 'meter.derived.feed-inPower',
143
+ name: 'feed-in power',
144
+ type: 'number',
145
+ unit: 'kW',
146
+ role: 'value.power.active',
147
+ desc: 'Power to grid',
148
+ },
149
+ },
129
150
  {
130
151
  state: {
131
152
  id: 'meter.reactivePower',
@@ -227,6 +248,11 @@ class SmartLoggerMeter extends DriverBase {
227
248
  register: { reg: 32361, type: dataType.int64, gain: 100 },
228
249
  },
229
250
  ],
251
+ postHook: () => {
252
+ this.stateCache.set(`meter.derived.signConventionForPowerFeed-in`, 1, { type: 'number' });
253
+ const activePower = this.stateCache.get('meter.activePower')?.value ?? 0;
254
+ this.stateCache.set('meter.derived.feed-inPower', activePower, { type: 'number' });
255
+ },
230
256
  },
231
257
  ];
232
258
 
@@ -288,12 +314,6 @@ class SmartLoggerMeter extends DriverBase {
288
314
  state: { id: 'meter.powerFactor', name: 'Power factor', type: 'number', unit: '', role: 'value', desc: 'reg:37117, len:1' },
289
315
  register: { reg: 37117, type: dataType.int16, gain: 1000 },
290
316
  },
291
- /*
292
- {
293
- state: {id: 'meter.gridFrequency', name: 'Grid Frequency', type: 'number', unit: 'Hz', role: 'value.frequency', desc: 'reg:37118, len:1'},
294
- register: {reg: 37118, type: dataType.int16, gain: 100}
295
- },
296
- */
297
317
  {
298
318
  state: {
299
319
  id: 'meter.positiveActiveEnergy',
@@ -373,6 +393,9 @@ class SmartLoggerMeter extends DriverBase {
373
393
  register: { reg: 37136, type: dataType.int32 },
374
394
  },
375
395
  ],
396
+ postHook: path => {
397
+ this.stateCache.set(`${path}meter.derived.directionOfPowerFlow`, 0, { type: 'number' });
398
+ },
376
399
  },
377
400
  ];
378
401
 
package/lib/register.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { deviceType, driverClasses, dataRefreshRate } = require(`${__dirname}/types.js`);
4
4
  const { StateMap } = require(`${__dirname}/tools.js`);
5
- //const getDriverHandler = require(__dirname + '/drivers/drivers_old.js');
6
5
  const getDriverHandler = require(`${__dirname}/drivers/index.js`);
7
6
 
8
7
  class Registers {
@@ -42,7 +41,30 @@ class Registers {
42
41
  unit: 'kW',
43
42
  role: 'value.power',
44
43
  },
45
- { id: 'collected.usableSurplusPower', name: 'usable surplus power', type: 'number', unit: 'kW', role: 'value.power' },
44
+ {
45
+ id: 'collected.usableSurplusPower',
46
+ name: 'usable surplus power',
47
+ type: 'number',
48
+ unit: 'kW',
49
+ role: 'value.power',
50
+ desc: 'depreciated: Please use collected.surplus.usablePower instead',
51
+ },
52
+ {
53
+ id: 'collected.surplus.power',
54
+ name: 'surplus power',
55
+ type: 'number',
56
+ unit: 'kW',
57
+ role: 'value.power',
58
+ desc: 'Power from solar minus house consumption',
59
+ },
60
+ {
61
+ id: 'collected.surplus.usablePower',
62
+ name: 'usable surplus power',
63
+ type: 'number',
64
+ unit: 'kW',
65
+ role: 'value.power',
66
+ desc: 'Power from solar minus house consumption and usable battery power',
67
+ },
46
68
  { id: 'collected.externalPower', name: 'external power', type: 'number', unit: 'kW', role: 'value.power' },
47
69
  //{ id: 'collected.ratedPower', name: 'rated power', type: 'number', unit: 'kW', role: 'value.power' },
48
70
  ],
@@ -55,11 +77,13 @@ class Registers {
55
77
 
56
78
  function calcUsableSurplus() {
57
79
  let surplusPower = 0;
80
+ let usableSurplusPower = 0;
81
+
58
82
  if (this.adapter.control) {
59
- surplusPower += meterPower;
83
+ surplusPower += feedinPower;
60
84
 
61
- let minSoc = this.adapter.control.get('usableSurplus.minSoc')?.value ?? 0;
62
- const bufferSoc = this.adapter.control.get('usableSurplus.bufferSoc')?.value ?? 0;
85
+ const minSoc = this.adapter.control.get('usableSurplus.minSoc')?.value ?? 0;
86
+ let bufferSoc = this.adapter.control.get('usableSurplus.bufferSoc')?.value ?? 0;
63
87
  const residualPower = this.adapter.control.get('usableSurplus.residualPower')?.value ?? 0;
64
88
  const soc = this.stateCache.get('collected.SOC')?.value ?? 0;
65
89
  const allowNegativeValue = this.adapter.control.get('usableSurplus.allowNegativeValue')?.value ?? false;
@@ -68,19 +92,25 @@ class Registers {
68
92
  if (chargeDischarge < 0) {
69
93
  surplusPower += chargeDischarge;
70
94
  }
95
+ if (soc >= minSoc) {
96
+ if (chargeDischarge > 0) surplusPower += chargeDischarge;
97
+ if (bufferSoc === 0 || soc < bufferSoc) {
98
+ surplusPower -= residualPower / 1000;
99
+ }
100
+ }
101
+
102
+ usableSurplusPower = surplusPower;
71
103
 
72
104
  if (bufferSoc > 0) {
73
105
  let threshold = hysterese / 2;
74
- if (minSoc > bufferSoc - threshold) {
75
- minSoc = bufferSoc - threshold;
76
- }
106
+
77
107
  if (this.bufferOn) {
78
108
  threshold = -threshold;
79
109
  }
80
- if (soc >= bufferSoc + threshold) {
110
+ if (soc > minSoc && soc >= bufferSoc + threshold) {
81
111
  this.bufferOn = true;
82
112
  const bufferPower = this.adapter.control.get('usableSurplus.bufferPower')?.value ?? 0;
83
- surplusPower += bufferPower / 1000;
113
+ usableSurplusPower += bufferPower / 1000;
84
114
  } else {
85
115
  this.bufferOn = false;
86
116
  }
@@ -88,25 +118,23 @@ class Registers {
88
118
  this.bufferOn = false;
89
119
  }
90
120
 
91
- if (soc >= minSoc) {
92
- if (chargeDischarge > 0) surplusPower += chargeDischarge;
93
- if (!this.bufferOn) {
94
- surplusPower -= residualPower / 1000;
95
- }
96
- }
97
-
98
121
  if (surplusPower > ratedPower) {
99
122
  surplusPower = ratedPower;
100
123
  }
124
+
125
+ if (usableSurplusPower > ratedPower) {
126
+ usableSurplusPower = ratedPower;
127
+ }
101
128
  if (!allowNegativeValue) {
102
129
  if (surplusPower < 0.1) surplusPower = 0;
130
+ if (usableSurplusPower < 0.1) usableSurplusPower = 0;
103
131
  }
104
132
 
105
133
  this.adapter.log.debug(
106
134
  `### Caculate usableSurplus power ${surplusPower} bufferOn ${this.bufferOn} soc ${soc} minSoc ${minSoc} bufferSoc ${bufferSoc} threshold ${hysterese / 2}`,
107
135
  );
108
136
  }
109
- return surplusPower;
137
+ return [surplusPower, usableSurplusPower];
110
138
  }
111
139
 
112
140
  for (const inverter of inverters) {
@@ -121,28 +149,36 @@ class Registers {
121
149
  //maxChargePower = this.stateCache.get(`${inverter.path}.battery.maximumChargingPower`)?.value ?? 0;
122
150
  }
123
151
 
124
- const meterPower = this.stateCache.get('meter.activePower')?.value ?? 0;
152
+ const feedinPower = this.stateCache.get('meter.derived.feed-inPower')?.value ?? 0;
153
+
125
154
  const extPower = this.adapter.control.get('externalPower')?.value ?? 0;
126
155
  //extPower = extPower / 1000;
127
156
 
128
157
  //Zu geringe Erzeugerenegie
129
- let houseConsum = actPower - meterPower + extPower;
158
+ let houseConsum = actPower - feedinPower + extPower;
130
159
  if (houseConsum < 0) {
131
160
  houseConsum = 0;
132
161
  }
133
162
  //Überschuss (Differenz)
134
- const surplusPower = calcUsableSurplus.bind(this)();
163
+ const surplusArray = calcUsableSurplus.bind(this)();
135
164
 
136
165
  this.stateCache.set('collected.inputPower', inPower, { type: 'number', renew: true });
137
166
  this.stateCache.set('collected.inputPowerWithEfficiencyLoss', inPowerEff, { type: 'number' });
138
167
  this.stateCache.set('collected.activePower', actPower, { type: 'number', renew: true });
168
+ //this.stateCache.set('collected.feed-inPower', feedinPower, { type: 'number' }); //positive -> power to grid
139
169
  this.stateCache.set('collected.houseConsumption', houseConsum, { type: 'number' });
140
170
  this.stateCache.set('collected.chargeDischargePower', chargeDischarge, { type: 'number' });
141
- this.stateCache.set('collected.usableSurplusPower', surplusPower, {
171
+ this.stateCache.set('collected.usableSurplusPower', surplusArray[1], {
142
172
  type: 'number',
143
173
  });
174
+ this.stateCache.set('collected.surplus.power', surplusArray[0], {
175
+ type: 'number',
176
+ });
177
+ this.stateCache.set('collected.surplus.usablePower', surplusArray[1], {
178
+ type: 'number',
179
+ });
180
+
144
181
  this.stateCache.set('collected.externalPower', extPower, { type: 'number' });
145
- //this.stateCache.set('collected.ratedPower', ratedPower, { type: 'number' });
146
182
  },
147
183
  },
148
184
  {
@@ -175,7 +211,7 @@ class Registers {
175
211
  { id: 'collected.accumulatedEnergyYield', name: 'Accumulated energy yield', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
176
212
  { id: 'collected.consumptionSum', name: 'Consumption sum', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
177
213
  { id: 'collected.gridExportStart', name: 'Grid export start today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
178
- { id: 'collected.gridImportStart', name: 'Grid export start today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
214
+ { id: 'collected.gridImportStart', name: 'Grid import start today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
179
215
  { id: 'collected.consumptionStart', name: 'Consumption start today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
180
216
  { id: 'collected.gridExportToday', name: 'Grid export today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
181
217
  { id: 'collected.gridImportToday', name: 'Grid import today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
@@ -211,9 +247,10 @@ class Registers {
211
247
  let totalCharge = 0;
212
248
  let ratedCap = 0;
213
249
  let load = 0;
250
+ let feedinEnergy;
251
+ let supplyFromGrid;
214
252
 
215
253
  for (const inverter of inverters) {
216
- //v0.4.x
217
254
  if (inverter.driverClass != driverClasses.inverter) {
218
255
  continue;
219
256
  }
@@ -237,27 +274,25 @@ class Registers {
237
274
  this.stateCache.set('collected.dailyInputYield', inYield, { type: 'number' }); //deprecated
238
275
  this.stateCache.set('collected.dailySolarYield', solarYield, { type: 'number' });
239
276
  this.stateCache.set('collected.accumulatedEnergyYield', enYield, { type: 'number' });
240
- const conSum = enYield + this.stateCache.get('meter.reverseActiveEnergy')?.value - this.stateCache.get('meter.positiveActiveEnergy')?.value;
277
+ const sign = this.stateCache.get('meter.derived.signConventionForPowerFeed-in')?.value ?? 1;
278
+ if (sign === -1) {
279
+ //Emma Meter Power is positive when power is taken from grid
280
+ //Emma Meter Power is negative when power is feed in to grid
281
+ feedinEnergy = this.stateCache.get('meter.reverseActiveEnergy')?.value ?? 0;
282
+ supplyFromGrid = this.stateCache.get('meter.positiveActiveEnergy')?.value ?? 0;
283
+ } else {
284
+ feedinEnergy = this.stateCache.get('meter.positiveActiveEnergy')?.value ?? 0;
285
+ supplyFromGrid = this.stateCache.get('meter.reverseActiveEnergy')?.value ?? 0;
286
+ }
287
+ const conSum = enYield + supplyFromGrid - feedinEnergy;
241
288
  this.stateCache.set('collected.consumptionSum', conSum, { type: 'number' });
242
289
  // compute export and import today
243
- this.stateCache.set(
244
- 'collected.gridExportToday',
245
- this.stateCache.get('meter.positiveActiveEnergy')?.value - this.stateCache.get('collected.gridExportStart')?.value,
246
- { type: 'number' },
247
- );
248
- this.stateCache.set(
249
- 'collected.gridImportToday',
250
- this.stateCache.get('meter.reverseActiveEnergy')?.value - this.stateCache.get('collected.gridImportStart')?.value,
251
- { type: 'number' },
252
- );
253
- // compute consumption today
254
- this.stateCache.set(
255
- 'collected.consumptionSum',
256
- this.stateCache.get('collected.accumulatedEnergyYield')?.value +
257
- this.stateCache.get('meter.reverseActiveEnergy')?.value -
258
- this.stateCache.get('meter.positiveActiveEnergy')?.value,
259
- { type: 'number' },
260
- );
290
+ this.stateCache.set('collected.gridExportToday', feedinEnergy - this.stateCache.get('collected.gridExportStart')?.value, {
291
+ type: 'number',
292
+ });
293
+ this.stateCache.set('collected.gridImportToday', supplyFromGrid - this.stateCache.get('collected.gridImportStart')?.value, {
294
+ type: 'number',
295
+ });
261
296
  this.stateCache.set(
262
297
  'collected.consumptionToday',
263
298
  this.stateCache.get('collected.consumptionSum')?.value - this.stateCache.get('collected.consumptionStart')?.value,
@@ -438,10 +473,16 @@ class Registers {
438
473
  //state
439
474
  async mitnightProcess() {
440
475
  // copy current export/import kWh - used to compute daily import/export in kWh
441
- this.stateCache.set('collected.gridExportStart', this.stateCache.get('meter.positiveActiveEnergy')?.value, { type: 'number' });
442
- this.stateCache.set('collected.gridImportStart', this.stateCache.get('meter.reverseActiveEnergy')?.value, { type: 'number' });
476
+ const sign = this.stateCache.get('meter.derived.signConventionForPowerFeed-in')?.value ?? 1;
477
+ if (sign === -1) {
478
+ this.stateCache.set('collected.gridExportStart', this.stateCache.get('meter.reverseActiveEnergy')?.value ?? 0, { type: 'number' });
479
+ this.stateCache.set('collected.gridImportStart', this.stateCache.get('meter.positiveActiveEnergy')?.value ?? 0, { type: 'number' });
480
+ } else {
481
+ this.stateCache.set('collected.gridExportStart', this.stateCache.get('meter.positiveActiveEnergy')?.value ?? 0, { type: 'number' });
482
+ this.stateCache.set('collected.gridImportStart', this.stateCache.get('meter.reverseActiveEnergy')?.value ?? 0, { type: 'number' });
483
+ }
443
484
  // copy consumption Sum to Start for the next day
444
- this.stateCache.set('collected.consumptionStart', this.stateCache.get('collected.consumptionSum')?.value, { type: 'number' });
485
+ this.stateCache.set('collected.consumptionStart', this.stateCache.get('collected.consumptionSum')?.value ?? 0, { type: 'number' });
445
486
  for (const device of this.adapter.devices) {
446
487
  if (device.instance.mitnightProcess) {
447
488
  await device.instance.mitnightProcess();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.sun2000",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "sun2000",
5
5
  "author": {
6
6
  "name": "bolliy",
@@ -27,8 +27,8 @@
27
27
  "node": ">= 20"
28
28
  },
29
29
  "dependencies": {
30
- "@iobroker/adapter-core": "^3.2.3",
31
- "modbus-serial": "^8.0.21",
30
+ "@iobroker/adapter-core": "^3.3.2",
31
+ "modbus-serial": "^8.0.22",
32
32
  "suncalc2": "^1.8.1",
33
33
  "tcp-port-used": "^1.0.2"
34
34
  },
@@ -38,26 +38,14 @@
38
38
  "@alcalzone/release-script-plugin-license": "^3.7.0",
39
39
  "@alcalzone/release-script-plugin-manual-review": "^3.7.0",
40
40
  "@eslint/eslintrc": "^3.3.1",
41
- "@eslint/js": "^9.25.0",
42
- "@iobroker/adapter-dev": "^1.4.0",
43
- "@iobroker/eslint-config": "^2.0.2",
44
- "@iobroker/testing": "^5.0.4",
41
+ "@eslint/js": "^9.36.0",
42
+ "@iobroker/adapter-dev": "^1.5.0",
43
+ "@iobroker/eslint-config": "^2.2.0",
44
+ "@iobroker/testing": "^5.1.1",
45
45
  "@tsconfig/node20": "^20.1.6",
46
- "@types/chai": "^4.3.20",
47
- "@types/chai-as-promised": "^7.1.8",
48
- "@types/mocha": "^10.0.10",
49
- "@types/node": "^22.15.34",
50
- "@types/proxyquire": "^1.3.31",
51
- "@types/sinon": "^17.0.4",
52
- "@types/sinon-chai": "^3.2.12",
53
- "chai": "^4.5.0",
54
- "chai-as-promised": "^7.1.2",
55
- "globals": "^16.2.0",
56
- "mocha": "^11.7.1",
57
- "proxyquire": "^2.1.3",
58
- "sinon": "^19.0.5",
59
- "sinon-chai": "^3.7.0",
60
- "typescript": "~5.8.3"
46
+ "@types/node": "^24.6.2",
47
+ "globals": "^16.4.0",
48
+ "typescript": "~5.9.3"
61
49
  },
62
50
  "main": "main.js",
63
51
  "files": [