@enyo-energy/sunspec-sdk 0.0.77 → 0.0.79

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.
@@ -181,6 +181,7 @@ class BaseSunspecDevice {
181
181
  if (this.applianceId) {
182
182
  await this.applianceManager.updateApplianceState(this.applianceId, enyo_appliance_js_1.EnyoApplianceConnectionType.Connector, enyo_appliance_js_1.EnyoApplianceStateEnum.Connected);
183
183
  }
184
+ await this.onConnectionRestored();
184
185
  const postStats = this.sunspecClient.getConnectionStats();
185
186
  console.log(`${this.constructor.name} ${this.applianceId}: Reconnection successful after ${attempt} attempt(s) (opens=${postStats.opens}, closes=${postStats.closes}, openUnits=${postStats.openUnits})`);
186
187
  return true;
@@ -202,6 +203,14 @@ class BaseSunspecDevice {
202
203
  async onConnectionFailure(_consecutiveFailures) {
203
204
  // Default: no-op. SunspecInverter overrides to publish a faulted status.
204
205
  }
206
+ /**
207
+ * Hook for subclasses to react to a successful reconnect. Called once
208
+ * after the appliance state has been updated to Connected.
209
+ */
210
+ async onConnectionRestored() {
211
+ // Default: no-op. SunspecInverter overrides to publish a healthy status
212
+ // when recovering from a previously emitted connection-lost fault.
213
+ }
205
214
  /**
206
215
  * Detect a silent read failure: the SDK's per-model readers swallow modbus errors and
207
216
  * return null/[] rather than throwing, so a readData() cycle can complete "successfully"
@@ -368,9 +377,18 @@ class SunspecInverter extends BaseSunspecDevice {
368
377
  }
369
378
  await this.loadErrorState();
370
379
  this.startDataBusListening();
380
+ // Cold-start recovery: if the persisted state says we were stuck in
381
+ // connection_lost but connect() just succeeded (we read commonBlock,
382
+ // settings, controls and MPPT above), the Modbus path is provably
383
+ // healthy. Clear the stale fault and emit Healthy via the same hook
384
+ // the in-process reconnect path uses.
385
+ if (this.errorState.lastStatus === 'connection_lost') {
386
+ await this.onConnectionRestored();
387
+ }
371
388
  }
372
389
  async disconnect() {
373
390
  this.stopDataBusListening();
391
+ await this.removePersistedErrorState();
374
392
  if (this.applianceId) {
375
393
  try {
376
394
  await this.applianceManager.updateApplianceState(this.applianceId, enyo_appliance_js_1.EnyoApplianceConnectionType.Connector, enyo_appliance_js_1.EnyoApplianceStateEnum.Offline);
@@ -562,6 +580,15 @@ class SunspecInverter extends BaseSunspecDevice {
562
580
  console.error(`Inverter ${this.applianceId}: failed to persist error state: ${error}`);
563
581
  }
564
582
  }
583
+ async removePersistedErrorState() {
584
+ const storage = this.storage ?? this.energyApp.useStorage();
585
+ try {
586
+ await storage.remove(this.storageKey());
587
+ }
588
+ catch (error) {
589
+ console.error(`Inverter ${this.applianceId}: failed to remove persisted error state: ${error}`);
590
+ }
591
+ }
565
592
  async detectAndEmitStatusTransition(data, timestamp) {
566
593
  if (!this.applianceId)
567
594
  return undefined;
@@ -610,6 +637,20 @@ class SunspecInverter extends BaseSunspecDevice {
610
637
  console.log(`Inverter ${this.applianceId}: emitting faulted (${sunspec_interfaces_js_1.SUNSPEC_CONNECTION_LOST_CODE}) after ${consecutiveFailures} consecutive reconnect failures`);
611
638
  this.dataBus.sendMessage([message]);
612
639
  }
640
+ async onConnectionRestored() {
641
+ if (!this.applianceId || !this.dataBus)
642
+ return;
643
+ if (this.errorState.lastStatus !== 'connection_lost')
644
+ return;
645
+ const message = this.buildStatusMessage(enyo_appliance_js_1.EnyoApplianceStatusEnum.Healthy, [], new Date());
646
+ this.errorState = {
647
+ activeCodes: [],
648
+ lastStatus: 'healthy',
649
+ };
650
+ await this.persistErrorState();
651
+ console.log(`Inverter ${this.applianceId}: emitting healthy after reconnect`);
652
+ this.dataBus.sendMessage([message]);
653
+ }
613
654
  /**
614
655
  * Compute the currently active feed-in / production limit in Watts from the
615
656
  * Model 121 settings (WMax) and Model 123 controls (WMaxLim_Ena, WMaxLimPct).
@@ -69,6 +69,11 @@ export declare abstract class BaseSunspecDevice {
69
69
  * per failed attempt with the running consecutive-failure count.
70
70
  */
71
71
  protected onConnectionFailure(_consecutiveFailures: number): Promise<void>;
72
+ /**
73
+ * Hook for subclasses to react to a successful reconnect. Called once
74
+ * after the appliance state has been updated to Connected.
75
+ */
76
+ protected onConnectionRestored(): Promise<void>;
72
77
  /**
73
78
  * Detect a silent read failure: the SDK's per-model readers swallow modbus errors and
74
79
  * return null/[] rather than throwing, so a readData() cycle can complete "successfully"
@@ -143,8 +148,10 @@ export declare class SunspecInverter extends BaseSunspecDevice {
143
148
  private storageKey;
144
149
  private loadErrorState;
145
150
  private persistErrorState;
151
+ private removePersistedErrorState;
146
152
  private detectAndEmitStatusTransition;
147
153
  protected onConnectionFailure(consecutiveFailures: number): Promise<void>;
154
+ protected onConnectionRestored(): Promise<void>;
148
155
  /**
149
156
  * Compute the currently active feed-in / production limit in Watts from the
150
157
  * Model 121 settings (WMax) and Model 123 controls (WMaxLim_Ena, WMaxLimPct).
@@ -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.77';
12
+ exports.SDK_VERSION = '0.0.79';
13
13
  /**
14
14
  * Gets the current SDK version.
15
15
  * @returns The semantic version string of the SDK
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export declare const SDK_VERSION = "0.0.77";
8
+ export declare const SDK_VERSION = "0.0.79";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
@@ -69,6 +69,11 @@ export declare abstract class BaseSunspecDevice {
69
69
  * per failed attempt with the running consecutive-failure count.
70
70
  */
71
71
  protected onConnectionFailure(_consecutiveFailures: number): Promise<void>;
72
+ /**
73
+ * Hook for subclasses to react to a successful reconnect. Called once
74
+ * after the appliance state has been updated to Connected.
75
+ */
76
+ protected onConnectionRestored(): Promise<void>;
72
77
  /**
73
78
  * Detect a silent read failure: the SDK's per-model readers swallow modbus errors and
74
79
  * return null/[] rather than throwing, so a readData() cycle can complete "successfully"
@@ -143,8 +148,10 @@ export declare class SunspecInverter extends BaseSunspecDevice {
143
148
  private storageKey;
144
149
  private loadErrorState;
145
150
  private persistErrorState;
151
+ private removePersistedErrorState;
146
152
  private detectAndEmitStatusTransition;
147
153
  protected onConnectionFailure(consecutiveFailures: number): Promise<void>;
154
+ protected onConnectionRestored(): Promise<void>;
148
155
  /**
149
156
  * Compute the currently active feed-in / production limit in Watts from the
150
157
  * Model 121 settings (WMax) and Model 123 controls (WMaxLim_Ena, WMaxLimPct).
@@ -175,6 +175,7 @@ export class BaseSunspecDevice {
175
175
  if (this.applianceId) {
176
176
  await this.applianceManager.updateApplianceState(this.applianceId, EnyoApplianceConnectionType.Connector, EnyoApplianceStateEnum.Connected);
177
177
  }
178
+ await this.onConnectionRestored();
178
179
  const postStats = this.sunspecClient.getConnectionStats();
179
180
  console.log(`${this.constructor.name} ${this.applianceId}: Reconnection successful after ${attempt} attempt(s) (opens=${postStats.opens}, closes=${postStats.closes}, openUnits=${postStats.openUnits})`);
180
181
  return true;
@@ -196,6 +197,14 @@ export class BaseSunspecDevice {
196
197
  async onConnectionFailure(_consecutiveFailures) {
197
198
  // Default: no-op. SunspecInverter overrides to publish a faulted status.
198
199
  }
200
+ /**
201
+ * Hook for subclasses to react to a successful reconnect. Called once
202
+ * after the appliance state has been updated to Connected.
203
+ */
204
+ async onConnectionRestored() {
205
+ // Default: no-op. SunspecInverter overrides to publish a healthy status
206
+ // when recovering from a previously emitted connection-lost fault.
207
+ }
199
208
  /**
200
209
  * Detect a silent read failure: the SDK's per-model readers swallow modbus errors and
201
210
  * return null/[] rather than throwing, so a readData() cycle can complete "successfully"
@@ -361,9 +370,18 @@ export class SunspecInverter extends BaseSunspecDevice {
361
370
  }
362
371
  await this.loadErrorState();
363
372
  this.startDataBusListening();
373
+ // Cold-start recovery: if the persisted state says we were stuck in
374
+ // connection_lost but connect() just succeeded (we read commonBlock,
375
+ // settings, controls and MPPT above), the Modbus path is provably
376
+ // healthy. Clear the stale fault and emit Healthy via the same hook
377
+ // the in-process reconnect path uses.
378
+ if (this.errorState.lastStatus === 'connection_lost') {
379
+ await this.onConnectionRestored();
380
+ }
364
381
  }
365
382
  async disconnect() {
366
383
  this.stopDataBusListening();
384
+ await this.removePersistedErrorState();
367
385
  if (this.applianceId) {
368
386
  try {
369
387
  await this.applianceManager.updateApplianceState(this.applianceId, EnyoApplianceConnectionType.Connector, EnyoApplianceStateEnum.Offline);
@@ -555,6 +573,15 @@ export class SunspecInverter extends BaseSunspecDevice {
555
573
  console.error(`Inverter ${this.applianceId}: failed to persist error state: ${error}`);
556
574
  }
557
575
  }
576
+ async removePersistedErrorState() {
577
+ const storage = this.storage ?? this.energyApp.useStorage();
578
+ try {
579
+ await storage.remove(this.storageKey());
580
+ }
581
+ catch (error) {
582
+ console.error(`Inverter ${this.applianceId}: failed to remove persisted error state: ${error}`);
583
+ }
584
+ }
558
585
  async detectAndEmitStatusTransition(data, timestamp) {
559
586
  if (!this.applianceId)
560
587
  return undefined;
@@ -603,6 +630,20 @@ export class SunspecInverter extends BaseSunspecDevice {
603
630
  console.log(`Inverter ${this.applianceId}: emitting faulted (${SUNSPEC_CONNECTION_LOST_CODE}) after ${consecutiveFailures} consecutive reconnect failures`);
604
631
  this.dataBus.sendMessage([message]);
605
632
  }
633
+ async onConnectionRestored() {
634
+ if (!this.applianceId || !this.dataBus)
635
+ return;
636
+ if (this.errorState.lastStatus !== 'connection_lost')
637
+ return;
638
+ const message = this.buildStatusMessage(EnyoApplianceStatusEnum.Healthy, [], new Date());
639
+ this.errorState = {
640
+ activeCodes: [],
641
+ lastStatus: 'healthy',
642
+ };
643
+ await this.persistErrorState();
644
+ console.log(`Inverter ${this.applianceId}: emitting healthy after reconnect`);
645
+ this.dataBus.sendMessage([message]);
646
+ }
606
647
  /**
607
648
  * Compute the currently active feed-in / production limit in Watts from the
608
649
  * Model 121 settings (WMax) and Model 123 controls (WMaxLim_Ena, WMaxLimPct).
package/dist/version.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export declare const SDK_VERSION = "0.0.77";
8
+ export declare const SDK_VERSION = "0.0.79";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
package/dist/version.js CHANGED
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export const SDK_VERSION = '0.0.77';
8
+ export const SDK_VERSION = '0.0.79';
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enyo-energy/sunspec-sdk",
3
- "version": "0.0.77",
3
+ "version": "0.0.79",
4
4
  "description": "enyo Energy Sunspec SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",