iobroker.sun2000 2.0.0 → 2.1.1
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 +11 -0
- package/io-package.json +28 -28
- package/lib/controls/config_map.js +11 -0
- package/lib/controls/service_queue.js +51 -0
- package/lib/drivers/driver_base.js +0 -1
- package/lib/drivers/driver_emma.js +39 -11
- package/lib/drivers/driver_inverter.js +40 -0
- package/lib/register.js +54 -19
- package/main.js +0 -10
- package/package.json +8 -20
package/README.md
CHANGED
|
@@ -65,6 +65,17 @@ 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.1.1 (2025-09-24)
|
|
69
|
+
* dependency and configuration updates
|
|
70
|
+
* fix: adjust event value limits based on usableSurplus parameters
|
|
71
|
+
* fix: swap register values for power consumption in Emma driver #190
|
|
72
|
+
* emma: improve power calculation with exponential moving average in EmmaCharger
|
|
73
|
+
* update surplus power state definitions and deprecate old identifiers
|
|
74
|
+
|
|
75
|
+
### 2.1.0 (2025-07-06)
|
|
76
|
+
* emma: system time is being determined [#179](https://github.com/bolliy/ioBroker.sun2000/issues/179)
|
|
77
|
+
* control: add grid power scheduling functionality to inverter driver [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)
|
|
78
|
+
|
|
68
79
|
### 2.0.0 (2025-06-29)
|
|
69
80
|
* add support for chargers via Emma and save the data in the charger path #171
|
|
70
81
|
* Establish data consistency between meter.activePower and the power of the meter phases #174
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "sun2000",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.1",
|
|
5
5
|
"news": {
|
|
6
|
+
"2.1.1": {
|
|
7
|
+
"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",
|
|
8
|
+
"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",
|
|
9
|
+
"ru": "обновления зависимостей и конфигурации\nисправление: корректировка предельных значений событий на основе пригодных для использования Дополнительные параметры\nисправление: значения регистра свопов для энергопотребления в драйвере Emma #190\nemma: улучшите расчет мощности с экспоненциальной скользящей средней в EmmaCharger\nобновить определения состояния избыточной мощности и обесценить старые идентификаторы",
|
|
10
|
+
"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",
|
|
11
|
+
"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",
|
|
12
|
+
"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",
|
|
13
|
+
"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",
|
|
14
|
+
"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",
|
|
15
|
+
"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",
|
|
16
|
+
"uk": "оновлення залежності та конфігурації\nвиправити: регулювати ліміти значення події на основі Параметри Surplus\nвиправити: значення swap для споживання енергії в машині Емма #190\nемма: поліпшення розрахунку потужності з постійним рухомим середнім в ЕммаКхаргер\nоновлення надлишок визначення стану живлення та депресувати старі ідентифікатори",
|
|
17
|
+
"zh-cn": "依赖和配置更新\n固定:根据可用值调整事件值限制 盈余参数\n修补: Emma 驱动器中用电量的交换记录值 # 190\nemma: 改进电量计算, 以 Emmacourer 中的指数移动平均值\n更新剩余电源状态定义并折旧旧标识符"
|
|
18
|
+
},
|
|
19
|
+
"2.1.0": {
|
|
20
|
+
"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)",
|
|
21
|
+
"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)",
|
|
22
|
+
"ru": "emma: системное время определяется [#179] (https://github.com/bolliy/ioBroker.sun2000/issues/179)\nуправление: добавьте функцию планирования мощности сети к драйверу инвертора [#176] (https://github.com/bolliy/ioBroker.sun2000/issues/176)",
|
|
23
|
+
"pt": "emma: tempo do sistema está sendo determinado [#179](https://github.com/bolliy/ioBroker.sun2000/issues/179)\ncontrol: add grid power screaming functionality to inverter driver [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)",
|
|
24
|
+
"nl": "emma: de systeemtijd wordt bepaald [#179](https://github.com/bolliy/ioBroker.sun2000/issues/179)\ncontrole: voeg net stroom planning functionaliteit aan omvormer driver [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)",
|
|
25
|
+
"fr": "emma: le temps du système est déterminé [#179](https://github.com/bolliy/ioBroker.sun2000/issues/179)\ncontrôle: ajouter la fonctionnalité de programmation du réseau au pilote d'onduleur [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)",
|
|
26
|
+
"it": "emma: il tempo di sistema viene determinato [#179](https://github.com/bolliy/ioBroker.sun2000/issues/179)\ncontrollo: aggiungere funzionalità di programmazione della griglia al driver inverter [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)",
|
|
27
|
+
"es": "emma: El tiempo del sistema se determina [#179](https://github.com/bolliy/ioBroker.sun2000/issues/179)\ncontrol: añadir funcionalidad de programación de energía de red al controlador inverter [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)",
|
|
28
|
+
"pl": "emma: ustala się czas systemowy [# 179] (https: / / github.com / bolliy / ioBroker.sun2000 / issues / 179)\ncontrol: add grid Power Scheduling Functional to inverter driver [# 176] (https: / / github.com / bolliy / ioBroker.sun2000 / issues / 176)",
|
|
29
|
+
"uk": "emma: час системи визначається [#179](https://github.com/bolliy/ioBroker.sun2000/products/179)\ncontrol: add power scheduling features to inverter драйвер [#176](https://github.com/bolliy/ioBroker.sun2000/issues/176)",
|
|
30
|
+
"zh-cn": "emma:系统时间正在确定 [# 179] (https://github.com/bolliy/ioBroker.sun2000/issues/179)\n控制:向反向驱动器添加电网电源调度功能[#176](https://github.com/bolliy/ioBroker.sun2000/issues/1976)"
|
|
31
|
+
},
|
|
6
32
|
"2.0.0": {
|
|
7
33
|
"en": "add support for chargers via Emma and save the data in the charger path #171\nEstablish data consistency between meter.activePower and the power of the meter phases #174\nallows `control.battery.chargeFromGridFunction` when using the Emma\nbreaking change: \nthe value `sun2000.x.inverter.x.battery.runningStatus` is saved as a number. The translated value (such as STANDBY, RUNNING etc.) is stored in the path sun2000.x.inverter.x.battery.derived",
|
|
8
34
|
"de": "unterstützung für Ladegeräte über Emma hinzufügen und die Daten im Ladepfad #171 speichern\nDatenkonsistenz zwischen meter.activePower und der Leistung der Zählerphasen #174 einstellen\nerlaubt `control.battery.chargeFromGridFunction` bei Verwendung der Emma\nänderung:\nder Wert `sun2000.x.inverter.x.battery.runningStatus` wird als Nummer gespeichert. Der übersetzte Wert (wie STANDBY, RUNNING etc.) wird im Pfad sun2000.x.inverter.x.battery.derived gespeichert",
|
|
@@ -67,32 +93,6 @@
|
|
|
67
93
|
"pl": "rozmieszczenie 1.2",
|
|
68
94
|
"uk": "розгортання 1.2",
|
|
69
95
|
"zh-cn": "部署"
|
|
70
|
-
},
|
|
71
|
-
"1.2.2-alpha.0": {
|
|
72
|
-
"en": "test release",
|
|
73
|
-
"de": "prüfbericht",
|
|
74
|
-
"ru": "испытательный выпуск",
|
|
75
|
-
"pt": "liberação de teste",
|
|
76
|
-
"nl": "loslaten van de test",
|
|
77
|
-
"fr": "libération d'essai",
|
|
78
|
-
"it": "rilascio del test",
|
|
79
|
-
"es": "prueba de liberación",
|
|
80
|
-
"pl": "uwalnianie testowe",
|
|
81
|
-
"uk": "тестовий реліз",
|
|
82
|
-
"zh-cn": "测试发布"
|
|
83
|
-
},
|
|
84
|
-
"1.1.0": {
|
|
85
|
-
"en": "startupTime/shutdownTime are read from the inverter as local time and not as UTC - fixed times are saved in path `derived`\nnew state [usableSurplusPower](https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9Cberschuss-(surplus))\ncontrol: checking and rounding integer numbers\nbetter solution for math rounding",
|
|
86
|
-
"de": "startzeit/Shutdown Zeit wird vom Wechselrichter als Ortszeit gelesen und nicht als UTC - Festzeiten werden im Pfad gespeichert `derived `\nneuer Staat [usableSurplusPower](https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9Cberschuss-(surplus))\nsteuerung: überprüfung und rundung ganzzahliger zahlen\nbessere lösung für matherundung",
|
|
87
|
-
"ru": "startupTime/Sutdown Время считывается с инвертора как локальное время, а не как UTC - фиксированное время сохраняется в пути пункт\nновое состояние [usableSurplusPower] (https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9Cberschuss-(surplus))\nконтроль: проверка и округление целых чисел\nлучшее решение для математического округления",
|
|
88
|
-
"pt": "startupTime/shutdown O tempo é lido do inversor como hora local e não como UTC - os tempos fixos são salvos no caminho `derivod \"\nnovo estado [usableSurplusPower](https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9Cberschuss-(surplus)))\ncontrole: números inteiros de verificação e arredondamento\nmelhor solução para arredondamento de matemática",
|
|
89
|
-
"nl": "opstartenTijd/afsluiten De tijd wordt van de inverter gelezen als lokale tijd en niet als UTC - vaste tijden worden opgeslagen in het pad uit Wat\nnieuwe staat [bruikbaarSurplusPower](https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9Cberschuss-(surplus))\ncontrole: controle en afronding van gehele getallen\nbetere oplossing voor wiskunde afronden",
|
|
90
|
-
"fr": "startupTime/shutdown Le temps est lu à partir de l'onduleur en tant qu'heure locale et non pas en tant que UTC - les temps fixes sont sauvegardés dans le chemin « dérivé \"\nnouvel état [utilisableSurplusPower](https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9Cberschuss-(surplus))\ncontrôle: contrôle et arrondi des nombres entiers\nmeilleure solution pour arrondir les maths",
|
|
91
|
-
"it": "startupTime/shutdown Il tempo viene letto dall'inverter come ora locale e non come UTC - i tempi fissi vengono salvati nel percorso `derivati #\nnuovo stato [usableSurplusPower](https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9Cberschuss-(plus)\ncontrollo: controllo e arrotondamento numeri interi\nmigliore soluzione per arrotondamento di matematica",
|
|
92
|
-
"es": "startupTime/shutdown El tiempo se lee desde el inversor como hora local y no como UTC - los tiempos fijos se salvan en el camino `derived `\nnuevo estado [utilizableSurplusPower](https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9Cberschuss-(superávit))\ncontrol: números enteros de comprobación y redondeo\nmejor solución para redondeo de matemáticas",
|
|
93
|
-
"pl": "startupTime / shutdown Czas jest odczytywany z inwertera jako czasu lokalnego, a nie jako UTC - stałe czasy są zapisywane w ścieżce 'pochodnej'\nnową ustawę [usableSurplusPower] (https: / / github.com / bolliy / ioBroker.sun2000 / wiki /% C3% 9Cberschuss- (nadwyżka))\nkontrola: sprawdzanie i zaokrąglanie liczb całkowitych\nlepsze rozwiązanie do zaokrąglania matematycznego",
|
|
94
|
-
"uk": "javaScript licenses API Веб-сайт Час читає з інвертора як локальний час, так і не як UTC - виправлені часи зберігаються в шляху `derived й\nновий стан [usableSurplusPower](https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9Cberschuss-(surplus)))\nконтроль: перевірка та округлення цілих чисел\nкраще рішення для закруглення математики",
|
|
95
|
-
"zh-cn": "启动时间/下调 时间从反转器读作本地时间,而不是世界协调时 - 固定时间保存在路径中`衍生' `\n新状态[可使用SurpusPower](https://github.com/bolliy/ioBroker.sun2000/wiki/%C3%9 Cberschuss-(盈余))\n控件: 检查和四舍五入整数\n更好的数学四舍五入解决方案"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
],
|
|
222
222
|
"globalDependencies": [
|
|
223
223
|
{
|
|
224
|
-
"admin": ">=7.
|
|
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
|
},
|
|
@@ -473,6 +473,57 @@ class ServiceQueueMap {
|
|
|
473
473
|
return ret;
|
|
474
474
|
},
|
|
475
475
|
},
|
|
476
|
+
//power grid scheduling
|
|
477
|
+
{
|
|
478
|
+
state: {
|
|
479
|
+
id: 'grid.scheduling.activePowerPercentageDerating',
|
|
480
|
+
name: '[power grid scheduling] Active Power percentage derating',
|
|
481
|
+
type: 'number',
|
|
482
|
+
unit: '%',
|
|
483
|
+
role: 'level',
|
|
484
|
+
desc: 'reg:40125, len:1',
|
|
485
|
+
},
|
|
486
|
+
type: deviceType.gridPowerControl,
|
|
487
|
+
fn: async event => {
|
|
488
|
+
if (event.value > 100) {
|
|
489
|
+
event.value = 100;
|
|
490
|
+
}
|
|
491
|
+
if (event.value < 0) {
|
|
492
|
+
event.value = 0;
|
|
493
|
+
}
|
|
494
|
+
const ret = await this._writeRegisters(40125, dataType.numToArray(event.value * 10, dataType.uint16));
|
|
495
|
+
if (ret) {
|
|
496
|
+
this.inverterInfo.instance.stateCache.set(`${this.inverterInfo.path}.${event.id}`, event.value, { type: 'number' });
|
|
497
|
+
}
|
|
498
|
+
return ret;
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
state: {
|
|
503
|
+
id: 'grid.scheduling.FixedActivePowerDerated',
|
|
504
|
+
name: '[power grid scheduling] Fixed active power derated',
|
|
505
|
+
type: 'number',
|
|
506
|
+
unit: 'W',
|
|
507
|
+
role: 'level.power',
|
|
508
|
+
desc: 'reg:40126, len:2',
|
|
509
|
+
},
|
|
510
|
+
type: deviceType.gridPowerControl,
|
|
511
|
+
fn: async event => {
|
|
512
|
+
const max = this.inverterInfo.instance.stateCache.get(`${this.inverterInfo.path}.info.ratedPower`)?.value ?? 0;
|
|
513
|
+
//+10%
|
|
514
|
+
if (event.value > max * 1100) {
|
|
515
|
+
event.value = max * 1100;
|
|
516
|
+
}
|
|
517
|
+
if (event.value < 0) {
|
|
518
|
+
event.value = 0;
|
|
519
|
+
}
|
|
520
|
+
const ret = await this._writeRegisters(40126, dataType.numToArray(event.value, dataType.uint32));
|
|
521
|
+
if (ret) {
|
|
522
|
+
this.inverterInfo.instance.stateCache.set(`${this.inverterInfo.path}.${event.id}`, event.value, { type: 'number' });
|
|
523
|
+
}
|
|
524
|
+
return ret;
|
|
525
|
+
},
|
|
526
|
+
},
|
|
476
527
|
];
|
|
477
528
|
}
|
|
478
529
|
|
|
@@ -186,7 +186,6 @@ class DriverBase {
|
|
|
186
186
|
this.log.debug(`Device List: ${JSON.stringify(resp)}`);
|
|
187
187
|
Object.assign(allInfo, resp);
|
|
188
188
|
} catch (e) {
|
|
189
|
-
//this.log.error(`readDeviceList: No answer for OID=0x${objectId.toString(16).toUpperCase()}: ${e.message}`);
|
|
190
189
|
throw new Error(`readDeviceList: No answer for OID=0x${objectId.toString(16).toUpperCase()}: ${e.message}`);
|
|
191
190
|
}
|
|
192
191
|
const numDevices = parseInt(JSON.stringify(allInfo['135'] || '').replace(/[^0-9]/g, ''));
|
|
@@ -650,7 +650,7 @@ class Emma extends DriverBase {
|
|
|
650
650
|
role: 'value.power',
|
|
651
651
|
desc: 'reg:31651, len:2',
|
|
652
652
|
},
|
|
653
|
-
register: { reg: 31651, type: dataType.int32, gain: 1000 },
|
|
653
|
+
register: { reg: 31651, type: dataType.int32, gain: -1000 },
|
|
654
654
|
},
|
|
655
655
|
{
|
|
656
656
|
state: {
|
|
@@ -661,7 +661,7 @@ class Emma extends DriverBase {
|
|
|
661
661
|
role: 'value.power',
|
|
662
662
|
desc: 'reg:31653, len:2',
|
|
663
663
|
},
|
|
664
|
-
register: { reg: 31653, type: dataType.int32, gain: 1000 },
|
|
664
|
+
register: { reg: 31653, type: dataType.int32, gain: -1000 },
|
|
665
665
|
},
|
|
666
666
|
{
|
|
667
667
|
state: {
|
|
@@ -672,7 +672,7 @@ class Emma extends DriverBase {
|
|
|
672
672
|
role: 'value.power',
|
|
673
673
|
desc: 'reg:31655, len:2',
|
|
674
674
|
},
|
|
675
|
-
register: { reg: 31655, type: dataType.int32, gain: 1000 },
|
|
675
|
+
register: { reg: 31655, type: dataType.int32, gain: -1000 },
|
|
676
676
|
},
|
|
677
677
|
{
|
|
678
678
|
state: {
|
|
@@ -683,7 +683,7 @@ class Emma extends DriverBase {
|
|
|
683
683
|
role: 'value.power.consumption',
|
|
684
684
|
desc: 'reg:31679, len:4',
|
|
685
685
|
},
|
|
686
|
-
register: { reg:
|
|
686
|
+
register: { reg: 31687, type: dataType.int64, gain: 100 },
|
|
687
687
|
},
|
|
688
688
|
{
|
|
689
689
|
state: {
|
|
@@ -694,10 +694,32 @@ class Emma extends DriverBase {
|
|
|
694
694
|
role: 'value.power.consumption',
|
|
695
695
|
desc: 'reg:31687, len:4',
|
|
696
696
|
},
|
|
697
|
-
register: { reg:
|
|
697
|
+
register: { reg: 31679, type: dataType.int64, gain: 100 },
|
|
698
698
|
},
|
|
699
699
|
],
|
|
700
700
|
},
|
|
701
|
+
{
|
|
702
|
+
address: 40470,
|
|
703
|
+
length: 2,
|
|
704
|
+
info: 'Emma time management',
|
|
705
|
+
refresh: dataRefreshRate.low,
|
|
706
|
+
states: [
|
|
707
|
+
{
|
|
708
|
+
state: { id: 'emma.systemTime', name: 'System time', type: 'number', role: 'value', desc: 'reg:40470, len:2' },
|
|
709
|
+
register: { reg: 40470, type: dataType.uint32 },
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
state: { id: 'emma.derived.systemTime', name: 'System time', type: 'number', unit: '', role: 'value.time', desc: 'fixed system time' },
|
|
713
|
+
},
|
|
714
|
+
],
|
|
715
|
+
postHook: path => {
|
|
716
|
+
function fixTime(value) {
|
|
717
|
+
return value * 1000;
|
|
718
|
+
}
|
|
719
|
+
const systemTime = this.stateCache.get(`${path}emma.systemTime`)?.value;
|
|
720
|
+
this.stateCache.set(`${path}emma.derived.systemTime`, fixTime(systemTime), { type: 'number' });
|
|
721
|
+
},
|
|
722
|
+
},
|
|
701
723
|
];
|
|
702
724
|
|
|
703
725
|
this.registerFields.push.apply(this.registerFields, newFields);
|
|
@@ -830,24 +852,30 @@ class EmmaCharger extends DriverBase {
|
|
|
830
852
|
},
|
|
831
853
|
],
|
|
832
854
|
postHook: path => {
|
|
833
|
-
const now = new Date();
|
|
834
855
|
//this.log.debug('#### POSTHOOK CHARGER ####');
|
|
856
|
+
const now = new Date();
|
|
835
857
|
const totalCharged = this.stateCache.get(`${path}totalEnergyCharged`)?.value ?? 0;
|
|
836
|
-
|
|
837
|
-
|
|
858
|
+
const smoothingFactor = 0.5; // between 0 (slow) and 1 (fast), tune as needed
|
|
859
|
+
|
|
838
860
|
if (this._lastTime === undefined) {
|
|
839
861
|
this._lastTime = now;
|
|
840
862
|
this._lastEnergy = totalCharged;
|
|
841
863
|
this.stateCache.set(`${path}derived.outputPower`, 0, { type: 'number' });
|
|
864
|
+
//this.stateCache.set(`${path}derived.smoothedOutputPower`, 0, { type: 'number' });
|
|
865
|
+
this._smoothedPower = 0;
|
|
842
866
|
return;
|
|
843
867
|
}
|
|
844
868
|
const timeDiff = now.getTime() - this._lastTime.getTime();
|
|
845
|
-
if (timeDiff < 1000 *
|
|
869
|
+
if (timeDiff < 1000 * 30) return; // ignore changes less than 30 seconds
|
|
846
870
|
const energyDiff = totalCharged - this._lastEnergy;
|
|
847
871
|
const power = (energyDiff / timeDiff) * 1000 * 3600;
|
|
848
872
|
|
|
849
|
-
|
|
850
|
-
|
|
873
|
+
// Exponential Moving Average
|
|
874
|
+
this._smoothedPower = (this._smoothedPower ?? 0) * (1 - smoothingFactor) + power * smoothingFactor;
|
|
875
|
+
|
|
876
|
+
this.stateCache.set(`${path}derived.outputPower`, this._smoothedPower, { type: 'number' });
|
|
877
|
+
//this.stateCache.set(`${path}derived.smoothedOutputPower`, this._smoothedPower, { type: 'number' });
|
|
878
|
+
|
|
851
879
|
this._lastTime = now;
|
|
852
880
|
this._lastEnergy = totalCharged;
|
|
853
881
|
},
|
|
@@ -1186,6 +1186,15 @@ class InverterSun2000 extends DriverBase {
|
|
|
1186
1186
|
},
|
|
1187
1187
|
],
|
|
1188
1188
|
postHook: path => {
|
|
1189
|
+
/**
|
|
1190
|
+
* Adjusts a given time value by converting it from seconds to milliseconds
|
|
1191
|
+
* and applying the local timezone offset.
|
|
1192
|
+
*
|
|
1193
|
+
* @param {number} value - The time value in seconds to be adjusted. If the
|
|
1194
|
+
* value is greater than zero, it is converted to milliseconds and adjusted
|
|
1195
|
+
* for the local timezone.
|
|
1196
|
+
* @returns {number} - The adjusted time value in milliseconds.
|
|
1197
|
+
*/
|
|
1189
1198
|
function fixTime(value) {
|
|
1190
1199
|
if (value > 0) {
|
|
1191
1200
|
value = value * 1000;
|
|
@@ -1200,6 +1209,37 @@ class InverterSun2000 extends DriverBase {
|
|
|
1200
1209
|
this.stateCache.set(`${path}derived.startupTime`, fixTime(startup), { type: 'number' });
|
|
1201
1210
|
},
|
|
1202
1211
|
},
|
|
1212
|
+
{
|
|
1213
|
+
address: 40125,
|
|
1214
|
+
length: 3,
|
|
1215
|
+
info: 'grid power scheduling',
|
|
1216
|
+
refresh: dataRefreshRate.low,
|
|
1217
|
+
type: deviceType.inverter,
|
|
1218
|
+
states: [
|
|
1219
|
+
{
|
|
1220
|
+
state: {
|
|
1221
|
+
id: 'grid.scheduling.activePowerPercentageDerating',
|
|
1222
|
+
name: '[power grid scheduling] Fixed active power derated',
|
|
1223
|
+
type: 'number',
|
|
1224
|
+
unit: '%',
|
|
1225
|
+
role: 'value',
|
|
1226
|
+
desc: 'reg:40125, len:1',
|
|
1227
|
+
},
|
|
1228
|
+
register: { reg: 40125, type: dataType.uint16, gain: 10 },
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
state: {
|
|
1232
|
+
id: 'grid.scheduling.FixedActivePowerDerated',
|
|
1233
|
+
name: '[power grid scheduling] Fixed active power derated',
|
|
1234
|
+
type: 'number',
|
|
1235
|
+
unit: 'W',
|
|
1236
|
+
role: 'value.power',
|
|
1237
|
+
desc: 'reg:40126, len:2',
|
|
1238
|
+
},
|
|
1239
|
+
register: { reg: 40126, type: dataType.uint32 },
|
|
1240
|
+
},
|
|
1241
|
+
],
|
|
1242
|
+
},
|
|
1203
1243
|
{
|
|
1204
1244
|
address: 37100,
|
|
1205
1245
|
length: 38,
|
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
|
-
{
|
|
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
83
|
surplusPower += meterPower;
|
|
60
84
|
|
|
61
|
-
|
|
62
|
-
|
|
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,22 @@ class Registers {
|
|
|
68
92
|
if (chargeDischarge < 0) {
|
|
69
93
|
surplusPower += chargeDischarge;
|
|
70
94
|
}
|
|
95
|
+
if (soc >= minSoc) {
|
|
96
|
+
if (chargeDischarge > 0) surplusPower += chargeDischarge;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
usableSurplusPower = surplusPower;
|
|
71
100
|
|
|
72
101
|
if (bufferSoc > 0) {
|
|
73
102
|
let threshold = hysterese / 2;
|
|
74
|
-
|
|
75
|
-
minSoc = bufferSoc - threshold;
|
|
76
|
-
}
|
|
103
|
+
|
|
77
104
|
if (this.bufferOn) {
|
|
78
105
|
threshold = -threshold;
|
|
79
106
|
}
|
|
80
|
-
if (soc >= bufferSoc + threshold) {
|
|
107
|
+
if (soc > minSoc && soc >= bufferSoc + threshold) {
|
|
81
108
|
this.bufferOn = true;
|
|
82
109
|
const bufferPower = this.adapter.control.get('usableSurplus.bufferPower')?.value ?? 0;
|
|
83
|
-
|
|
110
|
+
usableSurplusPower += bufferPower / 1000;
|
|
84
111
|
} else {
|
|
85
112
|
this.bufferOn = false;
|
|
86
113
|
}
|
|
@@ -88,25 +115,27 @@ class Registers {
|
|
|
88
115
|
this.bufferOn = false;
|
|
89
116
|
}
|
|
90
117
|
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
if (!this.bufferOn) {
|
|
94
|
-
surplusPower -= residualPower / 1000;
|
|
95
|
-
}
|
|
118
|
+
if (!this.bufferOn && surplusPower > residualPower / 1000) {
|
|
119
|
+
surplusPower -= residualPower / 1000;
|
|
96
120
|
}
|
|
97
121
|
|
|
98
122
|
if (surplusPower > ratedPower) {
|
|
99
123
|
surplusPower = ratedPower;
|
|
100
124
|
}
|
|
125
|
+
|
|
126
|
+
if (usableSurplusPower > ratedPower) {
|
|
127
|
+
usableSurplusPower = ratedPower;
|
|
128
|
+
}
|
|
101
129
|
if (!allowNegativeValue) {
|
|
102
130
|
if (surplusPower < 0.1) surplusPower = 0;
|
|
131
|
+
if (usableSurplusPower < 0.1) usableSurplusPower = 0;
|
|
103
132
|
}
|
|
104
133
|
|
|
105
134
|
this.adapter.log.debug(
|
|
106
135
|
`### Caculate usableSurplus power ${surplusPower} bufferOn ${this.bufferOn} soc ${soc} minSoc ${minSoc} bufferSoc ${bufferSoc} threshold ${hysterese / 2}`,
|
|
107
136
|
);
|
|
108
137
|
}
|
|
109
|
-
return surplusPower;
|
|
138
|
+
return [surplusPower, usableSurplusPower];
|
|
110
139
|
}
|
|
111
140
|
|
|
112
141
|
for (const inverter of inverters) {
|
|
@@ -131,18 +160,24 @@ class Registers {
|
|
|
131
160
|
houseConsum = 0;
|
|
132
161
|
}
|
|
133
162
|
//Überschuss (Differenz)
|
|
134
|
-
const
|
|
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 });
|
|
139
168
|
this.stateCache.set('collected.houseConsumption', houseConsum, { type: 'number' });
|
|
140
169
|
this.stateCache.set('collected.chargeDischargePower', chargeDischarge, { type: 'number' });
|
|
141
|
-
this.stateCache.set('collected.usableSurplusPower',
|
|
170
|
+
this.stateCache.set('collected.usableSurplusPower', surplusArray[1], {
|
|
171
|
+
type: 'number',
|
|
172
|
+
});
|
|
173
|
+
this.stateCache.set('collected.surplus.power', surplusArray[0], {
|
|
174
|
+
type: 'number',
|
|
175
|
+
});
|
|
176
|
+
this.stateCache.set('collected.surplus.usablePower', surplusArray[1], {
|
|
142
177
|
type: 'number',
|
|
143
178
|
});
|
|
179
|
+
|
|
144
180
|
this.stateCache.set('collected.externalPower', extPower, { type: 'number' });
|
|
145
|
-
//this.stateCache.set('collected.ratedPower', ratedPower, { type: 'number' });
|
|
146
181
|
},
|
|
147
182
|
},
|
|
148
183
|
{
|
|
@@ -175,7 +210,7 @@ class Registers {
|
|
|
175
210
|
{ id: 'collected.accumulatedEnergyYield', name: 'Accumulated energy yield', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
|
|
176
211
|
{ id: 'collected.consumptionSum', name: 'Consumption sum', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
|
|
177
212
|
{ id: 'collected.gridExportStart', name: 'Grid export start today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
|
|
178
|
-
{ id: 'collected.gridImportStart', name: 'Grid
|
|
213
|
+
{ id: 'collected.gridImportStart', name: 'Grid import start today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
|
|
179
214
|
{ id: 'collected.consumptionStart', name: 'Consumption start today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
|
|
180
215
|
{ id: 'collected.gridExportToday', name: 'Grid export today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
|
|
181
216
|
{ id: 'collected.gridImportToday', name: 'Grid import today', type: 'number', unit: 'kWh', role: 'value.power.consumption' },
|
package/main.js
CHANGED
|
@@ -475,16 +475,6 @@ class Sun2000 extends utils.Adapter {
|
|
|
475
475
|
meter: true,
|
|
476
476
|
driverClass: driverClasses.emma,
|
|
477
477
|
});
|
|
478
|
-
/*
|
|
479
|
-
for (const [i, id] of this.settings.chargerIds.entries()) {
|
|
480
|
-
this.devices.push({
|
|
481
|
-
index: i,
|
|
482
|
-
duration: 0,
|
|
483
|
-
modbusId: id,
|
|
484
|
-
driverClass: driverClasses.emmaCharger,
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
*/
|
|
488
478
|
}
|
|
489
479
|
|
|
490
480
|
await this.adjustInverval();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.sun2000",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "sun2000",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "bolliy",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"node": ">= 20"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@iobroker/adapter-core": "^3.2
|
|
30
|
+
"@iobroker/adapter-core": "^3.3.2",
|
|
31
31
|
"modbus-serial": "^8.0.21",
|
|
32
32
|
"suncalc2": "^1.8.1",
|
|
33
33
|
"tcp-port-used": "^1.0.2"
|
|
@@ -38,25 +38,13 @@
|
|
|
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.
|
|
42
|
-
"@iobroker/adapter-dev": "^1.
|
|
43
|
-
"@iobroker/eslint-config": "^2.0
|
|
44
|
-
"@iobroker/testing": "^5.
|
|
41
|
+
"@eslint/js": "^9.34.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/
|
|
47
|
-
"
|
|
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",
|
|
46
|
+
"@types/node": "^24.5.2",
|
|
47
|
+
"globals": "^16.4.0",
|
|
60
48
|
"typescript": "~5.8.3"
|
|
61
49
|
},
|
|
62
50
|
"main": "main.js",
|