@enyo-energy/energy-app-sdk 1.50.0 → 1.51.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 +45 -1
- package/dist/cjs/energy-app.cjs +13 -0
- package/dist/cjs/energy-app.d.cts +12 -0
- package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
- package/dist/cjs/implementations/heatpump/heatpump-control-validators.cjs +126 -0
- package/dist/cjs/implementations/heatpump/heatpump-control-validators.d.cts +62 -1
- package/dist/cjs/implementations/heatpump/heatpump-metadata-validators.cjs +19 -0
- package/dist/cjs/implementations/heatpump/heatpump-metadata-validators.d.cts +4 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/integrations/heatpump-integration-energy-app.cjs +121 -0
- package/dist/cjs/integrations/heatpump-integration-energy-app.d.cts +97 -1
- package/dist/cjs/packages/energy-app-vacations.cjs +2 -0
- package/dist/cjs/packages/energy-app-vacations.d.cts +48 -0
- package/dist/cjs/types/enyo-data-bus-value.cjs +27 -1
- package/dist/cjs/types/enyo-data-bus-value.d.cts +166 -0
- package/dist/cjs/types/enyo-heatpump-appliance.cjs +36 -0
- package/dist/cjs/types/enyo-heatpump-appliance.d.cts +58 -1
- package/dist/cjs/types/enyo-vacation.cjs +22 -0
- package/dist/cjs/types/enyo-vacation.d.cts +72 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/energy-app.d.ts +12 -0
- package/dist/energy-app.js +13 -0
- package/dist/enyo-energy-app-sdk.d.ts +3 -0
- package/dist/implementations/heatpump/heatpump-control-validators.d.ts +62 -1
- package/dist/implementations/heatpump/heatpump-control-validators.js +125 -1
- package/dist/implementations/heatpump/heatpump-metadata-validators.d.ts +4 -1
- package/dist/implementations/heatpump/heatpump-metadata-validators.js +19 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/integrations/heatpump-integration-energy-app.d.ts +97 -1
- package/dist/integrations/heatpump-integration-energy-app.js +122 -1
- package/dist/packages/energy-app-vacations.d.ts +48 -0
- package/dist/packages/energy-app-vacations.js +1 -0
- package/dist/types/enyo-data-bus-value.d.ts +166 -0
- package/dist/types/enyo-data-bus-value.js +26 -0
- package/dist/types/enyo-heatpump-appliance.d.ts +58 -1
- package/dist/types/enyo-heatpump-appliance.js +36 -0
- package/dist/types/enyo-vacation.d.ts +72 -0
- package/dist/types/enyo-vacation.js +19 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1864,6 +1864,19 @@ const history = await roomTemperatures.getRoomTemperatureHistory({
|
|
|
1864
1864
|
|
|
1865
1865
|
Requires the `RoomTemperatures` permission.
|
|
1866
1866
|
|
|
1867
|
+
#### `useVacations(): EnergyAppVacations`
|
|
1868
|
+
|
|
1869
|
+
Read the customer's absences (vacation mode), as planned in the enyo app — every absence that is not over yet, planned or running; they never overlap. Each `EnyoVacation` carries `startIso` / `endIso` and the customer's choices per appliance category: `heatpump.heating` — `mode` while away (`EnyoVacationHeatingModeEnum`: `FrostProtection` = `VACATION_FROST_PROTECTION_C` 7 °C, `Lowered` to `loweredTemperatureC` between `VACATION_LOWERED_MIN_C` and `VACATION_LOWERED_MAX_C` (10–20 °C), or `Normal`) and `preheatBeforeReturn` —, `heatpump.domesticHotWater.pause`, and `battery.gridServing` / `battery.minSocAtReturnPercent`. An early return moves `endIso` to that moment and fires the listener. Read-only. The energy manager turns an absence into [VacationStartedV1 / VacationStoppedV1](#vacationstartedv1--vacationstoppedv1).
|
|
1870
|
+
|
|
1871
|
+
```typescript
|
|
1872
|
+
const vacations = energyApp.useVacations();
|
|
1873
|
+
|
|
1874
|
+
applyVacations(await vacations.getVacations());
|
|
1875
|
+
vacations.listenForVacationsChanged((all) => applyVacations(all));
|
|
1876
|
+
```
|
|
1877
|
+
|
|
1878
|
+
Requires the `EnergyManager` permission.
|
|
1879
|
+
|
|
1867
1880
|
#### `useGridConnectionPoint(): EnergyAppGridConnectionPoint`
|
|
1868
1881
|
|
|
1869
1882
|
Read the site's grid connection details — main fuse rating, number of phases, the maximum allowed grid power, and the total power that load balancing may allocate to EV chargers. Use this to size dispatch envelopes and avoid violating the contractual cap.
|
|
@@ -3891,13 +3904,14 @@ Drives a heatpump. Manages building / DHW overheating commands and grid-power-av
|
|
|
3891
3904
|
|
|
3892
3905
|
> **Preferred: control heatpumps with the V3 messages.** `HeatpumpControlV3` (power, power sources, which zones to heat now, until when), `HeatpumpControlCancelV3` (cancel a session) and `HeatpumpControlStatusV3` (progress) are the preferred way to control a heatpump — for energy managers sending commands and for integrations receiving them. Use them for all new work. `HeatpumpOverheatingV1`, `SetHeatpumpAvailablePowerV2` and `HeatpumpAvailablePowerAnnouncementV1` are deprecated and only remain for heatpumps that do not declare `ControlV3`. See [HeatpumpControlV3](#heatpumpcontrolv3). Likewise, a heatpump offers its flexibility with `HeatpumpFlexibilityAnnouncementV3` rather than the generic `ApplianceFlexibilityAnnouncementV1` / `V2` — see [HeatpumpFlexibilityAnnouncementV3](#heatpumpflexibilityannouncementv3).
|
|
3893
3906
|
|
|
3894
|
-
- **Subscribed commands:** `HeatpumpControlV3`, `HeatpumpControlCancelV3`, `HeatpumpProgramSwitchV3`, `SetHeatpumpHeatingCurveOffsetV1`, `HeatpumpOverheatingV1` (deprecated), `HeatpumpAvailablePowerAnnouncementV1` (deprecated), `SetHeatpumpAvailablePowerV2` (deprecated), `SetHeatpumpRoomTemperatureV1`, `GridOperatorPowerLimitationV1` (broadcast)
|
|
3907
|
+
- **Subscribed commands:** `HeatpumpControlV3`, `HeatpumpControlCancelV3`, `HeatpumpProgramSwitchV3`, `SetHeatpumpHeatingCurveOffsetV1`, `VacationStartedV1`, `VacationStoppedV1`, `HeatpumpOverheatingV1` (deprecated), `HeatpumpAvailablePowerAnnouncementV1` (deprecated), `SetHeatpumpAvailablePowerV2` (deprecated), `SetHeatpumpRoomTemperatureV1`, `GridOperatorPowerLimitationV1` (broadcast)
|
|
3895
3908
|
- **Implement:** `handleHeatpumpOverheating`, `handleHeatpumpAvailablePowerAnnouncement`, `handleSetHeatpumpAvailablePower`, `handleGridOperatorPowerLimitation`
|
|
3896
3909
|
- **Optional overrides:**
|
|
3897
3910
|
- `handleSetHeatpumpRoomTemperature` — write a measured room temperature into a heating circuit. Answers `NotSupported` by default; override it when the heatpump declares `RoomTemperatureInput`.
|
|
3898
3911
|
- `handleHeatpumpControl` — support `HeatpumpControlV3` (see below). Answers `NotSupported` by default; override it and declare `ControlV3`.
|
|
3899
3912
|
- `handleHeatpumpProgramSwitch(request)` — switch a zone to comfort or eco, or back to its time program (see [HeatpumpProgramSwitchV3](#heatpumpprogramswitchv3)). Answers `NotSupported` by default; override it and declare `ProgramSwitch`.
|
|
3900
3913
|
- `handleHeatpumpHeatingCurveOffset(message)` — shift a heating circuit's curve permanently, relative to its initial curve (see [SetHeatpumpHeatingCurveOffsetV1](#setheatpumpheatingcurveoffsetv1)). Answers `NotSupported` by default; override it and declare `HeatingCurveOffset`.
|
|
3914
|
+
- `handleVacationStarted(message)` / `handleVacationStopped(message)` — the customer is away: heat with frost protection or lowered and / or pause hot water, then restore everything (see [VacationStartedV1 / VacationStoppedV1](#vacationstartedv1--vacationstoppedv1)). Answer `NotSupported` by default; override both and declare `VacationFrostProtection`, `VacationLowered` and / or `VacationHotWaterPause`.
|
|
3901
3915
|
- `handleHeatpumpControlCancel` — withdraw a cancelled session on the device. By default `Accepted` for heatpumps declaring `ControlV3`, `NotSupported` otherwise.
|
|
3902
3916
|
- `onHeatpumpControlEnded(applianceId, cause)` — return the heatpump to its own behaviour when a session ends; `cause` is `HeatpumpControlEndCauseEnum.Expired`, `Ended` or `Cancelled`. Does nothing by default.
|
|
3903
3917
|
- `resolveHeatpumpMetadata(applianceId)` — the metadata actions are validated against. Defaults to the stored appliance's `heatpump` metadata.
|
|
@@ -3996,6 +4010,33 @@ protected async handleHeatpumpHeatingCurveOffset(message: EnyoDataBusSetHeatpump
|
|
|
3996
4010
|
}
|
|
3997
4011
|
```
|
|
3998
4012
|
|
|
4013
|
+
#### VacationStartedV1 / VacationStoppedV1
|
|
4014
|
+
|
|
4015
|
+
The customer is away (`useVacations()`): `VacationStartedV1` carries the customer's preferences, nested by appliance type — `heatpump` (heating while away, whether hot water is paused) and `battery` — and the integration decides how to realise them, ideally with the device's own holiday / absence mode. `VacationStoppedV1` ends the vacation (return, early return or removed): restore everything so the zones follow their own time program again. Both are answered with the usual `CommandAcknowledgeV1`.
|
|
4016
|
+
|
|
4017
|
+
```typescript
|
|
4018
|
+
// VacationStartedV1
|
|
4019
|
+
{
|
|
4020
|
+
vacationId: 'vacation-1',
|
|
4021
|
+
returnIso: '2026-12-14T18:00:00.000Z', // planned return; safety net: end everything by then on your own
|
|
4022
|
+
heatpump: {
|
|
4023
|
+
heating: { mode: EnyoVacationHeatingEnum.FrostProtection, targetTemperatureC: 7 }, // absent = heat as usual
|
|
4024
|
+
domesticHotWater: { pause: true }, // absent or pause: false = hot water as usual; legionella protection keeps running
|
|
4025
|
+
},
|
|
4026
|
+
// battery: { gridServing: true, minSocAtReturnPercent: 50 }, // for batteries with their own vacation handling; not sent by the enyo energy manager today
|
|
4027
|
+
reason: { type: EnyoDataBusCommandReasonTypeEnum.UserRequest },
|
|
4028
|
+
}
|
|
4029
|
+
// VacationStoppedV1
|
|
4030
|
+
{ vacationId: 'vacation-1' }
|
|
4031
|
+
```
|
|
4032
|
+
|
|
4033
|
+
- **Only to heatpumps declaring a vacation feature.** `heatpump.heating.mode` `FrostProtection` (`EnyoVacationHeatingEnum`, the device's own frost protection ≈ `VACATION_FROST_PROTECTION_C` 7 °C) needs `VacationFrostProtection`; `Lowered` needs `VacationLowered` and a `targetTemperatureC` inside the appliance-level `vacationLoweredRangeC` (the setpoint the device can hold for all its circuits); `heatpump.domesticHotWater.pause: true` needs `VacationHotWaterPause`. A heatpump without any vacation feature gets the eco fallback via `HeatpumpProgramSwitchV3`.
|
|
4034
|
+
- **Idempotent, the complete wanted state.** The energy manager re-sends `VacationStartedV1` whenever the wanted state changes and at least hourly. Pre-heating for the return stays with the energy manager: when heating resumes it re-sends the message without `heatpump.heating`, when hot water resumes with `domesticHotWater.pause: false`, and when nothing is left it sends `VacationStoppedV1`.
|
|
4035
|
+
- **Ends by itself.** Write `returnIso` to the device as the end of its holiday mode, or end everything yourself at `returnIso` if no `VacationStoppedV1` arrives.
|
|
4036
|
+
- **Stop is always accepted.** Answer `VacationStoppedV1` `Accepted` even when nothing runs.
|
|
4037
|
+
- **Precedence.** While a vacation heating state is active the energy manager sends no `HeatpumpProgramSwitchV3` and no `HeatpumpControlV3` room action for the heatpump; while `heatpump.domesticHotWater.pause` is set, no hot water boosts.
|
|
4038
|
+
- **Validation** (run by the base class before the handlers, or `validateVacationStarted(data, metadata)` / `validateVacationStopped(data)` directly): malformed data is `Rejected`; a `heatpump.heating` mode whose feature is missing, a lowered target outside `vacationLoweredRangeC` or `heatpump.domesticHotWater.pause: true` without `VacationHotWaterPause` is `NotSupported`. A stop is only rejected when malformed.
|
|
4039
|
+
|
|
3999
4040
|
#### HeatpumpFlexibilityAnnouncementV3
|
|
4000
4041
|
|
|
4001
4042
|
The heatpump's offer, in the vocabulary of `HeatpumpControlV3`: per zone (`target` + `index`), how far it can be raised right now (`maxDeltaK` above `setpointC`, room in Kelvin of room temperature) and the smallest raise that actually starts it (`minDeltaK`, e.g. the remaining hysteresis), with which heat source at what power (`heatSources`: compressor and heating rod as separate entries, each with a `power` band and an optional `estimatedElectricalKwh` for the full raise), and when (`earliestStartIso` / `latestStartIso`, `minDurationMinutes`). An optional `required: {deltaK, byIso}` turns a "may" into a "must, by then" — e.g. hot water dropping below comfort at 18:00. Declare `FlexibilityAnnouncementV3` on the heatpump; for such heatpumps it supersedes the generic `ApplianceFlexibilityAnnouncementV1` / `V2`.
|
|
@@ -4032,6 +4073,9 @@ Beyond tanks and circuits, the heatpump metadata (`EnyoHeatpumpApplianceMetadata
|
|
|
4032
4073
|
| `RoomTemperature` | `heatingCircuits[].roomTemperatureC` in `HeatpumpTemperaturesUpdateV1`; where it is measured goes in `heatingCircuits[].roomTemperatureSource` (`None` / `Internal` / `External`) and, for an enyo sensor, `roomTemperatureSensor` |
|
|
4033
4074
|
| `RoomTemperatureInput` | The heatpump accepts measured room temperatures via `SetHeatpumpRoomTemperatureV1` |
|
|
4034
4075
|
| `HeatingCurveOffset` | The heatpump's app shifts a circuit's curve via `SetHeatpumpHeatingCurveOffsetV1`. `heatingCircuits[].heatingCurveOffsetRangeK` (allowed range, must contain 0), `activeHeatingCurveOffsetK` (effective shift read back) and `initialHeatingCurve` (the curve before the first shift) |
|
|
4076
|
+
| `VacationFrostProtection` | The heatpump's app handles `VacationStartedV1` with `heatpump.heating.mode` `FrostProtection` — the device's own frost protection while the customer is away |
|
|
4077
|
+
| `VacationLowered` | The heatpump's app handles `VacationStartedV1` with `heatpump.heating.mode` `Lowered`. `vacationLoweredRangeC` (required) — the setpoints (°C) the device can hold for all its circuits |
|
|
4078
|
+
| `VacationHotWaterPause` | The heatpump's app handles `VacationStartedV1` with `heatpump.domesticHotWater.pause: true`. Declare it only if legionella protection keeps running |
|
|
4035
4079
|
| — | `heatingCircuits[].curveOptimization` — the customer's choice for the room-temperature based curve optimisation: `enabled` (off by default) and `roomIds` (the rooms the circuit heats, see `useRoom()`). Customer-owned; integrations only read it |
|
|
4036
4080
|
| — | `building.recommendedHeatingCurve` — the curve the building actually needs, learned from the days the room held its target |
|
|
4037
4081
|
| `PvSurplusSignal` | The heatpump takes `availablePowerW` of `HeatpumpControlV3` as a PV surplus signal (e.g. Lambda register 102). Supersedes the deprecated `AvailablePowerAnnouncement` |
|
package/dist/cjs/energy-app.cjs
CHANGED
|
@@ -578,6 +578,19 @@ class EnergyApp {
|
|
|
578
578
|
useRoomTemperatures() {
|
|
579
579
|
return this.energyAppSdk.useRoomTemperatures();
|
|
580
580
|
}
|
|
581
|
+
/**
|
|
582
|
+
* Gets the Vacations API for reading the customer's absences (vacation
|
|
583
|
+
* mode): departure and return, what the heatpump heats to meanwhile,
|
|
584
|
+
* whether hot water pauses, whether to pre-heat for the return and how the
|
|
585
|
+
* battery is used. Read them with `getVacations()` and react to changes
|
|
586
|
+
* with `listenForVacationsChanged()`.
|
|
587
|
+
* @returns The Vacations API instance
|
|
588
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EnergyManager`
|
|
589
|
+
* permission is not granted.
|
|
590
|
+
*/
|
|
591
|
+
useVacations() {
|
|
592
|
+
return this.energyAppSdk.useVacations();
|
|
593
|
+
}
|
|
581
594
|
/**
|
|
582
595
|
* Gets the Timeseries Store API for storing the app's own timeseries —
|
|
583
596
|
* numbers and text — and querying them back raw or aggregated by the host.
|
|
@@ -55,6 +55,7 @@ import { EnergyAppWebhook } from "./packages/energy-app-webhook.cjs";
|
|
|
55
55
|
import { EnergyAppSubscriptions } from "./packages/energy-app-subscriptions.cjs";
|
|
56
56
|
import { EnergyAppRoom } from "./packages/energy-app-room.cjs";
|
|
57
57
|
import { EnergyAppRoomTemperatures } from "./packages/energy-app-room-temperatures.cjs";
|
|
58
|
+
import { EnergyAppVacations } from "./packages/energy-app-vacations.cjs";
|
|
58
59
|
import { EnergyAppTimeseriesStore } from "./packages/energy-app-timeseries-store.cjs";
|
|
59
60
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
60
61
|
/**
|
|
@@ -494,6 +495,17 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
|
|
|
494
495
|
* permission is not granted.
|
|
495
496
|
*/
|
|
496
497
|
useRoomTemperatures(): EnergyAppRoomTemperatures;
|
|
498
|
+
/**
|
|
499
|
+
* Gets the Vacations API for reading the customer's absences (vacation
|
|
500
|
+
* mode): departure and return, what the heatpump heats to meanwhile,
|
|
501
|
+
* whether hot water pauses, whether to pre-heat for the return and how the
|
|
502
|
+
* battery is used. Read them with `getVacations()` and react to changes
|
|
503
|
+
* with `listenForVacationsChanged()`.
|
|
504
|
+
* @returns The Vacations API instance
|
|
505
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `EnergyManager`
|
|
506
|
+
* permission is not granted.
|
|
507
|
+
*/
|
|
508
|
+
useVacations(): EnergyAppVacations;
|
|
497
509
|
/**
|
|
498
510
|
* Gets the Timeseries Store API for storing the app's own timeseries —
|
|
499
511
|
* numbers and text — and querying them back raw or aggregated by the host.
|
|
@@ -54,6 +54,7 @@ import { EnergyAppWebhook } from "./packages/energy-app-webhook.cjs";
|
|
|
54
54
|
import { EnergyAppSubscriptions } from "./packages/energy-app-subscriptions.cjs";
|
|
55
55
|
import { EnergyAppRoom } from "./packages/energy-app-room.cjs";
|
|
56
56
|
import { EnergyAppRoomTemperatures } from "./packages/energy-app-room-temperatures.cjs";
|
|
57
|
+
import { EnergyAppVacations } from "./packages/energy-app-vacations.cjs";
|
|
57
58
|
import { EnergyAppTimeseriesStore } from "./packages/energy-app-timeseries-store.cjs";
|
|
58
59
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
59
60
|
export declare enum EnergyAppStateEnum {
|
|
@@ -205,6 +206,8 @@ export interface EnyoEnergyAppSdk {
|
|
|
205
206
|
useRoom: () => EnergyAppRoom;
|
|
206
207
|
/** Get the Room Temperatures API for reading the current and past temperatures measured in the rooms of the home */
|
|
207
208
|
useRoomTemperatures: () => EnergyAppRoomTemperatures;
|
|
209
|
+
/** Get the Vacations API for reading the absences (vacation mode) the customer planned in the enyo app */
|
|
210
|
+
useVacations: () => EnergyAppVacations;
|
|
208
211
|
/** Get the Timeseries Store API for storing the app's own number and text timeseries and querying them raw or aggregated */
|
|
209
212
|
useTimeseriesStore: () => EnergyAppTimeseriesStore;
|
|
210
213
|
}
|
|
@@ -6,6 +6,8 @@ exports.validateHeatpumpControlAction = validateHeatpumpControlAction;
|
|
|
6
6
|
exports.computeHeatpumpSetpointRaise = computeHeatpumpSetpointRaise;
|
|
7
7
|
exports.validateHeatpumpProgramSwitch = validateHeatpumpProgramSwitch;
|
|
8
8
|
exports.validateHeatpumpHeatingCurveOffset = validateHeatpumpHeatingCurveOffset;
|
|
9
|
+
exports.validateVacationStarted = validateVacationStarted;
|
|
10
|
+
exports.validateVacationStopped = validateVacationStopped;
|
|
9
11
|
const enyo_data_bus_value_js_1 = require("../../types/enyo-data-bus-value.cjs");
|
|
10
12
|
const enyo_heatpump_appliance_js_1 = require("../../types/enyo-heatpump-appliance.cjs");
|
|
11
13
|
/**
|
|
@@ -345,3 +347,127 @@ function validateHeatpumpHeatingCurveOffset(data, metadata) {
|
|
|
345
347
|
}
|
|
346
348
|
return { answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted };
|
|
347
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Whether a value is a parsable ISO 8601 timestamp.
|
|
352
|
+
*
|
|
353
|
+
* @param value - The value to check.
|
|
354
|
+
* @returns `true` for a string `Date.parse` understands.
|
|
355
|
+
*/
|
|
356
|
+
function isIsoTimestamp(value) {
|
|
357
|
+
return typeof value === 'string' && Number.isFinite(Date.parse(value));
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Validates a `VacationStartedV1` against the heatpump's metadata and returns
|
|
361
|
+
* the answer it deserves.
|
|
362
|
+
*
|
|
363
|
+
* Used by `HeatpumpIntegrationEnergyApp` before it calls
|
|
364
|
+
* `handleVacationStarted`; an integration handling the message itself can call
|
|
365
|
+
* it too, and the energy manager can use it to decide between the vacation
|
|
366
|
+
* message and the eco fallback.
|
|
367
|
+
*
|
|
368
|
+
* Rules, in order — the first that fails decides:
|
|
369
|
+
* 1. Malformed data (**Rejected**): `vacationId` is not a non-empty string,
|
|
370
|
+
* `returnIso` is not an ISO 8601 timestamp, `heatpump.heating` has an
|
|
371
|
+
* unknown `mode` or a non-finite `targetTemperatureC`,
|
|
372
|
+
* `heatpump.domesticHotWater.pause` is not a boolean, or `battery` has a
|
|
373
|
+
* non-boolean `gridServing` or a `minSocAtReturnPercent` outside 0…100.
|
|
374
|
+
* 2. `heatpump.heating` asks for a mode whose feature is missing —
|
|
375
|
+
* `VacationFrostProtection` or `VacationLowered` (**NotSupported**).
|
|
376
|
+
* 3. A lowered `targetTemperatureC` outside `vacationLoweredRangeC`, or no
|
|
377
|
+
* range declared (**NotSupported**).
|
|
378
|
+
* 4. `heatpump.domesticHotWater.pause: true` without `VacationHotWaterPause`
|
|
379
|
+
* (**NotSupported**).
|
|
380
|
+
*
|
|
381
|
+
* The `battery` part is only checked for its shape — heatpumps ignore it.
|
|
382
|
+
*
|
|
383
|
+
* Without metadata only rule 1 can be checked.
|
|
384
|
+
*
|
|
385
|
+
* @param data - The message's data.
|
|
386
|
+
* @param metadata - The heatpump's metadata, if known.
|
|
387
|
+
* @returns The answer: `Accepted` when every rule holds.
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```typescript
|
|
391
|
+
* const result = validateVacationStarted(message.data, appliance.heatpump);
|
|
392
|
+
* if (result.answer !== EnyoCommandAcknowledgeAnswerEnum.Accepted) console.warn(result.rejectionReason);
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
function validateVacationStarted(data, metadata) {
|
|
396
|
+
const rejected = (rejectionReason) => ({ answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, rejectionReason });
|
|
397
|
+
const notSupported = (rejectionReason) => ({ answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.NotSupported, rejectionReason });
|
|
398
|
+
if (typeof data.vacationId !== 'string' || data.vacationId.length === 0) {
|
|
399
|
+
return rejected(`vacationId must be a non-empty string; got ${data.vacationId}.`);
|
|
400
|
+
}
|
|
401
|
+
if (!isIsoTimestamp(data.returnIso))
|
|
402
|
+
return rejected(`returnIso must be an ISO 8601 timestamp; got ${data.returnIso}.`);
|
|
403
|
+
const heating = data.heatpump?.heating;
|
|
404
|
+
if (heating) {
|
|
405
|
+
if (!Object.values(enyo_data_bus_value_js_1.EnyoVacationHeatingEnum).includes(heating.mode)) {
|
|
406
|
+
return rejected(`heatpump.heating.mode must be one of ${Object.values(enyo_data_bus_value_js_1.EnyoVacationHeatingEnum).join(', ')}; got ${heating.mode}.`);
|
|
407
|
+
}
|
|
408
|
+
if (typeof heating.targetTemperatureC !== 'number' || !Number.isFinite(heating.targetTemperatureC)) {
|
|
409
|
+
return rejected(`heatpump.heating.targetTemperatureC must be a finite number; got ${heating.targetTemperatureC}.`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const hotWater = data.heatpump?.domesticHotWater;
|
|
413
|
+
if (hotWater && typeof hotWater.pause !== 'boolean') {
|
|
414
|
+
return rejected(`heatpump.domesticHotWater.pause must be a boolean; got ${hotWater.pause}.`);
|
|
415
|
+
}
|
|
416
|
+
const battery = data.battery;
|
|
417
|
+
if (battery) {
|
|
418
|
+
if (typeof battery.gridServing !== 'boolean') {
|
|
419
|
+
return rejected(`battery.gridServing must be a boolean; got ${battery.gridServing}.`);
|
|
420
|
+
}
|
|
421
|
+
const soc = battery.minSocAtReturnPercent;
|
|
422
|
+
if (typeof soc !== 'number' || !Number.isFinite(soc) || soc < 0 || soc > 100) {
|
|
423
|
+
return rejected(`battery.minSocAtReturnPercent must be a number between 0 and 100; got ${soc}.`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (!metadata)
|
|
427
|
+
return { answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted };
|
|
428
|
+
const features = metadata.availableFeatures ?? [];
|
|
429
|
+
if (heating?.mode === enyo_data_bus_value_js_1.EnyoVacationHeatingEnum.FrostProtection
|
|
430
|
+
&& !features.includes(enyo_heatpump_appliance_js_1.EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationFrostProtection)) {
|
|
431
|
+
return notSupported('The heatpump does not support vacation frost protection (no VacationFrostProtection).');
|
|
432
|
+
}
|
|
433
|
+
if (heating?.mode === enyo_data_bus_value_js_1.EnyoVacationHeatingEnum.Lowered) {
|
|
434
|
+
if (!features.includes(enyo_heatpump_appliance_js_1.EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationLowered)) {
|
|
435
|
+
return notSupported('The heatpump does not support a lowered vacation temperature (no VacationLowered).');
|
|
436
|
+
}
|
|
437
|
+
const range = metadata.vacationLoweredRangeC;
|
|
438
|
+
if (!range)
|
|
439
|
+
return notSupported('The heatpump declares no vacationLoweredRangeC.');
|
|
440
|
+
if (heating.targetTemperatureC < range.minC || heating.targetTemperatureC > range.maxC) {
|
|
441
|
+
return notSupported(`heatpump.heating.targetTemperatureC ${heating.targetTemperatureC} °C is outside vacationLoweredRangeC ${range.minC}…${range.maxC} °C.`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (hotWater?.pause && !features.includes(enyo_heatpump_appliance_js_1.EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationHotWaterPause)) {
|
|
445
|
+
return notSupported('The heatpump does not support pausing hot water while away (no VacationHotWaterPause).');
|
|
446
|
+
}
|
|
447
|
+
return { answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted };
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Validates a `VacationStoppedV1` and returns the answer it deserves. It is
|
|
451
|
+
* only `Rejected` when malformed (`vacationId` not a non-empty string) — a
|
|
452
|
+
* stop must be accepted even when nothing runs, so it needs no metadata.
|
|
453
|
+
*
|
|
454
|
+
* Used by `HeatpumpIntegrationEnergyApp` before it calls
|
|
455
|
+
* `handleVacationStopped`.
|
|
456
|
+
*
|
|
457
|
+
* @param data - The message's data.
|
|
458
|
+
* @returns The answer: `Accepted` unless the data is malformed.
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```typescript
|
|
462
|
+
* const result = validateVacationStopped(message.data);
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
function validateVacationStopped(data) {
|
|
466
|
+
if (typeof data.vacationId !== 'string' || data.vacationId.length === 0) {
|
|
467
|
+
return {
|
|
468
|
+
answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected,
|
|
469
|
+
rejectionReason: `vacationId must be a non-empty string; got ${data.vacationId}.`,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
return { answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted };
|
|
473
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EnyoCommandAcknowledgeActionResult, EnyoCommandAcknowledgeAnswerEnum, EnyoDataBusHeatpumpFlexibilityAnnouncementV3, EnyoDataBusHeatpumpProgramSwitchV3, EnyoDataBusSetHeatpumpHeatingCurveOffsetV1, EnyoHeatpumpControlAction, EnyoHeatpumpControlTargetEnum, EnyoPowerSourceShare } from '../../types/enyo-data-bus-value.cjs';
|
|
1
|
+
import { EnyoCommandAcknowledgeActionResult, EnyoCommandAcknowledgeAnswerEnum, EnyoDataBusHeatpumpFlexibilityAnnouncementV3, EnyoDataBusHeatpumpProgramSwitchV3, EnyoDataBusSetHeatpumpHeatingCurveOffsetV1, EnyoDataBusVacationStartedV1, EnyoDataBusVacationStoppedV1, EnyoHeatpumpControlAction, EnyoHeatpumpControlTargetEnum, EnyoPowerSourceShare } from '../../types/enyo-data-bus-value.cjs';
|
|
2
2
|
import { EnyoHeatpumpApplianceAvailableFeaturesEnum, EnyoHeatpumpApplianceMetadata } from '../../types/enyo-heatpump-appliance.cjs';
|
|
3
3
|
/**
|
|
4
4
|
* The overheating feature each `HeatpumpControlV3` target needs — and that a
|
|
@@ -212,3 +212,64 @@ export interface HeatpumpHeatingCurveOffsetValidationResult {
|
|
|
212
212
|
* ```
|
|
213
213
|
*/
|
|
214
214
|
export declare function validateHeatpumpHeatingCurveOffset(data: EnyoDataBusSetHeatpumpHeatingCurveOffsetV1['data'], metadata: EnyoHeatpumpApplianceMetadata | undefined): HeatpumpHeatingCurveOffsetValidationResult;
|
|
215
|
+
/** The answer {@link validateVacationStarted} or {@link validateVacationStopped} gives. */
|
|
216
|
+
export interface HeatpumpVacationValidationResult {
|
|
217
|
+
/** `Accepted` when every rule holds. */
|
|
218
|
+
answer: EnyoCommandAcknowledgeAnswerEnum;
|
|
219
|
+
/** Why the command was rejected or is not supported. */
|
|
220
|
+
rejectionReason?: string;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Validates a `VacationStartedV1` against the heatpump's metadata and returns
|
|
224
|
+
* the answer it deserves.
|
|
225
|
+
*
|
|
226
|
+
* Used by `HeatpumpIntegrationEnergyApp` before it calls
|
|
227
|
+
* `handleVacationStarted`; an integration handling the message itself can call
|
|
228
|
+
* it too, and the energy manager can use it to decide between the vacation
|
|
229
|
+
* message and the eco fallback.
|
|
230
|
+
*
|
|
231
|
+
* Rules, in order — the first that fails decides:
|
|
232
|
+
* 1. Malformed data (**Rejected**): `vacationId` is not a non-empty string,
|
|
233
|
+
* `returnIso` is not an ISO 8601 timestamp, `heatpump.heating` has an
|
|
234
|
+
* unknown `mode` or a non-finite `targetTemperatureC`,
|
|
235
|
+
* `heatpump.domesticHotWater.pause` is not a boolean, or `battery` has a
|
|
236
|
+
* non-boolean `gridServing` or a `minSocAtReturnPercent` outside 0…100.
|
|
237
|
+
* 2. `heatpump.heating` asks for a mode whose feature is missing —
|
|
238
|
+
* `VacationFrostProtection` or `VacationLowered` (**NotSupported**).
|
|
239
|
+
* 3. A lowered `targetTemperatureC` outside `vacationLoweredRangeC`, or no
|
|
240
|
+
* range declared (**NotSupported**).
|
|
241
|
+
* 4. `heatpump.domesticHotWater.pause: true` without `VacationHotWaterPause`
|
|
242
|
+
* (**NotSupported**).
|
|
243
|
+
*
|
|
244
|
+
* The `battery` part is only checked for its shape — heatpumps ignore it.
|
|
245
|
+
*
|
|
246
|
+
* Without metadata only rule 1 can be checked.
|
|
247
|
+
*
|
|
248
|
+
* @param data - The message's data.
|
|
249
|
+
* @param metadata - The heatpump's metadata, if known.
|
|
250
|
+
* @returns The answer: `Accepted` when every rule holds.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```typescript
|
|
254
|
+
* const result = validateVacationStarted(message.data, appliance.heatpump);
|
|
255
|
+
* if (result.answer !== EnyoCommandAcknowledgeAnswerEnum.Accepted) console.warn(result.rejectionReason);
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
export declare function validateVacationStarted(data: EnyoDataBusVacationStartedV1['data'], metadata: EnyoHeatpumpApplianceMetadata | undefined): HeatpumpVacationValidationResult;
|
|
259
|
+
/**
|
|
260
|
+
* Validates a `VacationStoppedV1` and returns the answer it deserves. It is
|
|
261
|
+
* only `Rejected` when malformed (`vacationId` not a non-empty string) — a
|
|
262
|
+
* stop must be accepted even when nothing runs, so it needs no metadata.
|
|
263
|
+
*
|
|
264
|
+
* Used by `HeatpumpIntegrationEnergyApp` before it calls
|
|
265
|
+
* `handleVacationStopped`.
|
|
266
|
+
*
|
|
267
|
+
* @param data - The message's data.
|
|
268
|
+
* @returns The answer: `Accepted` unless the data is malformed.
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```typescript
|
|
272
|
+
* const result = validateVacationStopped(message.data);
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
export declare function validateVacationStopped(data: EnyoDataBusVacationStoppedV1['data']): HeatpumpVacationValidationResult;
|
|
@@ -450,6 +450,9 @@ function assertValidHeatpumpTimeProgram(program) {
|
|
|
450
450
|
* - Heating curve offset: errors from {@link checkHeatingCurveOffset}.
|
|
451
451
|
* Warning: `HeatingCurveOffset` is declared but no circuit has a
|
|
452
452
|
* `heatingCurveOffsetRangeK` — no offset other than `0` could be accepted.
|
|
453
|
+
* - Error: `VacationLowered` is declared without a `vacationLoweredRangeC`,
|
|
454
|
+
* a `vacationLoweredRangeC` is set without `VacationLowered`, or the range
|
|
455
|
+
* is not finite or has `minC` above `maxC`.
|
|
453
456
|
* - SG Ready relays: errors and warnings from {@link checkSgReadyRelays}.
|
|
454
457
|
*
|
|
455
458
|
* @param metadata - The heatpump metadata to validate.
|
|
@@ -505,6 +508,22 @@ function validateHeatpumpAdvancedMetadata(metadata) {
|
|
|
505
508
|
&& !circuits.some((c) => c.heatingCurveOffsetRangeK)) {
|
|
506
509
|
warnings.push('HeatingCurveOffset is declared but no heating circuit has a heatingCurveOffsetRangeK — only offset 0 could be accepted');
|
|
507
510
|
}
|
|
511
|
+
const declaresVacationLowered = features.includes(enyo_heatpump_appliance_js_1.EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationLowered);
|
|
512
|
+
const vacationRange = metadata.vacationLoweredRangeC;
|
|
513
|
+
if (declaresVacationLowered && !vacationRange) {
|
|
514
|
+
errors.push('VacationLowered is declared but vacationLoweredRangeC is missing');
|
|
515
|
+
}
|
|
516
|
+
if (vacationRange) {
|
|
517
|
+
if (!declaresVacationLowered) {
|
|
518
|
+
errors.push('vacationLoweredRangeC is set but the heatpump does not declare VacationLowered');
|
|
519
|
+
}
|
|
520
|
+
if (!isFiniteNumber(vacationRange.minC) || !isFiniteNumber(vacationRange.maxC)) {
|
|
521
|
+
errors.push('vacationLoweredRangeC.minC and maxC must be finite numbers');
|
|
522
|
+
}
|
|
523
|
+
else if (vacationRange.minC > vacationRange.maxC) {
|
|
524
|
+
errors.push(`vacationLoweredRangeC ${vacationRange.minC}…${vacationRange.maxC} °C has minC above maxC`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
508
527
|
for (const circuit of circuits) {
|
|
509
528
|
const prefix = `heatingCircuits[index=${circuit.index}]`;
|
|
510
529
|
if (circuit.heatingCurve)
|
|
@@ -117,9 +117,12 @@ export declare function assertValidHeatpumpTimeProgram(program: EnyoHeatpumpTime
|
|
|
117
117
|
* - Heating curve offset: errors from {@link checkHeatingCurveOffset}.
|
|
118
118
|
* Warning: `HeatingCurveOffset` is declared but no circuit has a
|
|
119
119
|
* `heatingCurveOffsetRangeK` — no offset other than `0` could be accepted.
|
|
120
|
+
* - Error: `VacationLowered` is declared without a `vacationLoweredRangeC`,
|
|
121
|
+
* a `vacationLoweredRangeC` is set without `VacationLowered`, or the range
|
|
122
|
+
* is not finite or has `minC` above `maxC`.
|
|
120
123
|
* - SG Ready relays: errors and warnings from {@link checkSgReadyRelays}.
|
|
121
124
|
*
|
|
122
125
|
* @param metadata - The heatpump metadata to validate.
|
|
123
126
|
* @returns The validation result.
|
|
124
127
|
*/
|
|
125
|
-
export declare function validateHeatpumpAdvancedMetadata(metadata: Pick<EnyoHeatpumpApplianceMetadata, 'availableFeatures' | 'heatingCircuits' | 'domesticHotWater' | 'building'> & Partial<Pick<EnyoHeatpumpApplianceMetadata, 'sgReadyRelays' | 'additionalDevices'>>): HeatpumpMetadataValidationResult;
|
|
128
|
+
export declare function validateHeatpumpAdvancedMetadata(metadata: Pick<EnyoHeatpumpApplianceMetadata, 'availableFeatures' | 'heatingCircuits' | 'domesticHotWater' | 'building'> & Partial<Pick<EnyoHeatpumpApplianceMetadata, 'sgReadyRelays' | 'additionalDevices' | 'vacationLoweredRangeC'>>): HeatpumpMetadataValidationResult;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -205,6 +205,8 @@ __exportStar(require("./types/enyo-room.cjs"), exports);
|
|
|
205
205
|
__exportStar(require("./packages/energy-app-room.cjs"), exports);
|
|
206
206
|
__exportStar(require("./types/enyo-room-temperature.cjs"), exports);
|
|
207
207
|
__exportStar(require("./packages/energy-app-room-temperatures.cjs"), exports);
|
|
208
|
+
__exportStar(require("./types/enyo-vacation.cjs"), exports);
|
|
209
|
+
__exportStar(require("./packages/energy-app-vacations.cjs"), exports);
|
|
208
210
|
__exportStar(require("./implementations/subscriptions/define-subscription-plan.cjs"), exports);
|
|
209
211
|
__exportStar(require("./implementations/subscriptions/subscription-plan-validators.cjs"), exports);
|
|
210
212
|
__exportStar(require("./implementations/subscriptions/subscription-offer-validators.cjs"), exports);
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -189,6 +189,8 @@ export * from './types/enyo-room.cjs';
|
|
|
189
189
|
export * from './packages/energy-app-room.cjs';
|
|
190
190
|
export * from './types/enyo-room-temperature.cjs';
|
|
191
191
|
export * from './packages/energy-app-room-temperatures.cjs';
|
|
192
|
+
export * from './types/enyo-vacation.cjs';
|
|
193
|
+
export * from './packages/energy-app-vacations.cjs';
|
|
192
194
|
export * from './implementations/subscriptions/define-subscription-plan.cjs';
|
|
193
195
|
export * from './implementations/subscriptions/subscription-plan-validators.cjs';
|
|
194
196
|
export * from './implementations/subscriptions/subscription-offer-validators.cjs';
|
|
@@ -78,6 +78,15 @@ function combineHeatpumpControlAnswers(answers) {
|
|
|
78
78
|
* {@link handleHeatpumpHeatingCurveOffset} and declare
|
|
79
79
|
* {@link EnyoHeatpumpApplianceAvailableFeaturesEnum.HeatingCurveOffset}).
|
|
80
80
|
* The base class validates it with {@link checkHeatingCurveOffset} first.
|
|
81
|
+
* - `VacationStartedV1` / `VacationStoppedV1` — the customer is away: heat
|
|
82
|
+
* with frost protection or lowered and / or pause hot water, then restore
|
|
83
|
+
* everything (optional: override {@link handleVacationStarted} /
|
|
84
|
+
* {@link handleVacationStopped} and declare
|
|
85
|
+
* {@link EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationFrostProtection},
|
|
86
|
+
* {@link EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationLowered} and / or
|
|
87
|
+
* {@link EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationHotWaterPause}).
|
|
88
|
+
* The base class validates them with {@link validateVacationStarted} /
|
|
89
|
+
* {@link validateVacationStopped} first.
|
|
81
90
|
* - `GridOperatorPowerLimitationV1` — §14a EnWG broadcast (handled in base).
|
|
82
91
|
*
|
|
83
92
|
* Publishes `HeatpumpFlexibilityAnnouncementV3` offers via
|
|
@@ -138,6 +147,8 @@ class HeatpumpIntegrationEnergyApp extends integration_energy_app_js_1.Integrati
|
|
|
138
147
|
this.registerCommandHandler(enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.HeatpumpControlCancelV3, (msg) => this.dispatchHeatpumpControlCancel(msg));
|
|
139
148
|
this.registerCommandHandler(enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.HeatpumpProgramSwitchV3, (msg) => this.dispatchHeatpumpProgramSwitch(msg));
|
|
140
149
|
this.registerCommandHandler(enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.SetHeatpumpHeatingCurveOffsetV1, (msg) => this.dispatchHeatpumpHeatingCurveOffset(msg));
|
|
150
|
+
this.registerCommandHandler(enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.VacationStartedV1, (msg) => this.dispatchVacationStarted(msg));
|
|
151
|
+
this.registerCommandHandler(enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.VacationStoppedV1, (msg) => this.dispatchVacationStopped(msg));
|
|
141
152
|
}
|
|
142
153
|
/**
|
|
143
154
|
* Stops the integration, drops every running `HeatpumpControlV3` session
|
|
@@ -558,6 +569,99 @@ class HeatpumpIntegrationEnergyApp extends integration_energy_app_js_1.Integrati
|
|
|
558
569
|
const metadata = await this.resolveHeatpumpMetadata(message.applianceId);
|
|
559
570
|
return (0, heatpump_control_validators_js_1.validateHeatpumpHeatingCurveOffset)(message.data, metadata);
|
|
560
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Handles a `VacationStartedV1` command: the customer is away, so bring the
|
|
574
|
+
* heatpump into the wanted state — heat with frost protection or at a
|
|
575
|
+
* lowered room temperature (`message.data.heatpump.heating`; absent = heat
|
|
576
|
+
* as usual) and pause hot water
|
|
577
|
+
* (`message.data.heatpump.domesticHotWater.pause`). How to realise it is up
|
|
578
|
+
* to the integration; the `battery` part is not meant for heatpumps. Answers `NotSupported` by default. Override
|
|
579
|
+
* it together with {@link handleVacationStopped} and declare the vacation
|
|
580
|
+
* features the device supports
|
|
581
|
+
* ({@link EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationFrostProtection},
|
|
582
|
+
* {@link EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationLowered} with
|
|
583
|
+
* `vacationLoweredRangeC`,
|
|
584
|
+
* {@link EnyoHeatpumpApplianceAvailableFeaturesEnum.VacationHotWaterPause}).
|
|
585
|
+
*
|
|
586
|
+
* What an implementation must do:
|
|
587
|
+
* - Prefer the device's own holiday / absence mode and write
|
|
588
|
+
* `message.data.returnIso` as its end, so everything ends even when the
|
|
589
|
+
* app or the energy manager is not running. Without one, write the
|
|
590
|
+
* setpoints and restore them yourself at `returnIso` if no
|
|
591
|
+
* `VacationStoppedV1` arrives.
|
|
592
|
+
* - Treat the message as idempotent and as the complete wanted state: the
|
|
593
|
+
* energy manager re-sends it whenever the wanted state changes and at
|
|
594
|
+
* least hourly. A message without `heatpump.heating` means heating runs
|
|
595
|
+
* as usual again (e.g. pre-heating for the return); without
|
|
596
|
+
* `heatpump.domesticHotWater` or with `pause: false`, hot water is
|
|
597
|
+
* reheated again.
|
|
598
|
+
* - Keep legionella protection running while hot water is paused.
|
|
599
|
+
* - Answer `Rejected` (with `rejectionReason`) when the state cannot be
|
|
600
|
+
* written right now, e.g. the device is in a manual mode.
|
|
601
|
+
*
|
|
602
|
+
* The base class has already validated the message with
|
|
603
|
+
* {@link validateVacationStarted}: the data is well-formed, the features
|
|
604
|
+
* for the requested heating mode and hot water pause are declared and a
|
|
605
|
+
* lowered target lies inside `vacationLoweredRangeC`.
|
|
606
|
+
*
|
|
607
|
+
* @param _message - The vacation started command.
|
|
608
|
+
* @returns `Accepted` once the state is written, `Rejected` (with
|
|
609
|
+
* `rejectionReason`) when it cannot be written right now,
|
|
610
|
+
* `NotSupported` when the integration does not support vacations.
|
|
611
|
+
*
|
|
612
|
+
* @example
|
|
613
|
+
* ```ts
|
|
614
|
+
* protected async handleVacationStarted(message: EnyoDataBusVacationStartedV1) {
|
|
615
|
+
* const {heatpump, returnIso} = message.data;
|
|
616
|
+
* await this.device.writeHolidayMode({
|
|
617
|
+
* roomSetpointC: heatpump?.heating?.targetTemperatureC,
|
|
618
|
+
* pauseHotWater: heatpump?.domesticHotWater?.pause ?? false,
|
|
619
|
+
* endIso: returnIso,
|
|
620
|
+
* });
|
|
621
|
+
* return {answer: EnyoCommandAcknowledgeAnswerEnum.Accepted};
|
|
622
|
+
* }
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
async handleVacationStarted(_message) {
|
|
626
|
+
return {
|
|
627
|
+
answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.NotSupported,
|
|
628
|
+
rejectionReason: 'Heatpump integration does not support VacationStartedV1',
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Handles a `VacationStoppedV1` command: the vacation is over (return,
|
|
633
|
+
* early return or removed), so restore everything
|
|
634
|
+
* {@link handleVacationStarted} changed and let the heatpump follow its
|
|
635
|
+
* own time program again. Answers `NotSupported` by default; override it
|
|
636
|
+
* together with {@link handleVacationStarted}.
|
|
637
|
+
*
|
|
638
|
+
* An implementation must answer `Accepted` even when nothing runs (e.g.
|
|
639
|
+
* the device already ended its holiday mode at `returnIso`), and
|
|
640
|
+
* `Rejected` (with `rejectionReason`) only when the device cannot be
|
|
641
|
+
* restored right now.
|
|
642
|
+
*
|
|
643
|
+
* The base class has already validated the message with
|
|
644
|
+
* {@link validateVacationStopped}.
|
|
645
|
+
*
|
|
646
|
+
* @param _message - The vacation stopped command.
|
|
647
|
+
* @returns `Accepted` once everything is restored (or nothing ran),
|
|
648
|
+
* `Rejected` (with `rejectionReason`) when it cannot be restored right
|
|
649
|
+
* now, `NotSupported` when the integration does not support vacations.
|
|
650
|
+
*
|
|
651
|
+
* @example
|
|
652
|
+
* ```ts
|
|
653
|
+
* protected async handleVacationStopped(_message: EnyoDataBusVacationStoppedV1) {
|
|
654
|
+
* await this.device.endHolidayMode();
|
|
655
|
+
* return {answer: EnyoCommandAcknowledgeAnswerEnum.Accepted};
|
|
656
|
+
* }
|
|
657
|
+
* ```
|
|
658
|
+
*/
|
|
659
|
+
async handleVacationStopped(_message) {
|
|
660
|
+
return {
|
|
661
|
+
answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.NotSupported,
|
|
662
|
+
rejectionReason: 'Heatpump integration does not support VacationStoppedV1',
|
|
663
|
+
};
|
|
664
|
+
}
|
|
561
665
|
/**
|
|
562
666
|
* Publishes a `HeatpumpValuesUpdateV1` message reporting the heatpump's
|
|
563
667
|
* current operation mode and energy values to the data bus.
|
|
@@ -721,6 +825,23 @@ class HeatpumpIntegrationEnergyApp extends integration_energy_app_js_1.Integrati
|
|
|
721
825
|
}
|
|
722
826
|
return this.handleHeatpumpHeatingCurveOffset(message);
|
|
723
827
|
}
|
|
828
|
+
/** Validates a `VacationStartedV1` against the metadata, then calls {@link handleVacationStarted}. */
|
|
829
|
+
async dispatchVacationStarted(message) {
|
|
830
|
+
const metadata = await this.resolveHeatpumpMetadata(message.applianceId);
|
|
831
|
+
const validation = (0, heatpump_control_validators_js_1.validateVacationStarted)(message.data, metadata);
|
|
832
|
+
if (validation.answer !== enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted) {
|
|
833
|
+
return validation;
|
|
834
|
+
}
|
|
835
|
+
return this.handleVacationStarted(message);
|
|
836
|
+
}
|
|
837
|
+
/** Validates a `VacationStoppedV1`, then calls {@link handleVacationStopped}. */
|
|
838
|
+
async dispatchVacationStopped(message) {
|
|
839
|
+
const validation = (0, heatpump_control_validators_js_1.validateVacationStopped)(message.data);
|
|
840
|
+
if (validation.answer !== enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted) {
|
|
841
|
+
return validation;
|
|
842
|
+
}
|
|
843
|
+
return this.handleVacationStopped(message);
|
|
844
|
+
}
|
|
724
845
|
async dispatchHeatpumpControlCancel(message) {
|
|
725
846
|
const response = await this.handleHeatpumpControlCancel(message);
|
|
726
847
|
if (response.answer !== enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.NotSupported
|