@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.
- package/dist/cjs/implementations/appliances/appliance-manager.cjs +399 -0
- package/dist/cjs/implementations/appliances/appliance-manager.d.cts +191 -0
- package/dist/cjs/implementations/appliances/identifier-strategies.cjs +180 -0
- package/dist/cjs/implementations/appliances/identifier-strategies.d.cts +140 -0
- package/dist/cjs/implementations/appliances/in-memory-appliance-manager.cjs +281 -0
- package/dist/cjs/implementations/appliances/in-memory-appliance-manager.d.cts +119 -0
- package/dist/cjs/implementations/data-bus/demo-data-bus.cjs +246 -0
- package/dist/cjs/implementations/data-bus/demo-data-bus.d.cts +111 -0
- package/dist/cjs/implementations/modbus/EnergyAppModbusDataTypeConverter.d.cts +2 -2
- package/dist/cjs/implementations/modbus/EnergyAppModbusFaultTolerantReader.cjs +76 -0
- package/dist/cjs/implementations/modbus/EnergyAppModbusFaultTolerantReader.d.cts +31 -1
- package/dist/cjs/implementations/modbus/EnergyAppModbusRegisterMapper.d.cts +2 -2
- package/dist/cjs/implementations/modbus/interfaces.d.cts +7 -93
- 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/index.cjs +2 -3
- package/dist/cjs/index.d.cts +2 -3
- package/dist/cjs/types/enyo-data-bus-value.d.cts +2 -1
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/implementations/appliances/appliance-manager.d.ts +191 -0
- package/dist/implementations/appliances/appliance-manager.js +395 -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/appliances/identifier-strategies.d.ts +140 -0
- package/dist/implementations/appliances/identifier-strategies.js +171 -0
- package/dist/implementations/appliances/in-memory-appliance-manager.d.ts +119 -0
- package/dist/implementations/appliances/in-memory-appliance-manager.js +277 -0
- package/dist/implementations/data-bus/demo-data-bus.d.ts +111 -0
- package/dist/implementations/data-bus/demo-data-bus.js +242 -0
- package/dist/implementations/modbus/EnergyAppModbusDataTypeConverter.d.ts +2 -2
- package/dist/implementations/modbus/EnergyAppModbusFaultTolerantReader.d.ts +31 -1
- package/dist/implementations/modbus/EnergyAppModbusFaultTolerantReader.js +76 -0
- package/dist/implementations/modbus/EnergyAppModbusRegisterMapper.d.ts +2 -2
- package/dist/implementations/modbus/interfaces.d.ts +7 -93
- 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/index.d.ts +2 -3
- package/dist/index.js +2 -3
- package/dist/types/enyo-data-bus-value.d.ts +2 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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
|
|
10
|
+
export interface EnergyAppModbusStateValueMapping<T> {
|
|
15
11
|
value: number;
|
|
16
|
-
mappedState:
|
|
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?:
|
|
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
|
+
}
|