@enyo-energy/energy-app-sdk 0.0.170 → 0.0.172
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 +215 -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 +16 -0
- package/dist/cjs/energy-app.d.cts +13 -0
- package/dist/cjs/enyo-energy-app-sdk.d.cts +5 -0
- package/dist/cjs/implementations/automation/automation-validators.cjs +254 -0
- package/dist/cjs/implementations/automation/automation-validators.d.cts +63 -0
- package/dist/cjs/index.cjs +3 -0
- package/dist/cjs/index.d.cts +3 -0
- package/dist/cjs/packages/energy-app-automation.cjs +2 -0
- package/dist/cjs/packages/energy-app-automation.d.cts +133 -0
- package/dist/cjs/packages/energy-app-modbus-rtu.d.cts +15 -0
- package/dist/cjs/packages/energy-app-modbus.d.cts +14 -0
- package/dist/cjs/packages/energy-app-udp.d.cts +14 -0
- package/dist/cjs/types/enyo-air-conditioning-appliance.cjs +13 -1
- package/dist/cjs/types/enyo-air-conditioning-appliance.d.cts +14 -1
- package/dist/cjs/types/enyo-appliance.d.cts +10 -0
- package/dist/cjs/types/enyo-automation.cjs +77 -0
- package/dist/cjs/types/enyo-automation.d.cts +232 -0
- package/dist/cjs/types/enyo-data-bus-value.cjs +2 -0
- package/dist/cjs/types/enyo-data-bus-value.d.cts +42 -1
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- 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 +13 -0
- package/dist/energy-app.js +16 -0
- package/dist/enyo-energy-app-sdk.d.ts +5 -0
- package/dist/implementations/automation/automation-validators.d.ts +63 -0
- package/dist/implementations/automation/automation-validators.js +245 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/packages/energy-app-automation.d.ts +133 -0
- package/dist/packages/energy-app-automation.js +1 -0
- package/dist/packages/energy-app-modbus-rtu.d.ts +15 -0
- package/dist/packages/energy-app-modbus.d.ts +14 -0
- package/dist/packages/energy-app-udp.d.ts +14 -0
- package/dist/types/enyo-air-conditioning-appliance.d.ts +14 -1
- package/dist/types/enyo-air-conditioning-appliance.js +12 -0
- package/dist/types/enyo-appliance.d.ts +10 -0
- package/dist/types/enyo-automation.d.ts +232 -0
- package/dist/types/enyo-automation.js +74 -0
- package/dist/types/enyo-data-bus-value.d.ts +42 -1
- package/dist/types/enyo-data-bus-value.js +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,14 @@ The official TypeScript SDK for building Energy Apps on the enyo platform. Creat
|
|
|
55
55
|
- [BatteryCommandForecast](#batterycommandforecast)
|
|
56
56
|
- [HeatpumpForecast](#heatpumpforecast)
|
|
57
57
|
- [Validators](#validators)
|
|
58
|
+
- [Automations](#automations)
|
|
59
|
+
- [The model](#-the-model)
|
|
60
|
+
- [Guide: Energy Manager apps](#-guide-energy-manager-apps)
|
|
61
|
+
- [Guide: regular energy apps (smart plugs like Shelly)](#-guide-regular-energy-apps-smart-plugs-like-shelly)
|
|
62
|
+
- [End-to-end: pool pump on solar](#-end-to-end-pool-pump-on-solar)
|
|
63
|
+
- [Mandatory vs Flexible (current limitation)](#-mandatory-vs-flexible-current-limitation)
|
|
64
|
+
- [Permissions](#-permissions)
|
|
65
|
+
- [Validators](#-validators)
|
|
58
66
|
- [Examples](#examples)
|
|
59
67
|
- [Basic Energy App](#basic-energy-app)
|
|
60
68
|
- [Device Integration](#device-integration)
|
|
@@ -2082,6 +2090,213 @@ try {
|
|
|
2082
2090
|
|
|
2083
2091
|
Granular helpers are exported alongside the top-level validators: `validateChargerSchedule`, `validateBatterySchedule`, `validateDhwBoostWindows`, `validateRoomPreHeatingWindows`, `validateBufferTankBoostWindows`, `validatePowerAnnouncementSchedule`, `validateTemperatureForecast`.
|
|
2084
2092
|
|
|
2093
|
+
## Automations
|
|
2094
|
+
|
|
2095
|
+
Automations let the end-user wire up simple **"when a trigger is active, do one or more actions"** rules — for example *"when PV surplus is above 2000 W, switch my pool pump for at least 10 minutes."* They are composed by the user in the platform UI from building blocks that energy apps contribute:
|
|
2096
|
+
|
|
2097
|
+
- An **Energy Manager app** *registers the trigger types* it can evaluate and publishes the live trigger state (and, optionally, a forecast).
|
|
2098
|
+
- A **regular energy app** (e.g. a Shelly smart-plug integration) *declares which of its appliances can be an action target* and *executes the switching* when the trigger fires.
|
|
2099
|
+
- The **user** creates the concrete automation in the app; energy apps only **read and observe** automations through the SDK — there is no `create()` on the SDK (authoring lives in the platform).
|
|
2100
|
+
|
|
2101
|
+
Access the API via `useAutomations()`:
|
|
2102
|
+
|
|
2103
|
+
```typescript
|
|
2104
|
+
const automations = sdk.useAutomations();
|
|
2105
|
+
```
|
|
2106
|
+
|
|
2107
|
+
### 🧩 The model
|
|
2108
|
+
|
|
2109
|
+
An `EnyoAutomation` is `{ id, name, enabled, trigger, actions[] }`. The pieces:
|
|
2110
|
+
|
|
2111
|
+
| Concept | Type | Values / fields |
|
|
2112
|
+
| --- | --- | --- |
|
|
2113
|
+
| **Trigger** | `EnyoAutomationTriggerTypeEnum` | `PvSurplusThreshold` → `{ thresholdW }` (activate above, deactivate below) |
|
|
2114
|
+
| **Action — smart plug** | `EnyoAutomationActionTypeEnum.SmartPlugSwitch` | `{ applianceId, minDurationMinutes }` — `minDurationMinutes` is `5…360` in steps of `5` |
|
|
2115
|
+
| **Action — MQTT** | `EnyoAutomationActionTypeEnum.Mqtt` | `{ topic, payloadTemplate, updateChargingPvSurplus, publishOptions? }` |
|
|
2116
|
+
| **Scheduling** | `EnyoAutomationSchedulingModeEnum` | `Mandatory` (run exactly while active) or `Flexible` (Energy Manager may choose whether/when within the active window) |
|
|
2117
|
+
| **Target kind** | `EnyoAutomationTargetKindEnum` | `Load` (consumes power — counts in the energy balance) or `Signal` (control signal only) |
|
|
2118
|
+
|
|
2119
|
+
The **MQTT** `payloadTemplate` is JSON that may embed the placeholders in `EnyoAutomationMqttPlaceholderEnum` — `{{state}}` (`on`/`off`), `{{surplusW}}`, `{{timestampIso}}`, `{{automationId}}` — which the platform substitutes before publishing.
|
|
2120
|
+
|
|
2121
|
+
Two things travel over the **data bus** vs. the **API**:
|
|
2122
|
+
|
|
2123
|
+
- **Trigger state** is the data-bus message `AutomationTriggerV1` (`EnyoDataBusAutomationTriggerV1`): `{ automationId, data: { active, trigger } }`, where `trigger` is the per-type `EnyoAutomationTriggerData` (for PV surplus: `{ triggerType, surplusW, thresholdW }`).
|
|
2124
|
+
- The **forecast** is a method — `publishAutomationForecast()` — not a data-bus message.
|
|
2125
|
+
|
|
2126
|
+
### ⚡ Guide: Energy Manager apps
|
|
2127
|
+
|
|
2128
|
+
Requires the **`EnergyManager`** permission (to register triggers / publish forecasts) and **`SendDataBusValues`** (to emit the trigger message).
|
|
2129
|
+
|
|
2130
|
+
**1. Register the trigger type once, at startup.** Registration is by enum value only — all user-facing wording is handled by the UI.
|
|
2131
|
+
|
|
2132
|
+
```typescript
|
|
2133
|
+
import {EnergyApp, EnyoAutomationTriggerTypeEnum} from '@enyo-energy/energy-app-sdk';
|
|
2134
|
+
|
|
2135
|
+
const sdk = new EnergyApp();
|
|
2136
|
+
const automations = sdk.useAutomations();
|
|
2137
|
+
|
|
2138
|
+
sdk.register(async () => {
|
|
2139
|
+
await automations.registerTrigger(EnyoAutomationTriggerTypeEnum.PvSurplusThreshold);
|
|
2140
|
+
});
|
|
2141
|
+
```
|
|
2142
|
+
|
|
2143
|
+
**2. Evaluate the condition and publish trigger state.** Watch the aggregated PV surplus and, for every automation that uses your trigger, publish an `AutomationTriggerV1` message whenever it crosses the user-configured `thresholdW`.
|
|
2144
|
+
|
|
2145
|
+
Message-type identifiers are the `EnyoDataBusMessageEnum` values passed as strings, the same convention used everywhere else in the data-bus API.
|
|
2146
|
+
|
|
2147
|
+
```typescript
|
|
2148
|
+
import {EnyoAutomationTriggerTypeEnum} from '@enyo-energy/energy-app-sdk';
|
|
2149
|
+
|
|
2150
|
+
const dataBus = sdk.useDataBus();
|
|
2151
|
+
|
|
2152
|
+
dataBus.listenForMessages(['AggregatedStateUpdateV1'], (message: any) => {
|
|
2153
|
+
// gridFeedInW is the surplus fed to the grid (or derive from -gridPowerW when negative)
|
|
2154
|
+
const surplusW = message.data?.gridFeedInW ?? 0;
|
|
2155
|
+
|
|
2156
|
+
for (const automation of currentAutomations) {
|
|
2157
|
+
if (automation.trigger.type !== EnyoAutomationTriggerTypeEnum.PvSurplusThreshold) continue;
|
|
2158
|
+
const thresholdW = automation.trigger.thresholdW;
|
|
2159
|
+
const active = surplusW > thresholdW;
|
|
2160
|
+
|
|
2161
|
+
dataBus.sendMessage([{
|
|
2162
|
+
type: 'message',
|
|
2163
|
+
message: 'AutomationTriggerV1',
|
|
2164
|
+
automationId: automation.id,
|
|
2165
|
+
data: {
|
|
2166
|
+
active,
|
|
2167
|
+
trigger: {
|
|
2168
|
+
triggerType: EnyoAutomationTriggerTypeEnum.PvSurplusThreshold,
|
|
2169
|
+
surplusW,
|
|
2170
|
+
thresholdW,
|
|
2171
|
+
},
|
|
2172
|
+
},
|
|
2173
|
+
}]);
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
```
|
|
2177
|
+
|
|
2178
|
+
> Debounce/hysteresis (e.g. only emit on a real transition, add a deactivate margin) is the Energy Manager's responsibility — send a message when the `active` state actually changes, not on every tick.
|
|
2179
|
+
|
|
2180
|
+
**3. (Optional) Publish a forecast.** Combine your PV-surplus forecast with each automation's threshold to predict the windows where the trigger will be active, so the system can plan ahead. The forecast marks occupied windows only (no watts).
|
|
2181
|
+
|
|
2182
|
+
```typescript
|
|
2183
|
+
await automations.publishAutomationForecast({
|
|
2184
|
+
automationId: 'pool-pump',
|
|
2185
|
+
resolution: '15m',
|
|
2186
|
+
entries: [
|
|
2187
|
+
{ timestampIso: '2026-07-04T10:00:00.000Z', active: true, mandatory: false, hasLoad: true },
|
|
2188
|
+
{ timestampIso: '2026-07-04T10:15:00.000Z', active: true, mandatory: false, hasLoad: true },
|
|
2189
|
+
{ timestampIso: '2026-07-04T10:30:00.000Z', active: false },
|
|
2190
|
+
],
|
|
2191
|
+
});
|
|
2192
|
+
```
|
|
2193
|
+
|
|
2194
|
+
### 🔌 Guide: regular energy apps (smart plugs like Shelly)
|
|
2195
|
+
|
|
2196
|
+
Requires the **`Automation`** permission (to read/observe automations) plus whatever your device needs to switch (e.g. network device access, Modbus, or `RestrictedInternetAccess` for a Shelly HTTP call).
|
|
2197
|
+
|
|
2198
|
+
**1. Advertise which appliances can be an action target.** When you register (or update) an appliance, set `supportedAutomationActions`. The automation UI then offers this appliance only for the action types it lists. A three-channel Shelly registers three `SmartPlug` appliances, one per channel.
|
|
2199
|
+
|
|
2200
|
+
```typescript
|
|
2201
|
+
import {EnyoApplianceTypeEnum, EnyoAutomationActionTypeEnum} from '@enyo-energy/energy-app-sdk';
|
|
2202
|
+
|
|
2203
|
+
await sdk.useAppliances().save(
|
|
2204
|
+
{
|
|
2205
|
+
name: [{language: 'en', name: 'Pool pump'}],
|
|
2206
|
+
type: EnyoApplianceTypeEnum.SmartPlug,
|
|
2207
|
+
networkDeviceIds: [shellyDeviceId],
|
|
2208
|
+
supportedAutomationActions: [EnyoAutomationActionTypeEnum.SmartPlugSwitch],
|
|
2209
|
+
},
|
|
2210
|
+
'shelly-pool-ch0',
|
|
2211
|
+
);
|
|
2212
|
+
```
|
|
2213
|
+
|
|
2214
|
+
**2. Learn which automations target your appliances.** Fetch on startup and keep in sync with the listeners. Each listener returns an id you can pass to `removeListener()`.
|
|
2215
|
+
|
|
2216
|
+
```typescript
|
|
2217
|
+
const automations = sdk.useAutomations();
|
|
2218
|
+
|
|
2219
|
+
let mine = (await automations.list()).filter(hasSmartPlugActionForMyAppliances);
|
|
2220
|
+
|
|
2221
|
+
automations.listenForAutomationCreated((a) => { /* add if it targets my appliance */ });
|
|
2222
|
+
automations.listenForAutomationUpdated((a) => { /* replace */ });
|
|
2223
|
+
automations.listenForAutomationRemoved((automationId) => { /* drop + switch off */ });
|
|
2224
|
+
```
|
|
2225
|
+
|
|
2226
|
+
**3. Switch on the trigger message.** Subscribe to `AutomationTriggerV1`, resolve the automation's `SmartPlugSwitch` action to one of your appliances, and drive the relay. Honor `minDurationMinutes` locally (keep it on for at least that long after switching on).
|
|
2227
|
+
|
|
2228
|
+
```typescript
|
|
2229
|
+
import {EnyoAutomationActionTypeEnum} from '@enyo-energy/energy-app-sdk';
|
|
2230
|
+
|
|
2231
|
+
sdk.useDataBus().listenForMessages(['AutomationTriggerV1'], async (message: any) => {
|
|
2232
|
+
const {automationId, data} = message; // data: { active, trigger }
|
|
2233
|
+
const automation = mine.find((a) => a.id === automationId);
|
|
2234
|
+
if (!automation) return;
|
|
2235
|
+
|
|
2236
|
+
for (const action of automation.actions) {
|
|
2237
|
+
if (action.type !== EnyoAutomationActionTypeEnum.SmartPlugSwitch) continue;
|
|
2238
|
+
if (data.active) {
|
|
2239
|
+
await switchOn(action.applianceId);
|
|
2240
|
+
scheduleMinRuntimeGuard(action.applianceId, action.minDurationMinutes);
|
|
2241
|
+
} else if (minRuntimeElapsed(action.applianceId)) {
|
|
2242
|
+
await switchOff(action.applianceId);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
});
|
|
2246
|
+
```
|
|
2247
|
+
|
|
2248
|
+
### 🅿️ End-to-end: pool pump on solar
|
|
2249
|
+
|
|
2250
|
+
The user's automation object (authored in the app) for *"when PV surplus > 2000 W, run the pool pump for at least 10 minutes, Energy Manager may choose the timing":*
|
|
2251
|
+
|
|
2252
|
+
```typescript
|
|
2253
|
+
const automation = {
|
|
2254
|
+
id: 'pool-pump',
|
|
2255
|
+
name: 'Pool pump on solar',
|
|
2256
|
+
enabled: true,
|
|
2257
|
+
trigger: {type: EnyoAutomationTriggerTypeEnum.PvSurplusThreshold, thresholdW: 2000},
|
|
2258
|
+
actions: [{
|
|
2259
|
+
id: 'switch-pump',
|
|
2260
|
+
type: EnyoAutomationActionTypeEnum.SmartPlugSwitch,
|
|
2261
|
+
applianceId: 'shelly-pool-ch0',
|
|
2262
|
+
minDurationMinutes: 10,
|
|
2263
|
+
schedulingMode: EnyoAutomationSchedulingModeEnum.Flexible,
|
|
2264
|
+
targetKind: EnyoAutomationTargetKindEnum.Load,
|
|
2265
|
+
}],
|
|
2266
|
+
};
|
|
2267
|
+
```
|
|
2268
|
+
|
|
2269
|
+
The **Energy Manager** publishes `AutomationTriggerV1` as the surplus crosses 2000 W; the **Shelly app** receives it and switches `shelly-pool-ch0`, keeping it on for ≥ 10 minutes.
|
|
2270
|
+
|
|
2271
|
+
### ⏳ Mandatory vs Flexible (current limitation)
|
|
2272
|
+
|
|
2273
|
+
- **`Mandatory`** works fully today: the device app reacts directly to the `AutomationTriggerV1` `active` flag (on when `true`, off when `false` once the minimum runtime has elapsed).
|
|
2274
|
+
- **`Flexible`** lets the Energy Manager decide *whether and when* to actually run the action inside the active window. That decision does **not yet** have a dedicated Energy-Manager→device dispatch message — `AutomationTriggerV1` only reports that the *condition* holds. Until a dispatch message (planned: `AutomationActionCommandV1`, carrying `automationId`, `actionId`, `command`, and a run duration) is added, a device app treats `Flexible` like `Mandatory` and self-enforces `minDurationMinutes`.
|
|
2275
|
+
|
|
2276
|
+
### 🔐 Permissions
|
|
2277
|
+
|
|
2278
|
+
| Capability | Permission |
|
|
2279
|
+
| --- | --- |
|
|
2280
|
+
| `list` / `getById` / `listenFor*` automations | `Automation` |
|
|
2281
|
+
| `registerTrigger` / `deregisterTrigger` / `publishAutomationForecast` | `EnergyManager` |
|
|
2282
|
+
| Emit the `AutomationTriggerV1` data-bus message | `SendDataBusValues` |
|
|
2283
|
+
|
|
2284
|
+
### ✅ Validators
|
|
2285
|
+
|
|
2286
|
+
`validateAutomation`, `validateAutomationTriggerData`, and `validateAutomationForecast` are exported as pure functions (throwing `AutomationValidationError`, whose message names the offending field) so you can validate before persisting or publishing.
|
|
2287
|
+
|
|
2288
|
+
```typescript
|
|
2289
|
+
import {validateAutomation, AutomationValidationError} from '@enyo-energy/energy-app-sdk';
|
|
2290
|
+
|
|
2291
|
+
try {
|
|
2292
|
+
validateAutomation(automation, knownSmartPlugApplianceIds);
|
|
2293
|
+
} catch (error) {
|
|
2294
|
+
if (error instanceof AutomationValidationError) {
|
|
2295
|
+
// surface error.message
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
```
|
|
2299
|
+
|
|
2085
2300
|
## Examples
|
|
2086
2301
|
|
|
2087
2302
|
### Basic Energy App
|
|
@@ -49,4 +49,5 @@ var EnergyAppPermissionTypeEnum;
|
|
|
49
49
|
EnergyAppPermissionTypeEnum["ChildProcess"] = "ChildProcess";
|
|
50
50
|
EnergyAppPermissionTypeEnum["Udp"] = "Udp";
|
|
51
51
|
EnergyAppPermissionTypeEnum["ProvidedFiles"] = "ProvidedFiles";
|
|
52
|
+
EnergyAppPermissionTypeEnum["Automation"] = "Automation";
|
|
52
53
|
})(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' | 'ProvidedFiles';
|
|
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' | 'Automation';
|
|
2
2
|
export declare enum EnergyAppPermissionTypeEnum {
|
|
3
3
|
RestrictedInternetAccess = "RestrictedInternetAccess",
|
|
4
4
|
NetworkDeviceDiscovery = "NetworkDeviceDiscovery",
|
|
@@ -45,5 +45,6 @@ export declare enum EnergyAppPermissionTypeEnum {
|
|
|
45
45
|
Wifi = "Wifi",
|
|
46
46
|
ChildProcess = "ChildProcess",
|
|
47
47
|
Udp = "Udp",
|
|
48
|
-
ProvidedFiles = "ProvidedFiles"
|
|
48
|
+
ProvidedFiles = "ProvidedFiles",
|
|
49
|
+
Automation = "Automation"
|
|
49
50
|
}
|
package/dist/cjs/energy-app.cjs
CHANGED
|
@@ -34,6 +34,9 @@ class EnergyApp {
|
|
|
34
34
|
this.energyAppSdk = energyAppSdkInstance;
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
healthcheck() {
|
|
38
|
+
return new Date();
|
|
39
|
+
}
|
|
37
40
|
isSystemOnline() {
|
|
38
41
|
return this.energyAppSdk.isSystemOnline();
|
|
39
42
|
}
|
|
@@ -336,6 +339,19 @@ class EnergyApp {
|
|
|
336
339
|
useFiles() {
|
|
337
340
|
return this.energyAppSdk.useFiles();
|
|
338
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Gets the Automation API for reading user-configured automations and
|
|
344
|
+
* listening for their creation, update and removal. Apps holding the
|
|
345
|
+
* `EnergyManager` permission can additionally register trigger types,
|
|
346
|
+
* report live trigger state, and publish automation forecasts.
|
|
347
|
+
* @returns The Automation API instance
|
|
348
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the required permission
|
|
349
|
+
* (`Automation` for reads, `EnergyManager` for provider methods) is
|
|
350
|
+
* not granted.
|
|
351
|
+
*/
|
|
352
|
+
useAutomations() {
|
|
353
|
+
return this.energyAppSdk.useAutomations();
|
|
354
|
+
}
|
|
339
355
|
/**
|
|
340
356
|
* Gets the current SDK version.
|
|
341
357
|
* @returns The semantic version string of the SDK
|
|
@@ -38,6 +38,7 @@ import { EnergyAppConfigurationManager } from "./packages/energy-app-configurati
|
|
|
38
38
|
import { EnergyAppApplianceEnergyManagerForecast } from "./packages/energy-app-appliance-energy-manager-forecast.cjs";
|
|
39
39
|
import { EnergyAppBattery } from "./packages/energy-app-battery.cjs";
|
|
40
40
|
import { EnergyAppFile } from "./packages/energy-app-file.cjs";
|
|
41
|
+
import { EnergyAppAutomation } from "./packages/energy-app-automation.cjs";
|
|
41
42
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
42
43
|
/**
|
|
43
44
|
* Concrete implementation of {@link EnyoEnergyAppSdk} that delegates every call
|
|
@@ -60,6 +61,7 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
|
|
|
60
61
|
private readonly energyAppSdk;
|
|
61
62
|
private udpInstance;
|
|
62
63
|
constructor();
|
|
64
|
+
healthcheck(): Date;
|
|
63
65
|
isSystemOnline(): boolean;
|
|
64
66
|
/**
|
|
65
67
|
* Registers a listener that gets called when the network status changes.
|
|
@@ -268,6 +270,17 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
|
|
|
268
270
|
* permission is not granted.
|
|
269
271
|
*/
|
|
270
272
|
useFiles(): EnergyAppFile;
|
|
273
|
+
/**
|
|
274
|
+
* Gets the Automation API for reading user-configured automations and
|
|
275
|
+
* listening for their creation, update and removal. Apps holding the
|
|
276
|
+
* `EnergyManager` permission can additionally register trigger types,
|
|
277
|
+
* report live trigger state, and publish automation forecasts.
|
|
278
|
+
* @returns The Automation API instance
|
|
279
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the required permission
|
|
280
|
+
* (`Automation` for reads, `EnergyManager` for provider methods) is
|
|
281
|
+
* not granted.
|
|
282
|
+
*/
|
|
283
|
+
useAutomations(): EnergyAppAutomation;
|
|
271
284
|
/**
|
|
272
285
|
* Gets the current SDK version.
|
|
273
286
|
* @returns The semantic version string of the SDK
|
|
@@ -37,6 +37,7 @@ import { EnergyAppConfigurationManager } from "./packages/energy-app-configurati
|
|
|
37
37
|
import { EnergyAppApplianceEnergyManagerForecast } from "./packages/energy-app-appliance-energy-manager-forecast.cjs";
|
|
38
38
|
import { EnergyAppBattery } from "./packages/energy-app-battery.cjs";
|
|
39
39
|
import { EnergyAppFile } from "./packages/energy-app-file.cjs";
|
|
40
|
+
import { EnergyAppAutomation } from "./packages/energy-app-automation.cjs";
|
|
40
41
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
41
42
|
export declare enum EnergyAppStateEnum {
|
|
42
43
|
Launching = "launching",
|
|
@@ -54,6 +55,8 @@ export declare enum EnergyAppStateEnum {
|
|
|
54
55
|
export interface EnyoEnergyAppSdk {
|
|
55
56
|
/** Register a callback that gets called when the package is initialized */
|
|
56
57
|
register: (callback: (packageName: string, version: number, channel: EnyoPackageChannel, deviceId: string) => void | Promise<void>) => void;
|
|
58
|
+
/** health check - returns the current date to check if alive */
|
|
59
|
+
healthcheck: () => Date;
|
|
57
60
|
/** Register a callback that gets called when the system is shutting down */
|
|
58
61
|
onShutdown: (callback: () => void | Promise<void>) => void;
|
|
59
62
|
/** Update the state of the Energy App. Default state set is launching*/
|
|
@@ -140,4 +143,6 @@ export interface EnyoEnergyAppSdk {
|
|
|
140
143
|
useBatteries: () => EnergyAppBattery;
|
|
141
144
|
/** 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
145
|
useFiles: () => EnergyAppFile;
|
|
146
|
+
/** Get the Automation API for reading user-configured automations and (with EnergyManager permission) registering triggers, reporting trigger state, and publishing automation forecasts */
|
|
147
|
+
useAutomations: () => EnergyAppAutomation;
|
|
143
148
|
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AUTOMATION_DURATION_STEP_MINUTES = exports.AUTOMATION_MAX_DURATION_MINUTES = exports.AUTOMATION_MIN_DURATION_MINUTES = exports.AutomationValidationError = void 0;
|
|
4
|
+
exports.validateAutomation = validateAutomation;
|
|
5
|
+
exports.validateTrigger = validateTrigger;
|
|
6
|
+
exports.validateAction = validateAction;
|
|
7
|
+
exports.validateAutomationForecast = validateAutomationForecast;
|
|
8
|
+
exports.validateAutomationTriggerData = validateAutomationTriggerData;
|
|
9
|
+
const enyo_automation_js_1 = require("../../types/enyo-automation.cjs");
|
|
10
|
+
const enyo_mqtt_js_1 = require("../../types/enyo-mqtt.cjs");
|
|
11
|
+
/**
|
|
12
|
+
* Thrown when an automation, action or automation forecast violates one of the
|
|
13
|
+
* invariants enforced by this module. The message names the offending field /
|
|
14
|
+
* index so callers can surface it directly to the user.
|
|
15
|
+
*/
|
|
16
|
+
class AutomationValidationError extends Error {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = 'AutomationValidationError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.AutomationValidationError = AutomationValidationError;
|
|
23
|
+
/** Minimum smart-plug on-duration, in minutes. */
|
|
24
|
+
exports.AUTOMATION_MIN_DURATION_MINUTES = 5;
|
|
25
|
+
/** Maximum smart-plug on-duration, in minutes (6 hours). */
|
|
26
|
+
exports.AUTOMATION_MAX_DURATION_MINUTES = 360;
|
|
27
|
+
/** Step size the smart-plug on-duration must be a multiple of, in minutes. */
|
|
28
|
+
exports.AUTOMATION_DURATION_STEP_MINUTES = 5;
|
|
29
|
+
/** Duration in seconds of each {@link EnyoForecastResolution} value. */
|
|
30
|
+
const FORECAST_RESOLUTION_SECONDS = {
|
|
31
|
+
'10s': 10,
|
|
32
|
+
'1m': 60,
|
|
33
|
+
'5m': 300,
|
|
34
|
+
'15m': 900,
|
|
35
|
+
'1h': 3600,
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Validates a complete {@link EnyoAutomation}. Throws on the first violation.
|
|
39
|
+
*
|
|
40
|
+
* When `knownSmartPlugApplianceIds` is provided, every smart-plug action's
|
|
41
|
+
* `applianceId` must be a member of it — use this to ensure the action targets
|
|
42
|
+
* an appliance that actually supports {@link EnyoAutomationActionTypeEnum.SmartPlugSwitch}.
|
|
43
|
+
*
|
|
44
|
+
* @param automation - The automation to validate.
|
|
45
|
+
* @param knownSmartPlugApplianceIds - Optional set/array of appliance ids that
|
|
46
|
+
* support the smart-plug-switch action; when given, smart-plug action targets
|
|
47
|
+
* are checked against it.
|
|
48
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
49
|
+
*/
|
|
50
|
+
function validateAutomation(automation, knownSmartPlugApplianceIds) {
|
|
51
|
+
if (!automation || typeof automation !== 'object') {
|
|
52
|
+
throw new AutomationValidationError('Automation must be an object.');
|
|
53
|
+
}
|
|
54
|
+
requireNonEmptyString(automation.id, 'Automation.id');
|
|
55
|
+
requireNonEmptyString(automation.name, 'Automation.name');
|
|
56
|
+
if (typeof automation.enabled !== 'boolean') {
|
|
57
|
+
throw new AutomationValidationError('Automation.enabled must be a boolean.');
|
|
58
|
+
}
|
|
59
|
+
validateTrigger(automation.trigger);
|
|
60
|
+
if (!Array.isArray(automation.actions) || automation.actions.length === 0) {
|
|
61
|
+
throw new AutomationValidationError('Automation.actions must contain at least one action.');
|
|
62
|
+
}
|
|
63
|
+
const knownIds = knownSmartPlugApplianceIds ? new Set(knownSmartPlugApplianceIds) : undefined;
|
|
64
|
+
const seenActionIds = new Set();
|
|
65
|
+
automation.actions.forEach((action, index) => {
|
|
66
|
+
requireNonEmptyString(action?.id, `Automation.actions[${index}].id`);
|
|
67
|
+
if (seenActionIds.has(action.id)) {
|
|
68
|
+
throw new AutomationValidationError(`Automation.actions[${index}].id is not unique: ${action.id}.`);
|
|
69
|
+
}
|
|
70
|
+
seenActionIds.add(action.id);
|
|
71
|
+
validateAction(action, index, knownIds);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validates the trigger of an automation. Throws on the first violation.
|
|
76
|
+
*
|
|
77
|
+
* @param trigger - The trigger configuration.
|
|
78
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
79
|
+
*/
|
|
80
|
+
function validateTrigger(trigger) {
|
|
81
|
+
if (!trigger || typeof trigger !== 'object') {
|
|
82
|
+
throw new AutomationValidationError('Automation.trigger must be an object.');
|
|
83
|
+
}
|
|
84
|
+
switch (trigger.type) {
|
|
85
|
+
case enyo_automation_js_1.EnyoAutomationTriggerTypeEnum.PvSurplusThreshold:
|
|
86
|
+
if (typeof trigger.thresholdW !== 'number' || !Number.isFinite(trigger.thresholdW) || trigger.thresholdW < 0) {
|
|
87
|
+
throw new AutomationValidationError('PvSurplusThreshold trigger.thresholdW must be a finite number >= 0.');
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
default:
|
|
91
|
+
throw new AutomationValidationError(`Automation.trigger.type is invalid: ${trigger.type}.`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Validates a single automation action. Throws on the first violation.
|
|
96
|
+
*
|
|
97
|
+
* @param action - The action to validate.
|
|
98
|
+
* @param index - Index of the action within its automation (for error messages).
|
|
99
|
+
* @param knownSmartPlugApplianceIds - Optional set of appliance ids that support
|
|
100
|
+
* the smart-plug-switch action.
|
|
101
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
102
|
+
*/
|
|
103
|
+
function validateAction(action, index = 0, knownSmartPlugApplianceIds) {
|
|
104
|
+
const label = `Automation.actions[${index}]`;
|
|
105
|
+
if (!action || typeof action !== 'object') {
|
|
106
|
+
throw new AutomationValidationError(`${label} must be an object.`);
|
|
107
|
+
}
|
|
108
|
+
requireEnumMember(action.schedulingMode, enyo_automation_js_1.EnyoAutomationSchedulingModeEnum, `${label}.schedulingMode`);
|
|
109
|
+
requireEnumMember(action.targetKind, enyo_automation_js_1.EnyoAutomationTargetKindEnum, `${label}.targetKind`);
|
|
110
|
+
switch (action.type) {
|
|
111
|
+
case enyo_automation_js_1.EnyoAutomationActionTypeEnum.Mqtt:
|
|
112
|
+
validateMqttAction(action, label);
|
|
113
|
+
break;
|
|
114
|
+
case enyo_automation_js_1.EnyoAutomationActionTypeEnum.SmartPlugSwitch:
|
|
115
|
+
validateSmartPlugSwitchAction(action, label, knownSmartPlugApplianceIds);
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
throw new AutomationValidationError(`${label}.type is invalid: ${action.type}.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Validates an {@link EnyoAutomationForecast}: resolution must be known and the
|
|
123
|
+
* entries must be chronologically ordered, non-overlapping, and spaced exactly
|
|
124
|
+
* one resolution step apart. Throws on the first violation.
|
|
125
|
+
*
|
|
126
|
+
* @param forecast - The forecast to validate.
|
|
127
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
128
|
+
*/
|
|
129
|
+
function validateAutomationForecast(forecast) {
|
|
130
|
+
if (!forecast || typeof forecast !== 'object') {
|
|
131
|
+
throw new AutomationValidationError('AutomationForecast must be an object.');
|
|
132
|
+
}
|
|
133
|
+
requireNonEmptyString(forecast.automationId, 'AutomationForecast.automationId');
|
|
134
|
+
const stepSeconds = FORECAST_RESOLUTION_SECONDS[forecast.resolution];
|
|
135
|
+
if (stepSeconds === undefined) {
|
|
136
|
+
throw new AutomationValidationError(`AutomationForecast.resolution is invalid: ${forecast.resolution}.`);
|
|
137
|
+
}
|
|
138
|
+
if (!Array.isArray(forecast.entries)) {
|
|
139
|
+
throw new AutomationValidationError('AutomationForecast.entries must be an array.');
|
|
140
|
+
}
|
|
141
|
+
let previousMs;
|
|
142
|
+
forecast.entries.forEach((entry, index) => {
|
|
143
|
+
const entryLabel = `AutomationForecast.entries[${index}]`;
|
|
144
|
+
if (!entry || typeof entry !== 'object') {
|
|
145
|
+
throw new AutomationValidationError(`${entryLabel} must be an object.`);
|
|
146
|
+
}
|
|
147
|
+
if (typeof entry.active !== 'boolean') {
|
|
148
|
+
throw new AutomationValidationError(`${entryLabel}.active must be a boolean.`);
|
|
149
|
+
}
|
|
150
|
+
const currentMs = Date.parse(entry.timestampIso);
|
|
151
|
+
if (Number.isNaN(currentMs)) {
|
|
152
|
+
throw new AutomationValidationError(`${entryLabel}.timestampIso is not a valid ISO 8601 timestamp: ${entry.timestampIso}.`);
|
|
153
|
+
}
|
|
154
|
+
if (previousMs !== undefined) {
|
|
155
|
+
const deltaSeconds = (currentMs - previousMs) / 1000;
|
|
156
|
+
if (deltaSeconds !== stepSeconds) {
|
|
157
|
+
throw new AutomationValidationError(`${entryLabel} is spaced ${deltaSeconds}s from the previous entry; expected ${stepSeconds}s for resolution ${forecast.resolution}.`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
previousMs = currentMs;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Validates the trigger-type-specific metadata carried by the
|
|
165
|
+
* `AutomationTriggerV1` data-bus message. Throws on the first violation.
|
|
166
|
+
*
|
|
167
|
+
* @param trigger - The trigger metadata payload.
|
|
168
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
169
|
+
*/
|
|
170
|
+
function validateAutomationTriggerData(trigger) {
|
|
171
|
+
if (!trigger || typeof trigger !== 'object') {
|
|
172
|
+
throw new AutomationValidationError('AutomationTriggerData must be an object.');
|
|
173
|
+
}
|
|
174
|
+
switch (trigger.triggerType) {
|
|
175
|
+
case enyo_automation_js_1.EnyoAutomationTriggerTypeEnum.PvSurplusThreshold:
|
|
176
|
+
requireFiniteNonNegative(trigger.surplusW, 'AutomationTriggerData.surplusW');
|
|
177
|
+
requireFiniteNonNegative(trigger.thresholdW, 'AutomationTriggerData.thresholdW');
|
|
178
|
+
break;
|
|
179
|
+
default:
|
|
180
|
+
throw new AutomationValidationError(`AutomationTriggerData.triggerType is invalid: ${trigger.triggerType}.`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function validateMqttAction(action, label) {
|
|
184
|
+
requireNonEmptyString(action.topic, `${label}.topic`);
|
|
185
|
+
if (typeof action.updateChargingPvSurplus !== 'boolean') {
|
|
186
|
+
throw new AutomationValidationError(`${label}.updateChargingPvSurplus must be a boolean.`);
|
|
187
|
+
}
|
|
188
|
+
if (typeof action.payloadTemplate !== 'string' || action.payloadTemplate.length === 0) {
|
|
189
|
+
throw new AutomationValidationError(`${label}.payloadTemplate must be a non-empty string.`);
|
|
190
|
+
}
|
|
191
|
+
validatePayloadTemplate(action.payloadTemplate, `${label}.payloadTemplate`);
|
|
192
|
+
const { qos } = action.publishOptions ?? {};
|
|
193
|
+
if (qos !== undefined && qos !== enyo_mqtt_js_1.MqttQos.AtMostOnce && qos !== enyo_mqtt_js_1.MqttQos.AtLeastOnce && qos !== enyo_mqtt_js_1.MqttQos.ExactlyOnce) {
|
|
194
|
+
throw new AutomationValidationError(`${label}.publishOptions.qos must be one of 0, 1, 2.`);
|
|
195
|
+
}
|
|
196
|
+
if (action.publishOptions?.retain !== undefined && typeof action.publishOptions.retain !== 'boolean') {
|
|
197
|
+
throw new AutomationValidationError(`${label}.publishOptions.retain must be a boolean.`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function validateSmartPlugSwitchAction(action, label, knownSmartPlugApplianceIds) {
|
|
201
|
+
requireNonEmptyString(action.applianceId, `${label}.applianceId`);
|
|
202
|
+
if (knownSmartPlugApplianceIds && !knownSmartPlugApplianceIds.has(action.applianceId)) {
|
|
203
|
+
throw new AutomationValidationError(`${label}.applianceId does not reference a smart-plug appliance that supports the smart-plug-switch action: ${action.applianceId}.`);
|
|
204
|
+
}
|
|
205
|
+
const { minDurationMinutes } = action;
|
|
206
|
+
if (typeof minDurationMinutes !== 'number' ||
|
|
207
|
+
!Number.isInteger(minDurationMinutes) ||
|
|
208
|
+
minDurationMinutes < exports.AUTOMATION_MIN_DURATION_MINUTES ||
|
|
209
|
+
minDurationMinutes > exports.AUTOMATION_MAX_DURATION_MINUTES ||
|
|
210
|
+
minDurationMinutes % exports.AUTOMATION_DURATION_STEP_MINUTES !== 0) {
|
|
211
|
+
throw new AutomationValidationError(`${label}.minDurationMinutes must be an integer between ${exports.AUTOMATION_MIN_DURATION_MINUTES} and ${exports.AUTOMATION_MAX_DURATION_MINUTES} in steps of ${exports.AUTOMATION_DURATION_STEP_MINUTES}.`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Ensures a payload template only references known placeholders and is
|
|
216
|
+
* structurally valid JSON once its placeholders are neutralised.
|
|
217
|
+
*/
|
|
218
|
+
function validatePayloadTemplate(template, label) {
|
|
219
|
+
const knownPlaceholders = new Set(Object.values(enyo_automation_js_1.EnyoAutomationMqttPlaceholderEnum));
|
|
220
|
+
const tokens = template.match(/{{[^}]*}}/g) ?? [];
|
|
221
|
+
for (const token of tokens) {
|
|
222
|
+
if (!knownPlaceholders.has(token)) {
|
|
223
|
+
throw new AutomationValidationError(`${label} references an unknown placeholder: ${token}. Allowed: ${Object.values(enyo_automation_js_1.EnyoAutomationMqttPlaceholderEnum).join(', ')}.`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Replace every placeholder with a neutral JSON scalar so the surrounding
|
|
227
|
+
// structure can be validated regardless of the placeholder's runtime value.
|
|
228
|
+
const neutralised = template.replace(/{{[^}]*}}/g, '0');
|
|
229
|
+
try {
|
|
230
|
+
JSON.parse(neutralised);
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
throw new AutomationValidationError(`${label} is not valid JSON once placeholders are substituted.`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function requireFiniteNonNegative(value, label) {
|
|
237
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
|
|
238
|
+
throw new AutomationValidationError(`${label} must be a finite number >= 0.`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function requireNonEmptyString(value, label) {
|
|
242
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
243
|
+
throw new AutomationValidationError(`${label} must be a non-empty string.`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function requireEnumMember(value, enumObject, label) {
|
|
247
|
+
const allowed = new Set(Object.values(enumObject));
|
|
248
|
+
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
249
|
+
throw new AutomationValidationError(`${label} is required.`);
|
|
250
|
+
}
|
|
251
|
+
if (!allowed.has(value)) {
|
|
252
|
+
throw new AutomationValidationError(`${label} is invalid: ${value}. Allowed values: ${Object.values(enumObject).join(', ')}.`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { EnyoAutomation, EnyoAutomationAction, EnyoAutomationForecast, EnyoAutomationTriggerData } from '../../types/enyo-automation.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when an automation, action or automation forecast violates one of the
|
|
4
|
+
* invariants enforced by this module. The message names the offending field /
|
|
5
|
+
* index so callers can surface it directly to the user.
|
|
6
|
+
*/
|
|
7
|
+
export declare class AutomationValidationError extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
/** Minimum smart-plug on-duration, in minutes. */
|
|
11
|
+
export declare const AUTOMATION_MIN_DURATION_MINUTES = 5;
|
|
12
|
+
/** Maximum smart-plug on-duration, in minutes (6 hours). */
|
|
13
|
+
export declare const AUTOMATION_MAX_DURATION_MINUTES = 360;
|
|
14
|
+
/** Step size the smart-plug on-duration must be a multiple of, in minutes. */
|
|
15
|
+
export declare const AUTOMATION_DURATION_STEP_MINUTES = 5;
|
|
16
|
+
/**
|
|
17
|
+
* Validates a complete {@link EnyoAutomation}. Throws on the first violation.
|
|
18
|
+
*
|
|
19
|
+
* When `knownSmartPlugApplianceIds` is provided, every smart-plug action's
|
|
20
|
+
* `applianceId` must be a member of it — use this to ensure the action targets
|
|
21
|
+
* an appliance that actually supports {@link EnyoAutomationActionTypeEnum.SmartPlugSwitch}.
|
|
22
|
+
*
|
|
23
|
+
* @param automation - The automation to validate.
|
|
24
|
+
* @param knownSmartPlugApplianceIds - Optional set/array of appliance ids that
|
|
25
|
+
* support the smart-plug-switch action; when given, smart-plug action targets
|
|
26
|
+
* are checked against it.
|
|
27
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateAutomation(automation: EnyoAutomation, knownSmartPlugApplianceIds?: Iterable<string>): void;
|
|
30
|
+
/**
|
|
31
|
+
* Validates the trigger of an automation. Throws on the first violation.
|
|
32
|
+
*
|
|
33
|
+
* @param trigger - The trigger configuration.
|
|
34
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateTrigger(trigger: EnyoAutomation['trigger']): void;
|
|
37
|
+
/**
|
|
38
|
+
* Validates a single automation action. Throws on the first violation.
|
|
39
|
+
*
|
|
40
|
+
* @param action - The action to validate.
|
|
41
|
+
* @param index - Index of the action within its automation (for error messages).
|
|
42
|
+
* @param knownSmartPlugApplianceIds - Optional set of appliance ids that support
|
|
43
|
+
* the smart-plug-switch action.
|
|
44
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateAction(action: EnyoAutomationAction, index?: number, knownSmartPlugApplianceIds?: Set<string>): void;
|
|
47
|
+
/**
|
|
48
|
+
* Validates an {@link EnyoAutomationForecast}: resolution must be known and the
|
|
49
|
+
* entries must be chronologically ordered, non-overlapping, and spaced exactly
|
|
50
|
+
* one resolution step apart. Throws on the first violation.
|
|
51
|
+
*
|
|
52
|
+
* @param forecast - The forecast to validate.
|
|
53
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
54
|
+
*/
|
|
55
|
+
export declare function validateAutomationForecast(forecast: EnyoAutomationForecast): void;
|
|
56
|
+
/**
|
|
57
|
+
* Validates the trigger-type-specific metadata carried by the
|
|
58
|
+
* `AutomationTriggerV1` data-bus message. Throws on the first violation.
|
|
59
|
+
*
|
|
60
|
+
* @param trigger - The trigger metadata payload.
|
|
61
|
+
* @throws {AutomationValidationError} On the first invariant violation.
|
|
62
|
+
*/
|
|
63
|
+
export declare function validateAutomationTriggerData(trigger: EnyoAutomationTriggerData): void;
|