@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.
- package/dist/cjs/energy-app-package-definition.cjs +1 -0
- package/dist/cjs/energy-app-package-definition.d.cts +1 -0
- package/dist/cjs/energy-app-permission.type.cjs +1 -0
- package/dist/cjs/energy-app-permission.type.d.cts +3 -2
- package/dist/cjs/energy-app.cjs +13 -0
- package/dist/cjs/energy-app.d.cts +12 -0
- package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
- package/dist/cjs/implementations/modbus/EnergyAppModbusBattery.cjs +388 -0
- package/dist/cjs/implementations/modbus/EnergyAppModbusBattery.d.cts +59 -0
- package/dist/cjs/implementations/modbus/EnergyAppModbusInverter.cjs +348 -0
- package/dist/cjs/implementations/modbus/EnergyAppModbusInverter.d.cts +56 -0
- package/dist/cjs/implementations/modbus/EnergyAppModbusMeter.cjs +199 -0
- package/dist/cjs/implementations/modbus/EnergyAppModbusMeter.d.cts +26 -0
- package/dist/cjs/implementations/modbus/sunspec/sunspec-devices.cjs +342 -0
- package/dist/cjs/implementations/modbus/sunspec/sunspec-devices.d.cts +95 -0
- package/dist/cjs/implementations/modbus/sunspec/sunspec-modbus-client.cjs +433 -0
- package/dist/cjs/implementations/modbus/sunspec/sunspec-modbus-client.d.cts +171 -0
- package/dist/cjs/implementations/udp/EnergyAppUdpServer.cjs +174 -0
- package/dist/cjs/implementations/udp/EnergyAppUdpServer.d.cts +42 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/packages/energy-app-electricity-prices.cjs +2 -0
- package/dist/cjs/packages/energy-app-electricity-prices.d.cts +74 -0
- package/dist/cjs/packages/energy-app-file.cjs +2 -0
- package/dist/cjs/packages/energy-app-file.d.cts +90 -0
- package/dist/cjs/types/enyo-data-bus-value.d.cts +2 -0
- package/dist/cjs/types/enyo-electricity-prices.cjs +2 -0
- package/dist/cjs/types/enyo-electricity-prices.d.cts +65 -0
- package/dist/cjs/types/enyo-energy-tariff.cjs +2 -0
- package/dist/cjs/types/enyo-energy-tariff.d.cts +64 -0
- package/dist/cjs/types/enyo-file.cjs +2 -0
- package/dist/cjs/types/enyo-file.d.cts +108 -0
- package/dist/cjs/types/enyo-storage.cjs +2 -0
- package/dist/cjs/types/enyo-storage.d.cts +4 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/energy-app-package-definition.d.ts +1 -0
- package/dist/energy-app-package-definition.js +1 -0
- package/dist/energy-app-permission.type.d.ts +3 -2
- package/dist/energy-app-permission.type.js +1 -0
- package/dist/energy-app.d.ts +12 -0
- package/dist/energy-app.js +13 -0
- package/dist/enyo-energy-app-sdk.d.ts +3 -0
- package/dist/implementations/appliances/demo-appliance-manager.d.ts +118 -0
- package/dist/implementations/appliances/demo-appliance-manager.js +277 -0
- package/dist/implementations/modbus/EnergyAppModbusBattery.d.ts +59 -0
- package/dist/implementations/modbus/EnergyAppModbusBattery.js +384 -0
- package/dist/implementations/modbus/EnergyAppModbusInverter.d.ts +56 -0
- package/dist/implementations/modbus/EnergyAppModbusInverter.js +344 -0
- package/dist/implementations/modbus/EnergyAppModbusMeter.d.ts +26 -0
- package/dist/implementations/modbus/EnergyAppModbusMeter.js +195 -0
- package/dist/implementations/modbus/sunspec/sunspec-devices.d.ts +95 -0
- package/dist/implementations/modbus/sunspec/sunspec-devices.js +335 -0
- package/dist/implementations/modbus/sunspec/sunspec-modbus-client.d.ts +171 -0
- package/dist/implementations/modbus/sunspec/sunspec-modbus-client.js +429 -0
- package/dist/implementations/udp/EnergyAppUdpServer.d.ts +42 -0
- package/dist/implementations/udp/EnergyAppUdpServer.js +169 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/packages/energy-app-electricity-prices.d.ts +74 -0
- package/dist/packages/energy-app-electricity-prices.js +1 -0
- package/dist/packages/energy-app-file.d.ts +90 -0
- package/dist/packages/energy-app-file.js +1 -0
- package/dist/types/enyo-data-bus-value.d.ts +2 -0
- package/dist/types/enyo-electricity-prices.d.ts +65 -0
- package/dist/types/enyo-electricity-prices.js +1 -0
- package/dist/types/enyo-energy-tariff.d.ts +64 -0
- package/dist/types/enyo-energy-tariff.js +1 -0
- package/dist/types/enyo-file.d.ts +108 -0
- package/dist/types/enyo-file.js +1 -0
- package/dist/types/enyo-storage.d.ts +4 -0
- package/dist/types/enyo-storage.js +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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
|
/**
|
|
@@ -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
|
}
|
package/dist/cjs/energy-app.cjs
CHANGED
|
@@ -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
|
+
}
|