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 +12 -0
- package/io-package.json +28 -28
- package/lib/controls/config_map.js +11 -0
- package/lib/drivers/driver_emma.js +55 -12
- package/lib/drivers/driver_inverter.js +25 -24
- package/lib/drivers/driver_slogger.js +29 -6
- package/lib/register.js +88 -47
- package/package.json +10 -22
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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 (
|
|
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
|
-
|
|
859
|
-
|
|
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 *
|
|
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
|
-
|
|
872
|
-
|
|
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
|
-
{
|
|
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 +=
|
|
83
|
+
surplusPower += feedinPower;
|
|
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,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
|
-
|
|
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
|
-
|
|
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
|
|
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 -
|
|
158
|
+
let houseConsum = actPower - feedinPower + extPower;
|
|
130
159
|
if (houseConsum < 0) {
|
|
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 });
|
|
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',
|
|
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
|
|
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
|
|
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
|
-
'
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
442
|
-
|
|
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.
|
|
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
|
|
31
|
-
"modbus-serial": "^8.0.
|
|
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.
|
|
42
|
-
"@iobroker/adapter-dev": "^1.
|
|
43
|
-
"@iobroker/eslint-config": "^2.0
|
|
44
|
-
"@iobroker/testing": "^5.
|
|
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/
|
|
47
|
-
"
|
|
48
|
-
"
|
|
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": [
|