@enyo-energy/sunspec-sdk 0.0.61 → 0.0.62
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 +3 -2
- package/dist/cjs/connection-retry-manager.d.cts +3 -2
- package/dist/cjs/sunspec-devices.cjs +33 -0
- package/dist/cjs/sunspec-devices.d.cts +10 -0
- package/dist/cjs/sunspec-interfaces.cjs +2 -1
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/connection-retry-manager.d.ts +3 -2
- package/dist/connection-retry-manager.js +3 -2
- package/dist/sunspec-devices.d.ts +10 -0
- package/dist/sunspec-devices.js +33 -0
- package/dist/sunspec-interfaces.js +2 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
* Called on each readData() cycle; determines whether enough time has elapsed
|
|
7
7
|
* to attempt a reconnection based on the current retry phase.
|
|
8
8
|
*
|
|
9
|
-
* Default schedule:
|
|
9
|
+
* Default schedule (infinite retry, max 15m backoff):
|
|
10
10
|
* - Phase 1: every 10s for 1 minute
|
|
11
11
|
* - Phase 2: every 30s for 2 minutes
|
|
12
12
|
* - Phase 3: every 1m for 5 minutes
|
|
13
|
-
* - Phase 4: every 5m
|
|
13
|
+
* - Phase 4: every 5m for 10 minutes
|
|
14
|
+
* - Phase 5: every 15m forever
|
|
14
15
|
*/
|
|
15
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
17
|
exports.ConnectionRetryManager = void 0;
|
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* Called on each readData() cycle; determines whether enough time has elapsed
|
|
6
6
|
* to attempt a reconnection based on the current retry phase.
|
|
7
7
|
*
|
|
8
|
-
* Default schedule:
|
|
8
|
+
* Default schedule (infinite retry, max 15m backoff):
|
|
9
9
|
* - Phase 1: every 10s for 1 minute
|
|
10
10
|
* - Phase 2: every 30s for 2 minutes
|
|
11
11
|
* - Phase 3: every 1m for 5 minutes
|
|
12
|
-
* - Phase 4: every 5m
|
|
12
|
+
* - Phase 4: every 5m for 10 minutes
|
|
13
|
+
* - Phase 5: every 15m forever
|
|
13
14
|
*/
|
|
14
15
|
import { IRetryConfig, IRetryPhase } from './sunspec-interfaces.cjs';
|
|
15
16
|
export declare class ConnectionRetryManager {
|
|
@@ -187,6 +187,24 @@ class BaseSunspecDevice {
|
|
|
187
187
|
async onConnectionFailure(_consecutiveFailures) {
|
|
188
188
|
// Default: no-op. SunspecInverter overrides to publish a faulted status.
|
|
189
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Detect a silent read failure: the SDK's per-model readers swallow modbus errors and
|
|
192
|
+
* return null/[] rather than throwing, so a readData() cycle can complete "successfully"
|
|
193
|
+
* with no usable data while the underlying connection is broken. Once
|
|
194
|
+
* `connectionHealth.isHealthy()` flips to false, treat the device as offline so the
|
|
195
|
+
* appliance state is updated and the retry manager starts the backoff schedule.
|
|
196
|
+
*
|
|
197
|
+
* Returns true if the device was marked offline.
|
|
198
|
+
*/
|
|
199
|
+
async markOfflineIfUnhealthy() {
|
|
200
|
+
if (this.sunspecClient.isHealthy(this.unitId)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
console.warn(`${this.constructor.name} ${this.applianceId}: connection unhealthy after read cycle ` +
|
|
204
|
+
`(consecutiveFailures=${this.sunspecClient.getConnectionHealth().getConsecutiveFailures()}) — marking offline`);
|
|
205
|
+
await this.markOffline();
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
190
208
|
/**
|
|
191
209
|
* Mark the device as offline: close the underlying socket so the next readData()
|
|
192
210
|
* cycle sees isConnected() === false and tryReconnect() can establish a fresh
|
|
@@ -338,6 +356,11 @@ class SunspecInverter extends BaseSunspecDevice {
|
|
|
338
356
|
const mpptDataList = await this.sunspecClient.readAllMPPTData(this.unitId);
|
|
339
357
|
const inverterSettings = await this.sunspecClient.readInverterSettings(this.unitId);
|
|
340
358
|
const dcStrings = this.mapMPPTToStrings(mpptDataList);
|
|
359
|
+
// SDK readers swallow modbus errors and return null/[]; detect that here so the
|
|
360
|
+
// appliance is marked offline and the retry manager starts its backoff.
|
|
361
|
+
if (await this.markOfflineIfUnhealthy()) {
|
|
362
|
+
return messages;
|
|
363
|
+
}
|
|
341
364
|
if (inverterData) {
|
|
342
365
|
const pvPowerW = dcStrings.reduce((sum, s) => sum + (s.powerW || 0), 0);
|
|
343
366
|
console.debug(`Got PV Power from DC strings: ${pvPowerW}W`);
|
|
@@ -806,6 +829,11 @@ class SunspecBattery extends BaseSunspecDevice {
|
|
|
806
829
|
const batteryData = await this.sunspecClient.readBatteryData(this.unitId);
|
|
807
830
|
const mpptDataList = await this.sunspecClient.readAllMPPTData(this.unitId);
|
|
808
831
|
const mpptBatteryPowerW = this.extractBatteryPowerFromMPPT(mpptDataList);
|
|
832
|
+
// SDK readers swallow modbus errors and return null/[]; detect that here so the
|
|
833
|
+
// appliance is marked offline and the retry manager starts its backoff.
|
|
834
|
+
if (await this.markOfflineIfUnhealthy()) {
|
|
835
|
+
return messages;
|
|
836
|
+
}
|
|
809
837
|
if (batteryData) {
|
|
810
838
|
const advancedBatteryModel = this.sunspecClient.findModel(this.unitId, 801);
|
|
811
839
|
const batteryBaseModel = this.sunspecClient.findModel(this.unitId, sunspec_interfaces_js_1.SunspecModelId.BatteryBase);
|
|
@@ -1291,6 +1319,11 @@ class SunspecMeter extends BaseSunspecDevice {
|
|
|
1291
1319
|
try {
|
|
1292
1320
|
// Read meter data
|
|
1293
1321
|
const meterData = await this.sunspecClient.readMeterData(this.unitId);
|
|
1322
|
+
// SDK readers swallow modbus errors and return null; detect that here so the
|
|
1323
|
+
// appliance is marked offline and the retry manager starts its backoff.
|
|
1324
|
+
if (await this.markOfflineIfUnhealthy()) {
|
|
1325
|
+
return messages;
|
|
1326
|
+
}
|
|
1294
1327
|
if (meterData) {
|
|
1295
1328
|
const meterMessage = {
|
|
1296
1329
|
id: (0, node_crypto_1.randomUUID)(),
|
|
@@ -60,6 +60,16 @@ export declare abstract class BaseSunspecDevice {
|
|
|
60
60
|
* per failed attempt with the running consecutive-failure count.
|
|
61
61
|
*/
|
|
62
62
|
protected onConnectionFailure(_consecutiveFailures: number): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Detect a silent read failure: the SDK's per-model readers swallow modbus errors and
|
|
65
|
+
* return null/[] rather than throwing, so a readData() cycle can complete "successfully"
|
|
66
|
+
* with no usable data while the underlying connection is broken. Once
|
|
67
|
+
* `connectionHealth.isHealthy()` flips to false, treat the device as offline so the
|
|
68
|
+
* appliance state is updated and the retry manager starts the backoff schedule.
|
|
69
|
+
*
|
|
70
|
+
* Returns true if the device was marked offline.
|
|
71
|
+
*/
|
|
72
|
+
protected markOfflineIfUnhealthy(): Promise<boolean>;
|
|
63
73
|
/**
|
|
64
74
|
* Mark the device as offline: close the underlying socket so the next readData()
|
|
65
75
|
* cycle sees isConnected() === false and tryReconnect() can establish a fresh
|
|
@@ -9,7 +9,8 @@ exports.DEFAULT_RETRY_CONFIG = {
|
|
|
9
9
|
{ intervalMs: 10_000, durationMs: 60_000 }, // Phase 1: every 10s for 1 minute
|
|
10
10
|
{ intervalMs: 30_000, durationMs: 120_000 }, // Phase 2: every 30s for 2 minutes
|
|
11
11
|
{ intervalMs: 60_000, durationMs: 300_000 }, // Phase 3: every 1m for 5 minutes
|
|
12
|
-
{ intervalMs: 300_000, durationMs:
|
|
12
|
+
{ intervalMs: 300_000, durationMs: 600_000 }, // Phase 4: every 5m for 10 minutes
|
|
13
|
+
{ intervalMs: 900_000, durationMs: 0 }, // Phase 5: every 15m forever
|
|
13
14
|
]
|
|
14
15
|
};
|
|
15
16
|
/**
|
package/dist/cjs/version.cjs
CHANGED
|
@@ -9,7 +9,7 @@ exports.getSdkVersion = getSdkVersion;
|
|
|
9
9
|
/**
|
|
10
10
|
* Current version of the enyo Energy App SDK.
|
|
11
11
|
*/
|
|
12
|
-
exports.SDK_VERSION = '0.0.
|
|
12
|
+
exports.SDK_VERSION = '0.0.62';
|
|
13
13
|
/**
|
|
14
14
|
* Gets the current SDK version.
|
|
15
15
|
* @returns The semantic version string of the SDK
|
package/dist/cjs/version.d.cts
CHANGED
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* Called on each readData() cycle; determines whether enough time has elapsed
|
|
6
6
|
* to attempt a reconnection based on the current retry phase.
|
|
7
7
|
*
|
|
8
|
-
* Default schedule:
|
|
8
|
+
* Default schedule (infinite retry, max 15m backoff):
|
|
9
9
|
* - Phase 1: every 10s for 1 minute
|
|
10
10
|
* - Phase 2: every 30s for 2 minutes
|
|
11
11
|
* - Phase 3: every 1m for 5 minutes
|
|
12
|
-
* - Phase 4: every 5m
|
|
12
|
+
* - Phase 4: every 5m for 10 minutes
|
|
13
|
+
* - Phase 5: every 15m forever
|
|
13
14
|
*/
|
|
14
15
|
import { IRetryConfig, IRetryPhase } from './sunspec-interfaces.js';
|
|
15
16
|
export declare class ConnectionRetryManager {
|
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* Called on each readData() cycle; determines whether enough time has elapsed
|
|
6
6
|
* to attempt a reconnection based on the current retry phase.
|
|
7
7
|
*
|
|
8
|
-
* Default schedule:
|
|
8
|
+
* Default schedule (infinite retry, max 15m backoff):
|
|
9
9
|
* - Phase 1: every 10s for 1 minute
|
|
10
10
|
* - Phase 2: every 30s for 2 minutes
|
|
11
11
|
* - Phase 3: every 1m for 5 minutes
|
|
12
|
-
* - Phase 4: every 5m
|
|
12
|
+
* - Phase 4: every 5m for 10 minutes
|
|
13
|
+
* - Phase 5: every 15m forever
|
|
13
14
|
*/
|
|
14
15
|
import { DEFAULT_RETRY_CONFIG } from './sunspec-interfaces.js';
|
|
15
16
|
export class ConnectionRetryManager {
|
|
@@ -60,6 +60,16 @@ export declare abstract class BaseSunspecDevice {
|
|
|
60
60
|
* per failed attempt with the running consecutive-failure count.
|
|
61
61
|
*/
|
|
62
62
|
protected onConnectionFailure(_consecutiveFailures: number): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Detect a silent read failure: the SDK's per-model readers swallow modbus errors and
|
|
65
|
+
* return null/[] rather than throwing, so a readData() cycle can complete "successfully"
|
|
66
|
+
* with no usable data while the underlying connection is broken. Once
|
|
67
|
+
* `connectionHealth.isHealthy()` flips to false, treat the device as offline so the
|
|
68
|
+
* appliance state is updated and the retry manager starts the backoff schedule.
|
|
69
|
+
*
|
|
70
|
+
* Returns true if the device was marked offline.
|
|
71
|
+
*/
|
|
72
|
+
protected markOfflineIfUnhealthy(): Promise<boolean>;
|
|
63
73
|
/**
|
|
64
74
|
* Mark the device as offline: close the underlying socket so the next readData()
|
|
65
75
|
* cycle sees isConnected() === false and tryReconnect() can establish a fresh
|
package/dist/sunspec-devices.js
CHANGED
|
@@ -184,6 +184,24 @@ export class BaseSunspecDevice {
|
|
|
184
184
|
async onConnectionFailure(_consecutiveFailures) {
|
|
185
185
|
// Default: no-op. SunspecInverter overrides to publish a faulted status.
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Detect a silent read failure: the SDK's per-model readers swallow modbus errors and
|
|
189
|
+
* return null/[] rather than throwing, so a readData() cycle can complete "successfully"
|
|
190
|
+
* with no usable data while the underlying connection is broken. Once
|
|
191
|
+
* `connectionHealth.isHealthy()` flips to false, treat the device as offline so the
|
|
192
|
+
* appliance state is updated and the retry manager starts the backoff schedule.
|
|
193
|
+
*
|
|
194
|
+
* Returns true if the device was marked offline.
|
|
195
|
+
*/
|
|
196
|
+
async markOfflineIfUnhealthy() {
|
|
197
|
+
if (this.sunspecClient.isHealthy(this.unitId)) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
console.warn(`${this.constructor.name} ${this.applianceId}: connection unhealthy after read cycle ` +
|
|
201
|
+
`(consecutiveFailures=${this.sunspecClient.getConnectionHealth().getConsecutiveFailures()}) — marking offline`);
|
|
202
|
+
await this.markOffline();
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
187
205
|
/**
|
|
188
206
|
* Mark the device as offline: close the underlying socket so the next readData()
|
|
189
207
|
* cycle sees isConnected() === false and tryReconnect() can establish a fresh
|
|
@@ -334,6 +352,11 @@ export class SunspecInverter extends BaseSunspecDevice {
|
|
|
334
352
|
const mpptDataList = await this.sunspecClient.readAllMPPTData(this.unitId);
|
|
335
353
|
const inverterSettings = await this.sunspecClient.readInverterSettings(this.unitId);
|
|
336
354
|
const dcStrings = this.mapMPPTToStrings(mpptDataList);
|
|
355
|
+
// SDK readers swallow modbus errors and return null/[]; detect that here so the
|
|
356
|
+
// appliance is marked offline and the retry manager starts its backoff.
|
|
357
|
+
if (await this.markOfflineIfUnhealthy()) {
|
|
358
|
+
return messages;
|
|
359
|
+
}
|
|
337
360
|
if (inverterData) {
|
|
338
361
|
const pvPowerW = dcStrings.reduce((sum, s) => sum + (s.powerW || 0), 0);
|
|
339
362
|
console.debug(`Got PV Power from DC strings: ${pvPowerW}W`);
|
|
@@ -801,6 +824,11 @@ export class SunspecBattery extends BaseSunspecDevice {
|
|
|
801
824
|
const batteryData = await this.sunspecClient.readBatteryData(this.unitId);
|
|
802
825
|
const mpptDataList = await this.sunspecClient.readAllMPPTData(this.unitId);
|
|
803
826
|
const mpptBatteryPowerW = this.extractBatteryPowerFromMPPT(mpptDataList);
|
|
827
|
+
// SDK readers swallow modbus errors and return null/[]; detect that here so the
|
|
828
|
+
// appliance is marked offline and the retry manager starts its backoff.
|
|
829
|
+
if (await this.markOfflineIfUnhealthy()) {
|
|
830
|
+
return messages;
|
|
831
|
+
}
|
|
804
832
|
if (batteryData) {
|
|
805
833
|
const advancedBatteryModel = this.sunspecClient.findModel(this.unitId, 801);
|
|
806
834
|
const batteryBaseModel = this.sunspecClient.findModel(this.unitId, SunspecModelId.BatteryBase);
|
|
@@ -1285,6 +1313,11 @@ export class SunspecMeter extends BaseSunspecDevice {
|
|
|
1285
1313
|
try {
|
|
1286
1314
|
// Read meter data
|
|
1287
1315
|
const meterData = await this.sunspecClient.readMeterData(this.unitId);
|
|
1316
|
+
// SDK readers swallow modbus errors and return null; detect that here so the
|
|
1317
|
+
// appliance is marked offline and the retry manager starts its backoff.
|
|
1318
|
+
if (await this.markOfflineIfUnhealthy()) {
|
|
1319
|
+
return messages;
|
|
1320
|
+
}
|
|
1288
1321
|
if (meterData) {
|
|
1289
1322
|
const meterMessage = {
|
|
1290
1323
|
id: randomUUID(),
|
|
@@ -6,7 +6,8 @@ export const DEFAULT_RETRY_CONFIG = {
|
|
|
6
6
|
{ intervalMs: 10_000, durationMs: 60_000 }, // Phase 1: every 10s for 1 minute
|
|
7
7
|
{ intervalMs: 30_000, durationMs: 120_000 }, // Phase 2: every 30s for 2 minutes
|
|
8
8
|
{ intervalMs: 60_000, durationMs: 300_000 }, // Phase 3: every 1m for 5 minutes
|
|
9
|
-
{ intervalMs: 300_000, durationMs:
|
|
9
|
+
{ intervalMs: 300_000, durationMs: 600_000 }, // Phase 4: every 5m for 10 minutes
|
|
10
|
+
{ intervalMs: 900_000, durationMs: 0 }, // Phase 5: every 15m forever
|
|
10
11
|
]
|
|
11
12
|
};
|
|
12
13
|
/**
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED