@enyo-energy/sunspec-sdk 0.0.78 → 0.0.80

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.
@@ -377,9 +377,18 @@ class SunspecInverter extends BaseSunspecDevice {
377
377
  }
378
378
  await this.loadErrorState();
379
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
+ }
380
388
  }
381
389
  async disconnect() {
382
390
  this.stopDataBusListening();
391
+ await this.removePersistedErrorState();
383
392
  if (this.applianceId) {
384
393
  try {
385
394
  await this.applianceManager.updateApplianceState(this.applianceId, enyo_appliance_js_1.EnyoApplianceConnectionType.Connector, enyo_appliance_js_1.EnyoApplianceStateEnum.Offline);
@@ -435,10 +444,7 @@ class SunspecInverter extends BaseSunspecDevice {
435
444
  }
436
445
  };
437
446
  messages.push(inverterMessage);
438
- const statusMessage = await this.detectAndEmitStatusTransition(inverterData, timestamp);
439
- if (statusMessage) {
440
- messages.push(statusMessage);
441
- }
447
+ await this.detectAndEmitStatusTransition(inverterData, timestamp);
442
448
  }
443
449
  this.consecutiveReconnectFailures = 0;
444
450
  if (this.applianceId) {
@@ -571,17 +577,29 @@ class SunspecInverter extends BaseSunspecDevice {
571
577
  console.error(`Inverter ${this.applianceId}: failed to persist error state: ${error}`);
572
578
  }
573
579
  }
580
+ async removePersistedErrorState() {
581
+ const storage = this.storage ?? this.energyApp.useStorage();
582
+ try {
583
+ await storage.remove(this.storageKey());
584
+ }
585
+ catch (error) {
586
+ console.error(`Inverter ${this.applianceId}: failed to remove persisted error state: ${error}`);
587
+ }
588
+ }
574
589
  async detectAndEmitStatusTransition(data, timestamp) {
575
- if (!this.applianceId)
576
- return undefined;
590
+ if (!this.applianceId || !this.dataBus)
591
+ return;
577
592
  const { codes, codeIds } = this.decodeActiveErrors(data);
578
593
  const recoveringFromConnectionLoss = this.errorState.lastStatus === 'connection_lost';
579
594
  if (!recoveringFromConnectionLoss && !this.hasErrorSetChanged(codeIds)) {
580
- return undefined;
595
+ return;
581
596
  }
582
597
  const newStatus = codeIds.length === 0
583
598
  ? enyo_appliance_js_1.EnyoApplianceStatusEnum.Healthy
584
599
  : enyo_appliance_js_1.EnyoApplianceStatusEnum.Faulted;
600
+ const message = this.buildStatusMessage(newStatus, codes, timestamp);
601
+ console.log(`Inverter ${this.applianceId}: status transition -> ${newStatus} (codes=[${codeIds.join(', ')}])`);
602
+ this.dataBus.sendMessage([message]);
585
603
  this.errorState = {
586
604
  evt1: data.events,
587
605
  evt2: data.events2,
@@ -593,8 +611,6 @@ class SunspecInverter extends BaseSunspecDevice {
593
611
  lastStatus: newStatus === enyo_appliance_js_1.EnyoApplianceStatusEnum.Healthy ? 'healthy' : 'faulted',
594
612
  };
595
613
  await this.persistErrorState();
596
- console.log(`Inverter ${this.applianceId}: status transition -> ${newStatus} (codes=[${codeIds.join(', ')}])`);
597
- return this.buildStatusMessage(newStatus, codes, timestamp);
598
614
  }
599
615
  async onConnectionFailure(consecutiveFailures) {
600
616
  if (!this.applianceId || !this.dataBus)
@@ -611,13 +627,13 @@ class SunspecInverter extends BaseSunspecDevice {
611
627
  ]
612
628
  }];
613
629
  const message = this.buildStatusMessage(enyo_appliance_js_1.EnyoApplianceStatusEnum.Faulted, errorCodes, new Date());
630
+ console.log(`Inverter ${this.applianceId}: emitting faulted (${sunspec_interfaces_js_1.SUNSPEC_CONNECTION_LOST_CODE}) after ${consecutiveFailures} consecutive reconnect failures`);
631
+ this.dataBus.sendMessage([message]);
614
632
  this.errorState = {
615
633
  activeCodes: [sunspec_interfaces_js_1.SUNSPEC_CONNECTION_LOST_CODE],
616
634
  lastStatus: 'connection_lost',
617
635
  };
618
636
  await this.persistErrorState();
619
- console.log(`Inverter ${this.applianceId}: emitting faulted (${sunspec_interfaces_js_1.SUNSPEC_CONNECTION_LOST_CODE}) after ${consecutiveFailures} consecutive reconnect failures`);
620
- this.dataBus.sendMessage([message]);
621
637
  }
622
638
  async onConnectionRestored() {
623
639
  if (!this.applianceId || !this.dataBus)
@@ -625,13 +641,13 @@ class SunspecInverter extends BaseSunspecDevice {
625
641
  if (this.errorState.lastStatus !== 'connection_lost')
626
642
  return;
627
643
  const message = this.buildStatusMessage(enyo_appliance_js_1.EnyoApplianceStatusEnum.Healthy, [], new Date());
644
+ console.log(`Inverter ${this.applianceId}: emitting healthy after reconnect`);
645
+ this.dataBus.sendMessage([message]);
628
646
  this.errorState = {
629
647
  activeCodes: [],
630
648
  lastStatus: 'healthy',
631
649
  };
632
650
  await this.persistErrorState();
633
- console.log(`Inverter ${this.applianceId}: emitting healthy after reconnect`);
634
- this.dataBus.sendMessage([message]);
635
651
  }
636
652
  /**
637
653
  * Compute the currently active feed-in / production limit in Watts from the
@@ -148,6 +148,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
148
148
  private storageKey;
149
149
  private loadErrorState;
150
150
  private persistErrorState;
151
+ private removePersistedErrorState;
151
152
  private detectAndEmitStatusTransition;
152
153
  protected onConnectionFailure(consecutiveFailures: number): Promise<void>;
153
154
  protected onConnectionRestored(): Promise<void>;
@@ -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.78';
12
+ exports.SDK_VERSION = '0.0.80';
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.78";
8
+ export declare const SDK_VERSION = "0.0.80";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
@@ -148,6 +148,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
148
148
  private storageKey;
149
149
  private loadErrorState;
150
150
  private persistErrorState;
151
+ private removePersistedErrorState;
151
152
  private detectAndEmitStatusTransition;
152
153
  protected onConnectionFailure(consecutiveFailures: number): Promise<void>;
153
154
  protected onConnectionRestored(): Promise<void>;
@@ -370,9 +370,18 @@ export class SunspecInverter extends BaseSunspecDevice {
370
370
  }
371
371
  await this.loadErrorState();
372
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
+ }
373
381
  }
374
382
  async disconnect() {
375
383
  this.stopDataBusListening();
384
+ await this.removePersistedErrorState();
376
385
  if (this.applianceId) {
377
386
  try {
378
387
  await this.applianceManager.updateApplianceState(this.applianceId, EnyoApplianceConnectionType.Connector, EnyoApplianceStateEnum.Offline);
@@ -428,10 +437,7 @@ export class SunspecInverter extends BaseSunspecDevice {
428
437
  }
429
438
  };
430
439
  messages.push(inverterMessage);
431
- const statusMessage = await this.detectAndEmitStatusTransition(inverterData, timestamp);
432
- if (statusMessage) {
433
- messages.push(statusMessage);
434
- }
440
+ await this.detectAndEmitStatusTransition(inverterData, timestamp);
435
441
  }
436
442
  this.consecutiveReconnectFailures = 0;
437
443
  if (this.applianceId) {
@@ -564,17 +570,29 @@ export class SunspecInverter extends BaseSunspecDevice {
564
570
  console.error(`Inverter ${this.applianceId}: failed to persist error state: ${error}`);
565
571
  }
566
572
  }
573
+ async removePersistedErrorState() {
574
+ const storage = this.storage ?? this.energyApp.useStorage();
575
+ try {
576
+ await storage.remove(this.storageKey());
577
+ }
578
+ catch (error) {
579
+ console.error(`Inverter ${this.applianceId}: failed to remove persisted error state: ${error}`);
580
+ }
581
+ }
567
582
  async detectAndEmitStatusTransition(data, timestamp) {
568
- if (!this.applianceId)
569
- return undefined;
583
+ if (!this.applianceId || !this.dataBus)
584
+ return;
570
585
  const { codes, codeIds } = this.decodeActiveErrors(data);
571
586
  const recoveringFromConnectionLoss = this.errorState.lastStatus === 'connection_lost';
572
587
  if (!recoveringFromConnectionLoss && !this.hasErrorSetChanged(codeIds)) {
573
- return undefined;
588
+ return;
574
589
  }
575
590
  const newStatus = codeIds.length === 0
576
591
  ? EnyoApplianceStatusEnum.Healthy
577
592
  : EnyoApplianceStatusEnum.Faulted;
593
+ const message = this.buildStatusMessage(newStatus, codes, timestamp);
594
+ console.log(`Inverter ${this.applianceId}: status transition -> ${newStatus} (codes=[${codeIds.join(', ')}])`);
595
+ this.dataBus.sendMessage([message]);
578
596
  this.errorState = {
579
597
  evt1: data.events,
580
598
  evt2: data.events2,
@@ -586,8 +604,6 @@ export class SunspecInverter extends BaseSunspecDevice {
586
604
  lastStatus: newStatus === EnyoApplianceStatusEnum.Healthy ? 'healthy' : 'faulted',
587
605
  };
588
606
  await this.persistErrorState();
589
- console.log(`Inverter ${this.applianceId}: status transition -> ${newStatus} (codes=[${codeIds.join(', ')}])`);
590
- return this.buildStatusMessage(newStatus, codes, timestamp);
591
607
  }
592
608
  async onConnectionFailure(consecutiveFailures) {
593
609
  if (!this.applianceId || !this.dataBus)
@@ -604,13 +620,13 @@ export class SunspecInverter extends BaseSunspecDevice {
604
620
  ]
605
621
  }];
606
622
  const message = this.buildStatusMessage(EnyoApplianceStatusEnum.Faulted, errorCodes, new Date());
623
+ console.log(`Inverter ${this.applianceId}: emitting faulted (${SUNSPEC_CONNECTION_LOST_CODE}) after ${consecutiveFailures} consecutive reconnect failures`);
624
+ this.dataBus.sendMessage([message]);
607
625
  this.errorState = {
608
626
  activeCodes: [SUNSPEC_CONNECTION_LOST_CODE],
609
627
  lastStatus: 'connection_lost',
610
628
  };
611
629
  await this.persistErrorState();
612
- console.log(`Inverter ${this.applianceId}: emitting faulted (${SUNSPEC_CONNECTION_LOST_CODE}) after ${consecutiveFailures} consecutive reconnect failures`);
613
- this.dataBus.sendMessage([message]);
614
630
  }
615
631
  async onConnectionRestored() {
616
632
  if (!this.applianceId || !this.dataBus)
@@ -618,13 +634,13 @@ export class SunspecInverter extends BaseSunspecDevice {
618
634
  if (this.errorState.lastStatus !== 'connection_lost')
619
635
  return;
620
636
  const message = this.buildStatusMessage(EnyoApplianceStatusEnum.Healthy, [], new Date());
637
+ console.log(`Inverter ${this.applianceId}: emitting healthy after reconnect`);
638
+ this.dataBus.sendMessage([message]);
621
639
  this.errorState = {
622
640
  activeCodes: [],
623
641
  lastStatus: 'healthy',
624
642
  };
625
643
  await this.persistErrorState();
626
- console.log(`Inverter ${this.applianceId}: emitting healthy after reconnect`);
627
- this.dataBus.sendMessage([message]);
628
644
  }
629
645
  /**
630
646
  * Compute the currently active feed-in / production limit in Watts from the
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.78";
8
+ export declare const SDK_VERSION = "0.0.80";
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.78';
8
+ export const SDK_VERSION = '0.0.80';
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.78",
3
+ "version": "0.0.80",
4
4
  "description": "enyo Energy Sunspec SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",