@enyo-energy/energy-app-sdk 1.42.0 → 1.44.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 +76 -1
- package/dist/cjs/energy-app-permission.type.cjs +2 -0
- package/dist/cjs/energy-app-permission.type.d.cts +3 -1
- package/dist/cjs/energy-app.cjs +26 -0
- package/dist/cjs/energy-app.d.cts +24 -0
- package/dist/cjs/enyo-energy-app-sdk.d.cts +6 -0
- package/dist/cjs/implementations/appliances/appliance-manager.cjs +56 -0
- package/dist/cjs/implementations/appliances/appliance-manager.d.cts +34 -1
- package/dist/cjs/implementations/heatpump/heatpump-control-validators.cjs +61 -0
- package/dist/cjs/implementations/heatpump/heatpump-control-validators.d.cts +38 -1
- package/dist/cjs/implementations/heatpump/heatpump-curve-optimization-rooms.cjs +96 -0
- package/dist/cjs/implementations/heatpump/heatpump-curve-optimization-rooms.d.cts +65 -0
- package/dist/cjs/implementations/heatpump/heatpump-heating-curve-offset.cjs +119 -0
- package/dist/cjs/implementations/heatpump/heatpump-heating-curve-offset.d.cts +77 -0
- package/dist/cjs/implementations/heatpump/heatpump-metadata-validators.cjs +52 -0
- package/dist/cjs/implementations/heatpump/heatpump-metadata-validators.d.cts +3 -0
- package/dist/cjs/index.cjs +6 -0
- package/dist/cjs/index.d.cts +6 -0
- package/dist/cjs/integrations/heatpump-integration-energy-app.cjs +89 -0
- package/dist/cjs/integrations/heatpump-integration-energy-app.d.cts +75 -1
- package/dist/cjs/packages/energy-app-room-temperatures.cjs +2 -0
- package/dist/cjs/packages/energy-app-room-temperatures.d.cts +51 -0
- package/dist/cjs/packages/energy-app-room.cjs +2 -0
- package/dist/cjs/packages/energy-app-room.d.cts +59 -0
- package/dist/cjs/types/enyo-data-bus-value.cjs +2 -0
- package/dist/cjs/types/enyo-data-bus-value.d.cts +60 -0
- package/dist/cjs/types/enyo-heatpump-appliance.cjs +28 -1
- package/dist/cjs/types/enyo-heatpump-appliance.d.cts +124 -1
- package/dist/cjs/types/enyo-room-temperature.cjs +2 -0
- package/dist/cjs/types/enyo-room-temperature.d.cts +102 -0
- package/dist/cjs/types/enyo-room.cjs +37 -0
- package/dist/cjs/types/enyo-room.d.cts +60 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/energy-app-permission.type.d.ts +3 -1
- package/dist/energy-app-permission.type.js +2 -0
- package/dist/energy-app.d.ts +24 -0
- package/dist/energy-app.js +26 -0
- package/dist/enyo-energy-app-sdk.d.ts +6 -0
- package/dist/implementations/appliances/appliance-manager.d.ts +34 -1
- package/dist/implementations/appliances/appliance-manager.js +56 -0
- package/dist/implementations/heatpump/heatpump-control-validators.d.ts +38 -1
- package/dist/implementations/heatpump/heatpump-control-validators.js +60 -0
- package/dist/implementations/heatpump/heatpump-curve-optimization-rooms.d.ts +65 -0
- package/dist/implementations/heatpump/heatpump-curve-optimization-rooms.js +89 -0
- package/dist/implementations/heatpump/heatpump-heating-curve-offset.d.ts +77 -0
- package/dist/implementations/heatpump/heatpump-heating-curve-offset.js +113 -0
- package/dist/implementations/heatpump/heatpump-metadata-validators.d.ts +3 -0
- package/dist/implementations/heatpump/heatpump-metadata-validators.js +52 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/integrations/heatpump-integration-energy-app.d.ts +75 -1
- package/dist/integrations/heatpump-integration-energy-app.js +90 -1
- package/dist/packages/energy-app-room-temperatures.d.ts +51 -0
- package/dist/packages/energy-app-room-temperatures.js +1 -0
- package/dist/packages/energy-app-room.d.ts +59 -0
- package/dist/packages/energy-app-room.js +1 -0
- package/dist/types/enyo-data-bus-value.d.ts +60 -0
- package/dist/types/enyo-data-bus-value.js +2 -0
- package/dist/types/enyo-heatpump-appliance.d.ts +124 -1
- package/dist/types/enyo-heatpump-appliance.js +27 -0
- package/dist/types/enyo-room-temperature.d.ts +102 -0
- package/dist/types/enyo-room-temperature.js +1 -0
- package/dist/types/enyo-room.d.ts +60 -0
- package/dist/types/enyo-room.js +34 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -409,6 +409,8 @@ Energy Apps use a granular permissions system to control access to system resour
|
|
|
409
409
|
- **`GridFeeRegister`** / **`GridFeeUse`**: Publish / resolve dynamic grid fees (time-variable network charges)
|
|
410
410
|
- **`CommandLog`**: Record and read back the commands apps issued to appliances
|
|
411
411
|
- **`Subscriptions`**: Read the subscriptions bought for the app on this device
|
|
412
|
+
- **`Room`**: Read the rooms of the home the customer set up in the enyo app
|
|
413
|
+
- **`RoomTemperatures`**: Read the current and past temperatures measured in those rooms
|
|
412
414
|
|
|
413
415
|
#### Site & Identity Permissions
|
|
414
416
|
|
|
@@ -1768,6 +1770,37 @@ const full = await location.getLocation(); // requires LocationC
|
|
|
1768
1770
|
if (full) console.log(`lat=${full.latitude} lon=${full.longitude}`);
|
|
1769
1771
|
```
|
|
1770
1772
|
|
|
1773
|
+
#### `useRoom(): EnergyAppRoom`
|
|
1774
|
+
|
|
1775
|
+
Read the rooms of the home, as the customer set them up in the enyo app: name, the appliances in each, the temperature the customer wants there (`targetTemperatureC`, optional — use `HEATPUMP_CURVE_OPTIMIZATION_DEFAULT_TARGET_C` = 21 °C until set) and, for the heating curve optimisation, the room's `warmth` (`EnyoRoomWarmthEnum`) and `thermostat` (`EnyoRoomThermostatEnum`). Read-only.
|
|
1776
|
+
|
|
1777
|
+
```typescript
|
|
1778
|
+
const rooms = energyApp.useRoom();
|
|
1779
|
+
|
|
1780
|
+
applyRooms(await rooms.getRooms());
|
|
1781
|
+
const circuitRooms = await rooms.getRoomsById(circuit.curveOptimization?.roomIds ?? []);
|
|
1782
|
+
rooms.listenForRoomsChanged((all) => applyRooms(all));
|
|
1783
|
+
```
|
|
1784
|
+
|
|
1785
|
+
Requires the `Room` permission.
|
|
1786
|
+
|
|
1787
|
+
#### `useRoomTemperatures(): EnergyAppRoomTemperatures`
|
|
1788
|
+
|
|
1789
|
+
Read the temperatures measured in the rooms, per sensor — a room can have several. `getCurrentRoomTemperatures(roomIds)` returns the latest reading of each sensor, `getRoomTemperatureHistory({roomIds, startDateIso, endDateIso, resolution})` the past ones in `'1m'` or `'15m'` buckets (average, min, max). There is no listener: follow the sensors named in the current values (`applianceId`, `sensorId`) via `TemperatureSensorValuesUpdateV1` on the data bus.
|
|
1790
|
+
|
|
1791
|
+
```typescript
|
|
1792
|
+
const roomTemperatures = energyApp.useRoomTemperatures();
|
|
1793
|
+
|
|
1794
|
+
const current = await roomTemperatures.getCurrentRoomTemperatures(['living', 'bath']);
|
|
1795
|
+
const history = await roomTemperatures.getRoomTemperatureHistory({
|
|
1796
|
+
roomIds: ['living', 'bath'],
|
|
1797
|
+
startDateIso: '2026-10-27T00:00:00Z',
|
|
1798
|
+
endDateIso: '2026-10-28T00:00:00Z',
|
|
1799
|
+
});
|
|
1800
|
+
```
|
|
1801
|
+
|
|
1802
|
+
Requires the `RoomTemperatures` permission.
|
|
1803
|
+
|
|
1771
1804
|
#### `useGridConnectionPoint(): EnergyAppGridConnectionPoint`
|
|
1772
1805
|
|
|
1773
1806
|
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.
|
|
@@ -3666,12 +3699,13 @@ Drives a heatpump. Manages building / DHW overheating commands and grid-power-av
|
|
|
3666
3699
|
|
|
3667
3700
|
> **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).
|
|
3668
3701
|
|
|
3669
|
-
- **Subscribed commands:** `HeatpumpControlV3`, `HeatpumpControlCancelV3`, `HeatpumpProgramSwitchV3`, `HeatpumpOverheatingV1` (deprecated), `HeatpumpAvailablePowerAnnouncementV1` (deprecated), `SetHeatpumpAvailablePowerV2` (deprecated), `SetHeatpumpRoomTemperatureV1`, `GridOperatorPowerLimitationV1` (broadcast)
|
|
3702
|
+
- **Subscribed commands:** `HeatpumpControlV3`, `HeatpumpControlCancelV3`, `HeatpumpProgramSwitchV3`, `SetHeatpumpHeatingCurveOffsetV1`, `HeatpumpOverheatingV1` (deprecated), `HeatpumpAvailablePowerAnnouncementV1` (deprecated), `SetHeatpumpAvailablePowerV2` (deprecated), `SetHeatpumpRoomTemperatureV1`, `GridOperatorPowerLimitationV1` (broadcast)
|
|
3670
3703
|
- **Implement:** `handleHeatpumpOverheating`, `handleHeatpumpAvailablePowerAnnouncement`, `handleSetHeatpumpAvailablePower`, `handleGridOperatorPowerLimitation`
|
|
3671
3704
|
- **Optional overrides:**
|
|
3672
3705
|
- `handleSetHeatpumpRoomTemperature` — write a measured room temperature into a heating circuit. Answers `NotSupported` by default; override it when the heatpump declares `RoomTemperatureInput`.
|
|
3673
3706
|
- `handleHeatpumpControl` — support `HeatpumpControlV3` (see below). Answers `NotSupported` by default; override it and declare `ControlV3`.
|
|
3674
3707
|
- `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`.
|
|
3708
|
+
- `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`.
|
|
3675
3709
|
- `handleHeatpumpControlCancel` — withdraw a cancelled session on the device. By default `Accepted` for heatpumps declaring `ControlV3`, `NotSupported` otherwise.
|
|
3676
3710
|
- `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.
|
|
3677
3711
|
- `resolveHeatpumpMetadata(applianceId)` — the metadata actions are validated against. Defaults to the stored appliance's `heatpump` metadata.
|
|
@@ -3732,6 +3766,44 @@ Switches one heating circuit or DHW zone to its **comfort** or **eco** setpoint
|
|
|
3732
3766
|
- **Owner's consent for price-driven eco.** An energy manager switches zones to eco for price reasons only while the general setting `heatpumpEcoModeInExpensiveTimes` (and `heatpumpControl`) is `true` — see [General settings](#general-settings).
|
|
3733
3767
|
- **Validation** (run by the base class before the handler, or `validateHeatpumpProgramSwitch(data, metadata)` directly): target `Room` / `Dhw`, a known mode, a valid `untilIso` for comfort / eco (`Rejected`); `ProgramSwitch` declared and the mode listed for the zone (`NotSupported`); a known zone index (`Rejected`).
|
|
3734
3768
|
|
|
3769
|
+
#### SetHeatpumpHeatingCurveOffsetV1
|
|
3770
|
+
|
|
3771
|
+
Shifts the heating curve of one heating circuit in parallel — **permanently**, until the next message. The energy manager uses it to lower the curve as far as the measured room temperature allows (less flow temperature, roughly 2.5 % less heating electricity per K).
|
|
3772
|
+
|
|
3773
|
+
```typescript
|
|
3774
|
+
{
|
|
3775
|
+
heatingCircuitIndex: 0,
|
|
3776
|
+
offsetK: -1.5, // K of flow temperature, relative to the initial curve; 0 restores it
|
|
3777
|
+
}
|
|
3778
|
+
```
|
|
3779
|
+
|
|
3780
|
+
- **Relative to the initial curve, never to the current one.** Before the first offset other than `0`, the integration stores the curve set on the device as `heatingCircuits[].initialHeatingCurve` (`ApplianceManager.rememberHeatpumpInitialHeatingCurve` keeps an already stored one). The same message may therefore arrive twice.
|
|
3781
|
+
- **`offsetK: 0` restores** the initial curve exactly, including vendor parameters, then clears it (`ApplianceManager.clearHeatpumpInitialHeatingCurve`, which also sets `activeHeatingCurveOffsetK` to `0`).
|
|
3782
|
+
- **No expiry.** If the hub is offline nobody could reset it anyway; the stored initial curve is the safety net.
|
|
3783
|
+
- **Vendor units.** Convert to whatever the device shifts in (K of room temperature, steps, …) and report the effective shift in K of flow temperature as `heatingCircuits[].activeHeatingCurveOffsetK`, read back from the device. The energy manager waits for it before the next change.
|
|
3784
|
+
- **Changed by hand.** If the curve read from the device differs from `initialHeatingCurve` shifted by `activeHeatingCurveOffsetK` by more than 0.5 K at any point (`isHeatingCurveManuallyChanged(initial, offsetK, read)`), store the read curve as the new `initialHeatingCurve`, set `activeHeatingCurveOffsetK` to `0` and log it in a plain sentence.
|
|
3785
|
+
- **Alongside `HeatpumpControlV3` and `HeatpumpProgramSwitchV3`.** Boost and eco act on top; the offset is the new base.
|
|
3786
|
+
- **Validation** (run by the base class before the handler via `checkHeatingCurveOffset`, or `validateHeatpumpHeatingCurveOffset(data, metadata)` directly): a valid index and finite `offsetK` (`Rejected`); `HeatingCurveOffset` declared (`NotSupported`); a known circuit and — unless `offsetK` is `0` — within `heatingCurveOffsetRangeK` (`Rejected`).
|
|
3787
|
+
- **Customer settings.** Whether to optimise at all, and which rooms of the home the circuit heats (`roomIds`, whether or not a room has a sensor), live in `heatingCircuits[].curveOptimization`. The rooms themselves come from `useRoom().getRoomsById(roomIds)`, their measured temperatures from `useRoomTemperatures()`; `curveOptimizationRooms(curveOptimization, rooms)` picks the ones the curve is judged by (the warmest rooms behind a manual thermostat, and every room with a self-regulating thermostat or none). A room at its target means the curve is just right when its thermostat is open, but only a room falling below its target shows the curve went too far behind a self-regulating one. Rooms without a sensor are left out; the room furthest below its target counts, and the curve is lowered by at most `HEATPUMP_CURVE_OPTIMIZATION_MAX_LOWERING_K` (3 K). Integrations only read them.
|
|
3788
|
+
|
|
3789
|
+
```typescript
|
|
3790
|
+
protected async handleHeatpumpHeatingCurveOffset(message: EnyoDataBusSetHeatpumpHeatingCurveOffsetV1) {
|
|
3791
|
+
const {heatingCircuitIndex: index, offsetK} = message.data;
|
|
3792
|
+
if (offsetK !== 0) {
|
|
3793
|
+
await this.appliances.rememberHeatpumpInitialHeatingCurve(message.applianceId, index, await this.readCurve(index));
|
|
3794
|
+
}
|
|
3795
|
+
await this.device.writeCurveShift(index, offsetK);
|
|
3796
|
+
if (offsetK === 0) {
|
|
3797
|
+
await this.appliances.clearHeatpumpInitialHeatingCurve(message.applianceId, index);
|
|
3798
|
+
} else {
|
|
3799
|
+
await this.appliances.updateHeatpumpHeatingCircuit(message.applianceId, index, {
|
|
3800
|
+
activeHeatingCurveOffsetK: await this.readEffectiveShiftK(index),
|
|
3801
|
+
});
|
|
3802
|
+
}
|
|
3803
|
+
return {answer: EnyoCommandAcknowledgeAnswerEnum.Accepted};
|
|
3804
|
+
}
|
|
3805
|
+
```
|
|
3806
|
+
|
|
3735
3807
|
#### HeatpumpFlexibilityAnnouncementV3
|
|
3736
3808
|
|
|
3737
3809
|
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`.
|
|
@@ -3767,6 +3839,9 @@ Beyond tanks and circuits, the heatpump metadata (`EnyoHeatpumpApplianceMetadata
|
|
|
3767
3839
|
| `ProgramSwitch` | `heatingCircuits[].supportedProgramModes` / `programModeTemperaturesC` / `activeProgramMode` and the same on `domesticHotWater[]` — which zones can be switched to comfort / eco with `HeatpumpProgramSwitchV3`, at which setpoints, and the mode they run in now |
|
|
3768
3840
|
| `RoomTemperature` | `heatingCircuits[].roomTemperatureC` in `HeatpumpTemperaturesUpdateV1`; where it is measured goes in `heatingCircuits[].roomTemperatureSource` (`None` / `Internal` / `External`) and, for an enyo sensor, `roomTemperatureSensor` |
|
|
3769
3841
|
| `RoomTemperatureInput` | The heatpump accepts measured room temperatures via `SetHeatpumpRoomTemperatureV1` |
|
|
3842
|
+
| `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) |
|
|
3843
|
+
| — | `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 |
|
|
3844
|
+
| — | `building.recommendedHeatingCurve` — the curve the building actually needs, learned from the days the room held its target |
|
|
3770
3845
|
| `PvSurplusSignal` | The heatpump takes `availablePowerW` of `HeatpumpControlV3` as a PV surplus signal (e.g. Lambda register 102). Supersedes the deprecated `AvailablePowerAnnouncement` |
|
|
3771
3846
|
| `ControlV3` | The heatpump's app handles `HeatpumpControlV3` / `HeatpumpControlCancelV3` and reports `HeatpumpControlStatusV3`. The energy manager sends V3 only to heatpumps declaring it. Needs at least one overheating feature to be actionable |
|
|
3772
3847
|
| `FlexibilityAnnouncementV3` | The heatpump publishes `HeatpumpFlexibilityAnnouncementV3` offers; the energy manager plans from them instead of deriving the headroom from metadata. Needs at least one overheating feature to be actionable |
|
|
@@ -69,4 +69,6 @@ var EnergyAppPermissionTypeEnum;
|
|
|
69
69
|
EnergyAppPermissionTypeEnum["VehicleIntegration"] = "VehicleIntegration";
|
|
70
70
|
EnergyAppPermissionTypeEnum["Webhook"] = "Webhook";
|
|
71
71
|
EnergyAppPermissionTypeEnum["Subscriptions"] = "Subscriptions";
|
|
72
|
+
EnergyAppPermissionTypeEnum["Room"] = "Room";
|
|
73
|
+
EnergyAppPermissionTypeEnum["RoomTemperatures"] = "RoomTemperatures";
|
|
72
74
|
})(EnergyAppPermissionTypeEnum || (exports.EnergyAppPermissionTypeEnum = EnergyAppPermissionTypeEnum = {}));
|
|
@@ -64,7 +64,9 @@ export declare enum EnergyAppPermissionTypeEnum {
|
|
|
64
64
|
Calibration = "Calibration",
|
|
65
65
|
VehicleIntegration = "VehicleIntegration",
|
|
66
66
|
Webhook = "Webhook",
|
|
67
|
-
Subscriptions = "Subscriptions"
|
|
67
|
+
Subscriptions = "Subscriptions",
|
|
68
|
+
Room = "Room",
|
|
69
|
+
RoomTemperatures = "RoomTemperatures"
|
|
68
70
|
}
|
|
69
71
|
/**
|
|
70
72
|
* String union of every permission name, derived from
|
package/dist/cjs/energy-app.cjs
CHANGED
|
@@ -552,6 +552,32 @@ class EnergyApp {
|
|
|
552
552
|
useSubscriptions() {
|
|
553
553
|
return this.energyAppSdk.useSubscriptions();
|
|
554
554
|
}
|
|
555
|
+
/**
|
|
556
|
+
* Gets the Room API for reading the rooms of the home: their name, the
|
|
557
|
+
* appliances in them, the temperature the customer wants there and, for
|
|
558
|
+
* the heating curve optimisation, their warmth and thermostat. Read the
|
|
559
|
+
* rooms with `getRooms()` or `getRoomsById()` and react to changes with
|
|
560
|
+
* `listenForRoomsChanged()`.
|
|
561
|
+
* @returns The Room API instance
|
|
562
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `Room` permission is
|
|
563
|
+
* not granted.
|
|
564
|
+
*/
|
|
565
|
+
useRoom() {
|
|
566
|
+
return this.energyAppSdk.useRoom();
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Gets the Room Temperatures API for reading the temperatures measured in
|
|
570
|
+
* the rooms of the home, per sensor: the current values with
|
|
571
|
+
* `getCurrentRoomTemperatures()` and the past ones with
|
|
572
|
+
* `getRoomTemperatureHistory()`. Follow changes via the
|
|
573
|
+
* `TemperatureSensorValuesUpdateV1` messages on the data bus.
|
|
574
|
+
* @returns The Room Temperatures API instance
|
|
575
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `RoomTemperatures`
|
|
576
|
+
* permission is not granted.
|
|
577
|
+
*/
|
|
578
|
+
useRoomTemperatures() {
|
|
579
|
+
return this.energyAppSdk.useRoomTemperatures();
|
|
580
|
+
}
|
|
555
581
|
/**
|
|
556
582
|
* Gets the current SDK version.
|
|
557
583
|
* @returns The semantic version string of the SDK
|
|
@@ -53,6 +53,8 @@ import { EnergyAppCommandLog } from "./packages/energy-app-command-log.cjs";
|
|
|
53
53
|
import { EnergyAppCascade } from "./packages/energy-app-cascade.cjs";
|
|
54
54
|
import { EnergyAppWebhook } from "./packages/energy-app-webhook.cjs";
|
|
55
55
|
import { EnergyAppSubscriptions } from "./packages/energy-app-subscriptions.cjs";
|
|
56
|
+
import { EnergyAppRoom } from "./packages/energy-app-room.cjs";
|
|
57
|
+
import { EnergyAppRoomTemperatures } from "./packages/energy-app-room-temperatures.cjs";
|
|
56
58
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
57
59
|
/**
|
|
58
60
|
* Concrete implementation of {@link EnyoEnergyAppSdk} that delegates every call
|
|
@@ -469,6 +471,28 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
|
|
|
469
471
|
* permission is not granted.
|
|
470
472
|
*/
|
|
471
473
|
useSubscriptions(): EnergyAppSubscriptions;
|
|
474
|
+
/**
|
|
475
|
+
* Gets the Room API for reading the rooms of the home: their name, the
|
|
476
|
+
* appliances in them, the temperature the customer wants there and, for
|
|
477
|
+
* the heating curve optimisation, their warmth and thermostat. Read the
|
|
478
|
+
* rooms with `getRooms()` or `getRoomsById()` and react to changes with
|
|
479
|
+
* `listenForRoomsChanged()`.
|
|
480
|
+
* @returns The Room API instance
|
|
481
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `Room` permission is
|
|
482
|
+
* not granted.
|
|
483
|
+
*/
|
|
484
|
+
useRoom(): EnergyAppRoom;
|
|
485
|
+
/**
|
|
486
|
+
* Gets the Room Temperatures API for reading the temperatures measured in
|
|
487
|
+
* the rooms of the home, per sensor: the current values with
|
|
488
|
+
* `getCurrentRoomTemperatures()` and the past ones with
|
|
489
|
+
* `getRoomTemperatureHistory()`. Follow changes via the
|
|
490
|
+
* `TemperatureSensorValuesUpdateV1` messages on the data bus.
|
|
491
|
+
* @returns The Room Temperatures API instance
|
|
492
|
+
* @throws {EnergyAppPermissionNotGrantedError} If the `RoomTemperatures`
|
|
493
|
+
* permission is not granted.
|
|
494
|
+
*/
|
|
495
|
+
useRoomTemperatures(): EnergyAppRoomTemperatures;
|
|
472
496
|
/**
|
|
473
497
|
* Gets the current SDK version.
|
|
474
498
|
* @returns The semantic version string of the SDK
|
|
@@ -52,6 +52,8 @@ import { EnergyAppCommandLog } from "./packages/energy-app-command-log.cjs";
|
|
|
52
52
|
import { EnergyAppCascade } from "./packages/energy-app-cascade.cjs";
|
|
53
53
|
import { EnergyAppWebhook } from "./packages/energy-app-webhook.cjs";
|
|
54
54
|
import { EnergyAppSubscriptions } from "./packages/energy-app-subscriptions.cjs";
|
|
55
|
+
import { EnergyAppRoom } from "./packages/energy-app-room.cjs";
|
|
56
|
+
import { EnergyAppRoomTemperatures } from "./packages/energy-app-room-temperatures.cjs";
|
|
55
57
|
import { UseFetchOptions } from "./types/enyo-fetch.cjs";
|
|
56
58
|
export declare enum EnergyAppStateEnum {
|
|
57
59
|
Launching = "launching",
|
|
@@ -198,4 +200,8 @@ export interface EnyoEnergyAppSdk {
|
|
|
198
200
|
useWebhook: () => EnergyAppWebhook;
|
|
199
201
|
/** Get the Subscriptions API for reading the subscriptions bought for this app on this device and listening for new or changed subscriptions */
|
|
200
202
|
useSubscriptions: () => EnergyAppSubscriptions;
|
|
203
|
+
/** Get the Room API for reading the rooms of the home the customer set up in the enyo app */
|
|
204
|
+
useRoom: () => EnergyAppRoom;
|
|
205
|
+
/** Get the Room Temperatures API for reading the current and past temperatures measured in the rooms of the home */
|
|
206
|
+
useRoomTemperatures: () => EnergyAppRoomTemperatures;
|
|
201
207
|
}
|
|
@@ -618,6 +618,62 @@ class ApplianceManager {
|
|
|
618
618
|
await this.energyApp.useAppliances().save(this.mergeApplianceData(appliance, { heatpump: { domesticHotWater: zones } }), applianceId);
|
|
619
619
|
await this.primeCacheFromSdk(applianceId);
|
|
620
620
|
}
|
|
621
|
+
/**
|
|
622
|
+
* Remembers the heating curve a circuit had before the first
|
|
623
|
+
* `SetHeatpumpHeatingCurveOffsetV1` changed it, as the circuit's
|
|
624
|
+
* `initialHeatingCurve` — the curve `offsetK: 0` restores. Call it before
|
|
625
|
+
* writing the first offset other than `0`.
|
|
626
|
+
*
|
|
627
|
+
* Keeps an already stored initial curve: every later offset is relative to
|
|
628
|
+
* the curve before the *first* change, so it must not be overwritten with
|
|
629
|
+
* an already shifted one. Works like {@link updateHeatpumpHeatingCircuit}
|
|
630
|
+
* otherwise.
|
|
631
|
+
*
|
|
632
|
+
* Does nothing when the appliance does not exist.
|
|
633
|
+
*
|
|
634
|
+
* @param applianceId The ID of the heatpump appliance
|
|
635
|
+
* @param index The `index` of the heating circuit
|
|
636
|
+
* @param curve The curve currently set on the device
|
|
637
|
+
* @returns `true` when the curve was stored, `false` when an initial curve
|
|
638
|
+
* was already stored or the appliance does not exist
|
|
639
|
+
* @throws {ApplianceManagerDisposedError} when called after {@link dispose}
|
|
640
|
+
*/
|
|
641
|
+
async rememberHeatpumpInitialHeatingCurve(applianceId, index, curve) {
|
|
642
|
+
this.throwIfDisposed();
|
|
643
|
+
const appliance = await this.energyApp.useAppliances().getById(applianceId);
|
|
644
|
+
if (!appliance)
|
|
645
|
+
return false;
|
|
646
|
+
const circuit = appliance.heatpump?.heatingCircuits?.find(c => c.index === index);
|
|
647
|
+
if (circuit?.initialHeatingCurve)
|
|
648
|
+
return false;
|
|
649
|
+
await this.updateHeatpumpHeatingCircuit(applianceId, index, { initialHeatingCurve: curve });
|
|
650
|
+
return true;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Forgets a circuit's `initialHeatingCurve` after the integration restored
|
|
654
|
+
* it on the device (`offsetK: 0`), and sets `activeHeatingCurveOffsetK` to
|
|
655
|
+
* `0`. Other circuits and fields are left untouched.
|
|
656
|
+
*
|
|
657
|
+
* Does nothing when the appliance or the circuit does not exist.
|
|
658
|
+
*
|
|
659
|
+
* @param applianceId The ID of the heatpump appliance
|
|
660
|
+
* @param index The `index` of the heating circuit
|
|
661
|
+
* @throws {ApplianceManagerDisposedError} when called after {@link dispose}
|
|
662
|
+
*/
|
|
663
|
+
async clearHeatpumpInitialHeatingCurve(applianceId, index) {
|
|
664
|
+
this.throwIfDisposed();
|
|
665
|
+
const appliance = await this.energyApp.useAppliances().getById(applianceId);
|
|
666
|
+
if (!appliance)
|
|
667
|
+
return;
|
|
668
|
+
const circuits = [...(appliance.heatpump?.heatingCircuits ?? [])];
|
|
669
|
+
const position = circuits.findIndex(c => c.index === index);
|
|
670
|
+
if (position < 0)
|
|
671
|
+
return;
|
|
672
|
+
const { initialHeatingCurve: _removed, ...rest } = circuits[position];
|
|
673
|
+
circuits[position] = { ...rest, activeHeatingCurveOffsetK: 0 };
|
|
674
|
+
await this.energyApp.useAppliances().save(this.mergeApplianceData(appliance, { heatpump: { heatingCircuits: circuits } }), applianceId);
|
|
675
|
+
await this.primeCacheFromSdk(applianceId);
|
|
676
|
+
}
|
|
621
677
|
/**
|
|
622
678
|
* Refetches an appliance from the SDK and reflects the result in the
|
|
623
679
|
* cache. Used by mutating methods to keep cache reads consistent with
|
|
@@ -3,7 +3,7 @@ import type { EnyoNetworkDevice } from "../../types/enyo-network-device.cjs";
|
|
|
3
3
|
import { EnyoAppliance, EnyoApplianceAvailableFeaturesEnum, EnyoApplianceConnectionType, EnyoApplianceGroup, EnyoApplianceMetadata, EnyoApplianceName, EnyoApplianceStateEnum, EnyoApplianceTopology, EnyoApplianceTypeEnum } from "../../types/enyo-appliance.cjs";
|
|
4
4
|
import type { EnyoApplianceCreatedFilter } from "../../packages/energy-app-appliance.cjs";
|
|
5
5
|
import type { EnyoChargerApplianceMetadata } from "../../types/enyo-charger-appliance.cjs";
|
|
6
|
-
import type { EnyoHeatpumpApplianceDomesticHotWater, EnyoHeatpumpApplianceHeatingCircuit, EnyoHeatpumpApplianceMetadata } from "../../types/enyo-heatpump-appliance.cjs";
|
|
6
|
+
import type { EnyoHeatpumpApplianceDomesticHotWater, EnyoHeatpumpApplianceHeatingCircuit, EnyoHeatpumpApplianceHeatingCurve, EnyoHeatpumpApplianceMetadata } from "../../types/enyo-heatpump-appliance.cjs";
|
|
7
7
|
import type { EnyoBatteryApplianceMetadata } from "../../types/enyo-battery-appliance.cjs";
|
|
8
8
|
import type { EnyoInverterApplianceMetadata } from "../../types/enyo-inverter-appliance.cjs";
|
|
9
9
|
import type { EnyoMeterAppliance } from "../../types/enyo-meter-appliance.cjs";
|
|
@@ -401,6 +401,39 @@ export declare class ApplianceManager {
|
|
|
401
401
|
* @throws {Error} when the zone does not exist and `patch` lacks `targetTemperatureC`
|
|
402
402
|
*/
|
|
403
403
|
updateHeatpumpDomesticHotWater(applianceId: string, index: number, patch: Partial<Omit<EnyoHeatpumpApplianceDomesticHotWater, 'index'>>): Promise<void>;
|
|
404
|
+
/**
|
|
405
|
+
* Remembers the heating curve a circuit had before the first
|
|
406
|
+
* `SetHeatpumpHeatingCurveOffsetV1` changed it, as the circuit's
|
|
407
|
+
* `initialHeatingCurve` — the curve `offsetK: 0` restores. Call it before
|
|
408
|
+
* writing the first offset other than `0`.
|
|
409
|
+
*
|
|
410
|
+
* Keeps an already stored initial curve: every later offset is relative to
|
|
411
|
+
* the curve before the *first* change, so it must not be overwritten with
|
|
412
|
+
* an already shifted one. Works like {@link updateHeatpumpHeatingCircuit}
|
|
413
|
+
* otherwise.
|
|
414
|
+
*
|
|
415
|
+
* Does nothing when the appliance does not exist.
|
|
416
|
+
*
|
|
417
|
+
* @param applianceId The ID of the heatpump appliance
|
|
418
|
+
* @param index The `index` of the heating circuit
|
|
419
|
+
* @param curve The curve currently set on the device
|
|
420
|
+
* @returns `true` when the curve was stored, `false` when an initial curve
|
|
421
|
+
* was already stored or the appliance does not exist
|
|
422
|
+
* @throws {ApplianceManagerDisposedError} when called after {@link dispose}
|
|
423
|
+
*/
|
|
424
|
+
rememberHeatpumpInitialHeatingCurve(applianceId: string, index: number, curve: EnyoHeatpumpApplianceHeatingCurve): Promise<boolean>;
|
|
425
|
+
/**
|
|
426
|
+
* Forgets a circuit's `initialHeatingCurve` after the integration restored
|
|
427
|
+
* it on the device (`offsetK: 0`), and sets `activeHeatingCurveOffsetK` to
|
|
428
|
+
* `0`. Other circuits and fields are left untouched.
|
|
429
|
+
*
|
|
430
|
+
* Does nothing when the appliance or the circuit does not exist.
|
|
431
|
+
*
|
|
432
|
+
* @param applianceId The ID of the heatpump appliance
|
|
433
|
+
* @param index The `index` of the heating circuit
|
|
434
|
+
* @throws {ApplianceManagerDisposedError} when called after {@link dispose}
|
|
435
|
+
*/
|
|
436
|
+
clearHeatpumpInitialHeatingCurve(applianceId: string, index: number): Promise<void>;
|
|
404
437
|
/**
|
|
405
438
|
* Refetches an appliance from the SDK and reflects the result in the
|
|
406
439
|
* cache. Used by mutating methods to keep cache reads consistent with
|
|
@@ -5,6 +5,7 @@ exports.isPvOnlyPowerSources = isPvOnlyPowerSources;
|
|
|
5
5
|
exports.validateHeatpumpControlAction = validateHeatpumpControlAction;
|
|
6
6
|
exports.computeHeatpumpSetpointRaise = computeHeatpumpSetpointRaise;
|
|
7
7
|
exports.validateHeatpumpProgramSwitch = validateHeatpumpProgramSwitch;
|
|
8
|
+
exports.validateHeatpumpHeatingCurveOffset = validateHeatpumpHeatingCurveOffset;
|
|
8
9
|
const enyo_data_bus_value_js_1 = require("../../types/enyo-data-bus-value.cjs");
|
|
9
10
|
const enyo_heatpump_appliance_js_1 = require("../../types/enyo-heatpump-appliance.cjs");
|
|
10
11
|
/**
|
|
@@ -284,3 +285,63 @@ function validateHeatpumpProgramSwitch(data, metadata, nowMs = Date.now()) {
|
|
|
284
285
|
}
|
|
285
286
|
return { answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted };
|
|
286
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Validates a `SetHeatpumpHeatingCurveOffsetV1` against the heatpump's
|
|
290
|
+
* metadata and returns the answer it deserves.
|
|
291
|
+
*
|
|
292
|
+
* Used by `HeatpumpIntegrationEnergyApp` before it calls
|
|
293
|
+
* `handleHeatpumpHeatingCurveOffset`; an integration handling the message
|
|
294
|
+
* itself can call it too.
|
|
295
|
+
*
|
|
296
|
+
* Rules, in order — the first that fails decides:
|
|
297
|
+
* 1. `heatingCircuitIndex` is an integer ≥ 0 and `offsetK` a finite number
|
|
298
|
+
* (**Rejected**).
|
|
299
|
+
* 2. The heatpump does not declare `HeatingCurveOffset` (**NotSupported**).
|
|
300
|
+
* 3. No heating circuit has the message's index (**Rejected**).
|
|
301
|
+
* 4. `offsetK` is not `0` and the circuit has no `heatingCurveOffsetRangeK`
|
|
302
|
+
* or `offsetK` lies outside it (**Rejected**). `0` — restoring the initial
|
|
303
|
+
* curve — is always allowed.
|
|
304
|
+
*
|
|
305
|
+
* Without metadata only rule 1 can be checked.
|
|
306
|
+
*
|
|
307
|
+
* @param data - The message's data.
|
|
308
|
+
* @param metadata - The heatpump's metadata, if known.
|
|
309
|
+
* @returns The answer: `Accepted` when every rule holds.
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* const result = validateHeatpumpHeatingCurveOffset(message.data, appliance.heatpump);
|
|
314
|
+
* if (result.answer !== EnyoCommandAcknowledgeAnswerEnum.Accepted) console.warn(result.rejectionReason);
|
|
315
|
+
* ```
|
|
316
|
+
*/
|
|
317
|
+
function validateHeatpumpHeatingCurveOffset(data, metadata) {
|
|
318
|
+
const rejected = (rejectionReason) => ({ answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, rejectionReason });
|
|
319
|
+
if (!Number.isInteger(data.heatingCircuitIndex) || data.heatingCircuitIndex < 0) {
|
|
320
|
+
return rejected(`heatingCircuitIndex must be an integer ≥ 0; got ${data.heatingCircuitIndex}.`);
|
|
321
|
+
}
|
|
322
|
+
if (typeof data.offsetK !== 'number' || !Number.isFinite(data.offsetK)) {
|
|
323
|
+
return rejected(`offsetK must be a finite number; got ${data.offsetK}.`);
|
|
324
|
+
}
|
|
325
|
+
if (!metadata)
|
|
326
|
+
return { answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted };
|
|
327
|
+
if (!(metadata.availableFeatures ?? []).includes(enyo_heatpump_appliance_js_1.EnyoHeatpumpApplianceAvailableFeaturesEnum.HeatingCurveOffset)) {
|
|
328
|
+
return {
|
|
329
|
+
answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.NotSupported,
|
|
330
|
+
rejectionReason: 'The heatpump does not support heating curve offsets (no HeatingCurveOffset).',
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
const circuit = (metadata.heatingCircuits ?? []).find((c) => c.index === data.heatingCircuitIndex);
|
|
334
|
+
if (!circuit) {
|
|
335
|
+
return rejected(`No heating circuit with index ${data.heatingCircuitIndex}.`);
|
|
336
|
+
}
|
|
337
|
+
if (data.offsetK === 0)
|
|
338
|
+
return { answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted };
|
|
339
|
+
const range = circuit.heatingCurveOffsetRangeK;
|
|
340
|
+
if (!range) {
|
|
341
|
+
return rejected(`Heating circuit ${circuit.index} declares no heatingCurveOffsetRangeK; only offsetK 0 is accepted.`);
|
|
342
|
+
}
|
|
343
|
+
if (data.offsetK < range.minK || data.offsetK > range.maxK) {
|
|
344
|
+
return rejected(`offsetK ${data.offsetK} K is outside the range ${range.minK}…${range.maxK} K of heating circuit ${circuit.index}.`);
|
|
345
|
+
}
|
|
346
|
+
return { answer: enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted };
|
|
347
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EnyoCommandAcknowledgeActionResult, EnyoCommandAcknowledgeAnswerEnum, EnyoDataBusHeatpumpFlexibilityAnnouncementV3, EnyoDataBusHeatpumpProgramSwitchV3, EnyoHeatpumpControlAction, EnyoHeatpumpControlTargetEnum, EnyoPowerSourceShare } from '../../types/enyo-data-bus-value.cjs';
|
|
1
|
+
import { EnyoCommandAcknowledgeActionResult, EnyoCommandAcknowledgeAnswerEnum, EnyoDataBusHeatpumpFlexibilityAnnouncementV3, EnyoDataBusHeatpumpProgramSwitchV3, EnyoDataBusSetHeatpumpHeatingCurveOffsetV1, 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
|
|
@@ -175,3 +175,40 @@ export interface HeatpumpProgramSwitchValidationResult {
|
|
|
175
175
|
* ```
|
|
176
176
|
*/
|
|
177
177
|
export declare function validateHeatpumpProgramSwitch(data: EnyoDataBusHeatpumpProgramSwitchV3['data'], metadata: EnyoHeatpumpApplianceMetadata | undefined, nowMs?: number): HeatpumpProgramSwitchValidationResult;
|
|
178
|
+
/** The answer {@link validateHeatpumpHeatingCurveOffset} gives a `SetHeatpumpHeatingCurveOffsetV1`. */
|
|
179
|
+
export interface HeatpumpHeatingCurveOffsetValidationResult {
|
|
180
|
+
/** `Accepted` when every rule holds. */
|
|
181
|
+
answer: EnyoCommandAcknowledgeAnswerEnum;
|
|
182
|
+
/** Why the offset was rejected or is not supported. */
|
|
183
|
+
rejectionReason?: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Validates a `SetHeatpumpHeatingCurveOffsetV1` against the heatpump's
|
|
187
|
+
* metadata and returns the answer it deserves.
|
|
188
|
+
*
|
|
189
|
+
* Used by `HeatpumpIntegrationEnergyApp` before it calls
|
|
190
|
+
* `handleHeatpumpHeatingCurveOffset`; an integration handling the message
|
|
191
|
+
* itself can call it too.
|
|
192
|
+
*
|
|
193
|
+
* Rules, in order — the first that fails decides:
|
|
194
|
+
* 1. `heatingCircuitIndex` is an integer ≥ 0 and `offsetK` a finite number
|
|
195
|
+
* (**Rejected**).
|
|
196
|
+
* 2. The heatpump does not declare `HeatingCurveOffset` (**NotSupported**).
|
|
197
|
+
* 3. No heating circuit has the message's index (**Rejected**).
|
|
198
|
+
* 4. `offsetK` is not `0` and the circuit has no `heatingCurveOffsetRangeK`
|
|
199
|
+
* or `offsetK` lies outside it (**Rejected**). `0` — restoring the initial
|
|
200
|
+
* curve — is always allowed.
|
|
201
|
+
*
|
|
202
|
+
* Without metadata only rule 1 can be checked.
|
|
203
|
+
*
|
|
204
|
+
* @param data - The message's data.
|
|
205
|
+
* @param metadata - The heatpump's metadata, if known.
|
|
206
|
+
* @returns The answer: `Accepted` when every rule holds.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```typescript
|
|
210
|
+
* const result = validateHeatpumpHeatingCurveOffset(message.data, appliance.heatpump);
|
|
211
|
+
* if (result.answer !== EnyoCommandAcknowledgeAnswerEnum.Accepted) console.warn(result.rejectionReason);
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export declare function validateHeatpumpHeatingCurveOffset(data: EnyoDataBusSetHeatpumpHeatingCurveOffsetV1['data'], metadata: EnyoHeatpumpApplianceMetadata | undefined): HeatpumpHeatingCurveOffsetValidationResult;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Which rooms of a heating circuit the room-temperature based heating curve
|
|
4
|
+
* optimisation is judged by. Shared by the energy manager, which lowers the
|
|
5
|
+
* curve, and the host's app, which tells the customer how to set each room's
|
|
6
|
+
* thermostat — both must agree on it.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.curveWarmthRank = curveWarmthRank;
|
|
10
|
+
exports.isManualRoomThermostat = isManualRoomThermostat;
|
|
11
|
+
exports.curveTopManualRank = curveTopManualRank;
|
|
12
|
+
exports.curveJudgedRoomIds = curveJudgedRoomIds;
|
|
13
|
+
exports.curveOptimizationRooms = curveOptimizationRooms;
|
|
14
|
+
const enyo_room_js_1 = require("../../types/enyo-room.cjs");
|
|
15
|
+
const WARMTH_RANK = {
|
|
16
|
+
[enyo_room_js_1.EnyoRoomWarmthEnum.Cool]: 0,
|
|
17
|
+
[enyo_room_js_1.EnyoRoomWarmthEnum.Normal]: 1,
|
|
18
|
+
[enyo_room_js_1.EnyoRoomWarmthEnum.Warm]: 2,
|
|
19
|
+
};
|
|
20
|
+
const MANUAL_THERMOSTATS = new Set([
|
|
21
|
+
enyo_room_js_1.EnyoRoomThermostatEnum.Steps5,
|
|
22
|
+
enyo_room_js_1.EnyoRoomThermostatEnum.Steps6,
|
|
23
|
+
enyo_room_js_1.EnyoRoomThermostatEnum.CelsiusDial,
|
|
24
|
+
]);
|
|
25
|
+
/**
|
|
26
|
+
* How much warmth a room asks for compared with the other rooms of its
|
|
27
|
+
* circuit: 0 for `cool`, 1 for `normal` (also when absent), 2 for `warm`.
|
|
28
|
+
*
|
|
29
|
+
* @param traits - The room's warmth, if known.
|
|
30
|
+
* @returns The rank.
|
|
31
|
+
*/
|
|
32
|
+
function curveWarmthRank(traits) {
|
|
33
|
+
return WARMTH_RANK[traits?.warmth ?? enyo_room_js_1.EnyoRoomWarmthEnum.Normal];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Whether a thermostat is set by hand: a dial with steps or a dial in °C.
|
|
37
|
+
* Absent counts as the common dial with steps 1–5.
|
|
38
|
+
*
|
|
39
|
+
* @param thermostat - The room's thermostat.
|
|
40
|
+
* @returns `true` for a manual thermostat.
|
|
41
|
+
*/
|
|
42
|
+
function isManualRoomThermostat(thermostat) {
|
|
43
|
+
return thermostat === undefined || MANUAL_THERMOSTATS.has(thermostat);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* How much warmth the most demanding room behind a manual thermostat asks for.
|
|
47
|
+
*
|
|
48
|
+
* @param roomIds - The rooms of the circuit.
|
|
49
|
+
* @param traitsOf - A room's warmth and thermostat.
|
|
50
|
+
* @returns The highest rank, or `undefined` when no room has a manual thermostat.
|
|
51
|
+
*/
|
|
52
|
+
function curveTopManualRank(roomIds, traitsOf) {
|
|
53
|
+
const ranks = roomIds
|
|
54
|
+
.filter((roomId) => isManualRoomThermostat(traitsOf(roomId)?.thermostat))
|
|
55
|
+
.map((roomId) => curveWarmthRank(traitsOf(roomId)));
|
|
56
|
+
return ranks.length > 0 ? Math.max(...ranks) : undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The rooms of a heating circuit the curve is judged by. One flow temperature
|
|
60
|
+
* heats every room of the circuit, so the curve has to serve the warmest wish:
|
|
61
|
+
* of the rooms with a manual thermostat, those asking for the most warmth keep
|
|
62
|
+
* it fully open and show what the curve does. Every other one is held back by
|
|
63
|
+
* its thermostat, reads the thermostat rather than the curve, and would mislead
|
|
64
|
+
* the controller. A room with no thermostat follows the curve, and one with a
|
|
65
|
+
* self-regulating thermostat holds its own target — both are always judged.
|
|
66
|
+
*
|
|
67
|
+
* @param roomIds - The rooms of the circuit.
|
|
68
|
+
* @param traitsOf - A room's warmth and thermostat.
|
|
69
|
+
* @returns The judged room IDs, in the order given.
|
|
70
|
+
*/
|
|
71
|
+
function curveJudgedRoomIds(roomIds, traitsOf) {
|
|
72
|
+
const top = curveTopManualRank(roomIds, traitsOf);
|
|
73
|
+
return roomIds.filter((roomId) => {
|
|
74
|
+
const traits = traitsOf(roomId);
|
|
75
|
+
return !isManualRoomThermostat(traits?.thermostat) || curveWarmthRank(traits) === top;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The rooms a heating circuit's curve optimisation is judged by, resolved
|
|
80
|
+
* against the rooms of the home ({@link EnergyAppRoom.getRooms} or
|
|
81
|
+
* {@link EnergyAppRoom.getRoomsById}). A room that no longer exists is
|
|
82
|
+
* dropped. Whether a judged room can be measured is up to
|
|
83
|
+
* {@link EnergyAppRoomTemperatures}.
|
|
84
|
+
*
|
|
85
|
+
* @param settings - The circuit's {@link EnyoHeatpumpApplianceHeatingCircuit.curveOptimization}.
|
|
86
|
+
* @param rooms - Every room of the home.
|
|
87
|
+
* @returns The judged rooms, in the order of `settings.roomIds`.
|
|
88
|
+
*/
|
|
89
|
+
function curveOptimizationRooms(settings, rooms) {
|
|
90
|
+
const live = (settings?.roomIds ?? []).flatMap((roomId) => {
|
|
91
|
+
const room = rooms.find((candidate) => candidate.id === roomId);
|
|
92
|
+
return room ? [room] : [];
|
|
93
|
+
});
|
|
94
|
+
const judged = curveJudgedRoomIds(live.map((room) => room.id), (roomId) => live.find((room) => room.id === roomId));
|
|
95
|
+
return live.filter((room) => judged.includes(room.id));
|
|
96
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which rooms of a heating circuit the room-temperature based heating curve
|
|
3
|
+
* optimisation is judged by. Shared by the energy manager, which lowers the
|
|
4
|
+
* curve, and the host's app, which tells the customer how to set each room's
|
|
5
|
+
* thermostat — both must agree on it.
|
|
6
|
+
*/
|
|
7
|
+
import type { EnyoHeatpumpHeatingCurveOptimizationSettings } from '../../types/enyo-heatpump-appliance.cjs';
|
|
8
|
+
import { type EnyoRoom, EnyoRoomThermostatEnum, EnyoRoomWarmthEnum } from '../../types/enyo-room.cjs';
|
|
9
|
+
/** What the curve rules need to know about a room. */
|
|
10
|
+
export interface CurveRoomTraits {
|
|
11
|
+
/** Absent: {@link EnyoRoomWarmthEnum.Normal}. */
|
|
12
|
+
warmth?: EnyoRoomWarmthEnum;
|
|
13
|
+
/** Absent: {@link EnyoRoomThermostatEnum.Steps5}. */
|
|
14
|
+
thermostat?: EnyoRoomThermostatEnum;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* How much warmth a room asks for compared with the other rooms of its
|
|
18
|
+
* circuit: 0 for `cool`, 1 for `normal` (also when absent), 2 for `warm`.
|
|
19
|
+
*
|
|
20
|
+
* @param traits - The room's warmth, if known.
|
|
21
|
+
* @returns The rank.
|
|
22
|
+
*/
|
|
23
|
+
export declare function curveWarmthRank(traits: CurveRoomTraits | undefined): number;
|
|
24
|
+
/**
|
|
25
|
+
* Whether a thermostat is set by hand: a dial with steps or a dial in °C.
|
|
26
|
+
* Absent counts as the common dial with steps 1–5.
|
|
27
|
+
*
|
|
28
|
+
* @param thermostat - The room's thermostat.
|
|
29
|
+
* @returns `true` for a manual thermostat.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isManualRoomThermostat(thermostat: EnyoRoomThermostatEnum | undefined): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* How much warmth the most demanding room behind a manual thermostat asks for.
|
|
34
|
+
*
|
|
35
|
+
* @param roomIds - The rooms of the circuit.
|
|
36
|
+
* @param traitsOf - A room's warmth and thermostat.
|
|
37
|
+
* @returns The highest rank, or `undefined` when no room has a manual thermostat.
|
|
38
|
+
*/
|
|
39
|
+
export declare function curveTopManualRank(roomIds: readonly string[], traitsOf: (roomId: string) => CurveRoomTraits | undefined): number | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* The rooms of a heating circuit the curve is judged by. One flow temperature
|
|
42
|
+
* heats every room of the circuit, so the curve has to serve the warmest wish:
|
|
43
|
+
* of the rooms with a manual thermostat, those asking for the most warmth keep
|
|
44
|
+
* it fully open and show what the curve does. Every other one is held back by
|
|
45
|
+
* its thermostat, reads the thermostat rather than the curve, and would mislead
|
|
46
|
+
* the controller. A room with no thermostat follows the curve, and one with a
|
|
47
|
+
* self-regulating thermostat holds its own target — both are always judged.
|
|
48
|
+
*
|
|
49
|
+
* @param roomIds - The rooms of the circuit.
|
|
50
|
+
* @param traitsOf - A room's warmth and thermostat.
|
|
51
|
+
* @returns The judged room IDs, in the order given.
|
|
52
|
+
*/
|
|
53
|
+
export declare function curveJudgedRoomIds(roomIds: readonly string[], traitsOf: (roomId: string) => CurveRoomTraits | undefined): string[];
|
|
54
|
+
/**
|
|
55
|
+
* The rooms a heating circuit's curve optimisation is judged by, resolved
|
|
56
|
+
* against the rooms of the home ({@link EnergyAppRoom.getRooms} or
|
|
57
|
+
* {@link EnergyAppRoom.getRoomsById}). A room that no longer exists is
|
|
58
|
+
* dropped. Whether a judged room can be measured is up to
|
|
59
|
+
* {@link EnergyAppRoomTemperatures}.
|
|
60
|
+
*
|
|
61
|
+
* @param settings - The circuit's {@link EnyoHeatpumpApplianceHeatingCircuit.curveOptimization}.
|
|
62
|
+
* @param rooms - Every room of the home.
|
|
63
|
+
* @returns The judged rooms, in the order of `settings.roomIds`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function curveOptimizationRooms(settings: EnyoHeatpumpHeatingCurveOptimizationSettings | undefined, rooms: readonly EnyoRoom[]): EnyoRoom[];
|