@enyo-energy/sunspec-sdk 0.0.32 → 0.0.34
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/dist/cjs/connection-retry-manager.cjs +63 -80
- package/dist/cjs/connection-retry-manager.d.cts +32 -27
- package/dist/cjs/sunspec-devices.cjs +178 -51
- package/dist/cjs/sunspec-devices.d.cts +47 -6
- package/dist/cjs/sunspec-interfaces.cjs +28 -5
- package/dist/cjs/sunspec-interfaces.d.cts +18 -5
- package/dist/cjs/sunspec-modbus-client.cjs +61 -91
- package/dist/cjs/sunspec-modbus-client.d.cts +27 -28
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/connection-retry-manager.d.ts +32 -27
- package/dist/connection-retry-manager.js +63 -80
- package/dist/sunspec-devices.d.ts +47 -6
- package/dist/sunspec-devices.js +178 -51
- package/dist/sunspec-interfaces.d.ts +18 -5
- package/dist/sunspec-interfaces.js +27 -4
- package/dist/sunspec-modbus-client.d.ts +27 -28
- package/dist/sunspec-modbus-client.js +62 -92
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
* - pad: 0x8000 (always returns this value)
|
|
17
17
|
* - string: all registers 0x0000 (NULL)
|
|
18
18
|
*/
|
|
19
|
-
import { SunspecModelId, SunspecBatteryChargeState, SunspecStorageControlMode, SunspecChargeSource, SunspecStorageMode, SunspecBatteryType, SunspecBatteryBankState } from "./sunspec-interfaces.js";
|
|
20
|
-
import { ConnectionRetryManager } from "./connection-retry-manager.js";
|
|
19
|
+
import { SunspecModelId, SunspecBatteryChargeState, SunspecStorageControlMode, SunspecChargeSource, SunspecEnableControl, SunspecStorageMode, SunspecBatteryType, SunspecBatteryBankState, } from "./sunspec-interfaces.js";
|
|
21
20
|
import { EnergyAppModbusConnectionHealth } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusConnectionHealth.js";
|
|
22
21
|
import { EnergyAppModbusFaultTolerantReader } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusFaultTolerantReader.js";
|
|
23
22
|
import { EnergyAppModbusDataTypeConverter } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusDataTypeConverter.js";
|
|
@@ -30,13 +29,11 @@ export class SunspecModbusClient {
|
|
|
30
29
|
faultTolerantReader = null;
|
|
31
30
|
modbusDataTypeConverter;
|
|
32
31
|
connectionParams = null;
|
|
33
|
-
retryManager;
|
|
34
32
|
autoReconnectEnabled = true;
|
|
35
|
-
constructor(energyApp
|
|
33
|
+
constructor(energyApp) {
|
|
36
34
|
this.energyApp = energyApp;
|
|
37
35
|
this.connectionHealth = new EnergyAppModbusConnectionHealth();
|
|
38
36
|
this.modbusDataTypeConverter = new EnergyAppModbusDataTypeConverter();
|
|
39
|
-
this.retryManager = new ConnectionRetryManager(retryConfig);
|
|
40
37
|
}
|
|
41
38
|
/**
|
|
42
39
|
* Connect to Modbus device
|
|
@@ -63,15 +60,12 @@ export class SunspecModbusClient {
|
|
|
63
60
|
}
|
|
64
61
|
this.connected = true;
|
|
65
62
|
this.connectionHealth.recordSuccess();
|
|
66
|
-
this.retryManager.reset();
|
|
67
63
|
console.log(`Connected to Sunspec device at ${host}:${port} unit ${unitId}`);
|
|
68
64
|
}
|
|
69
65
|
/**
|
|
70
66
|
* Disconnect from Modbus device
|
|
71
67
|
*/
|
|
72
68
|
async disconnect() {
|
|
73
|
-
// Cancel any pending retry attempts
|
|
74
|
-
this.retryManager.cancelPendingRetry();
|
|
75
69
|
if (this.modbusClient && this.connected) {
|
|
76
70
|
await this.modbusClient.disconnect();
|
|
77
71
|
this.modbusClient = null;
|
|
@@ -150,61 +144,12 @@ export class SunspecModbusClient {
|
|
|
150
144
|
return false;
|
|
151
145
|
}
|
|
152
146
|
}
|
|
153
|
-
/**
|
|
154
|
-
* Check connection health and trigger automatic reconnection if unhealthy
|
|
155
|
-
* Returns true if connection is healthy or was successfully restored
|
|
156
|
-
*/
|
|
157
|
-
async ensureHealthyConnection() {
|
|
158
|
-
// If already healthy, return immediately
|
|
159
|
-
if (this.isHealthy()) {
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
// If no connection params, we can't reconnect
|
|
163
|
-
if (!this.connectionParams) {
|
|
164
|
-
console.error('Connection unhealthy and no connection parameters stored for reconnection.');
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
// If auto-reconnect is disabled, just report the status
|
|
168
|
-
if (!this.autoReconnectEnabled) {
|
|
169
|
-
console.log('Connection unhealthy but auto-reconnect is disabled.');
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
// If a retry is already in progress, don't start another
|
|
173
|
-
if (this.retryManager.isRetryInProgress()) {
|
|
174
|
-
console.log('Retry already in progress...');
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
console.log('Connection unhealthy, initiating reconnection with exponential backoff...');
|
|
178
|
-
// Attempt reconnection with exponential backoff
|
|
179
|
-
let success = false;
|
|
180
|
-
while (this.retryManager.shouldRetry() && !success) {
|
|
181
|
-
success = await this.retryManager.scheduleRetry(() => this.reconnect());
|
|
182
|
-
if (success) {
|
|
183
|
-
// Re-discover models after successful reconnection
|
|
184
|
-
try {
|
|
185
|
-
await this.discoverModels();
|
|
186
|
-
console.log('Models re-discovered after reconnection.');
|
|
187
|
-
}
|
|
188
|
-
catch (e) {
|
|
189
|
-
console.warn('Failed to re-discover models after reconnection:', e);
|
|
190
|
-
}
|
|
191
|
-
return true;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (!success) {
|
|
195
|
-
console.error('Failed to restore healthy connection after all retry attempts.');
|
|
196
|
-
}
|
|
197
|
-
return success;
|
|
198
|
-
}
|
|
199
147
|
/**
|
|
200
148
|
* Enable or disable automatic reconnection
|
|
201
149
|
*/
|
|
202
150
|
setAutoReconnect(enabled) {
|
|
203
151
|
this.autoReconnectEnabled = enabled;
|
|
204
152
|
console.log(`Auto-reconnect ${enabled ? 'enabled' : 'disabled'}`);
|
|
205
|
-
if (!enabled) {
|
|
206
|
-
this.retryManager.cancelPendingRetry();
|
|
207
|
-
}
|
|
208
153
|
}
|
|
209
154
|
/**
|
|
210
155
|
* Check if auto-reconnect is enabled
|
|
@@ -212,12 +157,6 @@ export class SunspecModbusClient {
|
|
|
212
157
|
isAutoReconnectEnabled() {
|
|
213
158
|
return this.autoReconnectEnabled;
|
|
214
159
|
}
|
|
215
|
-
/**
|
|
216
|
-
* Get the retry manager for advanced configuration
|
|
217
|
-
*/
|
|
218
|
-
getRetryManager() {
|
|
219
|
-
return this.retryManager;
|
|
220
|
-
}
|
|
221
160
|
/**
|
|
222
161
|
* Detect the base address and addressing mode (0-based or 1-based) for SunSpec
|
|
223
162
|
*/
|
|
@@ -497,7 +436,7 @@ export class SunspecModbusClient {
|
|
|
497
436
|
}
|
|
498
437
|
}
|
|
499
438
|
/**
|
|
500
|
-
* Read inverter data from Model 103 (Three Phase)
|
|
439
|
+
* Read inverter data from Model 101 (Single Phase) / Model 103 (Three Phase)
|
|
501
440
|
*/
|
|
502
441
|
async readInverterData() {
|
|
503
442
|
const model = this.findModel(SunspecModelId.Inverter3Phase);
|
|
@@ -714,8 +653,7 @@ export class SunspecModbusClient {
|
|
|
714
653
|
}
|
|
715
654
|
}
|
|
716
655
|
/**
|
|
717
|
-
* Read
|
|
718
|
-
* Returns the scale factors for DC Current, DC Voltage, DC Power, and DC Energy
|
|
656
|
+
* Read scale factors from Model 160 (MPPT)
|
|
719
657
|
*
|
|
720
658
|
* MPPT Model 160 Scale Factor Register Offsets (relative to module start):
|
|
721
659
|
* - DCA_SF (Current Scale Factor): Offset 2
|
|
@@ -835,7 +773,7 @@ export class SunspecModbusClient {
|
|
|
835
773
|
}
|
|
836
774
|
}
|
|
837
775
|
/**
|
|
838
|
-
* Read all
|
|
776
|
+
* Read all MPPT strings from Model 160 (Multiple MPPT)
|
|
839
777
|
*/
|
|
840
778
|
async readAllMPPTData() {
|
|
841
779
|
const mpptData = [];
|
|
@@ -1149,7 +1087,7 @@ export class SunspecModbusClient {
|
|
|
1149
1087
|
}
|
|
1150
1088
|
}
|
|
1151
1089
|
/**
|
|
1152
|
-
* Read battery data from Model 124 (Basic Storage
|
|
1090
|
+
* Read battery data from Model 124 (Basic Storage) with fallback to Model 802 / Model 803
|
|
1153
1091
|
*/
|
|
1154
1092
|
async readBatteryData() {
|
|
1155
1093
|
// Try Model 124 first (Basic Storage Controls)
|
|
@@ -1417,7 +1355,7 @@ export class SunspecModbusClient {
|
|
|
1417
1355
|
return this.writeBatteryControls({ chaGriSet });
|
|
1418
1356
|
}
|
|
1419
1357
|
/**
|
|
1420
|
-
* Read
|
|
1358
|
+
* Read battery control settings from Model 124 (Basic Storage Controls)
|
|
1421
1359
|
*/
|
|
1422
1360
|
async readBatteryControls() {
|
|
1423
1361
|
const model = this.findModel(SunspecModelId.Battery);
|
|
@@ -1462,7 +1400,7 @@ export class SunspecModbusClient {
|
|
|
1462
1400
|
}
|
|
1463
1401
|
}
|
|
1464
1402
|
/**
|
|
1465
|
-
* Read meter data (Model 203
|
|
1403
|
+
* Read meter data from Model 201 (Single Phase) / Model 203 (Three Phase) / Model 204 (Split Phase)
|
|
1466
1404
|
*/
|
|
1467
1405
|
async readMeterData() {
|
|
1468
1406
|
let model = this.findModel(SunspecModelId.Meter3Phase);
|
|
@@ -1624,7 +1562,7 @@ export class SunspecModbusClient {
|
|
|
1624
1562
|
return this.connectionHealth;
|
|
1625
1563
|
}
|
|
1626
1564
|
/**
|
|
1627
|
-
* Read
|
|
1565
|
+
* Read inverter settings from Model 121 (Inverter Settings)
|
|
1628
1566
|
*/
|
|
1629
1567
|
async readInverterSettings() {
|
|
1630
1568
|
const model = this.findModel(SunspecModelId.Settings);
|
|
@@ -1713,7 +1651,7 @@ export class SunspecModbusClient {
|
|
|
1713
1651
|
}
|
|
1714
1652
|
}
|
|
1715
1653
|
/**
|
|
1716
|
-
* Read
|
|
1654
|
+
* Read inverter controls from Model 123 (Immediate Inverter Controls)
|
|
1717
1655
|
*/
|
|
1718
1656
|
async readInverterControls() {
|
|
1719
1657
|
const model = this.findModel(SunspecModelId.Controls);
|
|
@@ -1843,7 +1781,7 @@ export class SunspecModbusClient {
|
|
|
1843
1781
|
}
|
|
1844
1782
|
}
|
|
1845
1783
|
/**
|
|
1846
|
-
* Write
|
|
1784
|
+
* Write inverter controls to Model 123 (Immediate Inverter Controls)
|
|
1847
1785
|
*/
|
|
1848
1786
|
async writeInverterControls(controls) {
|
|
1849
1787
|
const model = this.findModel(SunspecModelId.Controls);
|
|
@@ -1853,32 +1791,35 @@ export class SunspecModbusClient {
|
|
|
1853
1791
|
}
|
|
1854
1792
|
const baseAddr = model.address;
|
|
1855
1793
|
try {
|
|
1856
|
-
// Connection control
|
|
1857
|
-
if (controls.Conn !== undefined
|
|
1858
|
-
|
|
1859
|
-
console.log(`
|
|
1794
|
+
// Connection control (Register 2)
|
|
1795
|
+
if (controls.Conn !== undefined) {
|
|
1796
|
+
await this.writeRegisterValue(baseAddr + 2, controls.Conn, 'uint16');
|
|
1797
|
+
console.log(`Set connection control to ${controls.Conn}`);
|
|
1860
1798
|
}
|
|
1861
|
-
// Power limit control
|
|
1862
|
-
if (controls.WMaxLimPct !== undefined
|
|
1863
|
-
const
|
|
1864
|
-
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
1799
|
+
// Power limit control (Register 3) - needs scale factor
|
|
1800
|
+
if (controls.WMaxLimPct !== undefined) {
|
|
1801
|
+
const scaleFactor = await this.readRegisterValue(baseAddr + 21, 1, 'int16');
|
|
1865
1802
|
const scaledValue = Math.round(controls.WMaxLimPct / Math.pow(10, scaleFactor));
|
|
1866
|
-
|
|
1803
|
+
await this.writeRegisterValue(baseAddr + 3, scaledValue, 'uint16');
|
|
1804
|
+
console.log(`Set power limit to ${controls.WMaxLimPct}% (scaled: ${scaledValue})`);
|
|
1867
1805
|
}
|
|
1868
|
-
|
|
1869
|
-
|
|
1806
|
+
// Throttle enable/disable (Register 7)
|
|
1807
|
+
if (controls.WMaxLim_Ena !== undefined) {
|
|
1808
|
+
await this.writeRegisterValue(baseAddr + 7, controls.WMaxLim_Ena, 'uint16');
|
|
1809
|
+
console.log(`Set throttle enable to ${controls.WMaxLim_Ena}`);
|
|
1870
1810
|
}
|
|
1871
|
-
// Power factor control
|
|
1872
|
-
if (controls.OutPFSet !== undefined
|
|
1873
|
-
const
|
|
1874
|
-
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
1811
|
+
// Power factor control (Register 8) - needs scale factor
|
|
1812
|
+
if (controls.OutPFSet !== undefined) {
|
|
1813
|
+
const scaleFactor = await this.readRegisterValue(baseAddr + 22, 1, 'int16');
|
|
1875
1814
|
const scaledValue = Math.round(controls.OutPFSet / Math.pow(10, scaleFactor));
|
|
1876
|
-
|
|
1815
|
+
await this.writeRegisterValue(baseAddr + 8, scaledValue, 'int16');
|
|
1816
|
+
console.log(`Set power factor to ${controls.OutPFSet} (scaled: ${scaledValue})`);
|
|
1877
1817
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1818
|
+
// Power factor enable/disable (Register 12)
|
|
1819
|
+
if (controls.OutPFSet_Ena !== undefined) {
|
|
1820
|
+
await this.writeRegisterValue(baseAddr + 12, controls.OutPFSet_Ena, 'uint16');
|
|
1821
|
+
console.log(`Set PF enable to ${controls.OutPFSet_Ena}`);
|
|
1880
1822
|
}
|
|
1881
|
-
// Add more control writes as needed
|
|
1882
1823
|
console.log('Inverter controls written successfully');
|
|
1883
1824
|
return true;
|
|
1884
1825
|
}
|
|
@@ -1887,4 +1828,33 @@ export class SunspecModbusClient {
|
|
|
1887
1828
|
return false;
|
|
1888
1829
|
}
|
|
1889
1830
|
}
|
|
1831
|
+
/**
|
|
1832
|
+
* Set the inverter feed-in power limit using Model 123 (Immediate Inverter Controls)
|
|
1833
|
+
*
|
|
1834
|
+
* When limitW is a number, reads WMax from Model 121, computes percentage,
|
|
1835
|
+
* writes WMaxLimPct and enables WMaxLim_Ena.
|
|
1836
|
+
* When limitW is null, disables the limit by setting WMaxLim_Ena = DISABLED.
|
|
1837
|
+
*
|
|
1838
|
+
* @param limitW - Power limit in Watts, or null to remove the limit
|
|
1839
|
+
* @returns true if successful, false otherwise
|
|
1840
|
+
*/
|
|
1841
|
+
async setFeedInLimit(limitW) {
|
|
1842
|
+
if (limitW === null) {
|
|
1843
|
+
// Remove limit: disable WMaxLim_Ena
|
|
1844
|
+
console.log('Removing feed-in limit (disabling WMaxLim_Ena)');
|
|
1845
|
+
return this.writeInverterControls({ WMaxLim_Ena: SunspecEnableControl.DISABLED });
|
|
1846
|
+
}
|
|
1847
|
+
// Read WMax from Model 121 to compute percentage
|
|
1848
|
+
const settings = await this.readInverterSettings();
|
|
1849
|
+
if (!settings || !settings.WMax) {
|
|
1850
|
+
console.error('Cannot set feed-in limit: unable to read WMax from Model 121');
|
|
1851
|
+
return false;
|
|
1852
|
+
}
|
|
1853
|
+
const pct = (limitW / settings.WMax) * 100;
|
|
1854
|
+
console.log(`Setting feed-in limit to ${limitW}W (${pct.toFixed(2)}% of WMax ${settings.WMax}W)`);
|
|
1855
|
+
return this.writeInverterControls({
|
|
1856
|
+
WMaxLimPct: pct,
|
|
1857
|
+
WMaxLim_Ena: SunspecEnableControl.ENABLED
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1890
1860
|
}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enyo-energy/sunspec-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
4
4
|
"description": "enyo Energy Sunspec SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"typescript": "^5.8.3"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@enyo-energy/energy-app-sdk": "^0.0.
|
|
40
|
+
"@enyo-energy/energy-app-sdk": "^0.0.68"
|
|
41
41
|
},
|
|
42
42
|
"volta": {
|
|
43
43
|
"node": "22.17.0"
|