@enyo-energy/energy-app-sdk 1.25.0 → 1.26.0

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 CHANGED
@@ -249,6 +249,7 @@ Every Energy App must be defined using `defineEnergyAppPackage()`:
249
249
  import {
250
250
  defineEnergyAppPackage,
251
251
  EnergyAppPackageCategory,
252
+ EnergyAppPackageOptionsDeviceDetectionModbusModeEnum,
252
253
  EnergyAppPermissionTypeEnum
253
254
  } from '@enyo-energy/energy-app-sdk';
254
255
 
@@ -291,11 +292,22 @@ const packageDef = defineEnergyAppPackage({
291
292
  },
292
293
  deviceDetection: {
293
294
  modbus: [{
295
+ // Optional, only on the first entry: how the entries and their matching
296
+ // values combine. Default: RegistersOr_MatchingValuesOr.
297
+ mode: EnergyAppPackageOptionsDeviceDetectionModbusModeEnum.RegistersAnd_MatchingValuesOr,
294
298
  unitIds: [1],
295
299
  registerAddress: 40001,
296
300
  registerSize: 2,
297
301
  type: 'string',
298
302
  matchingValues: ['SolarMax', 'SMA']
303
+ }, {
304
+ unitIds: [1],
305
+ // `registerType` defaults to 'holding'; use 'input' for function code 4.
306
+ registerType: 'input',
307
+ registerAddress: 30053,
308
+ registerSize: 2,
309
+ type: 'UInt32BE',
310
+ matchingValues: ['9401', '9402']
299
311
  }],
300
312
  mdns: [{
301
313
  // The Envoy advertises under a vendor-specific service type; without
@@ -1860,6 +1872,11 @@ forecast series concatenate into one timeline without translation. Every measure
1860
1872
  optional: you get what the provider holds and you asked for, and a measure it does not hold is simply
1861
1873
  absent rather than an error.
1862
1874
 
1875
+ `timestampIso` is the **start** of the bucket a reading or forecast entry covers. The irradiance
1876
+ fields are the mean over `[timestampIso, timestampIso + resolution)` — not the value at that moment,
1877
+ and not the mean of the preceding hour. Temperature, wind and cloud cover are the value at the start
1878
+ of the bucket for an hourly source, and the time-weighted mean over the bucket at coarser resolutions.
1879
+
1863
1880
  Aggregates per measure live in `statistics`, keyed by `WeatherHistoryMeasureEnum`. `averageValue` is
1864
1881
  time-weighted; for the irradiance measures it is a mean power density in W/m², so multiply by the
1865
1882
  covered duration in hours to get received energy in Wh/m². `symbol` is categorical and therefore
@@ -3993,6 +4010,12 @@ Each event carries the **complete** picture of the slot — render it as it arri
3993
4010
 
3994
4011
  New reason types came with this surface, so a skipped row can say *why* instead of falling back to a generic "scheduled optimization": `SessionComplete`, `AppliancePaused`, `NothingConnected`, `DeadlinePassed`, `WaitingForCheaperSlot`, `AboveOwnPriceLimit`, `BelowMinPower`, `OtherApplianceTurn`, `SupplyExhausted`, `OutsideSchedule` — grouped by the new `SessionState` and `Contention` reason categories. `ApplianceInitiatedDraw` and `PowerOffered` came with the waterfall states above.
3995
4012
 
4013
+ **Battery vs. charging car.** While a car charges, the house battery is either held out of it or allowed to help, according to the owner's `batteryEvDischargeMode`. Two reason types (category `BatteryState`) say which: `BatteryReservedFromEv` for the hold (`Discharge 0`) and `BatterySupportsEvCharging` for the release (`mode=Auto`). Both carry `context.batteryToEv` (`EnyoDataBusCommandReasonBatteryToEvContext`) with the owner's `mode`, the hold `trigger` (`EnyoBatteryToEvHoldTriggerEnum`: `OwnerBlocked`, `SocLimitReached`, `AllowanceSpent`, `NoMeasurement`), `socLimitPercent`, `allowanceWh` and `remainingWh`. The energy manager re-checks a hold periodically by briefly releasing the battery; that re-check sets `batteryToEv.probe: true` and should not be listed as its own entry in a command history.
4014
+
4015
+ **Short-cycling protection.** When an appliance is kept running (or kept off) to honour its minimum on/off time, state `DeviceProtection` with `context.switching` (`EnyoDataBusCommandReasonSwitchingContext`): `hold` (`EnyoSwitchingProtectionHoldEnum.KeptOn` / `KeptOff`), `minOnSeconds`, `minOffSeconds` and `untilIso`. The text can then say "Kept running for another 4 minutes to protect the compressor" instead of a generic "Protecting the device".
4016
+
4017
+ A hold that keeps an appliance **off** after a stop has its own type, `RestartDelay` (category `DeviceProtection`): set `context.switching` with `hold: KeptOff`, `minOffSeconds` and `untilIso` (when it may restart), and `powerW` to the power waiting for it — e.g. "Short pause after switching off — charging resumes at 13:15 so the car isn't switched on and off too often."
4018
+
3996
4019
  ## Dynamic Grid Fees & Tariff Bonuses
3997
4020
 
3998
4021
  An electricity price is rarely one number. It is the energy price, plus the grid operator's network
@@ -4560,6 +4583,28 @@ Notes:
4560
4583
  - V1 is unchanged and remains fully supported. V2 is an additive sibling, not a
4561
4584
  migration.
4562
4585
 
4586
+ #### Forecasting When a Heat Pump Runs
4587
+
4588
+ A heat pump integration can publish its own forecast of when the heat pump will run with
4589
+ `HeatpumpOperationForecastV1` (or `publishHeatpumpOperationForecast()` on `IntegrationEnergyApp`).
4590
+ It is a prediction, not a request for power — to offer power, send `ApplianceFlexibilityAnnouncementV2`.
4591
+
4592
+ Each entry covers one slot of `resolution`, starting at `timestampIso`. `outdoorTemperatureC` and
4593
+ `running` are always present; `flowTemperatureC`, `generatedHeatWh`, `consumptionWh` and
4594
+ `averagePowerW` are optional. Energy values are per slot, and `averagePowerW` is averaged over the
4595
+ whole slot. Each message replaces the previous forecast for the appliance.
4596
+
4597
+ ```typescript
4598
+ this.publishHeatpumpOperationForecast('heatpump-1', {
4599
+ resolution: ForecastResolutionEnum.OneHour,
4600
+ entries: [
4601
+ {timestampIso: '2026-10-06T06:00:00Z', outdoorTemperatureC: 4.5, running: true,
4602
+ flowTemperatureC: 38, generatedHeatWh: 5200, consumptionWh: 1600, averagePowerW: 1600},
4603
+ {timestampIso: '2026-10-06T07:00:00Z', outdoorTemperatureC: 5.0, running: false},
4604
+ ],
4605
+ });
4606
+ ```
4607
+
4563
4608
  #### Explaining Why a Command Was Issued
4564
4609
 
4565
4610
  Every data bus command can carry an `EnyoDataBusCommandReason`. Its `type`
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EnergyAppPackageFirmwareModeEnum = exports.EnergyAppPackageCompatibilityStatus = exports.EnergyAppPackageCategory = void 0;
3
+ exports.EnergyAppPackageFirmwareModeEnum = exports.EnergyAppPackageCompatibilityStatus = exports.EnergyAppPackageOptionsDeviceDetectionModbusModeEnum = exports.EnergyAppPackageCategory = void 0;
4
4
  exports.defineEnergyAppPackage = defineEnergyAppPackage;
5
5
  const version_js_1 = require("./version.cjs");
6
6
  var EnergyAppPackageCategory;
@@ -27,6 +27,34 @@ var EnergyAppPackageCategory;
27
27
  EnergyAppPackageCategory["Vehicle"] = "vehicle";
28
28
  EnergyAppPackageCategory["Other"] = "other";
29
29
  })(EnergyAppPackageCategory || (exports.EnergyAppPackageCategory = EnergyAppPackageCategory = {}));
30
+ /**
31
+ * How the Modbus device detection rules in
32
+ * {@link EnergyAppPackageOptionsDeviceDetection.modbus} are combined.
33
+ *
34
+ * Each name has two parts:
35
+ * - `Registers…`: how the entries (registers) of the `modbus` list combine.
36
+ * `Or` means one matching register is enough; `And` means every register has
37
+ * to match, all on the same unit id.
38
+ * - `MatchingValues…`: how the `matchingValues` within one register combine.
39
+ * `Or` means the register's value has to match one of them; `And` means it
40
+ * has to match all of them.
41
+ *
42
+ * Only read from the **first** entry of the `modbus` list and applies to the
43
+ * whole list; set it there and nowhere else. Defaults to
44
+ * {@link RegistersOr_MatchingValuesOr} when omitted, which is the behaviour of
45
+ * all existing rules.
46
+ */
47
+ var EnergyAppPackageOptionsDeviceDetectionModbusModeEnum;
48
+ (function (EnergyAppPackageOptionsDeviceDetectionModbusModeEnum) {
49
+ /** One register has to match, with one of its matching values. The default. */
50
+ EnergyAppPackageOptionsDeviceDetectionModbusModeEnum["RegistersOr_MatchingValuesOr"] = "RegistersOr_MatchingValuesOr";
51
+ /** One register has to match, with all of its matching values. */
52
+ EnergyAppPackageOptionsDeviceDetectionModbusModeEnum["RegistersOr_MatchingValuesAnd"] = "RegistersOr_MatchingValuesAnd";
53
+ /** Every register has to match, each with one of its matching values. */
54
+ EnergyAppPackageOptionsDeviceDetectionModbusModeEnum["RegistersAnd_MatchingValuesOr"] = "RegistersAnd_MatchingValuesOr";
55
+ /** Every register has to match, each with all of its matching values. */
56
+ EnergyAppPackageOptionsDeviceDetectionModbusModeEnum["RegistersAnd_MatchingValuesAnd"] = "RegistersAnd_MatchingValuesAnd";
57
+ })(EnergyAppPackageOptionsDeviceDetectionModbusModeEnum || (exports.EnergyAppPackageOptionsDeviceDetectionModbusModeEnum = EnergyAppPackageOptionsDeviceDetectionModbusModeEnum = {}));
30
58
  /**
31
59
  * Whether a declared compatibility entry means "this works" or "this is known
32
60
  * not to work".
@@ -40,8 +40,85 @@ export interface EnergyAppPackageOptionsDeviceDetectionHostname {
40
40
  operation: 'eq' | 'startsWith';
41
41
  matchingValue: string;
42
42
  }
43
+ /**
44
+ * Which Modbus register bank a device detection rule reads.
45
+ * - `'holding'`: holding registers, function code 3
46
+ * - `'input'`: input registers, function code 4
47
+ *
48
+ * The two banks are separate address spaces, so the same address may hold
49
+ * different data (or nothing) in each.
50
+ */
51
+ export type EnergyAppPackageOptionsDeviceDetectionModbusRegisterType = 'holding' | 'input';
52
+ /**
53
+ * How the Modbus device detection rules in
54
+ * {@link EnergyAppPackageOptionsDeviceDetection.modbus} are combined.
55
+ *
56
+ * Each name has two parts:
57
+ * - `Registers…`: how the entries (registers) of the `modbus` list combine.
58
+ * `Or` means one matching register is enough; `And` means every register has
59
+ * to match, all on the same unit id.
60
+ * - `MatchingValues…`: how the `matchingValues` within one register combine.
61
+ * `Or` means the register's value has to match one of them; `And` means it
62
+ * has to match all of them.
63
+ *
64
+ * Only read from the **first** entry of the `modbus` list and applies to the
65
+ * whole list; set it there and nowhere else. Defaults to
66
+ * {@link RegistersOr_MatchingValuesOr} when omitted, which is the behaviour of
67
+ * all existing rules.
68
+ */
69
+ export declare enum EnergyAppPackageOptionsDeviceDetectionModbusModeEnum {
70
+ /** One register has to match, with one of its matching values. The default. */
71
+ RegistersOr_MatchingValuesOr = "RegistersOr_MatchingValuesOr",
72
+ /** One register has to match, with all of its matching values. */
73
+ RegistersOr_MatchingValuesAnd = "RegistersOr_MatchingValuesAnd",
74
+ /** Every register has to match, each with one of its matching values. */
75
+ RegistersAnd_MatchingValuesOr = "RegistersAnd_MatchingValuesOr",
76
+ /** Every register has to match, each with all of its matching values. */
77
+ RegistersAnd_MatchingValuesAnd = "RegistersAnd_MatchingValuesAnd"
78
+ }
79
+ /**
80
+ * Optional device detection configuration for Modbus TCP register matching:
81
+ * read a register range and compare its decoded value against the matching
82
+ * values.
83
+ *
84
+ * How several entries and several matching values combine is set by
85
+ * {@link mode} on the first entry of the list.
86
+ *
87
+ * @example
88
+ * // Vendor name in holding registers AND model id in an input register:
89
+ * modbus: [
90
+ * {
91
+ * mode: EnergyAppPackageOptionsDeviceDetectionModbusModeEnum.RegistersAnd_MatchingValuesOr,
92
+ * unitIds: [1],
93
+ * registerAddress: 40001,
94
+ * registerSize: 2,
95
+ * type: 'string',
96
+ * matchingValues: ['SMA'],
97
+ * },
98
+ * {
99
+ * unitIds: [1],
100
+ * registerType: 'input',
101
+ * registerAddress: 30053,
102
+ * registerSize: 2,
103
+ * type: 'UInt32BE',
104
+ * matchingValues: ['9401', '9402'],
105
+ * },
106
+ * ]
107
+ */
43
108
  export interface EnergyAppPackageOptionsDeviceDetectionModbus {
109
+ /**
110
+ * How all entries of the `modbus` list and their matching values combine.
111
+ * Only read from the first entry of the list — set it there and leave it
112
+ * out on the others. Defaults to
113
+ * {@link EnergyAppPackageOptionsDeviceDetectionModbusModeEnum.RegistersOr_MatchingValuesOr}.
114
+ */
115
+ mode?: EnergyAppPackageOptionsDeviceDetectionModbusModeEnum;
44
116
  unitIds: number[];
117
+ /**
118
+ * Which register bank to read. Defaults to `'holding'` when omitted,
119
+ * which is the behaviour of all existing rules.
120
+ */
121
+ registerType?: EnergyAppPackageOptionsDeviceDetectionModbusRegisterType;
45
122
  /** Register address, for example 30001 */
46
123
  registerAddress: number;
47
124
  /** Register size, for example 2 for 30001 - 30002 */
@@ -221,6 +221,42 @@ class IntegrationEnergyApp extends energy_app_js_1.EnergyApp {
221
221
  };
222
222
  this.useDataBus().sendMessage([msg]);
223
223
  }
224
+ /**
225
+ * Publishes a `HeatpumpOperationForecastV1` — this heat pump's own forecast
226
+ * of when it will run, slot by slot.
227
+ *
228
+ * A prediction, not a request for power: to offer power the heat pump could
229
+ * absorb, use {@link publishFlexibilityAnnouncement}. Publish again whenever
230
+ * the forecast changes; each message replaces the previous one for the
231
+ * appliance.
232
+ *
233
+ * @param applianceId - The heat pump appliance the forecast is for.
234
+ * @param forecast - The slot length and the slots. See
235
+ * {@link EnyoDataBusHeatpumpOperationForecastV1.data}.
236
+ *
237
+ * @example
238
+ * ```typescript
239
+ * this.publishHeatpumpOperationForecast('heatpump-1', {
240
+ * resolution: ForecastResolutionEnum.OneHour,
241
+ * entries: [
242
+ * {timestampIso: '2026-10-06T06:00:00Z', outdoorTemperatureC: 4.5, running: true, averagePowerW: 1600},
243
+ * {timestampIso: '2026-10-06T07:00:00Z', outdoorTemperatureC: 5.0, running: false},
244
+ * ],
245
+ * });
246
+ * ```
247
+ */
248
+ publishHeatpumpOperationForecast(applianceId, forecast) {
249
+ const msg = {
250
+ id: this.generateMessageId(),
251
+ type: 'message',
252
+ message: enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.HeatpumpOperationForecastV1,
253
+ source: this.source,
254
+ applianceId,
255
+ timestampIso: new Date().toISOString(),
256
+ data: forecast
257
+ };
258
+ this.useDataBus().sendMessage([msg]);
259
+ }
224
260
  /**
225
261
  * Resolves the list of appliance IDs this integration is responsible for.
226
262
  *
@@ -1,5 +1,5 @@
1
1
  import { EnergyApp } from "../energy-app.cjs";
2
- import { EnyoDataBusApplianceFlexibilityAnnouncementV2, EnyoDataBusGridOperatorPowerLimitationExecutedV1, EnyoDataBusGridOperatorPowerLimitationV1, EnyoDataBusMessage, EnyoDataBusMessageEnum } from "../types/enyo-data-bus-value.cjs";
2
+ import { EnyoDataBusApplianceFlexibilityAnnouncementV2, EnyoDataBusGridOperatorPowerLimitationExecutedV1, EnyoDataBusGridOperatorPowerLimitationV1, EnyoDataBusHeatpumpOperationForecastV1, EnyoDataBusMessage, EnyoDataBusMessageEnum } from "../types/enyo-data-bus-value.cjs";
3
3
  import { EnyoApplianceTypeEnum } from "../types/enyo-appliance.cjs";
4
4
  import { EnyoSourceEnum } from "../types/enyo-source.enum.cjs";
5
5
  import { DataBusCommandHandler } from "../implementations/data-bus/data-bus-command-handler.cjs";
@@ -166,6 +166,31 @@ export declare abstract class IntegrationEnergyApp extends EnergyApp {
166
166
  * ```
167
167
  */
168
168
  publishFlexibilityAnnouncement(applianceId: string, flexibility: EnyoDataBusApplianceFlexibilityAnnouncementV2['data']['flexibility']): void;
169
+ /**
170
+ * Publishes a `HeatpumpOperationForecastV1` — this heat pump's own forecast
171
+ * of when it will run, slot by slot.
172
+ *
173
+ * A prediction, not a request for power: to offer power the heat pump could
174
+ * absorb, use {@link publishFlexibilityAnnouncement}. Publish again whenever
175
+ * the forecast changes; each message replaces the previous one for the
176
+ * appliance.
177
+ *
178
+ * @param applianceId - The heat pump appliance the forecast is for.
179
+ * @param forecast - The slot length and the slots. See
180
+ * {@link EnyoDataBusHeatpumpOperationForecastV1.data}.
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * this.publishHeatpumpOperationForecast('heatpump-1', {
185
+ * resolution: ForecastResolutionEnum.OneHour,
186
+ * entries: [
187
+ * {timestampIso: '2026-10-06T06:00:00Z', outdoorTemperatureC: 4.5, running: true, averagePowerW: 1600},
188
+ * {timestampIso: '2026-10-06T07:00:00Z', outdoorTemperatureC: 5.0, running: false},
189
+ * ],
190
+ * });
191
+ * ```
192
+ */
193
+ publishHeatpumpOperationForecast(applianceId: string, forecast: EnyoDataBusHeatpumpOperationForecastV1['data']): void;
169
194
  /**
170
195
  * Resolves the list of appliance IDs this integration is responsible for.
171
196
  *
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EnyoPowerSourceEnum = exports.EnyoHeatpumpHeatSourceEnum = exports.EnyoHeatpumpControlPurposeEnum = exports.EnyoChargingProfileTypeEnum = exports.EnyoCommandAcknowledgeAnswerEnum = exports.EnyoStorageControlDirectionEnum = exports.EnyoStorageControlModeEnum = exports.EnyoStorageScheduleDirectionEnum = exports.EnyoStorageScheduleModeEnum = exports.EnyoChargingLimitRequestResultEnum = exports.EnyoDataBusMessageEnum = exports.EnyoChargeInitiatorEnum = exports.EnyoPriceLimitModeEnum = exports.EnyoChargeModeEnum = exports.EnyoChargingStopReason = exports.EnyoChargingMeterValueContext = exports.EnyoStringStateEnum = exports.EnyoHeatingRodStateEnum = exports.EnyoInverterStateEnum = exports.EnyoBatteryStateEnum = exports.EnyoGridOperatorLimitTypeEnum = exports.EnyoDataBusCommandReasonCategoryEnum = exports.EnyoDataBusCommandReasonTypeEnum = void 0;
3
+ exports.EnyoPowerSourceEnum = exports.EnyoHeatpumpHeatSourceEnum = exports.EnyoHeatpumpControlPurposeEnum = exports.EnyoChargingProfileTypeEnum = exports.EnyoCommandAcknowledgeAnswerEnum = exports.EnyoStorageControlDirectionEnum = exports.EnyoStorageControlModeEnum = exports.EnyoStorageScheduleDirectionEnum = exports.EnyoStorageScheduleModeEnum = exports.EnyoChargingLimitRequestResultEnum = exports.EnyoDataBusMessageEnum = exports.EnyoChargeInitiatorEnum = exports.EnyoPriceLimitModeEnum = exports.EnyoChargeModeEnum = exports.EnyoChargingStopReason = exports.EnyoChargingMeterValueContext = exports.EnyoStringStateEnum = exports.EnyoHeatingRodStateEnum = exports.EnyoInverterStateEnum = exports.EnyoBatteryStateEnum = exports.EnyoGridOperatorLimitTypeEnum = exports.EnyoSwitchingProtectionHoldEnum = exports.EnyoBatteryToEvHoldTriggerEnum = exports.EnyoDataBusCommandReasonCategoryEnum = exports.EnyoDataBusCommandReasonTypeEnum = void 0;
4
4
  /**
5
5
  * Enum representing the reason type for why a data bus command was issued.
6
6
  * Used to attach context to commands for logging, debugging, and UI display.
@@ -71,7 +71,16 @@ var EnyoDataBusCommandReasonTypeEnum;
71
71
  EnyoDataBusCommandReasonTypeEnum["ScheduledOptimization"] = "scheduled-optimization";
72
72
  /** Command issued because the user explicitly requested it */
73
73
  EnyoDataBusCommandReasonTypeEnum["UserRequest"] = "user-request";
74
- /** Command issued to protect the device (e.g. overheating or safety limit) */
74
+ /**
75
+ * Command issued to protect the device (e.g. overheating or safety limit).
76
+ *
77
+ * When the protection is a minimum on/off time — an appliance kept running
78
+ * (or kept off) to avoid short-cycling its compressor — set
79
+ * {@link EnyoDataBusCommandReasonContext.switching} so the text can say how
80
+ * long the hold lasts instead of a generic "protecting the device". A hold
81
+ * that keeps an appliance OFF after a stop has its own type,
82
+ * {@link RestartDelay}.
83
+ */
75
84
  EnyoDataBusCommandReasonTypeEnum["DeviceProtection"] = "device-protection";
76
85
  /** Command issued because home consumption is high */
77
86
  EnyoDataBusCommandReasonTypeEnum["HomeConsumptionHigh"] = "home-consumption-high";
@@ -173,6 +182,53 @@ var EnyoDataBusCommandReasonTypeEnum;
173
182
  * other constraint were lifted, the appliance would still stay off.
174
183
  */
175
184
  EnyoDataBusCommandReasonTypeEnum["OutsideSchedule"] = "outside-schedule";
185
+ /**
186
+ * The house battery is held (`Discharge 0`) so that stored energy does not
187
+ * flow into a charging car.
188
+ *
189
+ * Stated by the decision maker that judged the owner's
190
+ * `batteryEvDischargeMode`, never inferred by the battery's own command
191
+ * path. Says WHY through {@link EnyoDataBusCommandReasonContext.batteryToEv}
192
+ * — the owner blocked it, the SoC limit is reached, the watt-hour allowance
193
+ * is spent, or the battery's power cannot be measured. Set
194
+ * {@link EnyoDataBusCommandReason.socPercent} to the pack's current SoC.
195
+ *
196
+ * Told apart from {@link BatterySoCLow}: the pack is not low, it is at the
197
+ * floor the owner chose for the car. Category
198
+ * {@link EnyoDataBusCommandReasonCategoryEnum.BatteryState}.
199
+ */
200
+ EnyoDataBusCommandReasonTypeEnum["BatteryReservedFromEv"] = "battery-reserved-from-ev";
201
+ /**
202
+ * While a car charges, the house battery is left to self-manage
203
+ * (`mode=Auto`) and may cover part of the car's draw — within what the
204
+ * owner allowed.
205
+ *
206
+ * The counterpart of {@link BatteryReservedFromEv}. Set
207
+ * {@link EnyoDataBusCommandReasonContext.batteryToEv} so the text can say
208
+ * how far: down to an SoC limit, or how many watt-hours of the allowance
209
+ * are left. A short re-check of an existing hold sets `batteryToEv.probe` —
210
+ * consumers SHOULD NOT show a probe as a separate history entry. Category
211
+ * {@link EnyoDataBusCommandReasonCategoryEnum.BatteryState}.
212
+ */
213
+ EnyoDataBusCommandReasonTypeEnum["BatterySupportsEvCharging"] = "battery-supports-ev-charging";
214
+ /**
215
+ * The appliance was switched off a moment ago and is held off for its
216
+ * declared minimum off-time, so it is not cycled on and off. Stated by the
217
+ * energy manager's switching protection.
218
+ *
219
+ * Set {@link EnyoDataBusCommandReasonContext.switching} with
220
+ * {@link EnyoSwitchingProtectionHoldEnum.KeptOff}, `minOffSeconds` and
221
+ * `untilIso` — when it may restart, i.e. the last stop plus the minimum
222
+ * off-time — and {@link EnyoDataBusCommandReason.powerW} to the power that
223
+ * is waiting for it, so the text can say why it will start.
224
+ *
225
+ * The specific form of {@link DeviceProtection} for a hold after a stop;
226
+ * a hold that keeps an appliance RUNNING stays `DeviceProtection` with
227
+ * {@link EnyoSwitchingProtectionHoldEnum.KeptOn}. Category
228
+ * {@link EnyoDataBusCommandReasonCategoryEnum.DeviceProtection} — it protects
229
+ * the appliance from short-cycling and is not about price, sun or contention.
230
+ */
231
+ EnyoDataBusCommandReasonTypeEnum["RestartDelay"] = "restart-delay";
176
232
  })(EnyoDataBusCommandReasonTypeEnum || (exports.EnyoDataBusCommandReasonTypeEnum = EnyoDataBusCommandReasonTypeEnum = {}));
177
233
  /**
178
234
  * Coarse, machine-readable grouping of why a data bus command was issued.
@@ -215,6 +271,54 @@ var EnyoDataBusCommandReasonCategoryEnum;
215
271
  /** Any reason not covered by the categories above. */
216
272
  EnyoDataBusCommandReasonCategoryEnum["Other"] = "other";
217
273
  })(EnyoDataBusCommandReasonCategoryEnum || (exports.EnyoDataBusCommandReasonCategoryEnum = EnyoDataBusCommandReasonCategoryEnum = {}));
274
+ /**
275
+ * Which part of the owner's battery-to-EV setting
276
+ * (`batteryEvDischargeMode`) caused a
277
+ * {@link EnyoDataBusCommandReasonTypeEnum.BatteryReservedFromEv} hold.
278
+ */
279
+ var EnyoBatteryToEvHoldTriggerEnum;
280
+ (function (EnyoBatteryToEvHoldTriggerEnum) {
281
+ /**
282
+ * {@link EnergyManagerBatteryEvDischargeModeEnum.BlockDischarge}: the owner
283
+ * never lets the battery charge the car.
284
+ */
285
+ EnyoBatteryToEvHoldTriggerEnum["OwnerBlocked"] = "owner-blocked";
286
+ /**
287
+ * {@link EnergyManagerBatteryEvDischargeModeEnum.SocLimit}: the pack has
288
+ * reached the owner's floor
289
+ * ({@link EnyoDataBusCommandReasonBatteryToEvContext.socLimitPercent}).
290
+ */
291
+ EnyoBatteryToEvHoldTriggerEnum["SocLimitReached"] = "soc-limit-reached";
292
+ /**
293
+ * {@link EnergyManagerBatteryEvDischargeModeEnum.FixedWh}: this session's
294
+ * allowance ({@link EnyoDataBusCommandReasonBatteryToEvContext.allowanceWh})
295
+ * has gone into the car.
296
+ */
297
+ EnyoBatteryToEvHoldTriggerEnum["AllowanceSpent"] = "allowance-spent";
298
+ /**
299
+ * The pack's live power cannot be read, so it cannot be shown NOT to feed
300
+ * the car. A fail-safe, stated out loud so that a site whose battery app
301
+ * publishes no power is diagnosable.
302
+ */
303
+ EnyoBatteryToEvHoldTriggerEnum["NoMeasurement"] = "no-measurement";
304
+ })(EnyoBatteryToEvHoldTriggerEnum || (exports.EnyoBatteryToEvHoldTriggerEnum = EnyoBatteryToEvHoldTriggerEnum = {}));
305
+ /**
306
+ * Which way a short-cycling protection holds an appliance.
307
+ */
308
+ var EnyoSwitchingProtectionHoldEnum;
309
+ (function (EnyoSwitchingProtectionHoldEnum) {
310
+ /**
311
+ * Kept running although the plan would stop it, because it has not yet run
312
+ * its minimum on-time — it may be drawing grid power meanwhile.
313
+ */
314
+ EnyoSwitchingProtectionHoldEnum["KeptOn"] = "kept-on";
315
+ /**
316
+ * Kept off although the plan would start it, because it has not yet rested
317
+ * its minimum off-time (e.g. the compressor gap between two starts). Goes
318
+ * with {@link EnyoDataBusCommandReasonTypeEnum.RestartDelay}.
319
+ */
320
+ EnyoSwitchingProtectionHoldEnum["KeptOff"] = "kept-off";
321
+ })(EnyoSwitchingProtectionHoldEnum || (exports.EnyoSwitchingProtectionHoldEnum = EnyoSwitchingProtectionHoldEnum = {}));
218
322
  /**
219
323
  * Whether a grid operator power limitation caps power drawn from the grid
220
324
  * (consumption) or power fed into the grid (production).
@@ -429,6 +533,8 @@ var EnyoDataBusMessageEnum;
429
533
  EnyoDataBusMessageEnum["ApplianceFlexibilityAnnouncementV2"] = "ApplianceFlexibilityAnnouncementV2";
430
534
  EnyoDataBusMessageEnum["ApplianceStateUpdateV1"] = "ApplianceStateUpdateV1";
431
535
  EnyoDataBusMessageEnum["HeatpumpValuesUpdateV1"] = "HeatpumpValuesUpdateV1";
536
+ /** A heat pump integration's forecast of when its heat pump will run, slot by slot. */
537
+ EnyoDataBusMessageEnum["HeatpumpOperationForecastV1"] = "HeatpumpOperationForecastV1";
432
538
  EnyoDataBusMessageEnum["HeatingRodValuesUpdateV1"] = "HeatingRodValuesUpdateV1";
433
539
  EnyoDataBusMessageEnum["ChargingStartedV1"] = "ChargingStartedV1";
434
540
  EnyoDataBusMessageEnum["ChargingMeterValuesUpdateV1"] = "ChargingMeterValuesUpdateV1";