@enyo-energy/sunspec-sdk 0.0.58 → 0.0.60

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.
@@ -92,13 +92,14 @@ class BaseSunspecDevice {
92
92
  unitId;
93
93
  port;
94
94
  baseAddress;
95
+ useTls;
95
96
  applianceId;
96
97
  lastUpdateTime = 0;
97
98
  dataBusListenerId;
98
99
  dataBus;
99
100
  retryManager;
100
101
  consecutiveReconnectFailures = 0;
101
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, retryConfig, appliance) {
102
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, retryConfig, appliance, useTls) {
102
103
  this.energyApp = energyApp;
103
104
  this.name = name;
104
105
  this.networkDevice = networkDevice;
@@ -107,6 +108,7 @@ class BaseSunspecDevice {
107
108
  this.unitId = unitId;
108
109
  this.port = port;
109
110
  this.baseAddress = baseAddress;
111
+ this.useTls = useTls;
110
112
  this.retryManager = new connection_retry_manager_js_1.ConnectionRetryManager(retryConfig);
111
113
  if (appliance) {
112
114
  this.applianceId = appliance.id;
@@ -127,8 +129,8 @@ class BaseSunspecDevice {
127
129
  async ensureConnected() {
128
130
  if (!this.sunspecClient.isConnected(this.unitId)) {
129
131
  await this.sunspecClient.connect(this.networkDevice.hostname, // primary
130
- this.port, this.unitId, this.networkDevice.ipAddress // secondary fallback
131
- );
132
+ this.port, this.unitId, this.networkDevice.ipAddress, // secondary fallback
133
+ this.useTls);
132
134
  await this.sunspecClient.discoverModels(this.unitId, this.baseAddress);
133
135
  }
134
136
  }
@@ -243,16 +245,16 @@ class SunspecInverter extends BaseSunspecDevice {
243
245
  static CONNECTION_FAULT_THRESHOLD = 3;
244
246
  storage;
245
247
  errorState = { activeCodes: [], lastStatus: 'healthy' };
246
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
247
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
248
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance, useTls) {
249
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance, useTls);
248
250
  this.capabilities = capabilities;
249
251
  }
250
252
  async connect() {
251
253
  // Ensure Sunspec client is connected
252
254
  if (!this.sunspecClient.isConnected(this.unitId)) {
253
255
  await this.sunspecClient.connect(this.networkDevice.hostname, // primary
254
- this.port, this.unitId, this.networkDevice.ipAddress // secondary fallback
255
- );
256
+ this.port, this.unitId, this.networkDevice.ipAddress, // secondary fallback
257
+ this.useTls);
256
258
  await this.sunspecClient.discoverModels(this.unitId, this.baseAddress);
257
259
  }
258
260
  // Get device info from common block
@@ -674,8 +676,8 @@ exports.SunspecInverter = SunspecInverter;
674
676
  */
675
677
  class SunspecBattery extends BaseSunspecDevice {
676
678
  capabilities;
677
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
678
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
679
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance, useTls) {
680
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance, useTls);
679
681
  this.capabilities = capabilities;
680
682
  }
681
683
  /**
@@ -1196,8 +1198,8 @@ exports.SunspecBattery = SunspecBattery;
1196
1198
  */
1197
1199
  class SunspecMeter extends BaseSunspecDevice {
1198
1200
  capabilities;
1199
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
1200
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
1201
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance, useTls) {
1202
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance, useTls);
1201
1203
  this.capabilities = capabilities;
1202
1204
  }
1203
1205
  /**
@@ -18,13 +18,14 @@ export declare abstract class BaseSunspecDevice {
18
18
  protected readonly unitId: number;
19
19
  protected readonly port: number;
20
20
  protected readonly baseAddress: number;
21
+ protected readonly useTls?: boolean | undefined;
21
22
  protected applianceId?: string;
22
23
  protected lastUpdateTime: number;
23
24
  protected dataBusListenerId?: string;
24
25
  protected dataBus?: EnergyAppDataBus;
25
26
  protected retryManager: ConnectionRetryManager;
26
27
  protected consecutiveReconnectFailures: number;
27
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
28
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, retryConfig?: IRetryConfig, appliance?: EnyoAppliance, useTls?: boolean | undefined);
28
29
  /**
29
30
  * Connect to the device and create/update the appliance
30
31
  */
@@ -76,7 +77,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
76
77
  private static readonly CONNECTION_FAULT_THRESHOLD;
77
78
  private storage?;
78
79
  private errorState;
79
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecInverterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
80
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecInverterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance, useTls?: boolean);
80
81
  connect(): Promise<void>;
81
82
  disconnect(): Promise<void>;
82
83
  readData(clockId: string, resolution: '10s' | '30s' | '1m' | '15m'): Promise<EnyoDataBusMessage[]>;
@@ -143,7 +144,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
143
144
  */
144
145
  export declare class SunspecBattery extends BaseSunspecDevice {
145
146
  private readonly capabilities;
146
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecBatteryCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
147
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecBatteryCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance, useTls?: boolean);
147
148
  /**
148
149
  * Connect to the battery and create/update the appliance
149
150
  */
@@ -260,7 +261,7 @@ export declare class SunspecBattery extends BaseSunspecDevice {
260
261
  */
261
262
  export declare class SunspecMeter extends BaseSunspecDevice {
262
263
  private readonly capabilities;
263
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecMeterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
264
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecMeterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance, useTls?: boolean);
264
265
  /**
265
266
  * Connect to the meter and create/update the appliance
266
267
  */
@@ -109,8 +109,9 @@ class SunspecModbusClient {
109
109
  * @param port Modbus port (default 502)
110
110
  * @param unitId Modbus unit ID (default 1)
111
111
  * @param secondaryHost Optional secondary host (ipAddress) for fallback during reconnection
112
+ * @param useTls Whether to use TLS/SSL for the Modbus connection (Modbus Security, typically port 802). Defaults to false.
112
113
  */
113
- async connect(host, port = 502, unitId = 1, secondaryHost) {
114
+ async connect(host, port = 502, unitId = 1, secondaryHost, useTls) {
114
115
  return this.withConnectionLock(async () => {
115
116
  if (this.connectionParams) {
116
117
  if (this.connectionParams.primaryHost !== host || this.connectionParams.port !== port) {
@@ -120,9 +121,12 @@ class SunspecModbusClient {
120
121
  if (secondaryHost && !this.connectionParams.secondaryHost) {
121
122
  this.connectionParams.secondaryHost = secondaryHost;
122
123
  }
124
+ if (useTls !== undefined) {
125
+ this.connectionParams.useTls = useTls;
126
+ }
123
127
  }
124
128
  else {
125
- this.connectionParams = { primaryHost: host, secondaryHost, port };
129
+ this.connectionParams = { primaryHost: host, secondaryHost, port, useTls };
126
130
  }
127
131
  this.knownUnits.add(unitId);
128
132
  if (this.modbusInstances.has(unitId)) {
@@ -268,7 +272,8 @@ class SunspecModbusClient {
268
272
  host,
269
273
  port,
270
274
  unitId,
271
- timeout: 5000
275
+ timeout: 5000,
276
+ useTls: this.connectionParams?.useTls
272
277
  });
273
278
  if (!candidate) {
274
279
  throw new Error(`useModbus().connect returned null for ${host}:${port} unit ${unitId}`);
@@ -584,7 +589,7 @@ class SunspecModbusClient {
584
589
  return value.replace(/\u0000/g, '').trim();
585
590
  }
586
591
  /**
587
- * Read an entire model's register block in a single Modbus call.
592
+ * Read an entire model's register block, chunking if needed.
588
593
  * Returns a Buffer containing all registers for the model.
589
594
  */
590
595
  async readModelBlock(unitId, model) {
@@ -593,12 +598,21 @@ class SunspecModbusClient {
593
598
  // This way buffer offsets match the convention used throughout: offset 0 = model ID,
594
599
  // offset 1 = model length, offset 2 = first data register, etc.
595
600
  const totalRegisters = model.length + 2;
596
- const result = await reader.readHoldingRegisters(model.address, totalRegisters);
597
- if (!result.success || !result.value) {
598
- throw new Error(`Failed to read model block ${model.id} at address ${model.address} (unit ${unitId}): ${result.error?.message || 'Unknown error'}`);
601
+ // Modbus FC03 caps a single read at 125 registers. Float models (211–214) have
602
+ // length 124, which combined with the 2-register header (126) exceeds the limit,
603
+ // so chunk reads larger than 125 registers and concatenate the results.
604
+ const MAX_REGISTERS_PER_READ = 125;
605
+ const chunks = [];
606
+ for (let offset = 0; offset < totalRegisters; offset += MAX_REGISTERS_PER_READ) {
607
+ const quantity = Math.min(MAX_REGISTERS_PER_READ, totalRegisters - offset);
608
+ const result = await reader.readHoldingRegisters(model.address + offset, quantity);
609
+ if (!result.success || !result.value) {
610
+ throw new Error(`Failed to read model block ${model.id} at address ${model.address + offset} (unit ${unitId}): ${result.error?.message || 'Unknown error'}`);
611
+ }
612
+ chunks.push(result.value);
599
613
  }
600
614
  this.connectionHealth.recordSuccess();
601
- return result.value;
615
+ return chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
602
616
  }
603
617
  /**
604
618
  * Extract a typed value from a model block buffer at a given register offset.
@@ -55,8 +55,9 @@ export declare class SunspecModbusClient {
55
55
  * @param port Modbus port (default 502)
56
56
  * @param unitId Modbus unit ID (default 1)
57
57
  * @param secondaryHost Optional secondary host (ipAddress) for fallback during reconnection
58
+ * @param useTls Whether to use TLS/SSL for the Modbus connection (Modbus Security, typically port 802). Defaults to false.
58
59
  */
59
- connect(host: string, port?: number, unitId?: number, secondaryHost?: string): Promise<void>;
60
+ connect(host: string, port?: number, unitId?: number, secondaryHost?: string, useTls?: boolean): Promise<void>;
60
61
  /**
61
62
  * Disconnect from all units of this network device.
62
63
  *
@@ -180,7 +181,7 @@ export declare class SunspecModbusClient {
180
181
  */
181
182
  private cleanString;
182
183
  /**
183
- * Read an entire model's register block in a single Modbus call.
184
+ * Read an entire model's register block, chunking if needed.
184
185
  * Returns a Buffer containing all registers for the model.
185
186
  */
186
187
  private readModelBlock;
@@ -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.58';
12
+ exports.SDK_VERSION = '0.0.60';
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.58";
8
+ export declare const SDK_VERSION = "0.0.60";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
@@ -18,13 +18,14 @@ export declare abstract class BaseSunspecDevice {
18
18
  protected readonly unitId: number;
19
19
  protected readonly port: number;
20
20
  protected readonly baseAddress: number;
21
+ protected readonly useTls?: boolean | undefined;
21
22
  protected applianceId?: string;
22
23
  protected lastUpdateTime: number;
23
24
  protected dataBusListenerId?: string;
24
25
  protected dataBus?: EnergyAppDataBus;
25
26
  protected retryManager: ConnectionRetryManager;
26
27
  protected consecutiveReconnectFailures: number;
27
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
28
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, retryConfig?: IRetryConfig, appliance?: EnyoAppliance, useTls?: boolean | undefined);
28
29
  /**
29
30
  * Connect to the device and create/update the appliance
30
31
  */
@@ -76,7 +77,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
76
77
  private static readonly CONNECTION_FAULT_THRESHOLD;
77
78
  private storage?;
78
79
  private errorState;
79
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecInverterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
80
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecInverterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance, useTls?: boolean);
80
81
  connect(): Promise<void>;
81
82
  disconnect(): Promise<void>;
82
83
  readData(clockId: string, resolution: '10s' | '30s' | '1m' | '15m'): Promise<EnyoDataBusMessage[]>;
@@ -143,7 +144,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
143
144
  */
144
145
  export declare class SunspecBattery extends BaseSunspecDevice {
145
146
  private readonly capabilities;
146
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecBatteryCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
147
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecBatteryCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance, useTls?: boolean);
147
148
  /**
148
149
  * Connect to the battery and create/update the appliance
149
150
  */
@@ -260,7 +261,7 @@ export declare class SunspecBattery extends BaseSunspecDevice {
260
261
  */
261
262
  export declare class SunspecMeter extends BaseSunspecDevice {
262
263
  private readonly capabilities;
263
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecMeterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
264
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecMeterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance, useTls?: boolean);
264
265
  /**
265
266
  * Connect to the meter and create/update the appliance
266
267
  */
@@ -89,13 +89,14 @@ export class BaseSunspecDevice {
89
89
  unitId;
90
90
  port;
91
91
  baseAddress;
92
+ useTls;
92
93
  applianceId;
93
94
  lastUpdateTime = 0;
94
95
  dataBusListenerId;
95
96
  dataBus;
96
97
  retryManager;
97
98
  consecutiveReconnectFailures = 0;
98
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, retryConfig, appliance) {
99
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, retryConfig, appliance, useTls) {
99
100
  this.energyApp = energyApp;
100
101
  this.name = name;
101
102
  this.networkDevice = networkDevice;
@@ -104,6 +105,7 @@ export class BaseSunspecDevice {
104
105
  this.unitId = unitId;
105
106
  this.port = port;
106
107
  this.baseAddress = baseAddress;
108
+ this.useTls = useTls;
107
109
  this.retryManager = new ConnectionRetryManager(retryConfig);
108
110
  if (appliance) {
109
111
  this.applianceId = appliance.id;
@@ -124,8 +126,8 @@ export class BaseSunspecDevice {
124
126
  async ensureConnected() {
125
127
  if (!this.sunspecClient.isConnected(this.unitId)) {
126
128
  await this.sunspecClient.connect(this.networkDevice.hostname, // primary
127
- this.port, this.unitId, this.networkDevice.ipAddress // secondary fallback
128
- );
129
+ this.port, this.unitId, this.networkDevice.ipAddress, // secondary fallback
130
+ this.useTls);
129
131
  await this.sunspecClient.discoverModels(this.unitId, this.baseAddress);
130
132
  }
131
133
  }
@@ -239,16 +241,16 @@ export class SunspecInverter extends BaseSunspecDevice {
239
241
  static CONNECTION_FAULT_THRESHOLD = 3;
240
242
  storage;
241
243
  errorState = { activeCodes: [], lastStatus: 'healthy' };
242
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
243
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
244
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance, useTls) {
245
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance, useTls);
244
246
  this.capabilities = capabilities;
245
247
  }
246
248
  async connect() {
247
249
  // Ensure Sunspec client is connected
248
250
  if (!this.sunspecClient.isConnected(this.unitId)) {
249
251
  await this.sunspecClient.connect(this.networkDevice.hostname, // primary
250
- this.port, this.unitId, this.networkDevice.ipAddress // secondary fallback
251
- );
252
+ this.port, this.unitId, this.networkDevice.ipAddress, // secondary fallback
253
+ this.useTls);
252
254
  await this.sunspecClient.discoverModels(this.unitId, this.baseAddress);
253
255
  }
254
256
  // Get device info from common block
@@ -669,8 +671,8 @@ export class SunspecInverter extends BaseSunspecDevice {
669
671
  */
670
672
  export class SunspecBattery extends BaseSunspecDevice {
671
673
  capabilities;
672
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
673
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
674
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance, useTls) {
675
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance, useTls);
674
676
  this.capabilities = capabilities;
675
677
  }
676
678
  /**
@@ -1190,8 +1192,8 @@ export class SunspecBattery extends BaseSunspecDevice {
1190
1192
  */
1191
1193
  export class SunspecMeter extends BaseSunspecDevice {
1192
1194
  capabilities;
1193
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
1194
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
1195
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance, useTls) {
1196
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance, useTls);
1195
1197
  this.capabilities = capabilities;
1196
1198
  }
1197
1199
  /**
@@ -55,8 +55,9 @@ export declare class SunspecModbusClient {
55
55
  * @param port Modbus port (default 502)
56
56
  * @param unitId Modbus unit ID (default 1)
57
57
  * @param secondaryHost Optional secondary host (ipAddress) for fallback during reconnection
58
+ * @param useTls Whether to use TLS/SSL for the Modbus connection (Modbus Security, typically port 802). Defaults to false.
58
59
  */
59
- connect(host: string, port?: number, unitId?: number, secondaryHost?: string): Promise<void>;
60
+ connect(host: string, port?: number, unitId?: number, secondaryHost?: string, useTls?: boolean): Promise<void>;
60
61
  /**
61
62
  * Disconnect from all units of this network device.
62
63
  *
@@ -180,7 +181,7 @@ export declare class SunspecModbusClient {
180
181
  */
181
182
  private cleanString;
182
183
  /**
183
- * Read an entire model's register block in a single Modbus call.
184
+ * Read an entire model's register block, chunking if needed.
184
185
  * Returns a Buffer containing all registers for the model.
185
186
  */
186
187
  private readModelBlock;
@@ -104,8 +104,9 @@ export class SunspecModbusClient {
104
104
  * @param port Modbus port (default 502)
105
105
  * @param unitId Modbus unit ID (default 1)
106
106
  * @param secondaryHost Optional secondary host (ipAddress) for fallback during reconnection
107
+ * @param useTls Whether to use TLS/SSL for the Modbus connection (Modbus Security, typically port 802). Defaults to false.
107
108
  */
108
- async connect(host, port = 502, unitId = 1, secondaryHost) {
109
+ async connect(host, port = 502, unitId = 1, secondaryHost, useTls) {
109
110
  return this.withConnectionLock(async () => {
110
111
  if (this.connectionParams) {
111
112
  if (this.connectionParams.primaryHost !== host || this.connectionParams.port !== port) {
@@ -115,9 +116,12 @@ export class SunspecModbusClient {
115
116
  if (secondaryHost && !this.connectionParams.secondaryHost) {
116
117
  this.connectionParams.secondaryHost = secondaryHost;
117
118
  }
119
+ if (useTls !== undefined) {
120
+ this.connectionParams.useTls = useTls;
121
+ }
118
122
  }
119
123
  else {
120
- this.connectionParams = { primaryHost: host, secondaryHost, port };
124
+ this.connectionParams = { primaryHost: host, secondaryHost, port, useTls };
121
125
  }
122
126
  this.knownUnits.add(unitId);
123
127
  if (this.modbusInstances.has(unitId)) {
@@ -263,7 +267,8 @@ export class SunspecModbusClient {
263
267
  host,
264
268
  port,
265
269
  unitId,
266
- timeout: 5000
270
+ timeout: 5000,
271
+ useTls: this.connectionParams?.useTls
267
272
  });
268
273
  if (!candidate) {
269
274
  throw new Error(`useModbus().connect returned null for ${host}:${port} unit ${unitId}`);
@@ -579,7 +584,7 @@ export class SunspecModbusClient {
579
584
  return value.replace(/\u0000/g, '').trim();
580
585
  }
581
586
  /**
582
- * Read an entire model's register block in a single Modbus call.
587
+ * Read an entire model's register block, chunking if needed.
583
588
  * Returns a Buffer containing all registers for the model.
584
589
  */
585
590
  async readModelBlock(unitId, model) {
@@ -588,12 +593,21 @@ export class SunspecModbusClient {
588
593
  // This way buffer offsets match the convention used throughout: offset 0 = model ID,
589
594
  // offset 1 = model length, offset 2 = first data register, etc.
590
595
  const totalRegisters = model.length + 2;
591
- const result = await reader.readHoldingRegisters(model.address, totalRegisters);
592
- if (!result.success || !result.value) {
593
- throw new Error(`Failed to read model block ${model.id} at address ${model.address} (unit ${unitId}): ${result.error?.message || 'Unknown error'}`);
596
+ // Modbus FC03 caps a single read at 125 registers. Float models (211–214) have
597
+ // length 124, which combined with the 2-register header (126) exceeds the limit,
598
+ // so chunk reads larger than 125 registers and concatenate the results.
599
+ const MAX_REGISTERS_PER_READ = 125;
600
+ const chunks = [];
601
+ for (let offset = 0; offset < totalRegisters; offset += MAX_REGISTERS_PER_READ) {
602
+ const quantity = Math.min(MAX_REGISTERS_PER_READ, totalRegisters - offset);
603
+ const result = await reader.readHoldingRegisters(model.address + offset, quantity);
604
+ if (!result.success || !result.value) {
605
+ throw new Error(`Failed to read model block ${model.id} at address ${model.address + offset} (unit ${unitId}): ${result.error?.message || 'Unknown error'}`);
606
+ }
607
+ chunks.push(result.value);
594
608
  }
595
609
  this.connectionHealth.recordSuccess();
596
- return result.value;
610
+ return chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
597
611
  }
598
612
  /**
599
613
  * Extract a typed value from a model block buffer at a given register offset.
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.58";
8
+ export declare const SDK_VERSION = "0.0.60";
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.58';
8
+ export const SDK_VERSION = '0.0.60';
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.58",
3
+ "version": "0.0.60",
4
4
  "description": "enyo Energy Sunspec SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -37,7 +37,7 @@
37
37
  "typescript": "^5.8.3"
38
38
  },
39
39
  "dependencies": {
40
- "@enyo-energy/energy-app-sdk": "^0.0.121"
40
+ "@enyo-energy/energy-app-sdk": "^0.0.123"
41
41
  },
42
42
  "volta": {
43
43
  "node": "22.17.0"