@enyo-energy/energy-app-sdk 0.0.167 → 0.0.168

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 (75) hide show
  1. package/dist/cjs/energy-app-package-definition.cjs +1 -0
  2. package/dist/cjs/energy-app-package-definition.d.cts +1 -0
  3. package/dist/cjs/energy-app-permission.type.cjs +1 -0
  4. package/dist/cjs/energy-app-permission.type.d.cts +3 -2
  5. package/dist/cjs/energy-app.cjs +13 -0
  6. package/dist/cjs/energy-app.d.cts +12 -0
  7. package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
  8. package/dist/cjs/implementations/modbus/EnergyAppModbusBattery.cjs +388 -0
  9. package/dist/cjs/implementations/modbus/EnergyAppModbusBattery.d.cts +59 -0
  10. package/dist/cjs/implementations/modbus/EnergyAppModbusInverter.cjs +348 -0
  11. package/dist/cjs/implementations/modbus/EnergyAppModbusInverter.d.cts +56 -0
  12. package/dist/cjs/implementations/modbus/EnergyAppModbusMeter.cjs +199 -0
  13. package/dist/cjs/implementations/modbus/EnergyAppModbusMeter.d.cts +26 -0
  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/implementations/udp/EnergyAppUdpServer.cjs +174 -0
  19. package/dist/cjs/implementations/udp/EnergyAppUdpServer.d.cts +42 -0
  20. package/dist/cjs/index.cjs +2 -0
  21. package/dist/cjs/index.d.cts +2 -0
  22. package/dist/cjs/packages/energy-app-electricity-prices.cjs +2 -0
  23. package/dist/cjs/packages/energy-app-electricity-prices.d.cts +74 -0
  24. package/dist/cjs/packages/energy-app-file.cjs +2 -0
  25. package/dist/cjs/packages/energy-app-file.d.cts +90 -0
  26. package/dist/cjs/types/enyo-data-bus-value.d.cts +2 -0
  27. package/dist/cjs/types/enyo-electricity-prices.cjs +2 -0
  28. package/dist/cjs/types/enyo-electricity-prices.d.cts +65 -0
  29. package/dist/cjs/types/enyo-energy-tariff.cjs +2 -0
  30. package/dist/cjs/types/enyo-energy-tariff.d.cts +64 -0
  31. package/dist/cjs/types/enyo-file.cjs +2 -0
  32. package/dist/cjs/types/enyo-file.d.cts +108 -0
  33. package/dist/cjs/types/enyo-storage.cjs +2 -0
  34. package/dist/cjs/types/enyo-storage.d.cts +4 -0
  35. package/dist/cjs/version.cjs +1 -1
  36. package/dist/cjs/version.d.cts +1 -1
  37. package/dist/energy-app-package-definition.d.ts +1 -0
  38. package/dist/energy-app-package-definition.js +1 -0
  39. package/dist/energy-app-permission.type.d.ts +3 -2
  40. package/dist/energy-app-permission.type.js +1 -0
  41. package/dist/energy-app.d.ts +12 -0
  42. package/dist/energy-app.js +13 -0
  43. package/dist/enyo-energy-app-sdk.d.ts +3 -0
  44. package/dist/implementations/appliances/demo-appliance-manager.d.ts +118 -0
  45. package/dist/implementations/appliances/demo-appliance-manager.js +277 -0
  46. package/dist/implementations/modbus/EnergyAppModbusBattery.d.ts +59 -0
  47. package/dist/implementations/modbus/EnergyAppModbusBattery.js +384 -0
  48. package/dist/implementations/modbus/EnergyAppModbusInverter.d.ts +56 -0
  49. package/dist/implementations/modbus/EnergyAppModbusInverter.js +344 -0
  50. package/dist/implementations/modbus/EnergyAppModbusMeter.d.ts +26 -0
  51. package/dist/implementations/modbus/EnergyAppModbusMeter.js +195 -0
  52. package/dist/implementations/modbus/sunspec/sunspec-devices.d.ts +95 -0
  53. package/dist/implementations/modbus/sunspec/sunspec-devices.js +335 -0
  54. package/dist/implementations/modbus/sunspec/sunspec-modbus-client.d.ts +171 -0
  55. package/dist/implementations/modbus/sunspec/sunspec-modbus-client.js +429 -0
  56. package/dist/implementations/udp/EnergyAppUdpServer.d.ts +42 -0
  57. package/dist/implementations/udp/EnergyAppUdpServer.js +169 -0
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.js +2 -0
  60. package/dist/packages/energy-app-electricity-prices.d.ts +74 -0
  61. package/dist/packages/energy-app-electricity-prices.js +1 -0
  62. package/dist/packages/energy-app-file.d.ts +90 -0
  63. package/dist/packages/energy-app-file.js +1 -0
  64. package/dist/types/enyo-data-bus-value.d.ts +2 -0
  65. package/dist/types/enyo-electricity-prices.d.ts +65 -0
  66. package/dist/types/enyo-electricity-prices.js +1 -0
  67. package/dist/types/enyo-energy-tariff.d.ts +64 -0
  68. package/dist/types/enyo-energy-tariff.js +1 -0
  69. package/dist/types/enyo-file.d.ts +108 -0
  70. package/dist/types/enyo-file.js +1 -0
  71. package/dist/types/enyo-storage.d.ts +4 -0
  72. package/dist/types/enyo-storage.js +1 -0
  73. package/dist/version.d.ts +1 -1
  74. package/dist/version.js +1 -1
  75. package/package.json +1 -1
@@ -17,6 +17,7 @@ var EnergyAppPackageCategory;
17
17
  EnergyAppPackageCategory["StaticElectricityTariff"] = "static-electricity-tariff";
18
18
  EnergyAppPackageCategory["TemperatureSensor"] = "temperature-sensor";
19
19
  EnergyAppPackageCategory["SmartPlug"] = "smart-plug";
20
+ EnergyAppPackageCategory["HeatingRod"] = "heating-rod";
20
21
  EnergyAppPackageCategory["Other"] = "other";
21
22
  })(EnergyAppPackageCategory || (exports.EnergyAppPackageCategory = EnergyAppPackageCategory = {}));
22
23
  /**
@@ -14,6 +14,7 @@ export declare enum EnergyAppPackageCategory {
14
14
  StaticElectricityTariff = "static-electricity-tariff",
15
15
  TemperatureSensor = "temperature-sensor",
16
16
  SmartPlug = "smart-plug",
17
+ HeatingRod = "heating-rod",
17
18
  Other = "other"
18
19
  }
19
20
  /**
@@ -48,4 +48,5 @@ var EnergyAppPermissionTypeEnum;
48
48
  EnergyAppPermissionTypeEnum["Wifi"] = "Wifi";
49
49
  EnergyAppPermissionTypeEnum["ChildProcess"] = "ChildProcess";
50
50
  EnergyAppPermissionTypeEnum["Udp"] = "Udp";
51
+ EnergyAppPermissionTypeEnum["ProvidedFiles"] = "ProvidedFiles";
51
52
  })(EnergyAppPermissionTypeEnum || (exports.EnergyAppPermissionTypeEnum = EnergyAppPermissionTypeEnum = {}));
@@ -1,4 +1,4 @@
1
- export type EnergyAppPermissionType = 'RestrictedInternetAccess' | 'NetworkDeviceDiscovery' | 'NetworkDeviceSearch' | 'NetworkDeviceAccess' | 'AllNetworkDeviceAccess' | 'Modbus' | 'Storage' | 'Appliance' | 'AllAppliances' | 'SendDataBusValues' | 'SubscribeDataBus' | 'SendDataBusCommands' | 'OcppServer' | 'ChargingCard' | 'Vehicle' | 'Charge' | 'SecretManager' | 'LocationZipCode' | 'LocationCoordinates' | 'Timeseries' | 'EnergyManagerInfo' | 'ElectricityTariff' | 'WeatherForecastRegister' | 'WeatherForecastUse' | 'PvForecastRegister' | 'PvForecastUse' | 'DynamicPriceForecastRegister' | 'DynamicPriceForecastUse' | 'PvSystemRegister' | 'PvSystemUse' | 'InverterControlCommands' | 'BatteryControlCommands' | 'BatteryStorageState' | 'ChargerControlCommands' | 'ModbusRtu' | 'EnergyPrices' | 'EnergyManager' | 'EebusDeviceManagement' | 'EebusDataAccess' | 'EebusControl' | 'Mqtt' | 'Bluetooth' | 'Wifi' | 'ChildProcess' | 'Udp';
1
+ export type EnergyAppPermissionType = 'RestrictedInternetAccess' | 'NetworkDeviceDiscovery' | 'NetworkDeviceSearch' | 'NetworkDeviceAccess' | 'AllNetworkDeviceAccess' | 'Modbus' | 'Storage' | 'Appliance' | 'AllAppliances' | 'SendDataBusValues' | 'SubscribeDataBus' | 'SendDataBusCommands' | 'OcppServer' | 'ChargingCard' | 'Vehicle' | 'Charge' | 'SecretManager' | 'LocationZipCode' | 'LocationCoordinates' | 'Timeseries' | 'EnergyManagerInfo' | 'ElectricityTariff' | 'WeatherForecastRegister' | 'WeatherForecastUse' | 'PvForecastRegister' | 'PvForecastUse' | 'DynamicPriceForecastRegister' | 'DynamicPriceForecastUse' | 'PvSystemRegister' | 'PvSystemUse' | 'InverterControlCommands' | 'BatteryControlCommands' | 'BatteryStorageState' | 'ChargerControlCommands' | 'ModbusRtu' | 'EnergyPrices' | 'EnergyManager' | 'EebusDeviceManagement' | 'EebusDataAccess' | 'EebusControl' | 'Mqtt' | 'Bluetooth' | 'Wifi' | 'ChildProcess' | 'Udp' | 'ProvidedFiles';
2
2
  export declare enum EnergyAppPermissionTypeEnum {
3
3
  RestrictedInternetAccess = "RestrictedInternetAccess",
4
4
  NetworkDeviceDiscovery = "NetworkDeviceDiscovery",
@@ -44,5 +44,6 @@ export declare enum EnergyAppPermissionTypeEnum {
44
44
  Bluetooth = "Bluetooth",
45
45
  Wifi = "Wifi",
46
46
  ChildProcess = "ChildProcess",
47
- Udp = "Udp"
47
+ Udp = "Udp",
48
+ ProvidedFiles = "ProvidedFiles"
48
49
  }
@@ -323,6 +323,19 @@ class EnergyApp {
323
323
  useBatteries() {
324
324
  return this.energyAppSdk.useBatteries();
325
325
  }
326
+ /**
327
+ * Gets the File API for providing user-facing files.
328
+ * Files are registered with a translated display name and optional
329
+ * translated explanation; their content (base64 + MIME type + concrete
330
+ * file name) is produced on demand by the registered handler when the user
331
+ * chooses to store the file.
332
+ * @returns The File API instance
333
+ * @throws {EnergyAppPermissionNotGrantedError} If the `ProvidedFiles`
334
+ * permission is not granted.
335
+ */
336
+ useFiles() {
337
+ return this.energyAppSdk.useFiles();
338
+ }
326
339
  /**
327
340
  * Gets the current SDK version.
328
341
  * @returns The semantic version string of the SDK
@@ -37,6 +37,7 @@ import { EnergyAppGridConnectionPoint } from "./packages/energy-app-grid-connect
37
37
  import { EnergyAppConfigurationManager } from "./packages/energy-app-configuration-manager.cjs";
38
38
  import { EnergyAppApplianceEnergyManagerForecast } from "./packages/energy-app-appliance-energy-manager-forecast.cjs";
39
39
  import { EnergyAppBattery } from "./packages/energy-app-battery.cjs";
40
+ import { EnergyAppFile } from "./packages/energy-app-file.cjs";
40
41
  import { UseFetchOptions } from "./types/enyo-fetch.cjs";
41
42
  /**
42
43
  * Concrete implementation of {@link EnyoEnergyAppSdk} that delegates every call
@@ -256,6 +257,17 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
256
257
  * @returns The Battery API instance
257
258
  */
258
259
  useBatteries(): EnergyAppBattery;
260
+ /**
261
+ * Gets the File API for providing user-facing files.
262
+ * Files are registered with a translated display name and optional
263
+ * translated explanation; their content (base64 + MIME type + concrete
264
+ * file name) is produced on demand by the registered handler when the user
265
+ * chooses to store the file.
266
+ * @returns The File API instance
267
+ * @throws {EnergyAppPermissionNotGrantedError} If the `ProvidedFiles`
268
+ * permission is not granted.
269
+ */
270
+ useFiles(): EnergyAppFile;
259
271
  /**
260
272
  * Gets the current SDK version.
261
273
  * @returns The semantic version string of the SDK
@@ -36,6 +36,7 @@ import { EnergyAppGridConnectionPoint } from "./packages/energy-app-grid-connect
36
36
  import { EnergyAppConfigurationManager } from "./packages/energy-app-configuration-manager.cjs";
37
37
  import { EnergyAppApplianceEnergyManagerForecast } from "./packages/energy-app-appliance-energy-manager-forecast.cjs";
38
38
  import { EnergyAppBattery } from "./packages/energy-app-battery.cjs";
39
+ import { EnergyAppFile } from "./packages/energy-app-file.cjs";
39
40
  import { UseFetchOptions } from "./types/enyo-fetch.cjs";
40
41
  export declare enum EnergyAppStateEnum {
41
42
  Launching = "launching",
@@ -137,4 +138,6 @@ export interface EnyoEnergyAppSdk {
137
138
  useApplianceEnergyManagerForecast: () => EnergyAppApplianceEnergyManagerForecast;
138
139
  /** Get the Battery API for retrieving the current runtime state of each battery storage (SoC, stored kWh, average price per kWh, optional solar share) */
139
140
  useBatteries: () => EnergyAppBattery;
141
+ /** Get the File API for providing user-facing files whose translated names/explanations are shown by the host and whose content is produced on demand when the user stores them */
142
+ useFiles: () => EnergyAppFile;
140
143
  }
@@ -0,0 +1,388 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EnergyAppModbusBattery = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const enyo_appliance_js_1 = require("../../types/enyo-appliance.cjs");
6
+ const enyo_appliance_js_2 = require("../../types/enyo-appliance.cjs");
7
+ const enyo_data_bus_value_js_1 = require("../../types/enyo-data-bus-value.cjs");
8
+ const enyo_source_enum_js_1 = require("../../types/enyo-source.enum.cjs");
9
+ const interfaces_js_1 = require("./interfaces.cjs");
10
+ const EnergyAppModbusRegisterMapper_js_1 = require("./EnergyAppModbusRegisterMapper.cjs");
11
+ const EnergyAppModbusFaultTolerantReader_js_1 = require("./EnergyAppModbusFaultTolerantReader.cjs");
12
+ class EnergyAppModbusBattery {
13
+ client;
14
+ config;
15
+ inverter;
16
+ _registerMapper;
17
+ _appliance;
18
+ _batteryMetadata;
19
+ constructor(client, config) {
20
+ this.client = client;
21
+ this.config = config;
22
+ this.config = config;
23
+ this.client = client;
24
+ this.inverter = config.inverter;
25
+ this._registerMapper = new EnergyAppModbusRegisterMapper_js_1.EnergyAppModbusRegisterMapper();
26
+ // Validate configuration
27
+ const validation = this._registerMapper.validateRegisterMap(config.registers);
28
+ if (!validation.valid) {
29
+ throw new interfaces_js_1.EnergyAppModbusConfigurationError(`Invalid battery configuration: ${validation.errors.join(', ')}`);
30
+ }
31
+ if (!config.inverter) {
32
+ throw new interfaces_js_1.EnergyAppModbusConfigurationError('Battery requires an inverter reference');
33
+ }
34
+ }
35
+ get appliance() {
36
+ if (!this._appliance) {
37
+ throw new Error('Battery appliance not initialized. Call connect() first.');
38
+ }
39
+ return this._appliance;
40
+ }
41
+ get networkDevice() {
42
+ if (!this.inverter) {
43
+ throw new Error('Battery requires an inverter reference');
44
+ }
45
+ return this.inverter.networkDevice;
46
+ }
47
+ async connect() {
48
+ if (!this.inverter) {
49
+ throw new Error('Battery requires an inverter reference');
50
+ }
51
+ if (!this.inverter.isConnected()) {
52
+ throw new Error('Inverter must be connected before connecting battery');
53
+ }
54
+ console.log(`Connecting battery to inverter ${this.config.name[0]?.name}...`);
55
+ // Discover battery metadata during connection
56
+ this._batteryMetadata = await this._discoverBatteryMetadata();
57
+ // Initialize appliance
58
+ await this._initializeAppliance();
59
+ console.log(`Successfully connected battery ${this.config.name[0]?.name}`);
60
+ }
61
+ async disconnect() {
62
+ try {
63
+ await this.inverter?.disconnect();
64
+ console.log(`Disconnected from battery at ${this.networkDevice.hostname}`);
65
+ }
66
+ catch (error) {
67
+ console.warn(`Error disconnecting from battery: ${error.message}`);
68
+ }
69
+ }
70
+ isConnected() {
71
+ return this.inverter?.isConnected() || false;
72
+ }
73
+ async updateData() {
74
+ if (!this.inverter || !this.inverter.isConnected() || !this._appliance) {
75
+ throw new Error('Battery not connected. Call connect() first.');
76
+ }
77
+ // Use the inverter's modbus instance
78
+ const modbusInstance = this.inverter._modbusInstance;
79
+ const connectionHealth = this.inverter._connectionHealth;
80
+ if (!modbusInstance) {
81
+ throw new Error('Inverter modbus instance not available');
82
+ }
83
+ const reader = new EnergyAppModbusFaultTolerantReader_js_1.EnergyAppModbusFaultTolerantReader(modbusInstance, connectionHealth);
84
+ const registerData = await this._registerMapper.readMultipleRegisters(reader, this.config.registers);
85
+ // Extract battery data
86
+ const batteryCurrent = registerData.current || undefined;
87
+ const batteryVoltage = registerData.voltage || undefined;
88
+ const batterySoC = registerData.soc || undefined;
89
+ const batteryPowerW = await this.getPower();
90
+ // Read current battery state if available
91
+ let batteryState;
92
+ try {
93
+ batteryState = await this.getBatteryState() || undefined;
94
+ }
95
+ catch (error) {
96
+ console.warn(`Failed to read battery state: ${error.message}`);
97
+ }
98
+ const message = {
99
+ type: 'message',
100
+ source: enyo_source_enum_js_1.EnyoSourceEnum.Device,
101
+ id: (0, node_crypto_1.randomUUID)(),
102
+ timestampIso: new Date().toISOString(),
103
+ message: enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.BatteryValuesUpdateV1,
104
+ applianceId: this._appliance.id,
105
+ data: {
106
+ state: batteryState,
107
+ batteryPowerW: batteryPowerW ?? undefined,
108
+ batterySoC
109
+ },
110
+ resolution: '10s'
111
+ };
112
+ console.log(`Battery Data (${this.config.name[0]?.name}): State=${batteryState || 'N/A'}, Power=${batteryPowerW}W, SoC=${batterySoC}%, Current=${batteryCurrent}A, Voltage=${batteryVoltage}V`);
113
+ return [message];
114
+ }
115
+ // Convenience methods for accessing specific register values
116
+ async getSoc() {
117
+ if (!this.inverter || !this.config.registers.soc) {
118
+ return null;
119
+ }
120
+ const modbusInstance = this.inverter._modbusInstance;
121
+ const connectionHealth = this.inverter._connectionHealth;
122
+ if (!modbusInstance) {
123
+ return null;
124
+ }
125
+ const reader = new EnergyAppModbusFaultTolerantReader_js_1.EnergyAppModbusFaultTolerantReader(modbusInstance, connectionHealth);
126
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.soc);
127
+ return result.success ? result.value : null;
128
+ }
129
+ async getCurrent() {
130
+ if (!this.inverter || !this.config.registers.current) {
131
+ return null;
132
+ }
133
+ const modbusInstance = this.inverter._modbusInstance;
134
+ const connectionHealth = this.inverter._connectionHealth;
135
+ if (!modbusInstance) {
136
+ return null;
137
+ }
138
+ const reader = new EnergyAppModbusFaultTolerantReader_js_1.EnergyAppModbusFaultTolerantReader(modbusInstance, connectionHealth);
139
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.current);
140
+ return result.success ? result.value : null;
141
+ }
142
+ async getPower() {
143
+ if (!this.inverter) {
144
+ return null;
145
+ }
146
+ // Priority 1: Direct power register
147
+ if (this.config.registers.power) {
148
+ const modbusInstance = this.inverter._modbusInstance;
149
+ const connectionHealth = this.inverter._connectionHealth;
150
+ if (!modbusInstance) {
151
+ return null;
152
+ }
153
+ const reader = new EnergyAppModbusFaultTolerantReader_js_1.EnergyAppModbusFaultTolerantReader(modbusInstance, connectionHealth);
154
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.power);
155
+ if (result.success && result.value !== undefined) {
156
+ return result.value;
157
+ }
158
+ }
159
+ // Priority 2: Calculate from percentage registers
160
+ if (this.config.registers.drainPercentage || this.config.registers.loadPercentage) {
161
+ const drainPercentage = await this.getDrainPercentage();
162
+ const loadPercentage = await this.getLoadPercentage();
163
+ const maxCapacityWh = this._batteryMetadata?.maxCapacityWh || null;
164
+ const percentagePower = this._calculatePowerFromPercentages(drainPercentage, loadPercentage, maxCapacityWh);
165
+ if (percentagePower !== null) {
166
+ return percentagePower;
167
+ }
168
+ }
169
+ // Priority 3: Calculate from current and voltage
170
+ const current = await this.getCurrent();
171
+ const voltage = await this.getVoltage();
172
+ if (current !== null && voltage !== null) {
173
+ return current * voltage;
174
+ }
175
+ return null;
176
+ }
177
+ async getVoltage() {
178
+ if (!this.inverter || !this.config.registers.voltage) {
179
+ return null;
180
+ }
181
+ const modbusInstance = this.inverter._modbusInstance;
182
+ const connectionHealth = this.inverter._connectionHealth;
183
+ if (!modbusInstance) {
184
+ return null;
185
+ }
186
+ const reader = new EnergyAppModbusFaultTolerantReader_js_1.EnergyAppModbusFaultTolerantReader(modbusInstance, connectionHealth);
187
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.voltage);
188
+ return result.success ? result.value : null;
189
+ }
190
+ /**
191
+ * Reads the drain percentage from modbus register.
192
+ * Drain percentage represents the percentage of maximum capacity being discharged.
193
+ *
194
+ * @returns Drain percentage (0-100), or null if not available
195
+ */
196
+ async getDrainPercentage() {
197
+ if (!this.inverter || !this.config.registers.drainPercentage) {
198
+ return null;
199
+ }
200
+ const modbusInstance = this.inverter._modbusInstance;
201
+ const connectionHealth = this.inverter._connectionHealth;
202
+ if (!modbusInstance) {
203
+ return null;
204
+ }
205
+ const reader = new EnergyAppModbusFaultTolerantReader_js_1.EnergyAppModbusFaultTolerantReader(modbusInstance, connectionHealth);
206
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.drainPercentage);
207
+ return result.success ? result.value : null;
208
+ }
209
+ /**
210
+ * Reads the load percentage from modbus register.
211
+ * Load percentage represents the percentage of maximum capacity being charged.
212
+ *
213
+ * @returns Load percentage (0-100), or null if not available
214
+ */
215
+ async getLoadPercentage() {
216
+ if (!this.inverter || !this.config.registers.loadPercentage) {
217
+ return null;
218
+ }
219
+ const modbusInstance = this.inverter._modbusInstance;
220
+ const connectionHealth = this.inverter._connectionHealth;
221
+ if (!modbusInstance) {
222
+ return null;
223
+ }
224
+ const reader = new EnergyAppModbusFaultTolerantReader_js_1.EnergyAppModbusFaultTolerantReader(modbusInstance, connectionHealth);
225
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.loadPercentage);
226
+ return result.success ? result.value : null;
227
+ }
228
+ /**
229
+ * Reads the current battery state from modbus registers.
230
+ * Maps the register value to EnyoBatteryApplianceStateEnum using the configured value mapping.
231
+ */
232
+ async getBatteryState() {
233
+ if (!this.inverter || !this.config.registers.state) {
234
+ return null;
235
+ }
236
+ const modbusInstance = this.inverter._modbusInstance;
237
+ const connectionHealth = this.inverter._connectionHealth;
238
+ if (!modbusInstance) {
239
+ return null;
240
+ }
241
+ try {
242
+ const reader = new EnergyAppModbusFaultTolerantReader_js_1.EnergyAppModbusFaultTolerantReader(modbusInstance, connectionHealth);
243
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.state);
244
+ if (!result.success || result.value === undefined) {
245
+ return null;
246
+ }
247
+ // Use configured value mapping if available
248
+ if (this.config.registers.state.valueMapping) {
249
+ const mapping = this.config.registers.state.valueMapping.find(m => m.value === result.value);
250
+ if (mapping) {
251
+ return mapping.mappedState;
252
+ }
253
+ else {
254
+ console.warn(`No mapping found for battery state value: ${result.value}. Available mappings: ${this.config.registers.state.valueMapping.map(m => m.value).join(', ')}`);
255
+ return null;
256
+ }
257
+ }
258
+ else {
259
+ console.warn('Battery state register configured without value mapping. Please configure valueMapping in register config.');
260
+ return null;
261
+ }
262
+ }
263
+ catch (error) {
264
+ console.warn(`Failed to read battery state: ${error.message}`);
265
+ return null;
266
+ }
267
+ }
268
+ /**
269
+ * Calculates battery power from drain and load percentages.
270
+ *
271
+ * @param drainPercentage - Percentage of max capacity being drained (discharging, positive power)
272
+ * @param loadPercentage - Percentage of max capacity being loaded (charging, negative power)
273
+ * @param maxCapacityWh - Maximum battery capacity in Wh
274
+ * @returns Power in watts (positive for discharging, negative for charging), or null if calculation not possible
275
+ */
276
+ _calculatePowerFromPercentages(drainPercentage, loadPercentage, maxCapacityWh) {
277
+ if (!maxCapacityWh) {
278
+ return null;
279
+ }
280
+ // Priority: drain percentage takes precedence over load percentage
281
+ if (drainPercentage !== null && drainPercentage !== undefined) {
282
+ // Discharging: positive power value
283
+ return (drainPercentage / 100) * maxCapacityWh;
284
+ }
285
+ if (loadPercentage !== null && loadPercentage !== undefined) {
286
+ // Charging: negative power value
287
+ return -(loadPercentage / 100) * maxCapacityWh;
288
+ }
289
+ return null;
290
+ }
291
+ /**
292
+ * Discovers and caches battery metadata from modbus registers during connection.
293
+ * This method reads static metadata like max capacity and power limits once
294
+ * and stores them for later use in appliance creation.
295
+ */
296
+ async _discoverBatteryMetadata() {
297
+ if (!this.inverter || !this.inverter.isConnected()) {
298
+ throw new Error('Inverter must be connected before discovering battery metadata');
299
+ }
300
+ const modbusInstance = this.inverter._modbusInstance;
301
+ const connectionHealth = this.inverter._connectionHealth;
302
+ if (!modbusInstance) {
303
+ throw new Error('Inverter modbus instance not available');
304
+ }
305
+ const reader = new EnergyAppModbusFaultTolerantReader_js_1.EnergyAppModbusFaultTolerantReader(modbusInstance, connectionHealth);
306
+ const metadata = {};
307
+ try {
308
+ // Read max capacity if configured
309
+ if (this.config.registers.maxCapacityWh) {
310
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.maxCapacityWh);
311
+ if (result.success && result.value !== undefined) {
312
+ metadata.maxCapacityWh = result.value;
313
+ console.log(`Discovered battery max capacity: ${result.value} Wh`);
314
+ }
315
+ }
316
+ // Read max discharge power if configured
317
+ if (this.config.registers.maxDischargePowerW) {
318
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.maxDischargePowerW);
319
+ if (result.success && result.value !== undefined) {
320
+ metadata.maxDischargePowerW = result.value;
321
+ console.log(`Discovered battery max discharge power: ${result.value} W`);
322
+ }
323
+ }
324
+ // Read max charging power if configured
325
+ if (this.config.registers.maxChargingPowerW) {
326
+ const result = await this._registerMapper.readRegister(reader, this.config.registers.maxChargingPowerW);
327
+ if (result.success && result.value !== undefined) {
328
+ metadata.maxChargingPowerW = result.value;
329
+ console.log(`Discovered battery max charging power: ${result.value} W`);
330
+ }
331
+ }
332
+ // Set connected appliance ID to the inverter's appliance ID if available
333
+ if (this.inverter.appliance?.id) {
334
+ metadata.connectedToApplianceId = this.inverter.appliance.id;
335
+ }
336
+ }
337
+ catch (error) {
338
+ console.warn(`Warning: Failed to discover some battery metadata: ${error.message}`);
339
+ }
340
+ return metadata;
341
+ }
342
+ async _initializeAppliance() {
343
+ if (!this.inverter) {
344
+ throw new Error('Battery requires an inverter reference');
345
+ }
346
+ const appliances = await this.client.useAppliances().list();
347
+ let existingAppliance = appliances.find(a => a.networkDeviceIds.includes(this.inverter.networkDevice.id) &&
348
+ a.type === enyo_appliance_js_2.EnyoApplianceTypeEnum.Storage);
349
+ if (!existingAppliance) {
350
+ // Create new appliance
351
+ existingAppliance = {
352
+ id: (0, node_crypto_1.randomUUID)(),
353
+ type: enyo_appliance_js_2.EnyoApplianceTypeEnum.Storage,
354
+ networkDeviceIds: [this.inverter.networkDevice.id],
355
+ name: this.config.name,
356
+ metadata: {
357
+ state: enyo_appliance_js_2.EnyoApplianceStateEnum.Connected,
358
+ connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
359
+ ...this.config.options?.topology && { topology: this.config.options.topology }
360
+ },
361
+ battery: this._batteryMetadata
362
+ };
363
+ await this.client.useAppliances().save(existingAppliance, undefined);
364
+ console.log(`Created new battery appliance: ${this.config.name[0]?.name}`);
365
+ }
366
+ else {
367
+ // Update existing appliance
368
+ existingAppliance = {
369
+ ...existingAppliance,
370
+ name: this.config.name,
371
+ metadata: {
372
+ ...existingAppliance.metadata,
373
+ state: enyo_appliance_js_2.EnyoApplianceStateEnum.Connected,
374
+ connectionType: enyo_appliance_js_1.EnyoApplianceConnectionType.Connector,
375
+ ...this.config.options?.topology && { topology: this.config.options.topology }
376
+ },
377
+ battery: this._batteryMetadata
378
+ };
379
+ await this.client.useAppliances().save(existingAppliance, existingAppliance.id);
380
+ console.log(`Updated existing battery appliance: ${this.config.name[0]?.name}`);
381
+ }
382
+ this._appliance = existingAppliance;
383
+ }
384
+ modbusClient() {
385
+ return this.inverter?.modbusClient();
386
+ }
387
+ }
388
+ exports.EnergyAppModbusBattery = EnergyAppModbusBattery;
@@ -0,0 +1,59 @@
1
+ import { EnyoAppliance } from "../../types/enyo-appliance.cjs";
2
+ import { EnyoBatteryStateEnum, type EnyoDataBusMessage } from "../../types/enyo-data-bus-value.cjs";
3
+ import { type EnergyAppModbusDevice, type EnergyAppModbusBatteryConfig, type IEnergyAppModbusInverter } from './interfaces.cjs';
4
+ import { EnergyApp } from "../../index.cjs";
5
+ export declare class EnergyAppModbusBattery implements EnergyAppModbusDevice {
6
+ readonly client: EnergyApp;
7
+ readonly config: EnergyAppModbusBatteryConfig;
8
+ readonly inverter?: IEnergyAppModbusInverter;
9
+ private readonly _registerMapper;
10
+ private _appliance?;
11
+ private _batteryMetadata?;
12
+ constructor(client: EnergyApp, config: EnergyAppModbusBatteryConfig);
13
+ get appliance(): EnyoAppliance;
14
+ get networkDevice(): import("../../types/enyo-network-device.cjs").EnyoNetworkDevice;
15
+ connect(): Promise<void>;
16
+ disconnect(): Promise<void>;
17
+ isConnected(): boolean;
18
+ updateData(): Promise<EnyoDataBusMessage[]>;
19
+ getSoc(): Promise<number | null>;
20
+ getCurrent(): Promise<number | null>;
21
+ getPower(): Promise<number | null>;
22
+ getVoltage(): Promise<number | null>;
23
+ /**
24
+ * Reads the drain percentage from modbus register.
25
+ * Drain percentage represents the percentage of maximum capacity being discharged.
26
+ *
27
+ * @returns Drain percentage (0-100), or null if not available
28
+ */
29
+ getDrainPercentage(): Promise<number | null>;
30
+ /**
31
+ * Reads the load percentage from modbus register.
32
+ * Load percentage represents the percentage of maximum capacity being charged.
33
+ *
34
+ * @returns Load percentage (0-100), or null if not available
35
+ */
36
+ getLoadPercentage(): Promise<number | null>;
37
+ /**
38
+ * Reads the current battery state from modbus registers.
39
+ * Maps the register value to EnyoBatteryApplianceStateEnum using the configured value mapping.
40
+ */
41
+ getBatteryState(): Promise<EnyoBatteryStateEnum | null>;
42
+ /**
43
+ * Calculates battery power from drain and load percentages.
44
+ *
45
+ * @param drainPercentage - Percentage of max capacity being drained (discharging, positive power)
46
+ * @param loadPercentage - Percentage of max capacity being loaded (charging, negative power)
47
+ * @param maxCapacityWh - Maximum battery capacity in Wh
48
+ * @returns Power in watts (positive for discharging, negative for charging), or null if calculation not possible
49
+ */
50
+ private _calculatePowerFromPercentages;
51
+ /**
52
+ * Discovers and caches battery metadata from modbus registers during connection.
53
+ * This method reads static metadata like max capacity and power limits once
54
+ * and stores them for later use in appliance creation.
55
+ */
56
+ private _discoverBatteryMetadata;
57
+ private _initializeAppliance;
58
+ modbusClient(): import("../../packages/energy-app-modbus.cjs").EnergyAppModbusInstance | undefined;
59
+ }