@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.
@@ -1,23 +1,13 @@
1
- import { SunspecBatteryChargeState, SunspecModelId, SunspecMPPTOperatingState, SunspecStorageMode } from "./sunspec-interfaces.js";
1
+ import { SunspecBatteryChargeState, SunspecModelId, SunspecMPPTOperatingState, SunspecStorageMode, SunspecInverterCapability } from "./sunspec-interfaces.js";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { EnyoApplianceConnectionType, EnyoApplianceStateEnum, EnyoApplianceTopologyFeatureEnum, EnyoApplianceTypeEnum } from "@enyo-energy/energy-app-sdk/dist/types/enyo-appliance.js";
4
+ import { ConnectionRetryManager } from "./connection-retry-manager.js";
4
5
  import { EnyoBatteryStateEnum, EnyoCommandAcknowledgeAnswerEnum, EnyoDataBusMessageEnum, EnyoInverterStateEnum, EnyoStringStateEnum } from "@enyo-energy/energy-app-sdk/dist/types/enyo-data-bus-value.js";
5
6
  import { EnyoSourceEnum } from "@enyo-energy/energy-app-sdk/dist/types/enyo-source.enum.js";
6
7
  import { EnyoMeterApplianceAvailableFeaturesEnum } from "@enyo-energy/energy-app-sdk/dist/types/enyo-meter-appliance.js";
7
8
  import { EnyoBatteryFeature, EnyoBatteryStorageMode } from "@enyo-energy/energy-app-sdk/dist/types/enyo-battery-appliance.js";
8
- /**
9
- * Extract battery discharge power from MPPT data.
10
- * Returns the discharge power in Watts (positive value), or 0 if no discharge.
11
- */
12
- function extractBatteryDischargePowerFromMPPT(mpptDataList) {
13
- let dischargePowerW = 0;
14
- for (const mppt of mpptDataList) {
15
- if (mppt.stringId === 'StDisCha 4' && mppt.dcPower !== undefined && mppt.dcPower > 0) {
16
- dischargePowerW += mppt.dcPower;
17
- }
18
- }
19
- return dischargePowerW;
20
- }
9
+ // TODO: Remove once added to @enyo-energy/energy-app-sdk EnyoDataBusMessageEnum
10
+ export const ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1 = 'SetInverterFeedInLimitV1';
21
11
  /**
22
12
  * Base abstract class for all Sunspec devices
23
13
  */
@@ -32,7 +22,10 @@ export class BaseSunspecDevice {
32
22
  baseAddress;
33
23
  applianceId;
34
24
  lastUpdateTime = 0;
35
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000) {
25
+ dataBusListenerId;
26
+ dataBus;
27
+ retryManager;
28
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, retryConfig) {
36
29
  this.energyApp = energyApp;
37
30
  this.name = name;
38
31
  this.networkDevice = networkDevice;
@@ -41,6 +34,7 @@ export class BaseSunspecDevice {
41
34
  this.unitId = unitId;
42
35
  this.port = port;
43
36
  this.baseAddress = baseAddress;
37
+ this.retryManager = new ConnectionRetryManager(retryConfig);
44
38
  }
45
39
  /**
46
40
  * Check if the device is connected
@@ -62,11 +56,86 @@ export class BaseSunspecDevice {
62
56
  await this.sunspecClient.discoverModels(this.baseAddress);
63
57
  }
64
58
  }
59
+ /**
60
+ * Attempt a reconnection if the tiered retry schedule allows it.
61
+ * Called from readData() when the device is disconnected.
62
+ * Returns true if reconnection succeeded.
63
+ */
64
+ async tryReconnect() {
65
+ this.retryManager.markDisconnected();
66
+ if (!this.retryManager.shouldAttemptNow()) {
67
+ return false;
68
+ }
69
+ this.retryManager.recordAttempt();
70
+ const phase = this.retryManager.getCurrentPhase();
71
+ const attempt = this.retryManager.getAttemptCount();
72
+ const elapsed = Math.round(this.retryManager.getElapsedMs() / 1000);
73
+ console.log(`${this.constructor.name} ${this.applianceId}: Reconnect attempt #${attempt} ` +
74
+ `(phase: ${phase.intervalMs / 1000}s interval, elapsed: ${elapsed}s)`);
75
+ try {
76
+ const success = await this.sunspecClient.reconnect();
77
+ if (success) {
78
+ // Re-discover models after reconnect
79
+ await this.sunspecClient.discoverModels(this.baseAddress);
80
+ this.retryManager.reset();
81
+ // Update appliance state to Connected
82
+ if (this.applianceId) {
83
+ await this.applianceManager.updateApplianceState(this.applianceId, EnyoApplianceConnectionType.Connector, EnyoApplianceStateEnum.Connected);
84
+ }
85
+ console.log(`${this.constructor.name} ${this.applianceId}: Reconnection successful after ${attempt} attempt(s)`);
86
+ return true;
87
+ }
88
+ }
89
+ catch (error) {
90
+ console.error(`${this.constructor.name} ${this.applianceId}: Reconnect attempt #${attempt} failed: ${error}`);
91
+ }
92
+ return false;
93
+ }
94
+ /**
95
+ * Mark the device as offline: update appliance state and start tracking disconnection.
96
+ */
97
+ async markOffline() {
98
+ this.retryManager.markDisconnected();
99
+ if (this.applianceId) {
100
+ try {
101
+ await this.applianceManager.updateApplianceState(this.applianceId, EnyoApplianceConnectionType.Connector, EnyoApplianceStateEnum.Offline);
102
+ }
103
+ catch (error) {
104
+ console.error(`${this.constructor.name} ${this.applianceId}: Failed to mark appliance offline: ${error}`);
105
+ }
106
+ }
107
+ }
108
+ sendCommandAcknowledge(messageId, acknowledgeMessage, answer, rejectionReason) {
109
+ if (!this.dataBus || !this.applianceId) {
110
+ return;
111
+ }
112
+ const ackMessage = {
113
+ id: randomUUID(),
114
+ message: EnyoDataBusMessageEnum.CommandAcknowledgeV1,
115
+ type: 'answer',
116
+ source: EnyoSourceEnum.Device,
117
+ applianceId: this.applianceId,
118
+ timestampIso: new Date().toISOString(),
119
+ data: {
120
+ messageId,
121
+ acknowledgeMessage: acknowledgeMessage,
122
+ answer,
123
+ rejectionReason
124
+ }
125
+ };
126
+ console.log(`${this.constructor.name} ${this.applianceId}: sending ${answer} for ${acknowledgeMessage} (messageId=${messageId}${rejectionReason ? `, reason=${rejectionReason}` : ''})`);
127
+ this.dataBus.sendMessage([ackMessage]);
128
+ }
65
129
  }
66
130
  /**
67
131
  * Sunspec Inverter implementation using dynamic model discovery
68
132
  */
69
133
  export class SunspecInverter extends BaseSunspecDevice {
134
+ capabilities;
135
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig) {
136
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig);
137
+ this.capabilities = capabilities;
138
+ }
70
139
  async connect() {
71
140
  // Ensure Sunspec client is connected
72
141
  if (!this.sunspecClient.isConnected()) {
@@ -107,8 +176,10 @@ export class SunspecInverter extends BaseSunspecDevice {
107
176
  if (mpptModel) {
108
177
  console.log(`MPPT model found for inverter ${this.networkDevice.hostname}`);
109
178
  }
179
+ this.startDataBusListening();
110
180
  }
111
181
  async disconnect() {
182
+ this.stopDataBusListening();
112
183
  if (this.applianceId) {
113
184
  await this.applianceManager.updateApplianceState(this.applianceId, EnyoApplianceConnectionType.Connector, EnyoApplianceStateEnum.Offline);
114
185
  }
@@ -119,7 +190,9 @@ export class SunspecInverter extends BaseSunspecDevice {
119
190
  }
120
191
  async readData(clockId, resolution) {
121
192
  if (!this.isConnected()) {
122
- return [];
193
+ await this.tryReconnect();
194
+ if (!this.isConnected())
195
+ return [];
123
196
  }
124
197
  const messages = [];
125
198
  const timestamp = new Date();
@@ -129,12 +202,9 @@ export class SunspecInverter extends BaseSunspecDevice {
129
202
  const mpptDataList = await this.sunspecClient.readAllMPPTData();
130
203
  const inverterSettings = await this.sunspecClient.readInverterSettings();
131
204
  const dcStrings = this.mapMPPTToStrings(mpptDataList);
132
- // Calculate battery discharge power to subtract from AC power
133
- const batteryDischargePowerW = extractBatteryDischargePowerFromMPPT(mpptDataList);
134
205
  if (inverterData) {
135
- const totalAcPowerW = inverterData.acPower || 0;
136
- const purePvPowerW = Math.max(0, totalAcPowerW - batteryDischargePowerW);
137
- console.log(`Got Battery Discharge power ${batteryDischargePowerW} and Inverter Power W ${totalAcPowerW} with pure PV Power ${purePvPowerW}`);
206
+ const pvPowerW = dcStrings.reduce((sum, s) => sum + (s.powerW || 0), 0);
207
+ console.log(`Got PV Power from DC strings: ${pvPowerW}W`);
138
208
  const inverterMessage = {
139
209
  id: randomUUID(),
140
210
  message: EnyoDataBusMessageEnum.InverterValuesUpdateV1,
@@ -145,7 +215,7 @@ export class SunspecInverter extends BaseSunspecDevice {
145
215
  timestampIso: timestamp.toISOString(),
146
216
  resolution,
147
217
  data: {
148
- pvPowerW: purePvPowerW,
218
+ pvPowerW,
149
219
  activePowerLimitationW: inverterSettings?.WMax,
150
220
  state: this.mapOperatingState(inverterData.operatingState),
151
221
  voltageL1: inverterData.voltageAN || 0,
@@ -169,6 +239,7 @@ export class SunspecInverter extends BaseSunspecDevice {
169
239
  }
170
240
  catch (error) {
171
241
  console.error(`Error updating inverter data: ${error}`);
242
+ await this.markOffline();
172
243
  }
173
244
  return messages;
174
245
  }
@@ -239,13 +310,72 @@ export class SunspecInverter extends BaseSunspecDevice {
239
310
  mapDcStringToApplianceMetadata(mpptDataList) {
240
311
  return mpptDataList.map(s => ({ index: s.index, name: s.name }));
241
312
  }
313
+ /**
314
+ * Start listening for inverter commands on the data bus.
315
+ * Idempotent — does nothing if already listening.
316
+ */
317
+ startDataBusListening() {
318
+ if (this.dataBusListenerId) {
319
+ return;
320
+ }
321
+ this.dataBus = this.energyApp.useDataBus();
322
+ this.dataBusListenerId = this.dataBus.listenForMessages([ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1], (entry) => this.handleInverterCommand(entry));
323
+ console.log(`Inverter ${this.applianceId}: started data bus listening (listener ${this.dataBusListenerId})`);
324
+ }
325
+ /**
326
+ * Stop listening for inverter commands on the data bus.
327
+ */
328
+ stopDataBusListening() {
329
+ if (this.dataBusListenerId && this.dataBus) {
330
+ this.dataBus.unsubscribe(this.dataBusListenerId);
331
+ console.log(`Inverter ${this.applianceId}: stopped data bus listening (listener ${this.dataBusListenerId})`);
332
+ }
333
+ this.dataBusListenerId = undefined;
334
+ this.dataBus = undefined;
335
+ }
336
+ handleInverterCommand(entry) {
337
+ if (entry.applianceId !== this.applianceId) {
338
+ return;
339
+ }
340
+ void (async () => {
341
+ try {
342
+ if (entry.message === ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1) {
343
+ await this.handleSetFeedInLimit(entry);
344
+ }
345
+ }
346
+ catch (error) {
347
+ console.error(`Inverter ${this.applianceId}: error handling ${entry.message}:`, error);
348
+ }
349
+ })();
350
+ }
351
+ async handleSetFeedInLimit(msg) {
352
+ // Check capability
353
+ if (!this.capabilities.includes(SunspecInverterCapability.FeedInLimit)) {
354
+ this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.NotSupported, 'FeedInLimit capability not enabled');
355
+ return;
356
+ }
357
+ if (!this.isConnected() || !this.applianceId) {
358
+ this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Not connected');
359
+ return;
360
+ }
361
+ console.log(`Inverter ${this.applianceId}: handling SetInverterFeedInLimitV1 (feedInLimitW=${msg.data.feedInLimitW})`);
362
+ const success = await this.sunspecClient.setFeedInLimit(msg.data.feedInLimitW);
363
+ if (!success) {
364
+ this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Failed to set feed-in limit');
365
+ return;
366
+ }
367
+ this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Accepted);
368
+ }
242
369
  }
243
370
  /**
244
371
  * Sunspec Battery implementation
245
372
  */
246
373
  export class SunspecBattery extends BaseSunspecDevice {
247
- dataBusListenerId;
248
- dataBus;
374
+ capabilities;
375
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig) {
376
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig);
377
+ this.capabilities = capabilities;
378
+ }
249
379
  /**
250
380
  * Connect to the battery and create/update the appliance
251
381
  */
@@ -332,7 +462,9 @@ export class SunspecBattery extends BaseSunspecDevice {
332
462
  */
333
463
  async readData(clockId, resolution) {
334
464
  if (!this.isConnected()) {
335
- return [];
465
+ await this.tryReconnect();
466
+ if (!this.isConnected())
467
+ return [];
336
468
  }
337
469
  const messages = [];
338
470
  const timestamp = new Date();
@@ -390,6 +522,7 @@ export class SunspecBattery extends BaseSunspecDevice {
390
522
  }
391
523
  catch (error) {
392
524
  console.error(`Error updating battery data: ${error}`);
525
+ await this.markOffline();
393
526
  }
394
527
  return messages;
395
528
  }
@@ -706,44 +839,35 @@ export class SunspecBattery extends BaseSunspecDevice {
706
839
  this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.NotSupported);
707
840
  return;
708
841
  }
709
- console.log(`Battery ${this.applianceId}: handling SetStorageDischargeLimitV1 (dischargeLimitPercent=${msg.data.dischargeLimitPercent})`);
710
- // Read current state for logging
842
+ console.log(`Battery ${this.applianceId}: handling SetStorageDischargeLimitV1 (dischargeLimitW=${msg.data.dischargeLimitW})`);
843
+ // Read current state to get wChaMax for percentage conversion
711
844
  const controls = await this.getBatteryControls();
712
- console.log(`Battery ${this.applianceId}: current state - outWRte=${controls?.outWRte}`);
845
+ console.log(`Battery ${this.applianceId}: current state - outWRte=${controls?.outWRte}, wChaMax=${controls?.wChaMax}`);
846
+ if (!controls?.wChaMax) {
847
+ this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Failed to read wChaMax for discharge limit conversion');
848
+ return;
849
+ }
850
+ // Convert watts to percentage of WDisChaMax (using wChaMax), clamped to 0-100%
851
+ const dischargeLimitPercent = Math.min(100, Math.max(0, (msg.data.dischargeLimitW / controls.wChaMax) * 100));
852
+ console.log(`Battery ${this.applianceId}: calculated discharge limit: ${dischargeLimitPercent.toFixed(1)}% (${msg.data.dischargeLimitW}W / ${controls.wChaMax}W)`);
713
853
  // Set discharge limit (Register 12: outWRte)
714
- const success = await this.writeBatteryControls({ outWRte: msg.data.dischargeLimitPercent });
854
+ const success = await this.writeBatteryControls({ outWRte: dischargeLimitPercent });
715
855
  if (!success) {
716
856
  this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Failed to set discharge limit');
717
857
  return;
718
858
  }
719
859
  this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Accepted);
720
860
  }
721
- sendCommandAcknowledge(messageId, acknowledgeMessage, answer, rejectionReason) {
722
- if (!this.dataBus || !this.applianceId) {
723
- return;
724
- }
725
- const ackMessage = {
726
- id: randomUUID(),
727
- message: EnyoDataBusMessageEnum.CommandAcknowledgeV1,
728
- type: 'answer',
729
- source: EnyoSourceEnum.Device,
730
- applianceId: this.applianceId,
731
- timestampIso: new Date().toISOString(),
732
- data: {
733
- messageId,
734
- acknowledgeMessage,
735
- answer,
736
- rejectionReason
737
- }
738
- };
739
- console.log(`Battery ${this.applianceId}: sending ${answer} for ${acknowledgeMessage} (messageId=${messageId}${rejectionReason ? `, reason=${rejectionReason}` : ''})`);
740
- this.dataBus.sendMessage([ackMessage]);
741
- }
742
861
  }
743
862
  /**
744
863
  * Sunspec Meter implementation
745
864
  */
746
865
  export class SunspecMeter extends BaseSunspecDevice {
866
+ capabilities;
867
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig) {
868
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig);
869
+ this.capabilities = capabilities;
870
+ }
747
871
  /**
748
872
  * Connect to the meter and create/update the appliance
749
873
  */
@@ -800,7 +924,9 @@ export class SunspecMeter extends BaseSunspecDevice {
800
924
  */
801
925
  async readData(clockId, resolution) {
802
926
  if (!this.isConnected()) {
803
- return [];
927
+ await this.tryReconnect();
928
+ if (!this.isConnected())
929
+ return [];
804
930
  }
805
931
  const messages = [];
806
932
  const timestamp = new Date();
@@ -831,6 +957,7 @@ export class SunspecMeter extends BaseSunspecDevice {
831
957
  }
832
958
  catch (error) {
833
959
  console.error(`Error updating meter data: ${error}`);
960
+ await this.markOffline();
834
961
  }
835
962
  return messages;
836
963
  }
@@ -2,13 +2,17 @@
2
2
  * SunSpec block interfaces with block numbers
3
3
  */
4
4
  /**
5
- * Configuration for connection retry with exponential backoff
5
+ * A single phase in the tiered retry schedule
6
+ */
7
+ export interface IRetryPhase {
8
+ intervalMs: number;
9
+ durationMs: number;
10
+ }
11
+ /**
12
+ * Configuration for connection retry with tiered schedule
6
13
  */
7
14
  export interface IRetryConfig {
8
- initialDelayMs: number;
9
- maxDelayMs: number;
10
- backoffFactor: number;
11
- maxAttempts: number;
15
+ phases: IRetryPhase[];
12
16
  }
13
17
  export declare const DEFAULT_RETRY_CONFIG: IRetryConfig;
14
18
  /**
@@ -551,6 +555,15 @@ export declare enum SunspecStorageMode {
551
555
  * 2. Set chaGriSet = 1 to allow grid charging
552
556
  * 3. Set wChaMax to the desired charging power in Watts
553
557
  */
558
+ export declare enum SunspecInverterCapability {
559
+ FeedInLimit = "feed-in-limit"
560
+ }
561
+ export declare enum SunspecBatteryCapability {
562
+ GridCharging = "grid-charging",
563
+ DischargeLimit = "discharge-limit"
564
+ }
565
+ export declare enum SunspecMeterCapability {
566
+ }
554
567
  export interface SunspecBatteryControls {
555
568
  storCtlMod?: number;
556
569
  chaGriSet?: number;
@@ -2,10 +2,12 @@
2
2
  * SunSpec block interfaces with block numbers
3
3
  */
4
4
  export const DEFAULT_RETRY_CONFIG = {
5
- initialDelayMs: 1000,
6
- maxDelayMs: 30000,
7
- backoffFactor: 1.5,
8
- maxAttempts: 10
5
+ phases: [
6
+ { intervalMs: 10_000, durationMs: 60_000 }, // Phase 1: every 10s for 1 minute
7
+ { intervalMs: 30_000, durationMs: 120_000 }, // Phase 2: every 30s for 2 minutes
8
+ { intervalMs: 60_000, durationMs: 300_000 }, // Phase 3: every 1m for 5 minutes
9
+ { intervalMs: 300_000, durationMs: 0 }, // Phase 4: every 5m forever
10
+ ]
9
11
  };
10
12
  /**
11
13
  * Common Sunspec Model IDs
@@ -197,3 +199,24 @@ export var SunspecStorageMode;
197
199
  SunspecStorageMode["HOLDING"] = "holding";
198
200
  SunspecStorageMode["AUTO"] = "auto"; // Both charge and discharge allowed
199
201
  })(SunspecStorageMode || (SunspecStorageMode = {}));
202
+ /**
203
+ * Battery control structure for writing to Model 124
204
+ * Used for controlling battery charge/discharge behavior
205
+ *
206
+ * IMPORTANT: To enable grid charging with specific power:
207
+ * 1. Set storCtlMod with appropriate bits to enable external control
208
+ * 2. Set chaGriSet = 1 to allow grid charging
209
+ * 3. Set wChaMax to the desired charging power in Watts
210
+ */
211
+ export var SunspecInverterCapability;
212
+ (function (SunspecInverterCapability) {
213
+ SunspecInverterCapability["FeedInLimit"] = "feed-in-limit";
214
+ })(SunspecInverterCapability || (SunspecInverterCapability = {}));
215
+ export var SunspecBatteryCapability;
216
+ (function (SunspecBatteryCapability) {
217
+ SunspecBatteryCapability["GridCharging"] = "grid-charging";
218
+ SunspecBatteryCapability["DischargeLimit"] = "discharge-limit";
219
+ })(SunspecBatteryCapability || (SunspecBatteryCapability = {}));
220
+ export var SunspecMeterCapability;
221
+ (function (SunspecMeterCapability) {
222
+ })(SunspecMeterCapability || (SunspecMeterCapability = {}));
@@ -16,9 +16,8 @@
16
16
  * - pad: 0x8000 (always returns this value)
17
17
  * - string: all registers 0x0000 (NULL)
18
18
  */
19
- import { type SunspecInverterControls, type SunspecInverterData, type SunspecInverterSettings, type SunspecMeterData, type SunspecModel, type SunspecMPPTData, type SunspecBatteryData, type SunspecBatteryBaseData, type SunspecBatteryControls, SunspecStorageMode, type IRetryConfig } from "./sunspec-interfaces.js";
20
- import { ConnectionRetryManager } from "./connection-retry-manager.js";
21
- import { IConnectionHealth } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/interfaces.js";
19
+ import { type SunspecInverterControls, type SunspecInverterData, type SunspecInverterSettings, type SunspecMeterData, type SunspecModel, type SunspecMPPTData, type SunspecBatteryData, type SunspecBatteryBaseData, type SunspecBatteryControls, SunspecStorageMode } from "./sunspec-interfaces.js";
20
+ import { EnergyAppModbusDataType, IConnectionHealth } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/interfaces.js";
22
21
  import { EnergyApp } from "@enyo-energy/energy-app-sdk";
23
22
  export declare class SunspecModbusClient {
24
23
  private energyApp;
@@ -29,9 +28,8 @@ export declare class SunspecModbusClient {
29
28
  private faultTolerantReader;
30
29
  private modbusDataTypeConverter;
31
30
  private connectionParams;
32
- private retryManager;
33
31
  private autoReconnectEnabled;
34
- constructor(energyApp: EnergyApp, retryConfig?: Partial<IRetryConfig>);
32
+ constructor(energyApp: EnergyApp);
35
33
  /**
36
34
  * Connect to Modbus device
37
35
  * @param host Primary host (hostname) to connect to
@@ -55,11 +53,6 @@ export declare class SunspecModbusClient {
55
53
  * Returns true if successful, false otherwise
56
54
  */
57
55
  private attemptConnection;
58
- /**
59
- * Check connection health and trigger automatic reconnection if unhealthy
60
- * Returns true if connection is healthy or was successfully restored
61
- */
62
- ensureHealthyConnection(): Promise<boolean>;
63
56
  /**
64
57
  * Enable or disable automatic reconnection
65
58
  */
@@ -68,10 +61,6 @@ export declare class SunspecModbusClient {
68
61
  * Check if auto-reconnect is enabled
69
62
  */
70
63
  isAutoReconnectEnabled(): boolean;
71
- /**
72
- * Get the retry manager for advanced configuration
73
- */
74
- getRetryManager(): ConnectionRetryManager;
75
64
  /**
76
65
  * Detect the base address and addressing mode (0-based or 1-based) for SunSpec
77
66
  */
@@ -119,13 +108,13 @@ export declare class SunspecModbusClient {
119
108
  /**
120
109
  * Helper to read register value(s) using the fault-tolerant reader with data type conversion
121
110
  */
122
- private readRegisterValue;
111
+ readRegisterValue(address: number, quantity: number | undefined, dataType: EnergyAppModbusDataType): Promise<number | string | number[]>;
123
112
  /**
124
113
  * Helper to write register value(s)
125
114
  */
126
- private writeRegisterValue;
115
+ writeRegisterValue(address: number, value: number | number[], dataType?: EnergyAppModbusDataType): Promise<boolean>;
127
116
  /**
128
- * Read inverter data from Model 103 (Three Phase)
117
+ * Read inverter data from Model 101 (Single Phase) / Model 103 (Three Phase)
129
118
  */
130
119
  readInverterData(): Promise<SunspecInverterData | null>;
131
120
  /**
@@ -140,11 +129,10 @@ export declare class SunspecModbusClient {
140
129
  * Apply scale factor to a value
141
130
  * Returns undefined if the value is unimplemented or scale factor is out of range
142
131
  */
143
- private applyScaleFactor;
144
- private logRegisterRead;
132
+ applyScaleFactor(value: number, scaleFactor: number, dataType?: 'uint16' | 'int16' | 'acc32', fieldName?: string, offset?: number, modelId?: number): number | undefined;
133
+ logRegisterRead(modelId: number, offset: number, fieldName: string, rawValue: number | string | undefined, dataType?: string): void;
145
134
  /**
146
- * Read MPPT Scale Factors for a specific module
147
- * Returns the scale factors for DC Current, DC Voltage, DC Power, and DC Energy
135
+ * Read scale factors from Model 160 (MPPT)
148
136
  *
149
137
  * MPPT Model 160 Scale Factor Register Offsets (relative to module start):
150
138
  * - DCA_SF (Current Scale Factor): Offset 2
@@ -165,7 +153,7 @@ export declare class SunspecModbusClient {
165
153
  */
166
154
  readMPPTData(moduleId?: number): Promise<SunspecMPPTData | null>;
167
155
  /**
168
- * Read all available MPPT strings
156
+ * Read all MPPT strings from Model 160 (Multiple MPPT)
169
157
  */
170
158
  readAllMPPTData(): Promise<SunspecMPPTData[]>;
171
159
  /**
@@ -191,7 +179,7 @@ export declare class SunspecModbusClient {
191
179
  */
192
180
  readBatteryBaseData(): Promise<SunspecBatteryBaseData | null>;
193
181
  /**
194
- * Read battery data from Model 124 (Basic Storage Controls)
182
+ * Read battery data from Model 124 (Basic Storage) with fallback to Model 802 / Model 803
195
183
  */
196
184
  readBatteryData(): Promise<SunspecBatteryData | null>;
197
185
  /**
@@ -207,11 +195,11 @@ export declare class SunspecModbusClient {
207
195
  */
208
196
  enableGridCharging(enable: boolean): Promise<boolean>;
209
197
  /**
210
- * Read current battery control settings
198
+ * Read battery control settings from Model 124 (Basic Storage Controls)
211
199
  */
212
200
  readBatteryControls(): Promise<SunspecBatteryControls | null>;
213
201
  /**
214
- * Read meter data (Model 203 for 3-phase)
202
+ * Read meter data from Model 201 (Single Phase) / Model 203 (Three Phase) / Model 204 (Split Phase)
215
203
  */
216
204
  readMeterData(): Promise<SunspecMeterData | null>;
217
205
  /**
@@ -235,11 +223,11 @@ export declare class SunspecModbusClient {
235
223
  */
236
224
  getConnectionHealth(): IConnectionHealth;
237
225
  /**
238
- * Read Block 121 - Inverter Basic Settings
226
+ * Read inverter settings from Model 121 (Inverter Settings)
239
227
  */
240
228
  readInverterSettings(): Promise<SunspecInverterSettings | null>;
241
229
  /**
242
- * Read Block 123 - Immediate Inverter Controls
230
+ * Read inverter controls from Model 123 (Immediate Inverter Controls)
243
231
  */
244
232
  readInverterControls(): Promise<SunspecInverterControls | null>;
245
233
  /**
@@ -247,7 +235,18 @@ export declare class SunspecModbusClient {
247
235
  */
248
236
  writeInverterSettings(settings: Partial<SunspecInverterSettings>): Promise<boolean>;
249
237
  /**
250
- * Write Block 123 - Immediate Inverter Controls
238
+ * Write inverter controls to Model 123 (Immediate Inverter Controls)
251
239
  */
252
240
  writeInverterControls(controls: Partial<SunspecInverterControls>): Promise<boolean>;
241
+ /**
242
+ * Set the inverter feed-in power limit using Model 123 (Immediate Inverter Controls)
243
+ *
244
+ * When limitW is a number, reads WMax from Model 121, computes percentage,
245
+ * writes WMaxLimPct and enables WMaxLim_Ena.
246
+ * When limitW is null, disables the limit by setting WMaxLim_Ena = DISABLED.
247
+ *
248
+ * @param limitW - Power limit in Watts, or null to remove the limit
249
+ * @returns true if successful, false otherwise
250
+ */
251
+ setFeedInLimit(limitW: number | null): Promise<boolean>;
253
252
  }