@enyo-energy/energy-app-sdk 0.0.37 → 0.0.39

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.
Files changed (47) hide show
  1. package/dist/cjs/implementations/appliances/appliance-manager.cjs +399 -0
  2. package/dist/cjs/implementations/appliances/appliance-manager.d.cts +191 -0
  3. package/dist/cjs/implementations/appliances/identifier-strategies.cjs +180 -0
  4. package/dist/cjs/implementations/appliances/identifier-strategies.d.cts +140 -0
  5. package/dist/cjs/implementations/appliances/in-memory-appliance-manager.cjs +281 -0
  6. package/dist/cjs/implementations/appliances/in-memory-appliance-manager.d.cts +119 -0
  7. package/dist/cjs/implementations/data-bus/demo-data-bus.cjs +246 -0
  8. package/dist/cjs/implementations/data-bus/demo-data-bus.d.cts +111 -0
  9. package/dist/cjs/implementations/modbus/EnergyAppModbusDataTypeConverter.d.cts +2 -2
  10. package/dist/cjs/implementations/modbus/EnergyAppModbusFaultTolerantReader.cjs +76 -0
  11. package/dist/cjs/implementations/modbus/EnergyAppModbusFaultTolerantReader.d.cts +31 -1
  12. package/dist/cjs/implementations/modbus/EnergyAppModbusRegisterMapper.d.cts +2 -2
  13. package/dist/cjs/implementations/modbus/interfaces.d.cts +7 -93
  14. package/dist/cjs/implementations/modbus/sunspec/sunspec-devices.cjs +342 -0
  15. package/dist/cjs/implementations/modbus/sunspec/sunspec-devices.d.cts +95 -0
  16. package/dist/cjs/implementations/modbus/sunspec/sunspec-modbus-client.cjs +433 -0
  17. package/dist/cjs/implementations/modbus/sunspec/sunspec-modbus-client.d.cts +171 -0
  18. package/dist/cjs/index.cjs +2 -3
  19. package/dist/cjs/index.d.cts +2 -3
  20. package/dist/cjs/types/enyo-data-bus-value.d.cts +2 -1
  21. package/dist/cjs/version.cjs +1 -1
  22. package/dist/cjs/version.d.cts +1 -1
  23. package/dist/implementations/appliances/appliance-manager.d.ts +191 -0
  24. package/dist/implementations/appliances/appliance-manager.js +395 -0
  25. package/dist/implementations/appliances/demo-appliance-manager.d.ts +118 -0
  26. package/dist/implementations/appliances/demo-appliance-manager.js +277 -0
  27. package/dist/implementations/appliances/identifier-strategies.d.ts +140 -0
  28. package/dist/implementations/appliances/identifier-strategies.js +171 -0
  29. package/dist/implementations/appliances/in-memory-appliance-manager.d.ts +119 -0
  30. package/dist/implementations/appliances/in-memory-appliance-manager.js +277 -0
  31. package/dist/implementations/data-bus/demo-data-bus.d.ts +111 -0
  32. package/dist/implementations/data-bus/demo-data-bus.js +242 -0
  33. package/dist/implementations/modbus/EnergyAppModbusDataTypeConverter.d.ts +2 -2
  34. package/dist/implementations/modbus/EnergyAppModbusFaultTolerantReader.d.ts +31 -1
  35. package/dist/implementations/modbus/EnergyAppModbusFaultTolerantReader.js +76 -0
  36. package/dist/implementations/modbus/EnergyAppModbusRegisterMapper.d.ts +2 -2
  37. package/dist/implementations/modbus/interfaces.d.ts +7 -93
  38. package/dist/implementations/modbus/sunspec/sunspec-devices.d.ts +95 -0
  39. package/dist/implementations/modbus/sunspec/sunspec-devices.js +335 -0
  40. package/dist/implementations/modbus/sunspec/sunspec-modbus-client.d.ts +171 -0
  41. package/dist/implementations/modbus/sunspec/sunspec-modbus-client.js +429 -0
  42. package/dist/index.d.ts +2 -3
  43. package/dist/index.js +2 -3
  44. package/dist/types/enyo-data-bus-value.d.ts +2 -1
  45. package/dist/version.d.ts +1 -1
  46. package/dist/version.js +1 -1
  47. package/package.json +1 -1
@@ -1,8 +1,4 @@
1
- import type { EnergyApp } from "../../index.cjs";
2
- import type { EnyoNetworkDevice } from "../../types/enyo-network-device.cjs";
3
- import type { EnyoAppliance, EnyoApplianceName, EnyoApplianceTopology } from "../../types/enyo-appliance.cjs";
4
- import { EnyoBatteryStateEnum, EnyoDataBusMessage, EnyoInverterStateEnum } from "../../types/enyo-data-bus-value.cjs";
5
- import { EnergyAppModbusInstance } from "../../packages/energy-app-modbus.cjs";
1
+ import type { EnyoApplianceName, EnyoApplianceTopology } from "../../types/enyo-appliance.cjs";
6
2
  /**
7
3
  * Data Types for Modbus Register Configuration
8
4
  *
@@ -11,20 +7,16 @@ import { EnergyAppModbusInstance } from "../../packages/energy-app-modbus.cjs";
11
7
  * - String type: string (requires length property in register config)
12
8
  */
13
9
  export type EnergyAppModbusDataType = 'uint16' | 'int16' | 'uint32' | 'int32' | 'float32' | 'string';
14
- export interface EnergyAppBatteryStateValueMapping {
10
+ export interface EnergyAppModbusStateValueMapping<T> {
15
11
  value: number;
16
- mappedState: EnyoBatteryStateEnum;
17
- }
18
- export interface EnergyAppInverterStateValueMapping {
19
- value: number;
20
- mappedState: EnyoInverterStateEnum;
12
+ mappedState: T;
21
13
  }
22
14
  /**
23
15
  * Register Configuration Interface
24
16
  *
25
17
  * @description Configuration for reading a Modbus register
26
18
  */
27
- export interface EnergyAppModbusRegisterConfig {
19
+ export interface EnergyAppModbusRegisterConfig<T> {
28
20
  /** Modbus register address */
29
21
  address: number;
30
22
  /** Data type of the register value */
@@ -36,10 +28,10 @@ export interface EnergyAppModbusRegisterConfig {
36
28
  /** Whether this register is required for device operation */
37
29
  required?: boolean;
38
30
  /** For mapping numeric values to enum states (only applies to numeric types) */
39
- valueMapping?: EnergyAppBatteryStateValueMapping[] | EnergyAppInverterStateValueMapping[];
31
+ valueMapping?: EnergyAppModbusStateValueMapping<T>[];
40
32
  }
41
33
  export interface EnergyAppRegisterMap {
42
- [key: string]: EnergyAppModbusRegisterConfig | undefined;
34
+ [key: string]: EnergyAppModbusRegisterConfig<any> | undefined;
43
35
  }
44
36
  export interface EnergyAppModbusConnectionOptions {
45
37
  unitId?: number;
@@ -55,54 +47,6 @@ export interface EnergyAppModbusDeviceConfig {
55
47
  topology?: EnyoApplianceTopology;
56
48
  };
57
49
  }
58
- export interface EnergyAppModbusInverterConfig extends EnergyAppModbusDeviceConfig {
59
- registers: {
60
- serialNumber?: EnergyAppModbusRegisterConfig;
61
- power?: EnergyAppModbusRegisterConfig;
62
- voltageL1?: EnergyAppModbusRegisterConfig;
63
- voltageL2?: EnergyAppModbusRegisterConfig;
64
- voltageL3?: EnergyAppModbusRegisterConfig;
65
- maxPvProductionW?: EnergyAppModbusRegisterConfig;
66
- state?: EnergyAppModbusRegisterConfig;
67
- activePowerLimitationW?: EnergyAppModbusRegisterConfig;
68
- string1Power?: EnergyAppModbusRegisterConfig;
69
- string1Voltage?: EnergyAppModbusRegisterConfig;
70
- string2Power?: EnergyAppModbusRegisterConfig;
71
- string2Voltage?: EnergyAppModbusRegisterConfig;
72
- string3Power?: EnergyAppModbusRegisterConfig;
73
- string3Voltage?: EnergyAppModbusRegisterConfig;
74
- string4Power?: EnergyAppModbusRegisterConfig;
75
- string4Voltage?: EnergyAppModbusRegisterConfig;
76
- [key: string]: EnergyAppModbusRegisterConfig | undefined;
77
- };
78
- }
79
- export interface EnergyAppModbusBatteryConfig extends EnergyAppModbusDeviceConfig {
80
- inverter?: IEnergyAppModbusInverter;
81
- registers: {
82
- current?: EnergyAppModbusRegisterConfig;
83
- voltage?: EnergyAppModbusRegisterConfig;
84
- soc?: EnergyAppModbusRegisterConfig;
85
- power?: EnergyAppModbusRegisterConfig;
86
- temperature?: EnergyAppModbusRegisterConfig;
87
- state?: EnergyAppModbusRegisterConfig;
88
- maxCapacityWh?: EnergyAppModbusRegisterConfig;
89
- maxDischargePowerW?: EnergyAppModbusRegisterConfig;
90
- maxChargingPowerW?: EnergyAppModbusRegisterConfig;
91
- drainPercentage?: EnergyAppModbusRegisterConfig;
92
- loadPercentage?: EnergyAppModbusRegisterConfig;
93
- [key: string]: EnergyAppModbusRegisterConfig | undefined;
94
- };
95
- }
96
- export interface EnergyAppModbusMeterConfig extends EnergyAppModbusDeviceConfig {
97
- registers: {
98
- gridPower?: EnergyAppModbusRegisterConfig;
99
- gridFeedInPower?: EnergyAppModbusRegisterConfig;
100
- gridConsumptionPower?: EnergyAppModbusRegisterConfig;
101
- gridFeedInEnergy?: EnergyAppModbusRegisterConfig;
102
- gridConsumptionEnergy?: EnergyAppModbusRegisterConfig;
103
- [key: string]: EnergyAppModbusRegisterConfig | undefined;
104
- };
105
- }
106
50
  export interface RegisterReadResult<T = any> {
107
51
  success: boolean;
108
52
  value?: T;
@@ -114,7 +58,7 @@ export interface IRegisterReader {
114
58
  isHealthy(): boolean;
115
59
  }
116
60
  export interface IRegisterMapper {
117
- readRegister<T>(reader: IRegisterReader, config: EnergyAppModbusRegisterConfig): Promise<RegisterReadResult<T>>;
61
+ readRegister<T, E>(reader: IRegisterReader, config: EnergyAppModbusRegisterConfig<E>): Promise<RegisterReadResult<T>>;
118
62
  readMultipleRegisters(reader: IRegisterReader, registerMap: EnergyAppRegisterMap): Promise<{
119
63
  [key: string]: any;
120
64
  }>;
@@ -159,36 +103,6 @@ export interface IConnectionHealth {
159
103
  getConsecutiveFailures(): number;
160
104
  getLastError(): Error | undefined;
161
105
  }
162
- export interface EnergyAppModbusDevice {
163
- readonly client: EnergyApp;
164
- readonly config: EnergyAppModbusDeviceConfig;
165
- readonly appliance: EnyoAppliance;
166
- readonly networkDevice: EnyoNetworkDevice;
167
- connect(): Promise<void>;
168
- disconnect(): Promise<void>;
169
- isConnected(): boolean;
170
- updateData(): Promise<EnyoDataBusMessage[]>;
171
- modbusClient(): EnergyAppModbusInstance | undefined;
172
- }
173
- export interface IEnergyAppModbusInverter extends EnergyAppModbusDevice {
174
- readonly config: EnergyAppModbusInverterConfig;
175
- getSerialNumber(): Promise<string | null>;
176
- getCurrentPower(): Promise<number | null>;
177
- getTotalEnergy(): Promise<number | null>;
178
- }
179
- export interface IEnergyAppModbusBattery extends EnergyAppModbusDevice {
180
- readonly config: EnergyAppModbusBatteryConfig;
181
- readonly inverter?: IEnergyAppModbusInverter;
182
- getSoc(): Promise<number | null>;
183
- getCurrent(): Promise<number | null>;
184
- getPower(): Promise<number | null>;
185
- }
186
- export interface IEnergyAppModbusMeter extends EnergyAppModbusDevice {
187
- readonly config: EnergyAppModbusMeterConfig;
188
- getGridPower(): Promise<number | null>;
189
- getGridFeedInEnergy(): Promise<number | null>;
190
- getGridConsumptionEnergy(): Promise<number | null>;
191
- }
192
106
  export declare class EnergyAppModbusConfigurationError extends Error {
193
107
  constructor(message: string);
194
108
  }
@@ -0,0 +1,342 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SunspecMeter = exports.SunspecBattery = exports.SunspecInverter = exports.BaseSunspecDevice = void 0;
4
+ const enyo_appliance_js_1 = require("../../../types/enyo-appliance.cjs");
5
+ const enyo_data_bus_value_js_1 = require("../../../types/enyo-data-bus-value.cjs");
6
+ const sunspec_modbus_client_js_1 = require("./sunspec-modbus-client.cjs");
7
+ /**
8
+ * Base abstract class for all Sunspec devices
9
+ */
10
+ class BaseSunspecDevice {
11
+ energyApp;
12
+ name;
13
+ networkDevice;
14
+ sunspecClient;
15
+ applianceManager;
16
+ unitId;
17
+ applianceId;
18
+ lastUpdateTime = 0;
19
+ constructor(energyApp, name, networkDevice, sunspecClient, applianceManager, unitId = 1) {
20
+ this.energyApp = energyApp;
21
+ this.name = name;
22
+ this.networkDevice = networkDevice;
23
+ this.sunspecClient = sunspecClient;
24
+ this.applianceManager = applianceManager;
25
+ this.unitId = unitId;
26
+ }
27
+ /**
28
+ * Check if the device is connected
29
+ */
30
+ isConnected() {
31
+ return this.sunspecClient.isConnected();
32
+ }
33
+ /**
34
+ * Get the appliance IDs managed by this device
35
+ */
36
+ /**
37
+ * Ensure the Sunspec client is connected and models are discovered
38
+ */
39
+ async ensureConnected() {
40
+ if (!this.sunspecClient.isConnected()) {
41
+ await this.sunspecClient.connect(this.networkDevice.ipAddress, 502, this.unitId);
42
+ await this.sunspecClient.discoverModels();
43
+ }
44
+ }
45
+ }
46
+ exports.BaseSunspecDevice = BaseSunspecDevice;
47
+ /**
48
+ * Sunspec Inverter implementation using dynamic model discovery
49
+ */
50
+ class SunspecInverter extends BaseSunspecDevice {
51
+ mpptData = [];
52
+ async connect() {
53
+ // Ensure Sunspec client is connected
54
+ if (!this.sunspecClient.isConnected()) {
55
+ await this.sunspecClient.connect(this.networkDevice.ipAddress, 502, this.unitId);
56
+ await this.sunspecClient.discoverModels();
57
+ }
58
+ // Get device info from common block
59
+ const commonData = await this.sunspecClient.readCommonBlock();
60
+ // Create or update appliance
61
+ try {
62
+ this.applianceId = await this.applianceManager.createOrUpdateAppliance({
63
+ name: this.name,
64
+ type: enyo_appliance_js_1.EnyoApplianceTypeEnum.Inverter,
65
+ networkDevices: [this.networkDevice],
66
+ metadata: {
67
+ connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
68
+ state: enyo_appliance_js_1.EnyoApplianceStateEnum.Connected,
69
+ serialNumber: commonData?.serialNumber,
70
+ modelName: commonData?.model,
71
+ vendorName: commonData?.manufacturer,
72
+ }
73
+ });
74
+ console.log(`Sunspec Inverter connected: ${this.networkDevice.hostname} (${this.applianceId})`);
75
+ }
76
+ catch (error) {
77
+ console.error(`Failed to create inverter appliance: ${error}`);
78
+ }
79
+ // Check for MPPT models
80
+ const mpptModel = this.sunspecClient.findModel(sunspec_modbus_client_js_1.SunspecModelId.MPPT);
81
+ if (mpptModel) {
82
+ console.log(`MPPT model found for inverter ${this.networkDevice.hostname}`);
83
+ }
84
+ }
85
+ async disconnect() {
86
+ if (this.applianceId) {
87
+ await this.applianceManager.updateApplianceState(this.applianceId, enyo_appliance_js_1.EnyoApplianceConnectionType.Connector, enyo_appliance_js_1.EnyoApplianceStateEnum.Offline);
88
+ }
89
+ // Note: We don't disconnect the sunspecClient as it may be shared
90
+ }
91
+ isConnected() {
92
+ return this.sunspecClient.isConnected();
93
+ }
94
+ async updateData() {
95
+ if (!this.isConnected()) {
96
+ return [];
97
+ }
98
+ const messages = [];
99
+ const timestamp = Date.now();
100
+ try {
101
+ // Read inverter data
102
+ const inverterData = await this.sunspecClient.readInverterData();
103
+ if (inverterData) {
104
+ // Map operating state
105
+ const operatingState = this.mapOperatingState(inverterData.operatingState);
106
+ const inverterMessage = {
107
+ type: 'inverter',
108
+ appliances: this.name,
109
+ data: {
110
+ timestamp,
111
+ power: inverterData.acPower,
112
+ dcPower: inverterData.dcPower,
113
+ voltage: inverterData.voltageAN, // Phase A voltage
114
+ voltageL1: inverterData.voltageAN,
115
+ voltageL2: inverterData.voltageBN,
116
+ voltageL3: inverterData.voltageCN,
117
+ current: inverterData.acCurrent,
118
+ currentL1: inverterData.phaseACurrent,
119
+ currentL2: inverterData.phaseBCurrent,
120
+ currentL3: inverterData.phaseCCurrent,
121
+ frequency: inverterData.frequency,
122
+ temperature: inverterData.cabinetTemperature,
123
+ state: operatingState,
124
+ energy: Number(inverterData.acEnergy || 0),
125
+ apparentPower: inverterData.apparentPower,
126
+ reactivePower: inverterData.reactivePower,
127
+ powerFactor: inverterData.powerFactor
128
+ }
129
+ };
130
+ messages.push(inverterMessage);
131
+ }
132
+ // Read MPPT data if available
133
+ const mpptDataList = await this.sunspecClient.readAllMPPTData();
134
+ for (const mpptData of mpptDataList) {
135
+ const mpptMessage = {
136
+ type: 'mppt',
137
+ appliances: this.name,
138
+ data: {
139
+ timestamp,
140
+ stringId: mpptData.id,
141
+ dcCurrent: mpptData.dcCurrent,
142
+ dcVoltage: mpptData.dcVoltage,
143
+ dcPower: mpptData.dcPower,
144
+ temperature: mpptData.temperature,
145
+ energy: Number(mpptData.dcEnergy || 0)
146
+ }
147
+ };
148
+ messages.push(mpptMessage);
149
+ }
150
+ this.lastUpdateTime = timestamp;
151
+ this.mpptData = mpptDataList; // Store MPPT data for later use
152
+ }
153
+ catch (error) {
154
+ console.error(`Error updating inverter data: ${error}`);
155
+ }
156
+ return messages;
157
+ }
158
+ mapOperatingState(state) {
159
+ if (!state)
160
+ return enyo_data_bus_value_js_1.EnyoInverterStateEnum.Off;
161
+ const stateMap = {
162
+ 1: enyo_data_bus_value_js_1.EnyoInverterStateEnum.Off,
163
+ 2: enyo_data_bus_value_js_1.EnyoInverterStateEnum.Sleeping,
164
+ 3: enyo_data_bus_value_js_1.EnyoInverterStateEnum.Starting,
165
+ 4: enyo_data_bus_value_js_1.EnyoInverterStateEnum.Mppt,
166
+ 5: enyo_data_bus_value_js_1.EnyoInverterStateEnum.Throttled,
167
+ 6: enyo_data_bus_value_js_1.EnyoInverterStateEnum.ShuttingDown,
168
+ 7: enyo_data_bus_value_js_1.EnyoInverterStateEnum.Fault,
169
+ 8: enyo_data_bus_value_js_1.EnyoInverterStateEnum.Standby
170
+ };
171
+ return stateMap[state] || enyo_data_bus_value_js_1.EnyoInverterStateEnum.Off;
172
+ }
173
+ }
174
+ exports.SunspecInverter = SunspecInverter;
175
+ /**
176
+ * Sunspec Battery implementation
177
+ */
178
+ class SunspecBattery extends BaseSunspecDevice {
179
+ /**
180
+ * Connect to the battery and create/update the appliance
181
+ */
182
+ async connect() {
183
+ // Ensure Sunspec client is connected
184
+ await this.ensureConnected();
185
+ // Check if battery models exist
186
+ const hasBattery = this.sunspecClient.findModel(sunspec_modbus_client_js_1.SunspecModelId.Battery) !== undefined ||
187
+ this.sunspecClient.findModel(sunspec_modbus_client_js_1.SunspecModelId.BatteryBase) !== undefined;
188
+ if (!hasBattery) {
189
+ throw new Error('No battery model found in device');
190
+ }
191
+ // Get device info
192
+ const commonData = await this.sunspecClient.readCommonBlock();
193
+ // Create or update appliance
194
+ try {
195
+ this.applianceId = await this.applianceManager.createOrUpdateAppliance({
196
+ name: this.name,
197
+ type: enyo_appliance_js_1.EnyoApplianceTypeEnum.Storage,
198
+ networkDevices: [this.networkDevice],
199
+ metadata: {
200
+ connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
201
+ state: enyo_appliance_js_1.EnyoApplianceStateEnum.Connected,
202
+ serialNumber: commonData?.serialNumber ? `${commonData.serialNumber}-BAT` : undefined,
203
+ modelName: commonData?.model ? `${commonData.model} Battery` : 'Battery',
204
+ vendorName: commonData?.manufacturer,
205
+ }
206
+ });
207
+ console.log(`Sunspec Battery connected: ${this.networkDevice.hostname} (${this.applianceId})`);
208
+ }
209
+ catch (error) {
210
+ console.error(`Failed to create battery appliance: ${error}`);
211
+ }
212
+ }
213
+ async disconnect() {
214
+ if (this.applianceId) {
215
+ await this.applianceManager.updateApplianceState(this.applianceId, enyo_appliance_js_1.EnyoApplianceConnectionType.Connector, enyo_appliance_js_1.EnyoApplianceStateEnum.Offline);
216
+ }
217
+ }
218
+ /**
219
+ * Update battery data and return data bus messages
220
+ */
221
+ async updateData() {
222
+ if (!this.isConnected()) {
223
+ return [];
224
+ }
225
+ const messages = [];
226
+ const timestamp = Date.now();
227
+ try {
228
+ // For now, return basic battery data
229
+ // In a real implementation, we would read from battery models
230
+ const batteryMessage = {
231
+ type: 'battery',
232
+ appliances: this.name,
233
+ data: {
234
+ timestamp,
235
+ soc: 50, // Placeholder - would read from battery model
236
+ power: 0,
237
+ voltage: 0,
238
+ current: 0,
239
+ temperature: 25,
240
+ state: enyo_data_bus_value_js_1.EnyoBatteryStateEnum.Holding
241
+ }
242
+ };
243
+ messages.push(batteryMessage);
244
+ this.lastUpdateTime = timestamp;
245
+ }
246
+ catch (error) {
247
+ console.error(`Error updating battery data: ${error}`);
248
+ }
249
+ return messages;
250
+ }
251
+ }
252
+ exports.SunspecBattery = SunspecBattery;
253
+ /**
254
+ * Sunspec Meter implementation
255
+ */
256
+ class SunspecMeter extends BaseSunspecDevice {
257
+ /**
258
+ * Connect to the meter and create/update the appliance
259
+ */
260
+ async connect() {
261
+ // Connect with specific unit ID for meter
262
+ await this.ensureConnected();
263
+ // Check if meter models exist
264
+ const hasMeter = this.sunspecClient.findModel(sunspec_modbus_client_js_1.SunspecModelId.Meter3Phase) !== undefined ||
265
+ this.sunspecClient.findModel(sunspec_modbus_client_js_1.SunspecModelId.MeterWye) !== undefined ||
266
+ this.sunspecClient.findModel(sunspec_modbus_client_js_1.SunspecModelId.MeterSinglePhase) !== undefined;
267
+ if (!hasMeter) {
268
+ throw new Error('No meter model found in device');
269
+ }
270
+ // Get device info
271
+ const commonData = await this.sunspecClient.readCommonBlock();
272
+ // Create or update appliance
273
+ try {
274
+ this.applianceId = await this.applianceManager.createOrUpdateAppliance({
275
+ name: this.name,
276
+ type: enyo_appliance_js_1.EnyoApplianceTypeEnum.Meter,
277
+ networkDevices: [this.networkDevice],
278
+ metadata: {
279
+ connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
280
+ state: enyo_appliance_js_1.EnyoApplianceStateEnum.Connected,
281
+ serialNumber: commonData?.serialNumber ? `${commonData.serialNumber}-MTR` : undefined,
282
+ modelName: commonData?.model ? `${commonData.model} Meter` : 'Meter',
283
+ vendorName: commonData?.manufacturer,
284
+ }
285
+ });
286
+ console.log(`Sunspec Meter connected: ${this.networkDevice.hostname} unit ${this.unitId} (${this.applianceId})`);
287
+ }
288
+ catch (error) {
289
+ console.error(`Failed to create meter appliance: ${error}`);
290
+ }
291
+ }
292
+ /**
293
+ * Disconnect from the meter and update appliance state
294
+ */
295
+ async disconnect() {
296
+ if (this.applianceId) {
297
+ await this.applianceManager.updateApplianceState(this.applianceId, enyo_appliance_js_1.EnyoApplianceConnectionType.Connector, enyo_appliance_js_1.EnyoApplianceStateEnum.Offline);
298
+ }
299
+ // Disconnect the client since meter uses its own connection
300
+ await this.sunspecClient.disconnect();
301
+ }
302
+ /**
303
+ * Update meter data and return data bus messages
304
+ */
305
+ async updateData() {
306
+ if (!this.isConnected()) {
307
+ return [];
308
+ }
309
+ const messages = [];
310
+ const timestamp = Date.now();
311
+ try {
312
+ // Read meter data
313
+ const meterData = await this.sunspecClient.readMeterData();
314
+ if (meterData) {
315
+ const meterMessage = {
316
+ type: 'meter',
317
+ appliances: this.name,
318
+ data: {
319
+ timestamp,
320
+ totalPower: meterData.totalPower,
321
+ phaseAPower: meterData.phaseAPower,
322
+ phaseBPower: meterData.phaseBPower,
323
+ phaseCPower: meterData.phaseCPower,
324
+ totalEnergy: Number(meterData.totalEnergy || 0),
325
+ exportedEnergy: Number(meterData.exportedEnergy || 0),
326
+ importedEnergy: Number(meterData.importedEnergy || 0),
327
+ voltage: meterData.voltage,
328
+ current: meterData.current,
329
+ frequency: meterData.frequency
330
+ }
331
+ };
332
+ messages.push(meterMessage);
333
+ }
334
+ this.lastUpdateTime = timestamp;
335
+ }
336
+ catch (error) {
337
+ console.error(`Error updating meter data: ${error}`);
338
+ }
339
+ return messages;
340
+ }
341
+ }
342
+ exports.SunspecMeter = SunspecMeter;
@@ -0,0 +1,95 @@
1
+ import type { EnergyApp } from "../../../index.cjs";
2
+ import type { EnyoNetworkDevice } from "../../../types/enyo-network-device.cjs";
3
+ import type { EnyoApplianceName } from "../../../types/enyo-appliance.cjs";
4
+ import { ApplianceManager } from "../../appliances/appliance-manager.cjs";
5
+ import { SunspecModbusClient } from "./sunspec-modbus-client.cjs";
6
+ /**
7
+ * Data bus message structure for Sunspec devices
8
+ */
9
+ export interface DataBusMessage {
10
+ type: string;
11
+ appliances: EnyoApplianceName[];
12
+ data: {
13
+ [key: string]: any;
14
+ timestamp: number;
15
+ };
16
+ }
17
+ /**
18
+ * Base abstract class for all Sunspec devices
19
+ */
20
+ export declare abstract class BaseSunspecDevice {
21
+ protected readonly energyApp: EnergyApp;
22
+ readonly name: EnyoApplianceName[];
23
+ readonly networkDevice: EnyoNetworkDevice;
24
+ protected readonly sunspecClient: SunspecModbusClient;
25
+ protected readonly applianceManager: ApplianceManager;
26
+ protected readonly unitId: number;
27
+ protected applianceId?: string;
28
+ protected lastUpdateTime: number;
29
+ protected constructor(energyApp: EnergyApp, name: EnyoApplianceName[], networkDevice: EnyoNetworkDevice, sunspecClient: SunspecModbusClient, applianceManager: ApplianceManager, unitId?: number);
30
+ /**
31
+ * Connect to the device and create/update the appliance
32
+ */
33
+ abstract connect(): Promise<void>;
34
+ /**
35
+ * Disconnect from the device and update appliance state
36
+ */
37
+ abstract disconnect(): Promise<void>;
38
+ /**
39
+ * Update device data and return data bus messages
40
+ */
41
+ abstract updateData(): Promise<DataBusMessage[]>;
42
+ /**
43
+ * Check if the device is connected
44
+ */
45
+ isConnected(): boolean;
46
+ /**
47
+ * Get the appliance IDs managed by this device
48
+ */
49
+ /**
50
+ * Ensure the Sunspec client is connected and models are discovered
51
+ */
52
+ protected ensureConnected(): Promise<void>;
53
+ }
54
+ /**
55
+ * Sunspec Inverter implementation using dynamic model discovery
56
+ */
57
+ export declare class SunspecInverter extends BaseSunspecDevice {
58
+ private mpptData;
59
+ connect(): Promise<void>;
60
+ disconnect(): Promise<void>;
61
+ isConnected(): boolean;
62
+ updateData(): Promise<DataBusMessage[]>;
63
+ private mapOperatingState;
64
+ }
65
+ /**
66
+ * Sunspec Battery implementation
67
+ */
68
+ export declare class SunspecBattery extends BaseSunspecDevice {
69
+ /**
70
+ * Connect to the battery and create/update the appliance
71
+ */
72
+ connect(): Promise<void>;
73
+ disconnect(): Promise<void>;
74
+ /**
75
+ * Update battery data and return data bus messages
76
+ */
77
+ updateData(): Promise<DataBusMessage[]>;
78
+ }
79
+ /**
80
+ * Sunspec Meter implementation
81
+ */
82
+ export declare class SunspecMeter extends BaseSunspecDevice {
83
+ /**
84
+ * Connect to the meter and create/update the appliance
85
+ */
86
+ connect(): Promise<void>;
87
+ /**
88
+ * Disconnect from the meter and update appliance state
89
+ */
90
+ disconnect(): Promise<void>;
91
+ /**
92
+ * Update meter data and return data bus messages
93
+ */
94
+ updateData(): Promise<DataBusMessage[]>;
95
+ }