@enyo-energy/energy-app-sdk 0.0.34 → 0.0.36
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/README.md +1069 -15
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
- package/dist/cjs/connect-ems-api.cjs +0 -2
- package/dist/cjs/connect-ems-api.d.cts +0 -30
- package/dist/cjs/connect-ems-permission.enum.cjs +0 -7
- package/dist/cjs/connect-ems-permission.enum.d.cts +0 -3
- package/dist/cjs/connect-ems-permission.type.cjs +0 -2
- package/dist/cjs/connect-ems-permission.type.d.cts +0 -1
- package/dist/cjs/connect-package-definition.cjs +0 -14
- package/dist/cjs/connect-package-definition.d.cts +0 -57
- package/dist/cjs/energy-app-package-configuration.cjs +0 -2
- package/dist/cjs/energy-app-package-configuration.d.cts +0 -30
- package/dist/cjs/hems-one-energy-app-sdk.cjs +0 -12
- package/dist/cjs/hems-one-energy-app-sdk.d.cts +0 -64
- package/dist/cjs/mockConnectEmsApi.cjs +0 -167
- package/dist/cjs/mockConnectEmsApi.d.cts +0 -10
- package/dist/cjs/packages/connect-appliance.cjs +0 -2
- package/dist/cjs/packages/connect-appliance.d.cts +0 -24
- package/dist/cjs/packages/connect-http-api.cjs +0 -2
- package/dist/cjs/packages/connect-http-api.d.cts +0 -15
- package/dist/cjs/packages/connect-interval.cjs +0 -2
- package/dist/cjs/packages/connect-interval.d.cts +0 -15
- package/dist/cjs/packages/connect-modbus.cjs +0 -2
- package/dist/cjs/packages/connect-modbus.d.cts +0 -60
- package/dist/cjs/packages/connect-network-devices.cjs +0 -2
- package/dist/cjs/packages/connect-network-devices.d.cts +0 -40
- package/dist/cjs/packages/connect-storage.cjs +0 -2
- package/dist/cjs/packages/connect-storage.d.cts +0 -14
- package/dist/cjs/types/connect-appliance-type.enum.cjs +0 -10
- package/dist/cjs/types/connect-appliance-type.enum.d.cts +0 -6
- package/dist/cjs/types/energy-app-settings.cjs +0 -2
- package/dist/cjs/types/energy-app-settings.d.cts +0 -74
- package/dist/cjs/types/hems-one-appliance.cjs +0 -33
- package/dist/cjs/types/hems-one-appliance.d.cts +0 -81
- package/dist/cjs/types/hems-one-authentication.cjs +0 -9
- package/dist/cjs/types/hems-one-authentication.d.cts +0 -69
- package/dist/cjs/types/hems-one-battery-appliance.cjs +0 -2
- package/dist/cjs/types/hems-one-battery-appliance.d.cts +0 -6
- package/dist/cjs/types/hems-one-charge.cjs +0 -15
- package/dist/cjs/types/hems-one-charge.d.cts +0 -82
- package/dist/cjs/types/hems-one-charger-appliance.cjs +0 -41
- package/dist/cjs/types/hems-one-charger-appliance.d.cts +0 -49
- package/dist/cjs/types/hems-one-charging-card.cjs +0 -2
- package/dist/cjs/types/hems-one-charging-card.d.cts +0 -11
- package/dist/cjs/types/hems-one-data-bus-command.cjs +0 -9
- package/dist/cjs/types/hems-one-data-bus-command.d.cts +0 -34
- package/dist/cjs/types/hems-one-data-bus-value.cjs +0 -78
- package/dist/cjs/types/hems-one-data-bus-value.d.cts +0 -379
- package/dist/cjs/types/hems-one-electricity-prices.cjs +0 -2
- package/dist/cjs/types/hems-one-electricity-prices.d.cts +0 -63
- package/dist/cjs/types/hems-one-energy-tariff.cjs +0 -2
- package/dist/cjs/types/hems-one-energy-tariff.d.cts +0 -70
- package/dist/cjs/types/hems-one-heatpump-appliance.cjs +0 -19
- package/dist/cjs/types/hems-one-heatpump-appliance.d.cts +0 -34
- package/dist/cjs/types/hems-one-inverter-appliance.cjs +0 -2
- package/dist/cjs/types/hems-one-inverter-appliance.d.cts +0 -3
- package/dist/cjs/types/hems-one-meter-appliance.cjs +0 -10
- package/dist/cjs/types/hems-one-meter-appliance.d.cts +0 -10
- package/dist/cjs/types/hems-one-network-device.cjs +0 -2
- package/dist/cjs/types/hems-one-network-device.d.cts +0 -31
- package/dist/cjs/types/hems-one-source.enum.cjs +0 -8
- package/dist/cjs/types/hems-one-source.enum.d.cts +0 -4
- package/dist/cjs/types/hems-one-vehicle.cjs +0 -2
- package/dist/cjs/types/hems-one-vehicle.d.cts +0 -12
- package/dist/cjs/types/hems-one-websocket-connection.cjs +0 -2
- package/dist/cjs/types/hems-one-websocket-connection.d.cts +0 -8
- package/dist/connect-ems-api.d.ts +0 -30
- package/dist/connect-ems-api.js +0 -1
- package/dist/connect-ems-permission.enum.d.ts +0 -3
- package/dist/connect-ems-permission.enum.js +0 -4
- package/dist/connect-ems-permission.type.d.ts +0 -1
- package/dist/connect-ems-permission.type.js +0 -1
- package/dist/connect-package-definition.d.ts +0 -57
- package/dist/connect-package-definition.js +0 -10
- package/dist/energy-app-package-configuration.d.ts +0 -30
- package/dist/energy-app-package-configuration.js +0 -1
- package/dist/hems-one-energy-app-sdk.d.ts +0 -67
- package/dist/hems-one-energy-app-sdk.js +0 -9
- package/dist/mockConnectEmsApi.d.ts +0 -10
- package/dist/mockConnectEmsApi.js +0 -160
- package/dist/packages/connect-appliance.d.ts +0 -24
- package/dist/packages/connect-appliance.js +0 -1
- package/dist/packages/connect-http-api.d.ts +0 -15
- package/dist/packages/connect-http-api.js +0 -1
- package/dist/packages/connect-interval.d.ts +0 -15
- package/dist/packages/connect-interval.js +0 -1
- package/dist/packages/connect-modbus.d.ts +0 -60
- package/dist/packages/connect-modbus.js +0 -1
- package/dist/packages/connect-network-devices.d.ts +0 -40
- package/dist/packages/connect-network-devices.js +0 -1
- package/dist/packages/connect-storage.d.ts +0 -14
- package/dist/packages/connect-storage.js +0 -1
- package/dist/types/connect-appliance-type.enum.d.ts +0 -6
- package/dist/types/connect-appliance-type.enum.js +0 -7
- package/dist/types/energy-app-settings.d.ts +0 -74
- package/dist/types/energy-app-settings.js +0 -1
- package/dist/types/hems-one-appliance.d.ts +0 -81
- package/dist/types/hems-one-appliance.js +0 -30
- package/dist/types/hems-one-authentication.d.ts +0 -69
- package/dist/types/hems-one-authentication.js +0 -6
- package/dist/types/hems-one-battery-appliance.d.ts +0 -6
- package/dist/types/hems-one-battery-appliance.js +0 -1
- package/dist/types/hems-one-charge.d.ts +0 -82
- package/dist/types/hems-one-charge.js +0 -12
- package/dist/types/hems-one-charger-appliance.d.ts +0 -49
- package/dist/types/hems-one-charger-appliance.js +0 -38
- package/dist/types/hems-one-charging-card.d.ts +0 -11
- package/dist/types/hems-one-charging-card.js +0 -1
- package/dist/types/hems-one-data-bus-command.d.ts +0 -34
- package/dist/types/hems-one-data-bus-command.js +0 -6
- package/dist/types/hems-one-data-bus-value.d.ts +0 -379
- package/dist/types/hems-one-data-bus-value.js +0 -75
- package/dist/types/hems-one-electricity-prices.d.ts +0 -63
- package/dist/types/hems-one-electricity-prices.js +0 -1
- package/dist/types/hems-one-energy-tariff.d.ts +0 -70
- package/dist/types/hems-one-energy-tariff.js +0 -1
- package/dist/types/hems-one-heatpump-appliance.d.ts +0 -34
- package/dist/types/hems-one-heatpump-appliance.js +0 -16
- package/dist/types/hems-one-inverter-appliance.d.ts +0 -3
- package/dist/types/hems-one-inverter-appliance.js +0 -1
- package/dist/types/hems-one-meter-appliance.d.ts +0 -10
- package/dist/types/hems-one-meter-appliance.js +0 -7
- package/dist/types/hems-one-network-device.d.ts +0 -31
- package/dist/types/hems-one-network-device.js +0 -1
- package/dist/types/hems-one-source.enum.d.ts +0 -4
- package/dist/types/hems-one-source.enum.js +0 -5
- package/dist/types/hems-one-vehicle.d.ts +0 -12
- package/dist/types/hems-one-vehicle.js +0 -1
- package/dist/types/hems-one-websocket-connection.d.ts +0 -8
- package/dist/types/hems-one-websocket-connection.js +0 -1
package/README.md
CHANGED
|
@@ -1,29 +1,610 @@
|
|
|
1
1
|
# enyo Energy App SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The official TypeScript SDK for building Energy Apps on the enyo platform. Create powerful energy management applications that integrate with inverters, batteries, charging stations, and smart home devices.
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/js/@enyo-energy%2Fenergy-app-sdk)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [Core Concepts](#core-concepts)
|
|
13
|
+
- [Energy App Lifecycle](#energy-app-lifecycle)
|
|
14
|
+
- [Package Definition](#package-definition)
|
|
15
|
+
- [Permissions System](#permissions-system)
|
|
16
|
+
- [API Reference](#api-reference)
|
|
17
|
+
- [Lifecycle Management](#lifecycle-management)
|
|
18
|
+
- [System APIs](#system-apis)
|
|
19
|
+
- [Device Communication](#device-communication)
|
|
20
|
+
- [Data Management](#data-management)
|
|
21
|
+
- [Energy Resources](#energy-resources)
|
|
22
|
+
- [User Features](#user-features)
|
|
23
|
+
- [Advanced Modbus Integration](#advanced-modbus-integration)
|
|
24
|
+
- [Examples](#examples)
|
|
25
|
+
- [Basic Energy App](#basic-energy-app)
|
|
26
|
+
- [Device Integration](#device-integration)
|
|
27
|
+
- [Data Bus Messaging](#data-bus-messaging)
|
|
28
|
+
- [Settings Management](#settings-management)
|
|
29
|
+
- [Troubleshooting](#troubleshooting)
|
|
30
|
+
- [CLI Tool](#cli-tool)
|
|
4
31
|
|
|
5
32
|
## Installation
|
|
6
33
|
|
|
7
|
-
|
|
34
|
+
Install the SDK using npm:
|
|
35
|
+
|
|
8
36
|
```bash
|
|
9
|
-
npm install @enyo/energy-app-sdk
|
|
37
|
+
npm install @enyo-energy/energy-app-sdk
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
Create a basic Energy App that responds to system events:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { EnergyApp, defineEnergyAppPackage, EnergyAppPackageCategory, EnergyAppStateEnum } from '@enyo-energy/energy-app-sdk';
|
|
46
|
+
|
|
47
|
+
// Initialize the Energy App
|
|
48
|
+
const energyApp = new EnergyApp();
|
|
49
|
+
|
|
50
|
+
energyApp.register((packageName: string, version: number) => {
|
|
51
|
+
console.log(`Energy App ${packageName} v${version} is starting...`);
|
|
52
|
+
console.log(`System is ${energyApp.isSystemOnline() ? 'online' : 'offline'}`);
|
|
53
|
+
|
|
54
|
+
// Set app state to running
|
|
55
|
+
energyApp.updateEnergyAppState(EnergyAppStateEnum.Running);
|
|
56
|
+
|
|
57
|
+
// Your app logic starts here
|
|
58
|
+
startApp();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
async function startApp() {
|
|
62
|
+
// Use SDK APIs
|
|
63
|
+
const storage = energyApp.useStorage();
|
|
64
|
+
const dataBus = energyApp.useDataBus();
|
|
65
|
+
|
|
66
|
+
// Store app configuration
|
|
67
|
+
await storage.save('config', { initialized: true, timestamp: Date.now() });
|
|
68
|
+
|
|
69
|
+
// Listen for data bus messages
|
|
70
|
+
dataBus.listenForMessages(['InverterValuesUpdateV1'], (message) => {
|
|
71
|
+
console.log('Received inverter data:', message);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Core Concepts
|
|
77
|
+
|
|
78
|
+
### Energy App Lifecycle
|
|
79
|
+
|
|
80
|
+
Energy Apps follow a specific lifecycle managed by the enyo system:
|
|
81
|
+
|
|
82
|
+
1. **Initialization**: Your app registers with the system
|
|
83
|
+
2. **Running**: App performs its main functionality
|
|
84
|
+
3. **State Management**: App reports its current state
|
|
85
|
+
4. **Shutdown**: Graceful cleanup when system stops
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const energyApp = new EnergyApp();
|
|
89
|
+
|
|
90
|
+
// Register startup callback
|
|
91
|
+
energyApp.register((packageName, version) => {
|
|
92
|
+
console.log(`${packageName} v${version} started`);
|
|
93
|
+
energyApp.updateEnergyAppState(EnergyAppStateEnum.Running);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Register shutdown callback
|
|
97
|
+
energyApp.onShutdown(async () => {
|
|
98
|
+
console.log('Cleaning up resources...');
|
|
99
|
+
// Perform cleanup tasks
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### App States
|
|
104
|
+
|
|
105
|
+
- `launching`: Initial state when app is starting up
|
|
106
|
+
- `running`: App is functioning normally
|
|
107
|
+
- `configuration-required`: App needs user configuration
|
|
108
|
+
- `internet-connection-required`: App needs internet connectivity
|
|
109
|
+
|
|
110
|
+
### Package Definition
|
|
111
|
+
|
|
112
|
+
Every Energy App must be defined using `defineEnergyAppPackage()`:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import {
|
|
116
|
+
defineEnergyAppPackage,
|
|
117
|
+
EnergyAppPackageCategory,
|
|
118
|
+
EnergyAppPermissionTypeEnum
|
|
119
|
+
} from '@enyo-energy/energy-app-sdk';
|
|
120
|
+
|
|
121
|
+
const packageDef = defineEnergyAppPackage({
|
|
122
|
+
version: '1',
|
|
123
|
+
packageName: 'solar-optimizer',
|
|
124
|
+
logo: './assets/logo.png',
|
|
125
|
+
categories: [
|
|
126
|
+
EnergyAppPackageCategory.Inverter,
|
|
127
|
+
EnergyAppPackageCategory.EnergyManagement
|
|
128
|
+
],
|
|
129
|
+
storeEntry: [
|
|
130
|
+
{
|
|
131
|
+
language: 'en',
|
|
132
|
+
title: 'Solar Optimizer',
|
|
133
|
+
shortDescription: 'Optimize your solar energy production',
|
|
134
|
+
description: 'Advanced solar energy optimization with AI-driven predictions and real-time adjustments.'
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
language: 'de',
|
|
138
|
+
title: 'Solar Optimierer',
|
|
139
|
+
shortDescription: 'Optimieren Sie Ihre Solarenergieproduktion',
|
|
140
|
+
description: 'Erweiterte Solarenergie-Optimierung mit KI-gesteuerten Vorhersagen und Echtzeitanpassungen.'
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
permissions: [
|
|
144
|
+
EnergyAppPermissionTypeEnum.Modbus,
|
|
145
|
+
EnergyAppPermissionTypeEnum.SendDataBusValues,
|
|
146
|
+
EnergyAppPermissionTypeEnum.SubscribeDataBus,
|
|
147
|
+
EnergyAppPermissionTypeEnum.Storage
|
|
148
|
+
],
|
|
149
|
+
options: {
|
|
150
|
+
restrictedInternetAccess: {
|
|
151
|
+
origins: ['api.weather.com', 'solar-forecasting.com']
|
|
152
|
+
},
|
|
153
|
+
deviceDetection: {
|
|
154
|
+
modbus: [{
|
|
155
|
+
unitIds: [1],
|
|
156
|
+
registerAddress: 40001,
|
|
157
|
+
registerSize: 2,
|
|
158
|
+
type: 'string',
|
|
159
|
+
matchingValues: ['SolarMax', 'SMA']
|
|
160
|
+
}]
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Package Categories
|
|
167
|
+
|
|
168
|
+
- `Inverter`: Solar inverter management
|
|
169
|
+
- `Wallbox`: EV charging station integration
|
|
170
|
+
- `Meter`: Energy metering applications
|
|
171
|
+
- `EnergyManagement`: Overall energy optimization
|
|
172
|
+
- `HeatPump`: Heat pump control systems
|
|
173
|
+
- `BatteryStorage`: Battery management
|
|
174
|
+
- `ClimateControl`: HVAC and climate systems
|
|
175
|
+
- `ElectricityTariff`: Dynamic pricing integration
|
|
176
|
+
|
|
177
|
+
### Permissions System
|
|
178
|
+
|
|
179
|
+
Energy Apps use a granular permissions system to control access to system resources:
|
|
180
|
+
|
|
181
|
+
#### Core Permissions
|
|
182
|
+
|
|
183
|
+
- **`Storage`**: Access persistent key-value storage
|
|
184
|
+
- **`NetworkDeviceDiscovery`**: Discover devices on the local network
|
|
185
|
+
- **`NetworkDeviceSearch`**: Search for specific network devices
|
|
186
|
+
- **`NetworkDeviceAccess`**: Access discovered network devices
|
|
187
|
+
- **`Modbus`**: Communicate via Modbus protocol
|
|
188
|
+
|
|
189
|
+
#### Data Bus Permissions
|
|
190
|
+
|
|
191
|
+
- **`SendDataBusValues`**: Send sensor data and measurements
|
|
192
|
+
- **`SubscribeDataBus`**: Listen to data from other devices
|
|
193
|
+
- **`SendDataBusCommands`**: Send control commands
|
|
194
|
+
|
|
195
|
+
#### Device Permissions
|
|
196
|
+
|
|
197
|
+
- **`Appliance`**: Manage appliances created by your package
|
|
198
|
+
- **`AllAppliances`**: Access all appliances in the system
|
|
199
|
+
- **`OcppServer`**: Run OCPP server for EV charging
|
|
200
|
+
- **`ChargingCard`**: Manage EV charging cards
|
|
201
|
+
- **`Vehicle`**: Access vehicle information
|
|
202
|
+
- **`Charge`**: Manage charging sessions
|
|
203
|
+
|
|
204
|
+
#### Internet Access
|
|
205
|
+
|
|
206
|
+
- **`RestrictedInternetAccess`**: Access specific internet domains only
|
|
207
|
+
|
|
208
|
+
## API Reference
|
|
209
|
+
|
|
210
|
+
### Lifecycle Management
|
|
211
|
+
|
|
212
|
+
#### `register(callback: (packageName: string, version: number) => void)`
|
|
213
|
+
|
|
214
|
+
Register a callback that executes when your Energy App starts:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
energyApp.register((packageName, version) => {
|
|
218
|
+
console.log(`${packageName} v${version} is now running`);
|
|
219
|
+
// Initialize your app here
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### `onShutdown(callback: () => Promise<void>)`
|
|
224
|
+
|
|
225
|
+
Register cleanup logic for graceful shutdown:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
energyApp.onShutdown(async () => {
|
|
229
|
+
// Close connections
|
|
230
|
+
await modbusClient.disconnect();
|
|
231
|
+
// Save final state
|
|
232
|
+
await storage.save('lastShutdown', Date.now());
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### `updateEnergyAppState(state: EnergyAppStateEnum)`
|
|
237
|
+
|
|
238
|
+
Update your app's current state:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { EnergyAppStateEnum } from '@enyo-energy/energy-app-sdk';
|
|
242
|
+
|
|
243
|
+
// App needs configuration
|
|
244
|
+
energyApp.updateEnergyAppState(EnergyAppStateEnum.ConfigurationRequired);
|
|
245
|
+
|
|
246
|
+
// App is ready and running
|
|
247
|
+
energyApp.updateEnergyAppState(EnergyAppStateEnum.Running);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### System APIs
|
|
251
|
+
|
|
252
|
+
#### `isSystemOnline(): boolean`
|
|
253
|
+
|
|
254
|
+
Check system connectivity:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
if (energyApp.isSystemOnline()) {
|
|
258
|
+
// Fetch remote data
|
|
259
|
+
syncWithCloud();
|
|
260
|
+
} else {
|
|
261
|
+
// Use cached data
|
|
262
|
+
loadOfflineData();
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### `useFetch(): typeof fetch`
|
|
267
|
+
|
|
268
|
+
Get HTTP client with system configuration:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const fetch = energyApp.useFetch();
|
|
272
|
+
|
|
273
|
+
const response = await fetch('https://api.weather.com/forecast', {
|
|
274
|
+
method: 'GET',
|
|
275
|
+
headers: { 'Authorization': 'Bearer token' }
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### `getSdkVersion(): string`
|
|
280
|
+
|
|
281
|
+
Get the SDK version:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
console.log(`Using SDK version: ${energyApp.getSdkVersion()}`);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Device Communication
|
|
288
|
+
|
|
289
|
+
#### `useNetworkDevices(): EnergyAppNetworkDevice`
|
|
290
|
+
|
|
291
|
+
Discover and access network devices:
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
const networkDevices = energyApp.useNetworkDevices();
|
|
295
|
+
|
|
296
|
+
// Discover devices
|
|
297
|
+
const devices = await networkDevices.discover();
|
|
298
|
+
|
|
299
|
+
// Search for specific device types
|
|
300
|
+
const inverters = await networkDevices.search({
|
|
301
|
+
deviceType: 'inverter',
|
|
302
|
+
manufacturer: 'SMA'
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Get device details
|
|
306
|
+
const deviceInfo = await networkDevices.getDeviceInfo(device.id);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### `useModbus(): EnergyAppModbus`
|
|
310
|
+
|
|
311
|
+
Access Modbus communication:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
const modbus = energyApp.useModbus();
|
|
315
|
+
|
|
316
|
+
// Connect to device
|
|
317
|
+
const client = await modbus.connect({
|
|
318
|
+
host: '192.168.1.100',
|
|
319
|
+
port: 502,
|
|
320
|
+
unitId: 1
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Read holding registers
|
|
324
|
+
const registers = await client.readHoldingRegisters(1001, 10);
|
|
325
|
+
|
|
326
|
+
// Write single register
|
|
327
|
+
await client.writeSingleRegister(2001, 500);
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### `useOcpp(): EnergyAppOcpp`
|
|
331
|
+
|
|
332
|
+
Handle OCPP charging station communication:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
const ocpp = energyApp.useOcpp();
|
|
336
|
+
|
|
337
|
+
// Start OCPP server
|
|
338
|
+
const server = ocpp.createServer({
|
|
339
|
+
port: 8080,
|
|
340
|
+
onChargePointConnect: (chargePointId) => {
|
|
341
|
+
console.log(`Charge point ${chargePointId} connected`);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Send remote commands
|
|
346
|
+
await server.sendRemoteStartTransaction(chargePointId, {
|
|
347
|
+
connectorId: 1,
|
|
348
|
+
idTag: 'user123'
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Data Management
|
|
353
|
+
|
|
354
|
+
#### `useStorage(): EnergyAppStorage`
|
|
355
|
+
|
|
356
|
+
Persistent key-value storage:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
const storage = energyApp.useStorage();
|
|
360
|
+
|
|
361
|
+
// Save configuration
|
|
362
|
+
await storage.save('config', {
|
|
363
|
+
inverterHost: '192.168.1.100',
|
|
364
|
+
pollInterval: 30000
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Load configuration
|
|
368
|
+
const config = await storage.load<ConfigType>('config');
|
|
369
|
+
|
|
370
|
+
// List all keys
|
|
371
|
+
const keys = await storage.listKeys();
|
|
372
|
+
|
|
373
|
+
// Remove data
|
|
374
|
+
await storage.remove('oldData');
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
#### `useDataBus(): EnergyAppDataBus`
|
|
378
|
+
|
|
379
|
+
Send and receive system-wide data:
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
const dataBus = energyApp.useDataBus();
|
|
383
|
+
|
|
384
|
+
// Send inverter data
|
|
385
|
+
dataBus.sendMessage([{
|
|
386
|
+
messageType: 'InverterValuesUpdateV1',
|
|
387
|
+
applianceId: 'inverter-1',
|
|
388
|
+
timestamp: Date.now(),
|
|
389
|
+
values: {
|
|
390
|
+
powerW: 3500,
|
|
391
|
+
energyWh: 25000,
|
|
392
|
+
voltageV: 230
|
|
393
|
+
}
|
|
394
|
+
}]);
|
|
395
|
+
|
|
396
|
+
// Listen for battery updates
|
|
397
|
+
const listenerId = dataBus.listenForMessages(
|
|
398
|
+
['BatteryValuesUpdateV1'],
|
|
399
|
+
(message) => {
|
|
400
|
+
console.log('Battery SoC:', message.values.stateOfCharge);
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
// Stop listening
|
|
405
|
+
dataBus.unsubscribe(listenerId);
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
#### `useInterval(): EnergyAppInterval`
|
|
409
|
+
|
|
410
|
+
Manage recurring tasks:
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
const interval = energyApp.useInterval();
|
|
414
|
+
|
|
415
|
+
// Create recurring data collection
|
|
416
|
+
const intervalId = interval.createInterval('30s', (clockId) => {
|
|
417
|
+
collectSensorData();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Stop interval
|
|
421
|
+
interval.stopInterval(intervalId);
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Available intervals**: `'10s'`, `'30s'`, `'1m'`, `'5m'`, `'1hr'`
|
|
425
|
+
|
|
426
|
+
### Energy Resources
|
|
427
|
+
|
|
428
|
+
#### `useAppliances(): EnergyAppAppliance`
|
|
429
|
+
|
|
430
|
+
Manage energy appliances:
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
const appliances = energyApp.useAppliances();
|
|
434
|
+
|
|
435
|
+
// Register new appliance
|
|
436
|
+
const applianceId = await appliances.save({
|
|
437
|
+
name: [{ language: 'en', name: 'Solar Inverter' }],
|
|
438
|
+
type: 'inverter',
|
|
439
|
+
manufacturer: 'SMA',
|
|
440
|
+
model: 'SB5000',
|
|
441
|
+
networkDevice: deviceInfo
|
|
442
|
+
}, undefined);
|
|
443
|
+
|
|
444
|
+
// List your appliances
|
|
445
|
+
const myAppliances = await appliances.list();
|
|
446
|
+
|
|
447
|
+
// Get appliance details
|
|
448
|
+
const appliance = await appliances.getById(applianceId);
|
|
449
|
+
|
|
450
|
+
// Remove appliance
|
|
451
|
+
await appliances.removeById(applianceId);
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
#### `useVehicle(): EnergyAppVehicle`
|
|
455
|
+
|
|
456
|
+
Access electric vehicle information:
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
const vehicles = energyApp.useVehicle();
|
|
460
|
+
|
|
461
|
+
// Get all vehicles
|
|
462
|
+
const vehicleList = await vehicles.getVehicles();
|
|
463
|
+
|
|
464
|
+
// Get vehicle details
|
|
465
|
+
const vehicle = await vehicles.getVehicleById(vehicleId);
|
|
466
|
+
|
|
467
|
+
// Update vehicle state
|
|
468
|
+
await vehicles.updateVehicleState(vehicleId, {
|
|
469
|
+
batteryLevel: 80,
|
|
470
|
+
isPluggedIn: true,
|
|
471
|
+
estimatedRange: 320
|
|
472
|
+
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### `useCharge(): EnergyAppCharge`
|
|
476
|
+
|
|
477
|
+
Manage charging sessions:
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
const charging = energyApp.useCharge();
|
|
481
|
+
|
|
482
|
+
// Start charging session
|
|
483
|
+
const sessionId = await charging.startCharge({
|
|
484
|
+
vehicleId: 'vehicle-123',
|
|
485
|
+
connectorId: 1,
|
|
486
|
+
maxPowerKw: 22
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
// Get active sessions
|
|
490
|
+
const sessions = await charging.getActiveSessions();
|
|
491
|
+
|
|
492
|
+
// Stop charging
|
|
493
|
+
await charging.stopCharge(sessionId);
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
#### `useChargingCard(): EnergyAppChargingCard`
|
|
497
|
+
|
|
498
|
+
Handle charging authentication:
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
const chargingCards = energyApp.useChargingCard();
|
|
502
|
+
|
|
503
|
+
// Validate charging card
|
|
504
|
+
const isValid = await chargingCards.validateCard('RFID-12345');
|
|
505
|
+
|
|
506
|
+
// Get card information
|
|
507
|
+
const cardInfo = await chargingCards.getCardInfo('RFID-12345');
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### User Features
|
|
511
|
+
|
|
512
|
+
#### `useAuthentication(): EnergyAppAuthentication`
|
|
513
|
+
|
|
514
|
+
Handle user authentication:
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
const auth = energyApp.useAuthentication();
|
|
518
|
+
|
|
519
|
+
// Get current user
|
|
520
|
+
const user = await auth.getCurrentUser();
|
|
521
|
+
|
|
522
|
+
// Check permissions
|
|
523
|
+
const hasPermission = await auth.hasPermission('admin');
|
|
524
|
+
|
|
525
|
+
// Authenticate action
|
|
526
|
+
const token = await auth.createAuthToken('user-action');
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
#### `useSettings(): EnergyAppSettings`
|
|
530
|
+
|
|
531
|
+
Manage app settings and configuration:
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
const settings = energyApp.useSettings();
|
|
535
|
+
|
|
536
|
+
// Add setting configuration
|
|
537
|
+
await settings.addSettingConfig({
|
|
538
|
+
name: 'pollInterval',
|
|
539
|
+
displayName: [{ language: 'en', name: 'Poll Interval (seconds)' }],
|
|
540
|
+
type: 'number',
|
|
541
|
+
defaultValue: '30',
|
|
542
|
+
validation: {
|
|
543
|
+
min: 10,
|
|
544
|
+
max: 300
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// Update setting value
|
|
549
|
+
await settings.updateSetting('pollInterval', '60');
|
|
550
|
+
|
|
551
|
+
// Listen for setting changes
|
|
552
|
+
settings.listenForSettingsChanges((settingName, newValue) => {
|
|
553
|
+
console.log(`Setting ${settingName} changed to ${newValue}`);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Get all settings
|
|
557
|
+
const allSettings = await settings.getSettingsConfig();
|
|
10
558
|
```
|
|
11
559
|
|
|
12
|
-
|
|
560
|
+
#### `useElectricityPrices(): EnergyAppElectricityPrices`
|
|
13
561
|
|
|
14
|
-
|
|
562
|
+
Access electricity pricing information:
|
|
15
563
|
|
|
16
|
-
|
|
564
|
+
```typescript
|
|
565
|
+
const prices = energyApp.useElectricityPrices();
|
|
566
|
+
|
|
567
|
+
// Get current electricity price
|
|
568
|
+
const currentPrice = await prices.getCurrentPrice();
|
|
569
|
+
|
|
570
|
+
// Get price forecast
|
|
571
|
+
const forecast = await prices.getPriceForecast({
|
|
572
|
+
hoursAhead: 24
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// Listen for price changes
|
|
576
|
+
prices.onPriceChange((newPrice) => {
|
|
577
|
+
console.log(`New electricity price: ${newPrice.pricePerKwh} €/kWh`);
|
|
578
|
+
});
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
#### `useNotification(): EnergyAppNotification`
|
|
582
|
+
|
|
583
|
+
Send notifications to users:
|
|
17
584
|
|
|
18
585
|
```typescript
|
|
19
|
-
|
|
586
|
+
const notifications = energyApp.useNotification();
|
|
20
587
|
|
|
21
|
-
|
|
588
|
+
// Send info notification
|
|
589
|
+
await notifications.sendNotification({
|
|
590
|
+
type: 'info',
|
|
591
|
+
title: 'Energy Optimization',
|
|
592
|
+
message: 'Your system is running optimally',
|
|
593
|
+
timestamp: Date.now()
|
|
594
|
+
});
|
|
22
595
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
596
|
+
// Send warning
|
|
597
|
+
await notifications.sendNotification({
|
|
598
|
+
type: 'warning',
|
|
599
|
+
title: 'High Energy Consumption',
|
|
600
|
+
message: 'Consider reducing energy usage during peak hours'
|
|
601
|
+
});
|
|
26
602
|
|
|
603
|
+
// Send critical alert
|
|
604
|
+
await notifications.sendNotification({
|
|
605
|
+
type: 'error',
|
|
606
|
+
title: 'System Fault',
|
|
607
|
+
message: 'Inverter communication lost - please check connection'
|
|
27
608
|
});
|
|
28
609
|
```
|
|
29
610
|
|
|
@@ -33,7 +614,7 @@ The SDK includes a powerful, vendor-agnostic Modbus implementation for energy ma
|
|
|
33
614
|
|
|
34
615
|
### ✨ Features
|
|
35
616
|
|
|
36
|
-
- **Vendor Agnostic** - Works with SMA, Fronius
|
|
617
|
+
- **Vendor Agnostic** - Works with SMA, Fronius or any Modbus device via configuration
|
|
37
618
|
- **Type Safe** - Full TypeScript support with proper DataBus message types
|
|
38
619
|
- **Configurable** - Register addresses, data types, scaling all configurable
|
|
39
620
|
- **Fault Tolerant** - Built-in connection health monitoring and retry logic
|
|
@@ -44,12 +625,12 @@ The SDK includes a powerful, vendor-agnostic Modbus implementation for energy ma
|
|
|
44
625
|
#### Basic Modbus Setup
|
|
45
626
|
|
|
46
627
|
```typescript
|
|
47
|
-
import { EnergyApp } from '@enyo/energy-app-sdk';
|
|
628
|
+
import { EnergyApp } from '@enyo-energy/energy-app-sdk';
|
|
48
629
|
import {
|
|
49
630
|
EnergyAppModbusInverter,
|
|
50
631
|
EnergyAppModbusBattery,
|
|
51
632
|
EnergyAppModbusMeter
|
|
52
|
-
} from '@enyo/energy-app-sdk';
|
|
633
|
+
} from '@enyo-energy/energy-app-sdk';
|
|
53
634
|
|
|
54
635
|
const energyApp = new EnergyApp();
|
|
55
636
|
const dependencies = { client: energyApp, randomUUID: () => crypto.randomUUID() };
|
|
@@ -213,4 +794,477 @@ The Modbus implementation follows a clean, modular architecture:
|
|
|
213
794
|
- **EnergyAppModbusFaultTolerantReader** - Fault-tolerant communication layer
|
|
214
795
|
- **EnergyAppModbusConnectionHealth** - Connection health monitoring
|
|
215
796
|
|
|
216
|
-
This modular design ensures maintainability, testability, and extensibility for future enhancements.
|
|
797
|
+
This modular design ensures maintainability, testability, and extensibility for future enhancements.
|
|
798
|
+
|
|
799
|
+
## Examples
|
|
800
|
+
|
|
801
|
+
### Basic Energy App
|
|
802
|
+
|
|
803
|
+
A simple energy monitoring application:
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
import { EnergyApp, defineEnergyAppPackage } from '@enyo-energy/energy-app-sdk';
|
|
807
|
+
|
|
808
|
+
const energyApp = new EnergyApp();
|
|
809
|
+
|
|
810
|
+
energyApp.register(async (packageName, version) => {
|
|
811
|
+
console.log(`Energy Monitor ${version} starting...`);
|
|
812
|
+
|
|
813
|
+
const storage = energyApp.useStorage();
|
|
814
|
+
const dataBus = energyApp.useDataBus();
|
|
815
|
+
const interval = energyApp.useInterval();
|
|
816
|
+
|
|
817
|
+
// Load configuration
|
|
818
|
+
const config = await storage.load('config') || { enabled: true };
|
|
819
|
+
|
|
820
|
+
// Listen for energy data
|
|
821
|
+
dataBus.listenForMessages(['InverterValuesUpdateV1', 'BatteryValuesUpdateV1'],
|
|
822
|
+
(message) => {
|
|
823
|
+
console.log(`Received ${message.messageType}:`, message.values);
|
|
824
|
+
// Store or process energy data
|
|
825
|
+
}
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
// Periodic health check
|
|
829
|
+
interval.createInterval('5m', async () => {
|
|
830
|
+
const isOnline = energyApp.isSystemOnline();
|
|
831
|
+
await storage.save('lastHealthCheck', {
|
|
832
|
+
timestamp: Date.now(),
|
|
833
|
+
online: isOnline
|
|
834
|
+
});
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
energyApp.updateEnergyAppState('running');
|
|
838
|
+
});
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
### Device Integration
|
|
842
|
+
|
|
843
|
+
Comprehensive device management with multiple protocols:
|
|
844
|
+
|
|
845
|
+
```typescript
|
|
846
|
+
import { EnergyApp } from '@enyo-energy/energy-app-sdk';
|
|
847
|
+
|
|
848
|
+
const energyApp = new EnergyApp();
|
|
849
|
+
|
|
850
|
+
energyApp.register(async (packageName, version) => {
|
|
851
|
+
const networkDevices = energyApp.useNetworkDevices();
|
|
852
|
+
const appliances = energyApp.useAppliances();
|
|
853
|
+
const modbus = energyApp.useModbus();
|
|
854
|
+
const dataBus = energyApp.useDataBus();
|
|
855
|
+
|
|
856
|
+
try {
|
|
857
|
+
// Discover network devices
|
|
858
|
+
console.log('Discovering devices...');
|
|
859
|
+
const devices = await networkDevices.discover();
|
|
860
|
+
|
|
861
|
+
for (const device of devices) {
|
|
862
|
+
console.log(`Found device: ${device.hostname} (${device.manufacturer})`);
|
|
863
|
+
|
|
864
|
+
if (device.manufacturer === 'SMA' && device.protocols?.includes('modbus')) {
|
|
865
|
+
// Create Modbus connection
|
|
866
|
+
const client = await modbus.connect({
|
|
867
|
+
host: device.hostname,
|
|
868
|
+
port: 502,
|
|
869
|
+
unitId: 1
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
// Register as appliance
|
|
873
|
+
const applianceId = await appliances.save({
|
|
874
|
+
name: [{ language: 'en', name: `${device.manufacturer} Inverter` }],
|
|
875
|
+
type: 'inverter',
|
|
876
|
+
manufacturer: device.manufacturer,
|
|
877
|
+
model: device.model || 'Unknown',
|
|
878
|
+
networkDevice: device
|
|
879
|
+
}, undefined);
|
|
880
|
+
|
|
881
|
+
// Start data collection
|
|
882
|
+
const interval = energyApp.useInterval();
|
|
883
|
+
interval.createInterval('30s', async () => {
|
|
884
|
+
try {
|
|
885
|
+
// Read power data (example registers)
|
|
886
|
+
const powerRegs = await client.readHoldingRegisters(30775, 2);
|
|
887
|
+
const power = powerRegs.getInt32BE(0);
|
|
888
|
+
|
|
889
|
+
// Send to data bus
|
|
890
|
+
dataBus.sendMessage([{
|
|
891
|
+
messageType: 'InverterValuesUpdateV1',
|
|
892
|
+
applianceId: applianceId,
|
|
893
|
+
timestamp: Date.now(),
|
|
894
|
+
values: {
|
|
895
|
+
powerW: power,
|
|
896
|
+
voltageV: 230, // Read from appropriate register
|
|
897
|
+
frequencyHz: 50
|
|
898
|
+
}
|
|
899
|
+
}]);
|
|
900
|
+
|
|
901
|
+
} catch (error) {
|
|
902
|
+
console.error('Failed to read from device:', error);
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
energyApp.updateEnergyAppState('running');
|
|
909
|
+
|
|
910
|
+
} catch (error) {
|
|
911
|
+
console.error('Device discovery failed:', error);
|
|
912
|
+
energyApp.updateEnergyAppState('configuration-required');
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
### Data Bus Messaging
|
|
918
|
+
|
|
919
|
+
Advanced data processing and message routing:
|
|
920
|
+
|
|
921
|
+
```typescript
|
|
922
|
+
import { EnergyApp } from '@enyo-energy/energy-app-sdk';
|
|
923
|
+
|
|
924
|
+
const energyApp = new EnergyApp();
|
|
925
|
+
|
|
926
|
+
class EnergyDataProcessor {
|
|
927
|
+
private dataBus = energyApp.useDataBus();
|
|
928
|
+
private storage = energyApp.useStorage();
|
|
929
|
+
private lastValues = new Map();
|
|
930
|
+
|
|
931
|
+
async initialize() {
|
|
932
|
+
// Listen for various energy data types
|
|
933
|
+
this.dataBus.listenForMessages([
|
|
934
|
+
'InverterValuesUpdateV1',
|
|
935
|
+
'BatteryValuesUpdateV1',
|
|
936
|
+
'MeterValuesUpdateV1'
|
|
937
|
+
], (message) => this.processEnergyData(message));
|
|
938
|
+
|
|
939
|
+
// Listen for commands
|
|
940
|
+
this.dataBus.listenForMessages([
|
|
941
|
+
'SetPowerLimitCommandV1'
|
|
942
|
+
], (message) => this.handleCommand(message));
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
private async processEnergyData(message: any) {
|
|
946
|
+
const { messageType, applianceId, values, timestamp } = message;
|
|
947
|
+
|
|
948
|
+
// Store latest values
|
|
949
|
+
this.lastValues.set(applianceId, { messageType, values, timestamp });
|
|
950
|
+
|
|
951
|
+
// Calculate aggregated metrics
|
|
952
|
+
await this.calculateSystemMetrics();
|
|
953
|
+
|
|
954
|
+
// Detect anomalies
|
|
955
|
+
this.detectAnomalies(messageType, values);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
private async calculateSystemMetrics() {
|
|
959
|
+
let totalPowerW = 0;
|
|
960
|
+
let totalEnergyWh = 0;
|
|
961
|
+
let batterySoC = 0;
|
|
962
|
+
|
|
963
|
+
for (const [applianceId, data] of this.lastValues) {
|
|
964
|
+
if (data.messageType === 'InverterValuesUpdateV1') {
|
|
965
|
+
totalPowerW += data.values.powerW || 0;
|
|
966
|
+
totalEnergyWh += data.values.energyWh || 0;
|
|
967
|
+
} else if (data.messageType === 'BatteryValuesUpdateV1') {
|
|
968
|
+
batterySoC = data.values.stateOfCharge || 0;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// Send aggregated metrics
|
|
973
|
+
this.dataBus.sendMessage([{
|
|
974
|
+
messageType: 'SystemMetricsUpdateV1',
|
|
975
|
+
applianceId: 'system',
|
|
976
|
+
timestamp: Date.now(),
|
|
977
|
+
values: {
|
|
978
|
+
totalPowerW,
|
|
979
|
+
totalEnergyWh,
|
|
980
|
+
batterySoC,
|
|
981
|
+
systemEfficiency: this.calculateEfficiency()
|
|
982
|
+
}
|
|
983
|
+
}]);
|
|
984
|
+
|
|
985
|
+
// Store historical data
|
|
986
|
+
await this.storage.save(`metrics_${Date.now()}`, {
|
|
987
|
+
totalPowerW,
|
|
988
|
+
totalEnergyWh,
|
|
989
|
+
batterySoC,
|
|
990
|
+
timestamp: Date.now()
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
private detectAnomalies(messageType: string, values: any) {
|
|
995
|
+
// Example: Detect power spikes
|
|
996
|
+
if (messageType === 'InverterValuesUpdateV1' && values.powerW > 10000) {
|
|
997
|
+
const notifications = energyApp.useNotification();
|
|
998
|
+
notifications.sendNotification({
|
|
999
|
+
type: 'warning',
|
|
1000
|
+
title: 'High Power Output',
|
|
1001
|
+
message: `Inverter reporting ${values.powerW}W - check for issues`
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
private handleCommand(message: any) {
|
|
1007
|
+
console.log('Received command:', message);
|
|
1008
|
+
// Process control commands
|
|
1009
|
+
|
|
1010
|
+
// Send acknowledgment
|
|
1011
|
+
this.dataBus.sendAnswer({
|
|
1012
|
+
originalMessageId: message.id,
|
|
1013
|
+
success: true,
|
|
1014
|
+
timestamp: Date.now()
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
private calculateEfficiency(): number {
|
|
1019
|
+
// Implement efficiency calculation logic
|
|
1020
|
+
return 95.5;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
energyApp.register(async (packageName, version) => {
|
|
1025
|
+
const processor = new EnergyDataProcessor();
|
|
1026
|
+
await processor.initialize();
|
|
1027
|
+
|
|
1028
|
+
console.log('Energy data processor ready');
|
|
1029
|
+
energyApp.updateEnergyAppState('running');
|
|
1030
|
+
});
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
### Settings Management
|
|
1034
|
+
|
|
1035
|
+
Dynamic configuration with user interface:
|
|
1036
|
+
|
|
1037
|
+
```typescript
|
|
1038
|
+
import { EnergyApp } from '@enyo-energy/energy-app-sdk';
|
|
1039
|
+
|
|
1040
|
+
const energyApp = new EnergyApp();
|
|
1041
|
+
|
|
1042
|
+
class ConfigurableEnergyApp {
|
|
1043
|
+
private settings = energyApp.useSettings();
|
|
1044
|
+
private config = {
|
|
1045
|
+
pollIntervalSec: 30,
|
|
1046
|
+
maxPowerW: 5000,
|
|
1047
|
+
enableNotifications: true,
|
|
1048
|
+
priceThreshold: 0.25
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
async initialize() {
|
|
1052
|
+
// Define app settings
|
|
1053
|
+
await this.setupSettings();
|
|
1054
|
+
|
|
1055
|
+
// Load current settings
|
|
1056
|
+
await this.loadSettings();
|
|
1057
|
+
|
|
1058
|
+
// Listen for setting changes
|
|
1059
|
+
this.settings.listenForSettingsChanges((settingName, newValue) => {
|
|
1060
|
+
this.handleSettingChange(settingName, newValue);
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
private async setupSettings() {
|
|
1065
|
+
const settingConfigs = [
|
|
1066
|
+
{
|
|
1067
|
+
name: 'pollInterval',
|
|
1068
|
+
displayName: [{ language: 'en', name: 'Data Collection Interval (seconds)' }],
|
|
1069
|
+
description: [{ language: 'en', name: 'How often to collect data from devices' }],
|
|
1070
|
+
type: 'number',
|
|
1071
|
+
defaultValue: '30',
|
|
1072
|
+
validation: {
|
|
1073
|
+
min: 10,
|
|
1074
|
+
max: 300
|
|
1075
|
+
}
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
name: 'maxPowerLimit',
|
|
1079
|
+
displayName: [{ language: 'en', name: 'Maximum Power Limit (W)' }],
|
|
1080
|
+
type: 'number',
|
|
1081
|
+
defaultValue: '5000',
|
|
1082
|
+
validation: {
|
|
1083
|
+
min: 1000,
|
|
1084
|
+
max: 20000
|
|
1085
|
+
}
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
name: 'enableNotifications',
|
|
1089
|
+
displayName: [{ language: 'en', name: 'Enable Notifications' }],
|
|
1090
|
+
type: 'boolean',
|
|
1091
|
+
defaultValue: 'true'
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
name: 'electricityPriceThreshold',
|
|
1095
|
+
displayName: [{ language: 'en', name: 'Price Alert Threshold (€/kWh)' }],
|
|
1096
|
+
type: 'number',
|
|
1097
|
+
defaultValue: '0.25',
|
|
1098
|
+
validation: {
|
|
1099
|
+
min: 0.01,
|
|
1100
|
+
max: 1.0,
|
|
1101
|
+
step: 0.01
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
];
|
|
1105
|
+
|
|
1106
|
+
// Add all settings
|
|
1107
|
+
for (const config of settingConfigs) {
|
|
1108
|
+
await this.settings.addSettingConfig(config);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
private async loadSettings() {
|
|
1113
|
+
const allSettings = await this.settings.getSettingsConfig();
|
|
1114
|
+
|
|
1115
|
+
for (const setting of allSettings) {
|
|
1116
|
+
switch (setting.name) {
|
|
1117
|
+
case 'pollInterval':
|
|
1118
|
+
this.config.pollIntervalSec = parseInt(setting.currentValue);
|
|
1119
|
+
break;
|
|
1120
|
+
case 'maxPowerLimit':
|
|
1121
|
+
this.config.maxPowerW = parseInt(setting.currentValue);
|
|
1122
|
+
break;
|
|
1123
|
+
case 'enableNotifications':
|
|
1124
|
+
this.config.enableNotifications = setting.currentValue === 'true';
|
|
1125
|
+
break;
|
|
1126
|
+
case 'electricityPriceThreshold':
|
|
1127
|
+
this.config.priceThreshold = parseFloat(setting.currentValue);
|
|
1128
|
+
break;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
console.log('Loaded configuration:', this.config);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
private async handleSettingChange(settingName: string, newValue: string) {
|
|
1136
|
+
console.log(`Setting ${settingName} changed to: ${newValue}`);
|
|
1137
|
+
|
|
1138
|
+
switch (settingName) {
|
|
1139
|
+
case 'pollInterval':
|
|
1140
|
+
this.config.pollIntervalSec = parseInt(newValue);
|
|
1141
|
+
await this.restartDataCollection();
|
|
1142
|
+
break;
|
|
1143
|
+
case 'maxPowerLimit':
|
|
1144
|
+
this.config.maxPowerW = parseInt(newValue);
|
|
1145
|
+
await this.updatePowerLimits();
|
|
1146
|
+
break;
|
|
1147
|
+
case 'enableNotifications':
|
|
1148
|
+
this.config.enableNotifications = newValue === 'true';
|
|
1149
|
+
break;
|
|
1150
|
+
case 'electricityPriceThreshold':
|
|
1151
|
+
this.config.priceThreshold = parseFloat(newValue);
|
|
1152
|
+
await this.updatePriceAlerts();
|
|
1153
|
+
break;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
private async restartDataCollection() {
|
|
1158
|
+
// Restart intervals with new timing
|
|
1159
|
+
console.log(`Restarting data collection with ${this.config.pollIntervalSec}s interval`);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
private async updatePowerLimits() {
|
|
1163
|
+
// Apply new power limits to devices
|
|
1164
|
+
console.log(`Setting maximum power limit to ${this.config.maxPowerW}W`);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
private async updatePriceAlerts() {
|
|
1168
|
+
// Update electricity price monitoring
|
|
1169
|
+
const prices = energyApp.useElectricityPrices();
|
|
1170
|
+
prices.onPriceChange((newPrice) => {
|
|
1171
|
+
if (this.config.enableNotifications &&
|
|
1172
|
+
newPrice.pricePerKwh > this.config.priceThreshold) {
|
|
1173
|
+
|
|
1174
|
+
const notifications = energyApp.useNotification();
|
|
1175
|
+
notifications.sendNotification({
|
|
1176
|
+
type: 'info',
|
|
1177
|
+
title: 'High Electricity Price',
|
|
1178
|
+
message: `Current price ${newPrice.pricePerKwh}€/kWh exceeds threshold`
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
energyApp.register(async (packageName, version) => {
|
|
1186
|
+
const app = new ConfigurableEnergyApp();
|
|
1187
|
+
await app.initialize();
|
|
1188
|
+
|
|
1189
|
+
energyApp.updateEnergyAppState('running');
|
|
1190
|
+
console.log('Configurable Energy App ready');
|
|
1191
|
+
});
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
## Troubleshooting
|
|
1195
|
+
|
|
1196
|
+
### Common Issues
|
|
1197
|
+
|
|
1198
|
+
#### `Missing energyAppSdk instance`
|
|
1199
|
+
This error occurs when running outside the enyo runtime environment. For development, ensure your app is properly packaged and deployed to the enyo system.
|
|
1200
|
+
|
|
1201
|
+
#### Permission Denied Errors
|
|
1202
|
+
- Check your package definition includes required permissions
|
|
1203
|
+
- Verify the permission names match exactly (case-sensitive)
|
|
1204
|
+
- Some permissions like `AllAppliances` require special approval
|
|
1205
|
+
|
|
1206
|
+
#### Modbus Connection Failures
|
|
1207
|
+
- Verify network device is reachable
|
|
1208
|
+
- Check Modbus unit ID and register addresses
|
|
1209
|
+
- Ensure device supports the Modbus protocol version you're using
|
|
1210
|
+
- Use connection health monitoring to detect issues
|
|
1211
|
+
|
|
1212
|
+
#### Data Bus Message Not Received
|
|
1213
|
+
- Confirm you have `SubscribeDataBus` permission
|
|
1214
|
+
- Check message type names are exact matches
|
|
1215
|
+
- Verify the listener is registered before messages are sent
|
|
1216
|
+
|
|
1217
|
+
### Best Practices
|
|
1218
|
+
|
|
1219
|
+
#### Error Handling
|
|
1220
|
+
Always wrap async operations in try-catch blocks:
|
|
1221
|
+
|
|
1222
|
+
```typescript
|
|
1223
|
+
try {
|
|
1224
|
+
await energyApp.useStorage().save('key', data);
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
console.error('Storage operation failed:', error);
|
|
1227
|
+
// Handle gracefully
|
|
1228
|
+
}
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
#### Resource Cleanup
|
|
1232
|
+
Register cleanup handlers for graceful shutdown:
|
|
1233
|
+
|
|
1234
|
+
```typescript
|
|
1235
|
+
energyApp.onShutdown(async () => {
|
|
1236
|
+
// Clean up intervals
|
|
1237
|
+
interval.stopInterval(intervalId);
|
|
1238
|
+
// Close connections
|
|
1239
|
+
await modbusClient.disconnect();
|
|
1240
|
+
// Save state
|
|
1241
|
+
await storage.save('lastShutdown', Date.now());
|
|
1242
|
+
});
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
#### State Management
|
|
1246
|
+
Always update your app state appropriately:
|
|
1247
|
+
|
|
1248
|
+
```typescript
|
|
1249
|
+
// On successful initialization
|
|
1250
|
+
energyApp.updateEnergyAppState('running');
|
|
1251
|
+
|
|
1252
|
+
// When configuration is needed
|
|
1253
|
+
energyApp.updateEnergyAppState('configuration-required');
|
|
1254
|
+
|
|
1255
|
+
// When internet is required
|
|
1256
|
+
energyApp.updateEnergyAppState('internet-connection-required');
|
|
1257
|
+
```
|
|
1258
|
+
|
|
1259
|
+
## CLI Tool
|
|
1260
|
+
|
|
1261
|
+
Use the enyo CLI to initialize projects and publish Energy Apps easily. The CLI provides scaffolding, testing, and deployment capabilities for rapid development.
|
|
1262
|
+
|
|
1263
|
+
For CLI documentation and installation instructions, visit the [enyo CLI repository](https://github.com/enyo-energy/enyo-cli).
|
|
1264
|
+
|
|
1265
|
+
---
|
|
1266
|
+
|
|
1267
|
+
**Package Version:** 0.0.34
|
|
1268
|
+
**SDK Version:** Auto-injected during build
|
|
1269
|
+
**License:** ISC
|
|
1270
|
+
**Repository:** [github.com/enyo-energy/energy-app-sdk](https://github.com/enyo-energy/energy-app-sdk)
|