@enyo-energy/sunspec-sdk 0.0.76 → 0.0.78

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.
@@ -133,6 +133,22 @@ class SunspecBatteryScheduleHandler extends storage_schedule_handler_js_1.Storag
133
133
  }
134
134
  async applyEntry(active) {
135
135
  const { direction, powerW } = active.entry;
136
+ if (direction === enyo_data_bus_value_js_1.EnyoStorageScheduleDirectionEnum.Idle) {
137
+ // Idle entry: hand the battery back to its own autonomous logic
138
+ // by restoring the snapshotted pre-schedule registers (same write
139
+ // set as the `mode: auto` rollback path). The schedule itself
140
+ // keeps running, so a subsequent Charge / Discharge entry can
141
+ // resume external control.
142
+ const baselineRegisters = this.originalBaseline;
143
+ if (!baselineRegisters) {
144
+ throw new Error(`no usable baseline for idle entry (originalBaseline=undefined)`);
145
+ }
146
+ await this.sunspecClient.writeBatteryControls(this.unitId, baselineRegisters);
147
+ await this.getSnapshotService()?.recordModification([
148
+ 'storCtlMod', 'chaGriSet', 'wChaMax', 'inWRte', 'outWRte',
149
+ ]);
150
+ return;
151
+ }
136
152
  const baseline = this.installedWChaMax;
137
153
  if (!baseline || baseline <= 0) {
138
154
  throw new Error(`no usable wChaMax baseline (installedWChaMax=${baseline})`);
@@ -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"
@@ -610,6 +619,20 @@ class SunspecInverter extends BaseSunspecDevice {
610
619
  console.log(`Inverter ${this.applianceId}: emitting faulted (${sunspec_interfaces_js_1.SUNSPEC_CONNECTION_LOST_CODE}) after ${consecutiveFailures} consecutive reconnect failures`);
611
620
  this.dataBus.sendMessage([message]);
612
621
  }
622
+ async onConnectionRestored() {
623
+ if (!this.applianceId || !this.dataBus)
624
+ return;
625
+ if (this.errorState.lastStatus !== 'connection_lost')
626
+ return;
627
+ const message = this.buildStatusMessage(enyo_appliance_js_1.EnyoApplianceStatusEnum.Healthy, [], new Date());
628
+ this.errorState = {
629
+ activeCodes: [],
630
+ lastStatus: 'healthy',
631
+ };
632
+ await this.persistErrorState();
633
+ console.log(`Inverter ${this.applianceId}: emitting healthy after reconnect`);
634
+ this.dataBus.sendMessage([message]);
635
+ }
613
636
  /**
614
637
  * Compute the currently active feed-in / production limit in Watts from the
615
638
  * 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"
@@ -145,6 +150,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
145
150
  private persistErrorState;
146
151
  private detectAndEmitStatusTransition;
147
152
  protected onConnectionFailure(consecutiveFailures: number): Promise<void>;
153
+ protected onConnectionRestored(): Promise<void>;
148
154
  /**
149
155
  * Compute the currently active feed-in / production limit in Watts from the
150
156
  * 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.76';
12
+ exports.SDK_VERSION = '0.0.78';
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.76";
8
+ export declare const SDK_VERSION = "0.0.78";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
@@ -130,6 +130,22 @@ export class SunspecBatteryScheduleHandler extends StorageScheduleHandler {
130
130
  }
131
131
  async applyEntry(active) {
132
132
  const { direction, powerW } = active.entry;
133
+ if (direction === EnyoStorageScheduleDirectionEnum.Idle) {
134
+ // Idle entry: hand the battery back to its own autonomous logic
135
+ // by restoring the snapshotted pre-schedule registers (same write
136
+ // set as the `mode: auto` rollback path). The schedule itself
137
+ // keeps running, so a subsequent Charge / Discharge entry can
138
+ // resume external control.
139
+ const baselineRegisters = this.originalBaseline;
140
+ if (!baselineRegisters) {
141
+ throw new Error(`no usable baseline for idle entry (originalBaseline=undefined)`);
142
+ }
143
+ await this.sunspecClient.writeBatteryControls(this.unitId, baselineRegisters);
144
+ await this.getSnapshotService()?.recordModification([
145
+ 'storCtlMod', 'chaGriSet', 'wChaMax', 'inWRte', 'outWRte',
146
+ ]);
147
+ return;
148
+ }
133
149
  const baseline = this.installedWChaMax;
134
150
  if (!baseline || baseline <= 0) {
135
151
  throw new Error(`no usable wChaMax baseline (installedWChaMax=${baseline})`);
@@ -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"
@@ -145,6 +150,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
145
150
  private persistErrorState;
146
151
  private detectAndEmitStatusTransition;
147
152
  protected onConnectionFailure(consecutiveFailures: number): Promise<void>;
153
+ protected onConnectionRestored(): Promise<void>;
148
154
  /**
149
155
  * Compute the currently active feed-in / production limit in Watts from the
150
156
  * 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"
@@ -603,6 +612,20 @@ export class SunspecInverter extends BaseSunspecDevice {
603
612
  console.log(`Inverter ${this.applianceId}: emitting faulted (${SUNSPEC_CONNECTION_LOST_CODE}) after ${consecutiveFailures} consecutive reconnect failures`);
604
613
  this.dataBus.sendMessage([message]);
605
614
  }
615
+ async onConnectionRestored() {
616
+ if (!this.applianceId || !this.dataBus)
617
+ return;
618
+ if (this.errorState.lastStatus !== 'connection_lost')
619
+ return;
620
+ const message = this.buildStatusMessage(EnyoApplianceStatusEnum.Healthy, [], new Date());
621
+ this.errorState = {
622
+ activeCodes: [],
623
+ lastStatus: 'healthy',
624
+ };
625
+ await this.persistErrorState();
626
+ console.log(`Inverter ${this.applianceId}: emitting healthy after reconnect`);
627
+ this.dataBus.sendMessage([message]);
628
+ }
606
629
  /**
607
630
  * Compute the currently active feed-in / production limit in Watts from the
608
631
  * 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.76";
8
+ export declare const SDK_VERSION = "0.0.78";
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.76';
8
+ export const SDK_VERSION = '0.0.78';
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.76",
3
+ "version": "0.0.78",
4
4
  "description": "enyo Energy Sunspec SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@enyo-energy/appliance-calibration": "0.0.2",
44
- "@enyo-energy/energy-app-sdk": "0.0.144"
44
+ "@enyo-energy/energy-app-sdk": "^0.0.149"
45
45
  },
46
46
  "volta": {
47
47
  "node": "22.17.0"