@enyo-energy/sunspec-sdk 0.0.42 → 0.0.43

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,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SunspecMeter = exports.SunspecBattery = exports.SunspecInverter = exports.BaseSunspecDevice = exports.ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1 = void 0;
3
+ exports.SunspecMeter = exports.SunspecBattery = exports.SunspecInverter = exports.BaseSunspecDevice = void 0;
4
4
  const sunspec_interfaces_js_1 = require("./sunspec-interfaces.cjs");
5
5
  const node_crypto_1 = require("node:crypto");
6
6
  const enyo_appliance_js_1 = require("@enyo-energy/energy-app-sdk/dist/types/enyo-appliance.js");
@@ -9,8 +9,6 @@ const enyo_data_bus_value_js_1 = require("@enyo-energy/energy-app-sdk/dist/types
9
9
  const enyo_source_enum_js_1 = require("@enyo-energy/energy-app-sdk/dist/types/enyo-source.enum.js");
10
10
  const enyo_meter_appliance_js_1 = require("@enyo-energy/energy-app-sdk/dist/types/enyo-meter-appliance.js");
11
11
  const enyo_battery_appliance_js_1 = require("@enyo-energy/energy-app-sdk/dist/types/enyo-battery-appliance.js");
12
- // TODO: Remove once added to @enyo-energy/energy-app-sdk EnyoDataBusMessageEnum
13
- exports.ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1 = 'SetInverterFeedInLimitV1';
14
12
  /**
15
13
  * Base abstract class for all Sunspec devices
16
14
  */
@@ -28,7 +26,7 @@ class BaseSunspecDevice {
28
26
  dataBusListenerId;
29
27
  dataBus;
30
28
  retryManager;
31
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, retryConfig) {
29
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, retryConfig, appliance) {
32
30
  this.energyApp = energyApp;
33
31
  this.name = name;
34
32
  this.networkDevice = networkDevice;
@@ -38,12 +36,15 @@ class BaseSunspecDevice {
38
36
  this.port = port;
39
37
  this.baseAddress = baseAddress;
40
38
  this.retryManager = new connection_retry_manager_js_1.ConnectionRetryManager(retryConfig);
39
+ if (appliance) {
40
+ this.applianceId = appliance.id;
41
+ }
41
42
  }
42
43
  /**
43
44
  * Check if the device is connected
44
45
  */
45
46
  isConnected() {
46
- return this.sunspecClient.isConnected();
47
+ return this.sunspecClient.isHealthy();
47
48
  }
48
49
  /**
49
50
  * Get the appliance IDs managed by this device
@@ -136,8 +137,8 @@ exports.BaseSunspecDevice = BaseSunspecDevice;
136
137
  */
137
138
  class SunspecInverter extends BaseSunspecDevice {
138
139
  capabilities;
139
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig) {
140
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig);
140
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
141
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
141
142
  this.capabilities = capabilities;
142
143
  }
143
144
  async connect() {
@@ -152,29 +153,31 @@ class SunspecInverter extends BaseSunspecDevice {
152
153
  const commonData = await this.sunspecClient.readCommonBlock();
153
154
  const inverterSettings = await this.sunspecClient.readInverterSettings();
154
155
  const mpptDataList = await this.sunspecClient.readAllMPPTData();
155
- // Create or update appliance
156
- try {
157
- this.applianceId = await this.applianceManager.createOrUpdateAppliance({
158
- name: this.name,
159
- type: enyo_appliance_js_1.EnyoApplianceTypeEnum.Inverter,
160
- networkDevices: [this.networkDevice],
161
- metadata: {
162
- connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
163
- state: enyo_appliance_js_1.EnyoApplianceStateEnum.Connected,
164
- serialNumber: commonData?.serialNumber,
165
- modelName: commonData?.model,
166
- vendorName: commonData?.manufacturer,
167
- modbus: { unitId: this.unitId },
168
- },
169
- inverter: {
170
- dcStrings: this.mapDcStringToApplianceMetadata(this.mapMPPTToStrings(mpptDataList)),
171
- maxPvProductionW: inverterSettings?.WMax
172
- }
173
- });
174
- console.log(`Sunspec Inverter connected: ${this.networkDevice.hostname} (${this.applianceId})`);
175
- }
176
- catch (error) {
177
- console.error(`Failed to create inverter appliance: ${error}`);
156
+ // Create or update appliance (skip if an existing appliance was provided)
157
+ if (!this.applianceId) {
158
+ try {
159
+ this.applianceId = await this.applianceManager.createOrUpdateAppliance({
160
+ name: this.name,
161
+ type: enyo_appliance_js_1.EnyoApplianceTypeEnum.Inverter,
162
+ networkDevices: [this.networkDevice],
163
+ metadata: {
164
+ connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
165
+ state: enyo_appliance_js_1.EnyoApplianceStateEnum.Connected,
166
+ serialNumber: commonData?.serialNumber,
167
+ modelName: commonData?.model,
168
+ vendorName: commonData?.manufacturer,
169
+ modbus: { unitId: this.unitId },
170
+ },
171
+ inverter: {
172
+ dcStrings: this.mapDcStringToApplianceMetadata(this.mapMPPTToStrings(mpptDataList)),
173
+ maxPvProductionW: inverterSettings?.WMax
174
+ }
175
+ });
176
+ console.log(`Sunspec Inverter connected: ${this.networkDevice.hostname} (${this.applianceId})`);
177
+ }
178
+ catch (error) {
179
+ console.error(`Failed to create inverter appliance: ${error}`);
180
+ }
178
181
  }
179
182
  // Check for MPPT models
180
183
  const mpptModel = this.sunspecClient.findModel(sunspec_interfaces_js_1.SunspecModelId.MPPT);
@@ -190,9 +193,6 @@ class SunspecInverter extends BaseSunspecDevice {
190
193
  }
191
194
  // Note: We don't disconnect the sunspecClient as it may be shared
192
195
  }
193
- isConnected() {
194
- return this.sunspecClient.isConnected();
195
- }
196
196
  async readData(clockId, resolution) {
197
197
  if (!this.isConnected()) {
198
198
  await this.tryReconnect();
@@ -324,7 +324,7 @@ class SunspecInverter extends BaseSunspecDevice {
324
324
  return;
325
325
  }
326
326
  this.dataBus = this.energyApp.useDataBus();
327
- this.dataBusListenerId = this.dataBus.listenForMessages([exports.ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1], (entry) => this.handleInverterCommand(entry));
327
+ this.dataBusListenerId = this.dataBus.listenForMessages([enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.SetInverterFeedInLimitV1], (entry) => this.handleInverterCommand(entry));
328
328
  console.log(`Inverter ${this.applianceId}: started data bus listening (listener ${this.dataBusListenerId})`);
329
329
  }
330
330
  /**
@@ -344,7 +344,7 @@ class SunspecInverter extends BaseSunspecDevice {
344
344
  }
345
345
  void (async () => {
346
346
  try {
347
- if (entry.message === exports.ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1) {
347
+ if (entry.message === enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.SetInverterFeedInLimitV1) {
348
348
  await this.handleSetFeedInLimit(entry);
349
349
  }
350
350
  }
@@ -378,8 +378,8 @@ exports.SunspecInverter = SunspecInverter;
378
378
  */
379
379
  class SunspecBattery extends BaseSunspecDevice {
380
380
  capabilities;
381
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig) {
382
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig);
381
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
382
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
383
383
  this.capabilities = capabilities;
384
384
  }
385
385
  /**
@@ -399,38 +399,40 @@ class SunspecBattery extends BaseSunspecDevice {
399
399
  const batteryData = await this.sunspecClient.readBatteryData();
400
400
  const storageMode = this.determineStorageMode(batteryData);
401
401
  const features = [];
402
- // Create or update appliance
403
402
  if (batteryData?.chaGriSet !== undefined) {
404
403
  features.push(enyo_battery_appliance_js_1.EnyoBatteryFeature.GridCharging);
405
404
  }
406
- try {
407
- this.applianceId = await this.applianceManager.createOrUpdateAppliance({
408
- name: this.name,
409
- type: enyo_appliance_js_1.EnyoApplianceTypeEnum.Storage,
410
- networkDevices: [this.networkDevice],
411
- metadata: {
412
- connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
413
- state: enyo_appliance_js_1.EnyoApplianceStateEnum.Connected,
414
- serialNumber: commonData?.serialNumber,
415
- modelName: commonData?.model,
416
- vendorName: commonData?.manufacturer,
417
- modbus: { unitId: this.unitId },
418
- },
419
- battery: {
420
- connectedToApplianceId: inverterApplianceId,
421
- storageMode: this.mapToEnyoStorageMode(storageMode),
422
- maxChargingPowerW: batteryData && batteryData.wChaMax !== undefined && batteryData.inWRte !== undefined ? batteryData.wChaMax * batteryData.inWRte : undefined,
423
- maxDischargePowerW: batteryData && batteryData.wChaMax !== undefined && batteryData.outWRte !== undefined ? batteryData.wChaMax * batteryData.outWRte : undefined,
424
- features,
425
- gridChargingEnabled: batteryData?.chaGriSet === 1
426
- }
427
- });
428
- console.log(`Sunspec Battery connected: ${this.networkDevice.hostname} (${this.applianceId})`);
429
- this.startDataBusListening();
430
- }
431
- catch (error) {
432
- console.error(`Failed to create battery appliance: ${error}`);
405
+ // Create or update appliance (skip if an existing appliance was provided)
406
+ if (!this.applianceId) {
407
+ try {
408
+ this.applianceId = await this.applianceManager.createOrUpdateAppliance({
409
+ name: this.name,
410
+ type: enyo_appliance_js_1.EnyoApplianceTypeEnum.Storage,
411
+ networkDevices: [this.networkDevice],
412
+ metadata: {
413
+ connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
414
+ state: enyo_appliance_js_1.EnyoApplianceStateEnum.Connected,
415
+ serialNumber: commonData?.serialNumber,
416
+ modelName: commonData?.model,
417
+ vendorName: commonData?.manufacturer,
418
+ modbus: { unitId: this.unitId },
419
+ },
420
+ battery: {
421
+ connectedToApplianceId: inverterApplianceId,
422
+ storageMode: this.mapToEnyoStorageMode(storageMode),
423
+ maxChargingPowerW: batteryData && batteryData.wChaMax !== undefined && batteryData.inWRte !== undefined ? batteryData.wChaMax * batteryData.inWRte : undefined,
424
+ maxDischargePowerW: batteryData && batteryData.wChaMax !== undefined && batteryData.outWRte !== undefined ? batteryData.wChaMax * batteryData.outWRte : undefined,
425
+ features,
426
+ gridChargingEnabled: batteryData?.chaGriSet === 1
427
+ }
428
+ });
429
+ console.log(`Sunspec Battery connected: ${this.networkDevice.hostname} (${this.applianceId})`);
430
+ }
431
+ catch (error) {
432
+ console.error(`Failed to create battery appliance: ${error}`);
433
+ }
433
434
  }
435
+ this.startDataBusListening();
434
436
  }
435
437
  async disconnect() {
436
438
  this.stopDataBusListening();
@@ -872,8 +874,8 @@ exports.SunspecBattery = SunspecBattery;
872
874
  */
873
875
  class SunspecMeter extends BaseSunspecDevice {
874
876
  capabilities;
875
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig) {
876
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig);
877
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
878
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
877
879
  this.capabilities = capabilities;
878
880
  }
879
881
  /**
@@ -891,31 +893,33 @@ class SunspecMeter extends BaseSunspecDevice {
891
893
  }
892
894
  // Get device info
893
895
  const commonData = await this.sunspecClient.readCommonBlock();
894
- // Create or update appliance
895
- try {
896
- this.applianceId = await this.applianceManager.createOrUpdateAppliance({
897
- name: this.name,
898
- type: enyo_appliance_js_1.EnyoApplianceTypeEnum.Meter,
899
- networkDevices: [this.networkDevice],
900
- metadata: {
901
- connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
902
- state: enyo_appliance_js_1.EnyoApplianceStateEnum.Connected,
903
- serialNumber: commonData?.serialNumber,
904
- modelName: commonData?.model,
905
- vendorName: commonData?.manufacturer,
906
- modbus: { unitId: this.unitId },
907
- },
908
- topology: {
909
- features: [enyo_appliance_js_1.EnyoApplianceTopologyFeatureEnum.IntermediateOfPrimaryMeter]
910
- },
911
- meter: {
912
- availableFeatures: [enyo_meter_appliance_js_1.EnyoMeterApplianceAvailableFeaturesEnum.LivePowerConsumption]
913
- }
914
- });
915
- console.log(`Sunspec Meter connected: ${this.networkDevice.hostname} unit ${this.unitId} (${this.applianceId})`);
916
- }
917
- catch (error) {
918
- console.error(`Failed to create meter appliance: ${error}`);
896
+ // Create or update appliance (skip if an existing appliance was provided)
897
+ if (!this.applianceId) {
898
+ try {
899
+ this.applianceId = await this.applianceManager.createOrUpdateAppliance({
900
+ name: this.name,
901
+ type: enyo_appliance_js_1.EnyoApplianceTypeEnum.Meter,
902
+ networkDevices: [this.networkDevice],
903
+ metadata: {
904
+ connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
905
+ state: enyo_appliance_js_1.EnyoApplianceStateEnum.Connected,
906
+ serialNumber: commonData?.serialNumber,
907
+ modelName: commonData?.model,
908
+ vendorName: commonData?.manufacturer,
909
+ modbus: { unitId: this.unitId },
910
+ },
911
+ topology: {
912
+ features: [enyo_appliance_js_1.EnyoApplianceTopologyFeatureEnum.IntermediateOfPrimaryMeter]
913
+ },
914
+ meter: {
915
+ availableFeatures: [enyo_meter_appliance_js_1.EnyoMeterApplianceAvailableFeaturesEnum.LivePowerConsumption]
916
+ }
917
+ });
918
+ console.log(`Sunspec Meter connected: ${this.networkDevice.hostname} unit ${this.unitId} (${this.applianceId})`);
919
+ }
920
+ catch (error) {
921
+ console.error(`Failed to create meter appliance: ${error}`);
922
+ }
919
923
  }
920
924
  }
921
925
  /**
@@ -1,22 +1,11 @@
1
- import { type SunspecBatteryBaseData, type SunspecBatteryControls, SunspecStorageMode, SunspecInverterCapability, SunspecBatteryCapability, SunspecMeterCapability } from "./sunspec-interfaces.cjs";
1
+ import { type IRetryConfig, type SunspecBatteryBaseData, SunspecBatteryCapability, type SunspecBatteryControls, SunspecInverterCapability, SunspecMeterCapability, SunspecStorageMode } from "./sunspec-interfaces.cjs";
2
2
  import { ApplianceManager, EnergyApp } from "@enyo-energy/energy-app-sdk";
3
- import { EnyoApplianceName } from "@enyo-energy/energy-app-sdk/dist/types/enyo-appliance.js";
3
+ import { type EnyoAppliance, EnyoApplianceName } from "@enyo-energy/energy-app-sdk/dist/types/enyo-appliance.js";
4
4
  import { EnyoNetworkDevice } from "@enyo-energy/energy-app-sdk/dist/types/enyo-network-device.js";
5
5
  import { SunspecModbusClient } from "./sunspec-modbus-client.cjs";
6
6
  import { ConnectionRetryManager } from "./connection-retry-manager.cjs";
7
- import { type IRetryConfig } from "./sunspec-interfaces.cjs";
8
7
  import { EnyoCommandAcknowledgeAnswerEnum, EnyoDataBusMessage, EnyoDataBusMessageEnum, EnyoDataBusMessageResolution } from "@enyo-energy/energy-app-sdk/dist/types/enyo-data-bus-value.js";
9
8
  import { EnergyAppDataBus } from "@enyo-energy/energy-app-sdk/dist/packages/energy-app-data-bus.js";
10
- export declare const ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1 = "SetInverterFeedInLimitV1";
11
- export interface EnyoDataBusSetInverterFeedInLimitV1 {
12
- id: string;
13
- type: 'message';
14
- message: string;
15
- applianceId: string;
16
- data: {
17
- feedInLimitW: number | null;
18
- };
19
- }
20
9
  /**
21
10
  * Base abstract class for all Sunspec devices
22
11
  */
@@ -34,7 +23,7 @@ export declare abstract class BaseSunspecDevice {
34
23
  protected dataBusListenerId?: string;
35
24
  protected dataBus?: EnergyAppDataBus;
36
25
  protected retryManager: ConnectionRetryManager;
37
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, retryConfig?: IRetryConfig);
26
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
38
27
  /**
39
28
  * Connect to the device and create/update the appliance
40
29
  */
@@ -75,10 +64,9 @@ export declare abstract class BaseSunspecDevice {
75
64
  */
76
65
  export declare class SunspecInverter extends BaseSunspecDevice {
77
66
  private readonly capabilities;
78
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecInverterCapability[], retryConfig?: IRetryConfig);
67
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecInverterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
79
68
  connect(): Promise<void>;
80
69
  disconnect(): Promise<void>;
81
- isConnected(): boolean;
82
70
  readData(clockId: string, resolution: '10s' | '30s' | '1m' | '15m'): Promise<EnyoDataBusMessage[]>;
83
71
  private mapOperatingState;
84
72
  /**
@@ -105,7 +93,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
105
93
  */
106
94
  export declare class SunspecBattery extends BaseSunspecDevice {
107
95
  private readonly capabilities;
108
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecBatteryCapability[], retryConfig?: IRetryConfig);
96
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecBatteryCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
109
97
  /**
110
98
  * Connect to the battery and create/update the appliance
111
99
  */
@@ -222,7 +210,7 @@ export declare class SunspecBattery extends BaseSunspecDevice {
222
210
  */
223
211
  export declare class SunspecMeter extends BaseSunspecDevice {
224
212
  private readonly capabilities;
225
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecMeterCapability[], retryConfig?: IRetryConfig);
213
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecMeterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
226
214
  /**
227
215
  * Connect to the meter and create/update the appliance
228
216
  */
@@ -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.42';
12
+ exports.SDK_VERSION = '0.0.43';
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.42";
8
+ export declare const SDK_VERSION = "0.0.43";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
@@ -1,22 +1,11 @@
1
- import { type SunspecBatteryBaseData, type SunspecBatteryControls, SunspecStorageMode, SunspecInverterCapability, SunspecBatteryCapability, SunspecMeterCapability } from "./sunspec-interfaces.js";
1
+ import { type IRetryConfig, type SunspecBatteryBaseData, SunspecBatteryCapability, type SunspecBatteryControls, SunspecInverterCapability, SunspecMeterCapability, SunspecStorageMode } from "./sunspec-interfaces.js";
2
2
  import { ApplianceManager, EnergyApp } from "@enyo-energy/energy-app-sdk";
3
- import { EnyoApplianceName } from "@enyo-energy/energy-app-sdk/dist/types/enyo-appliance.js";
3
+ import { type EnyoAppliance, EnyoApplianceName } from "@enyo-energy/energy-app-sdk/dist/types/enyo-appliance.js";
4
4
  import { EnyoNetworkDevice } from "@enyo-energy/energy-app-sdk/dist/types/enyo-network-device.js";
5
5
  import { SunspecModbusClient } from "./sunspec-modbus-client.js";
6
6
  import { ConnectionRetryManager } from "./connection-retry-manager.js";
7
- import { type IRetryConfig } from "./sunspec-interfaces.js";
8
7
  import { EnyoCommandAcknowledgeAnswerEnum, EnyoDataBusMessage, EnyoDataBusMessageEnum, EnyoDataBusMessageResolution } from "@enyo-energy/energy-app-sdk/dist/types/enyo-data-bus-value.js";
9
8
  import { EnergyAppDataBus } from "@enyo-energy/energy-app-sdk/dist/packages/energy-app-data-bus.js";
10
- export declare const ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1 = "SetInverterFeedInLimitV1";
11
- export interface EnyoDataBusSetInverterFeedInLimitV1 {
12
- id: string;
13
- type: 'message';
14
- message: string;
15
- applianceId: string;
16
- data: {
17
- feedInLimitW: number | null;
18
- };
19
- }
20
9
  /**
21
10
  * Base abstract class for all Sunspec devices
22
11
  */
@@ -34,7 +23,7 @@ export declare abstract class BaseSunspecDevice {
34
23
  protected dataBusListenerId?: string;
35
24
  protected dataBus?: EnergyAppDataBus;
36
25
  protected retryManager: ConnectionRetryManager;
37
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, retryConfig?: IRetryConfig);
26
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
38
27
  /**
39
28
  * Connect to the device and create/update the appliance
40
29
  */
@@ -75,10 +64,9 @@ export declare abstract class BaseSunspecDevice {
75
64
  */
76
65
  export declare class SunspecInverter extends BaseSunspecDevice {
77
66
  private readonly capabilities;
78
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecInverterCapability[], retryConfig?: IRetryConfig);
67
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecInverterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
79
68
  connect(): Promise<void>;
80
69
  disconnect(): Promise<void>;
81
- isConnected(): boolean;
82
70
  readData(clockId: string, resolution: '10s' | '30s' | '1m' | '15m'): Promise<EnyoDataBusMessage[]>;
83
71
  private mapOperatingState;
84
72
  /**
@@ -105,7 +93,7 @@ export declare class SunspecInverter extends BaseSunspecDevice {
105
93
  */
106
94
  export declare class SunspecBattery extends BaseSunspecDevice {
107
95
  private readonly capabilities;
108
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecBatteryCapability[], retryConfig?: IRetryConfig);
96
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecBatteryCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
109
97
  /**
110
98
  * Connect to the battery and create/update the appliance
111
99
  */
@@ -222,7 +210,7 @@ export declare class SunspecBattery extends BaseSunspecDevice {
222
210
  */
223
211
  export declare class SunspecMeter extends BaseSunspecDevice {
224
212
  private readonly capabilities;
225
- constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecMeterCapability[], retryConfig?: IRetryConfig);
213
+ constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number, port?: number, baseAddress?: number, capabilities?: SunspecMeterCapability[], retryConfig?: IRetryConfig, appliance?: EnyoAppliance);
226
214
  /**
227
215
  * Connect to the meter and create/update the appliance
228
216
  */
@@ -1,4 +1,4 @@
1
- import { SunspecBatteryChargeState, SunspecModelId, SunspecMPPTOperatingState, SunspecStorageMode, SunspecInverterCapability } from "./sunspec-interfaces.js";
1
+ import { SunspecBatteryChargeState, SunspecInverterCapability, SunspecModelId, SunspecMPPTOperatingState, SunspecStorageMode } 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
4
  import { ConnectionRetryManager } from "./connection-retry-manager.js";
@@ -6,8 +6,6 @@ import { EnyoBatteryStateEnum, EnyoCommandAcknowledgeAnswerEnum, EnyoDataBusMess
6
6
  import { EnyoSourceEnum } from "@enyo-energy/energy-app-sdk/dist/types/enyo-source.enum.js";
7
7
  import { EnyoMeterApplianceAvailableFeaturesEnum } from "@enyo-energy/energy-app-sdk/dist/types/enyo-meter-appliance.js";
8
8
  import { EnyoBatteryFeature, EnyoBatteryStorageMode } from "@enyo-energy/energy-app-sdk/dist/types/enyo-battery-appliance.js";
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';
11
9
  /**
12
10
  * Base abstract class for all Sunspec devices
13
11
  */
@@ -25,7 +23,7 @@ export class BaseSunspecDevice {
25
23
  dataBusListenerId;
26
24
  dataBus;
27
25
  retryManager;
28
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, retryConfig) {
26
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, retryConfig, appliance) {
29
27
  this.energyApp = energyApp;
30
28
  this.name = name;
31
29
  this.networkDevice = networkDevice;
@@ -35,12 +33,15 @@ export class BaseSunspecDevice {
35
33
  this.port = port;
36
34
  this.baseAddress = baseAddress;
37
35
  this.retryManager = new ConnectionRetryManager(retryConfig);
36
+ if (appliance) {
37
+ this.applianceId = appliance.id;
38
+ }
38
39
  }
39
40
  /**
40
41
  * Check if the device is connected
41
42
  */
42
43
  isConnected() {
43
- return this.sunspecClient.isConnected();
44
+ return this.sunspecClient.isHealthy();
44
45
  }
45
46
  /**
46
47
  * Get the appliance IDs managed by this device
@@ -132,8 +133,8 @@ export class BaseSunspecDevice {
132
133
  */
133
134
  export class SunspecInverter extends BaseSunspecDevice {
134
135
  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);
136
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
137
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
137
138
  this.capabilities = capabilities;
138
139
  }
139
140
  async connect() {
@@ -148,29 +149,31 @@ export class SunspecInverter extends BaseSunspecDevice {
148
149
  const commonData = await this.sunspecClient.readCommonBlock();
149
150
  const inverterSettings = await this.sunspecClient.readInverterSettings();
150
151
  const mpptDataList = await this.sunspecClient.readAllMPPTData();
151
- // Create or update appliance
152
- try {
153
- this.applianceId = await this.applianceManager.createOrUpdateAppliance({
154
- name: this.name,
155
- type: EnyoApplianceTypeEnum.Inverter,
156
- networkDevices: [this.networkDevice],
157
- metadata: {
158
- connectionType: EnyoApplianceConnectionType.Connector,
159
- state: EnyoApplianceStateEnum.Connected,
160
- serialNumber: commonData?.serialNumber,
161
- modelName: commonData?.model,
162
- vendorName: commonData?.manufacturer,
163
- modbus: { unitId: this.unitId },
164
- },
165
- inverter: {
166
- dcStrings: this.mapDcStringToApplianceMetadata(this.mapMPPTToStrings(mpptDataList)),
167
- maxPvProductionW: inverterSettings?.WMax
168
- }
169
- });
170
- console.log(`Sunspec Inverter connected: ${this.networkDevice.hostname} (${this.applianceId})`);
171
- }
172
- catch (error) {
173
- console.error(`Failed to create inverter appliance: ${error}`);
152
+ // Create or update appliance (skip if an existing appliance was provided)
153
+ if (!this.applianceId) {
154
+ try {
155
+ this.applianceId = await this.applianceManager.createOrUpdateAppliance({
156
+ name: this.name,
157
+ type: EnyoApplianceTypeEnum.Inverter,
158
+ networkDevices: [this.networkDevice],
159
+ metadata: {
160
+ connectionType: EnyoApplianceConnectionType.Connector,
161
+ state: EnyoApplianceStateEnum.Connected,
162
+ serialNumber: commonData?.serialNumber,
163
+ modelName: commonData?.model,
164
+ vendorName: commonData?.manufacturer,
165
+ modbus: { unitId: this.unitId },
166
+ },
167
+ inverter: {
168
+ dcStrings: this.mapDcStringToApplianceMetadata(this.mapMPPTToStrings(mpptDataList)),
169
+ maxPvProductionW: inverterSettings?.WMax
170
+ }
171
+ });
172
+ console.log(`Sunspec Inverter connected: ${this.networkDevice.hostname} (${this.applianceId})`);
173
+ }
174
+ catch (error) {
175
+ console.error(`Failed to create inverter appliance: ${error}`);
176
+ }
174
177
  }
175
178
  // Check for MPPT models
176
179
  const mpptModel = this.sunspecClient.findModel(SunspecModelId.MPPT);
@@ -186,9 +189,6 @@ export class SunspecInverter extends BaseSunspecDevice {
186
189
  }
187
190
  // Note: We don't disconnect the sunspecClient as it may be shared
188
191
  }
189
- isConnected() {
190
- return this.sunspecClient.isConnected();
191
- }
192
192
  async readData(clockId, resolution) {
193
193
  if (!this.isConnected()) {
194
194
  await this.tryReconnect();
@@ -320,7 +320,7 @@ export class SunspecInverter extends BaseSunspecDevice {
320
320
  return;
321
321
  }
322
322
  this.dataBus = this.energyApp.useDataBus();
323
- this.dataBusListenerId = this.dataBus.listenForMessages([ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1], (entry) => this.handleInverterCommand(entry));
323
+ this.dataBusListenerId = this.dataBus.listenForMessages([EnyoDataBusMessageEnum.SetInverterFeedInLimitV1], (entry) => this.handleInverterCommand(entry));
324
324
  console.log(`Inverter ${this.applianceId}: started data bus listening (listener ${this.dataBusListenerId})`);
325
325
  }
326
326
  /**
@@ -340,7 +340,7 @@ export class SunspecInverter extends BaseSunspecDevice {
340
340
  }
341
341
  void (async () => {
342
342
  try {
343
- if (entry.message === ENYO_DATA_BUS_SET_INVERTER_FEED_IN_LIMIT_V1) {
343
+ if (entry.message === EnyoDataBusMessageEnum.SetInverterFeedInLimitV1) {
344
344
  await this.handleSetFeedInLimit(entry);
345
345
  }
346
346
  }
@@ -373,8 +373,8 @@ export class SunspecInverter extends BaseSunspecDevice {
373
373
  */
374
374
  export class SunspecBattery extends BaseSunspecDevice {
375
375
  capabilities;
376
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig) {
377
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig);
376
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
377
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
378
378
  this.capabilities = capabilities;
379
379
  }
380
380
  /**
@@ -394,38 +394,40 @@ export class SunspecBattery extends BaseSunspecDevice {
394
394
  const batteryData = await this.sunspecClient.readBatteryData();
395
395
  const storageMode = this.determineStorageMode(batteryData);
396
396
  const features = [];
397
- // Create or update appliance
398
397
  if (batteryData?.chaGriSet !== undefined) {
399
398
  features.push(EnyoBatteryFeature.GridCharging);
400
399
  }
401
- try {
402
- this.applianceId = await this.applianceManager.createOrUpdateAppliance({
403
- name: this.name,
404
- type: EnyoApplianceTypeEnum.Storage,
405
- networkDevices: [this.networkDevice],
406
- metadata: {
407
- connectionType: EnyoApplianceConnectionType.Connector,
408
- state: EnyoApplianceStateEnum.Connected,
409
- serialNumber: commonData?.serialNumber,
410
- modelName: commonData?.model,
411
- vendorName: commonData?.manufacturer,
412
- modbus: { unitId: this.unitId },
413
- },
414
- battery: {
415
- connectedToApplianceId: inverterApplianceId,
416
- storageMode: this.mapToEnyoStorageMode(storageMode),
417
- maxChargingPowerW: batteryData && batteryData.wChaMax !== undefined && batteryData.inWRte !== undefined ? batteryData.wChaMax * batteryData.inWRte : undefined,
418
- maxDischargePowerW: batteryData && batteryData.wChaMax !== undefined && batteryData.outWRte !== undefined ? batteryData.wChaMax * batteryData.outWRte : undefined,
419
- features,
420
- gridChargingEnabled: batteryData?.chaGriSet === 1
421
- }
422
- });
423
- console.log(`Sunspec Battery connected: ${this.networkDevice.hostname} (${this.applianceId})`);
424
- this.startDataBusListening();
425
- }
426
- catch (error) {
427
- console.error(`Failed to create battery appliance: ${error}`);
400
+ // Create or update appliance (skip if an existing appliance was provided)
401
+ if (!this.applianceId) {
402
+ try {
403
+ this.applianceId = await this.applianceManager.createOrUpdateAppliance({
404
+ name: this.name,
405
+ type: EnyoApplianceTypeEnum.Storage,
406
+ networkDevices: [this.networkDevice],
407
+ metadata: {
408
+ connectionType: EnyoApplianceConnectionType.Connector,
409
+ state: EnyoApplianceStateEnum.Connected,
410
+ serialNumber: commonData?.serialNumber,
411
+ modelName: commonData?.model,
412
+ vendorName: commonData?.manufacturer,
413
+ modbus: { unitId: this.unitId },
414
+ },
415
+ battery: {
416
+ connectedToApplianceId: inverterApplianceId,
417
+ storageMode: this.mapToEnyoStorageMode(storageMode),
418
+ maxChargingPowerW: batteryData && batteryData.wChaMax !== undefined && batteryData.inWRte !== undefined ? batteryData.wChaMax * batteryData.inWRte : undefined,
419
+ maxDischargePowerW: batteryData && batteryData.wChaMax !== undefined && batteryData.outWRte !== undefined ? batteryData.wChaMax * batteryData.outWRte : undefined,
420
+ features,
421
+ gridChargingEnabled: batteryData?.chaGriSet === 1
422
+ }
423
+ });
424
+ console.log(`Sunspec Battery connected: ${this.networkDevice.hostname} (${this.applianceId})`);
425
+ }
426
+ catch (error) {
427
+ console.error(`Failed to create battery appliance: ${error}`);
428
+ }
428
429
  }
430
+ this.startDataBusListening();
429
431
  }
430
432
  async disconnect() {
431
433
  this.stopDataBusListening();
@@ -866,8 +868,8 @@ export class SunspecBattery extends BaseSunspecDevice {
866
868
  */
867
869
  export class SunspecMeter extends BaseSunspecDevice {
868
870
  capabilities;
869
- constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig) {
870
- super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig);
871
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1, port = 502, baseAddress = 40000, capabilities = [], retryConfig, appliance) {
872
+ super(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId, port, baseAddress, retryConfig, appliance);
871
873
  this.capabilities = capabilities;
872
874
  }
873
875
  /**
@@ -885,31 +887,33 @@ export class SunspecMeter extends BaseSunspecDevice {
885
887
  }
886
888
  // Get device info
887
889
  const commonData = await this.sunspecClient.readCommonBlock();
888
- // Create or update appliance
889
- try {
890
- this.applianceId = await this.applianceManager.createOrUpdateAppliance({
891
- name: this.name,
892
- type: EnyoApplianceTypeEnum.Meter,
893
- networkDevices: [this.networkDevice],
894
- metadata: {
895
- connectionType: EnyoApplianceConnectionType.Connector,
896
- state: EnyoApplianceStateEnum.Connected,
897
- serialNumber: commonData?.serialNumber,
898
- modelName: commonData?.model,
899
- vendorName: commonData?.manufacturer,
900
- modbus: { unitId: this.unitId },
901
- },
902
- topology: {
903
- features: [EnyoApplianceTopologyFeatureEnum.IntermediateOfPrimaryMeter]
904
- },
905
- meter: {
906
- availableFeatures: [EnyoMeterApplianceAvailableFeaturesEnum.LivePowerConsumption]
907
- }
908
- });
909
- console.log(`Sunspec Meter connected: ${this.networkDevice.hostname} unit ${this.unitId} (${this.applianceId})`);
910
- }
911
- catch (error) {
912
- console.error(`Failed to create meter appliance: ${error}`);
890
+ // Create or update appliance (skip if an existing appliance was provided)
891
+ if (!this.applianceId) {
892
+ try {
893
+ this.applianceId = await this.applianceManager.createOrUpdateAppliance({
894
+ name: this.name,
895
+ type: EnyoApplianceTypeEnum.Meter,
896
+ networkDevices: [this.networkDevice],
897
+ metadata: {
898
+ connectionType: EnyoApplianceConnectionType.Connector,
899
+ state: EnyoApplianceStateEnum.Connected,
900
+ serialNumber: commonData?.serialNumber,
901
+ modelName: commonData?.model,
902
+ vendorName: commonData?.manufacturer,
903
+ modbus: { unitId: this.unitId },
904
+ },
905
+ topology: {
906
+ features: [EnyoApplianceTopologyFeatureEnum.IntermediateOfPrimaryMeter]
907
+ },
908
+ meter: {
909
+ availableFeatures: [EnyoMeterApplianceAvailableFeaturesEnum.LivePowerConsumption]
910
+ }
911
+ });
912
+ console.log(`Sunspec Meter connected: ${this.networkDevice.hostname} unit ${this.unitId} (${this.applianceId})`);
913
+ }
914
+ catch (error) {
915
+ console.error(`Failed to create meter appliance: ${error}`);
916
+ }
913
917
  }
914
918
  }
915
919
  /**
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.42";
8
+ export declare const SDK_VERSION = "0.0.43";
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.42';
8
+ export const SDK_VERSION = '0.0.43';
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.42",
3
+ "version": "0.0.43",
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.85"
40
+ "@enyo-energy/energy-app-sdk": "^0.0.88"
41
41
  },
42
42
  "volta": {
43
43
  "node": "22.17.0"