@enyo-energy/energy-app-sdk 1.43.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.
Files changed (46) hide show
  1. package/README.md +35 -2
  2. package/dist/cjs/energy-app-permission.type.cjs +2 -0
  3. package/dist/cjs/energy-app-permission.type.d.cts +3 -1
  4. package/dist/cjs/energy-app.cjs +26 -0
  5. package/dist/cjs/energy-app.d.cts +24 -0
  6. package/dist/cjs/enyo-energy-app-sdk.d.cts +6 -0
  7. package/dist/cjs/implementations/heatpump/heatpump-curve-optimization-rooms.cjs +96 -0
  8. package/dist/cjs/implementations/heatpump/heatpump-curve-optimization-rooms.d.cts +65 -0
  9. package/dist/cjs/implementations/heatpump/heatpump-metadata-validators.cjs +1 -21
  10. package/dist/cjs/index.cjs +5 -0
  11. package/dist/cjs/index.d.cts +5 -0
  12. package/dist/cjs/packages/energy-app-room-temperatures.cjs +2 -0
  13. package/dist/cjs/packages/energy-app-room-temperatures.d.cts +51 -0
  14. package/dist/cjs/packages/energy-app-room.cjs +2 -0
  15. package/dist/cjs/packages/energy-app-room.d.cts +59 -0
  16. package/dist/cjs/types/enyo-heatpump-appliance.cjs +2 -1
  17. package/dist/cjs/types/enyo-heatpump-appliance.d.cts +12 -39
  18. package/dist/cjs/types/enyo-room-temperature.cjs +2 -0
  19. package/dist/cjs/types/enyo-room-temperature.d.cts +102 -0
  20. package/dist/cjs/types/enyo-room.cjs +37 -0
  21. package/dist/cjs/types/enyo-room.d.cts +60 -0
  22. package/dist/cjs/version.cjs +1 -1
  23. package/dist/cjs/version.d.cts +1 -1
  24. package/dist/energy-app-permission.type.d.ts +3 -1
  25. package/dist/energy-app-permission.type.js +2 -0
  26. package/dist/energy-app.d.ts +24 -0
  27. package/dist/energy-app.js +26 -0
  28. package/dist/enyo-energy-app-sdk.d.ts +6 -0
  29. package/dist/implementations/heatpump/heatpump-curve-optimization-rooms.d.ts +65 -0
  30. package/dist/implementations/heatpump/heatpump-curve-optimization-rooms.js +89 -0
  31. package/dist/implementations/heatpump/heatpump-metadata-validators.js +1 -21
  32. package/dist/index.d.ts +5 -0
  33. package/dist/index.js +5 -0
  34. package/dist/packages/energy-app-room-temperatures.d.ts +51 -0
  35. package/dist/packages/energy-app-room-temperatures.js +1 -0
  36. package/dist/packages/energy-app-room.d.ts +59 -0
  37. package/dist/packages/energy-app-room.js +1 -0
  38. package/dist/types/enyo-heatpump-appliance.d.ts +12 -39
  39. package/dist/types/enyo-heatpump-appliance.js +2 -1
  40. package/dist/types/enyo-room-temperature.d.ts +102 -0
  41. package/dist/types/enyo-room-temperature.js +1 -0
  42. package/dist/types/enyo-room.d.ts +60 -0
  43. package/dist/types/enyo-room.js +34 -0
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.js +1 -1
  46. 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.
@@ -3751,7 +3784,7 @@ Shifts the heating curve of one heating circuit in parallel — **permanently**,
3751
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.
3752
3785
  - **Alongside `HeatpumpControlV3` and `HeatpumpProgramSwitchV3`.** Boost and eco act on top; the offset is the new base.
3753
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`).
3754
- - **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 host resolves them into `rooms`: per room its target temperature (set in the enyo app, `HEATPUMP_CURVE_OPTIMIZATION_DEFAULT_TARGET_C` = 21 °C until then) and the temperature sensors assigned to it. 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.
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.
3755
3788
 
3756
3789
  ```typescript
3757
3790
  protected async handleHeatpumpHeatingCurveOffset(message: EnyoDataBusSetHeatpumpHeatingCurveOffsetV1) {
@@ -3807,7 +3840,7 @@ Beyond tanks and circuits, the heatpump metadata (`EnyoHeatpumpApplianceMetadata
3807
3840
  | `RoomTemperature` | `heatingCircuits[].roomTemperatureC` in `HeatpumpTemperaturesUpdateV1`; where it is measured goes in `heatingCircuits[].roomTemperatureSource` (`None` / `Internal` / `External`) and, for an enyo sensor, `roomTemperatureSensor` |
3808
3841
  | `RoomTemperatureInput` | The heatpump accepts measured room temperatures via `SetHeatpumpRoomTemperatureV1` |
3809
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) |
3810
- | — | `heatingCircuits[].curveOptimization` — the customer's choice for the room-temperature based curve optimisation: `enabled` (off by default), `roomIds` (the rooms the circuit heats) and, resolved by the host, `rooms` — per room `roomId`, `name`, its `targetRoomTemperatureC` (16–26 °C) and the `sensors` assigned to it. Customer-owned; integrations only read it |
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 |
3811
3844
  | — | `building.recommendedHeatingCurve` — the curve the building actually needs, learned from the days the room held its target |
3812
3845
  | `PvSurplusSignal` | The heatpump takes `availablePowerW` of `HeatpumpControlV3` as a PV surplus signal (e.g. Lambda register 102). Supersedes the deprecated `AvailablePowerAnnouncement` |
3813
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 |
@@ -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
@@ -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
  }
@@ -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[];
@@ -80,17 +80,13 @@ function checkProgramModes(zone, prefix, errors, warnings) {
80
80
  errors.push(`${prefix}: activeProgramMode "${zone.activeProgramMode}" is not a valid value`);
81
81
  }
82
82
  }
83
- /** Range the customer may choose for a room's `targetRoomTemperatureC`, in °C. */
84
- const CURVE_OPTIMIZATION_TARGET_RANGE_C = { min: 16, max: 26 };
85
83
  /**
86
84
  * Validates the heating-curve-offset fields of one heating circuit
87
85
  * (`heatingCurveOffsetRangeK`, `activeHeatingCurveOffsetK`,
88
86
  * `curveOptimization`) and appends the problems to `errors`.
89
87
  *
90
88
  * Errors: the range is not finite or does not contain `0`; the active offset
91
- * is not finite or lies outside the range; a room id is empty; a resolved room
92
- * lacks its `roomId`, a sensor lacks its `applianceId` / `sensorId`, or its
93
- * target lies outside 16…26 °C.
89
+ * is not finite or lies outside the range; a room id is empty.
94
90
  *
95
91
  * @param circuit - The heating circuit.
96
92
  * @param prefix - Path prefix for the messages, e.g. `heatingCircuits[index=0]`.
@@ -124,22 +120,6 @@ function checkHeatingCurveOffset(circuit, prefix, errors) {
124
120
  errors.push(`${prefix}: curveOptimization.roomIds[${i}] must be a non-empty string`);
125
121
  }
126
122
  });
127
- (settings.rooms ?? []).forEach((room, i) => {
128
- if (!room?.roomId) {
129
- errors.push(`${prefix}: curveOptimization.rooms[${i}] needs a roomId`);
130
- }
131
- (room?.sensors ?? []).forEach((sensor, j) => {
132
- if (!sensor?.applianceId || !sensor?.sensorId) {
133
- errors.push(`${prefix}: curveOptimization.rooms[${i}].sensors[${j}] needs applianceId and sensorId`);
134
- }
135
- });
136
- const target = room?.targetRoomTemperatureC;
137
- if (!(isFiniteNumber(target)
138
- && target >= CURVE_OPTIMIZATION_TARGET_RANGE_C.min && target <= CURVE_OPTIMIZATION_TARGET_RANGE_C.max)) {
139
- errors.push(`${prefix}: curveOptimization.rooms[${i}].targetRoomTemperatureC must lie within ` +
140
- `${CURVE_OPTIMIZATION_TARGET_RANGE_C.min}…${CURVE_OPTIMIZATION_TARGET_RANGE_C.max} °C; got ${target}`);
141
- }
142
- });
143
123
  }
144
124
  /**
145
125
  * Parses an `HH:mm` time of day into minutes since midnight.
@@ -110,6 +110,7 @@ __exportStar(require("./implementations/heatpump/heatpump-control-validators.cjs
110
110
  __exportStar(require("./implementations/heatpump/heatpump-control-session.cjs"), exports);
111
111
  __exportStar(require("./implementations/heatpump/heatpump-flexibility-validators.cjs"), exports);
112
112
  __exportStar(require("./implementations/heatpump/heatpump-heating-curve-offset.cjs"), exports);
113
+ __exportStar(require("./implementations/heatpump/heatpump-curve-optimization-rooms.cjs"), exports);
113
114
  __exportStar(require("./types/enyo-inverter-appliance.cjs"), exports);
114
115
  __exportStar(require("./types/enyo-onboarding.cjs"), exports);
115
116
  __exportStar(require("./packages/energy-app-onboarding.cjs"), exports);
@@ -193,6 +194,10 @@ __exportStar(require("./types/enyo-webhook.cjs"), exports);
193
194
  __exportStar(require("./packages/energy-app-webhook.cjs"), exports);
194
195
  __exportStar(require("./types/enyo-subscription.cjs"), exports);
195
196
  __exportStar(require("./packages/energy-app-subscriptions.cjs"), exports);
197
+ __exportStar(require("./types/enyo-room.cjs"), exports);
198
+ __exportStar(require("./packages/energy-app-room.cjs"), exports);
199
+ __exportStar(require("./types/enyo-room-temperature.cjs"), exports);
200
+ __exportStar(require("./packages/energy-app-room-temperatures.cjs"), exports);
196
201
  __exportStar(require("./implementations/subscriptions/define-subscription-plan.cjs"), exports);
197
202
  __exportStar(require("./implementations/subscriptions/subscription-plan-validators.cjs"), exports);
198
203
  __exportStar(require("./implementations/subscriptions/in-memory-subscriptions.cjs"), exports);
@@ -94,6 +94,7 @@ export * from './implementations/heatpump/heatpump-control-validators.cjs';
94
94
  export * from './implementations/heatpump/heatpump-control-session.cjs';
95
95
  export * from './implementations/heatpump/heatpump-flexibility-validators.cjs';
96
96
  export * from './implementations/heatpump/heatpump-heating-curve-offset.cjs';
97
+ export * from './implementations/heatpump/heatpump-curve-optimization-rooms.cjs';
97
98
  export * from './types/enyo-inverter-appliance.cjs';
98
99
  export * from './types/enyo-onboarding.cjs';
99
100
  export * from './packages/energy-app-onboarding.cjs';
@@ -177,6 +178,10 @@ export * from './types/enyo-webhook.cjs';
177
178
  export * from './packages/energy-app-webhook.cjs';
178
179
  export * from './types/enyo-subscription.cjs';
179
180
  export * from './packages/energy-app-subscriptions.cjs';
181
+ export * from './types/enyo-room.cjs';
182
+ export * from './packages/energy-app-room.cjs';
183
+ export * from './types/enyo-room-temperature.cjs';
184
+ export * from './packages/energy-app-room-temperatures.cjs';
180
185
  export * from './implementations/subscriptions/define-subscription-plan.cjs';
181
186
  export * from './implementations/subscriptions/subscription-plan-validators.cjs';
182
187
  export * from './implementations/subscriptions/in-memory-subscriptions.cjs';
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,51 @@
1
+ import { EnyoRoomCurrentTemperatures, EnyoRoomTemperatureHistoryRequest, EnyoRoomTemperatureHistoryResponse } from "../types/enyo-room-temperature.cjs";
2
+ /**
3
+ * Interface for reading the temperatures measured in the rooms of the home
4
+ * ({@link EnergyAppRoom}), current and past. A room can have several
5
+ * temperature sensors; each one is reported on its own.
6
+ *
7
+ * There is no listener: to follow a room's temperatures, read the current
8
+ * values once and then subscribe to the
9
+ * {@link EnyoDataBusMessageEnum.TemperatureSensorValuesUpdateV1} messages of
10
+ * the sensors they name ({@link EnyoRoomTemperatureSensorReading.applianceId},
11
+ * {@link EnyoRoomTemperatureSensorReading.sensorId}) on the data bus.
12
+ *
13
+ * Access to this API requires the `RoomTemperatures` permission
14
+ * ({@link EnergyAppPermissionType}).
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const roomTemperatures = energyApp.useRoomTemperatures();
19
+ *
20
+ * // Current values of the rooms a heating circuit heats.
21
+ * const current = await roomTemperatures.getCurrentRoomTemperatures(['living', 'bath']);
22
+ *
23
+ * // The last 24 hours in 15-minute buckets.
24
+ * const history = await roomTemperatures.getRoomTemperatureHistory({
25
+ * roomIds: ['living', 'bath'],
26
+ * startDateIso: '2026-10-27T00:00:00Z',
27
+ * endDateIso: '2026-10-28T00:00:00Z',
28
+ * });
29
+ * ```
30
+ */
31
+ export interface EnergyAppRoomTemperatures {
32
+ /**
33
+ * Returns the latest reading of every temperature sensor in the given
34
+ * rooms. An ID without a room is left out.
35
+ *
36
+ * @param roomIds - IDs of the rooms ({@link EnyoRoom.id}) to read
37
+ * @returns Promise that resolves to one entry per room found, in the order of `roomIds`
38
+ * @throws {EnergyAppPermissionNotGrantedError} If the `RoomTemperatures` permission is not granted.
39
+ */
40
+ getCurrentRoomTemperatures(roomIds: string[]): Promise<EnyoRoomCurrentTemperatures[]>;
41
+ /**
42
+ * Returns the measured temperatures of the given rooms over a time
43
+ * interval, per sensor and bucket. The sensors are those in the room now:
44
+ * a sensor moved to another room brings its earlier readings along.
45
+ *
46
+ * @param request - The rooms, the interval and the bucket size
47
+ * @returns Promise that resolves to the history of every room found
48
+ * @throws {EnergyAppPermissionNotGrantedError} If the `RoomTemperatures` permission is not granted.
49
+ */
50
+ getRoomTemperatureHistory(request: EnyoRoomTemperatureHistoryRequest): Promise<EnyoRoomTemperatureHistoryResponse>;
51
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,59 @@
1
+ import { EnyoRoom, EnyoRoomsChangedListener } from "../types/enyo-room.cjs";
2
+ /**
3
+ * Interface for reading the rooms of the home.
4
+ *
5
+ * Rooms are set up by the customer in the enyo app: a name, the appliances in
6
+ * it, the temperature the customer wants there and — for the heating curve
7
+ * optimisation — how warm it should be compared with the other rooms of its
8
+ * heating circuit and which thermostat it has. Apps only read them. The
9
+ * temperatures measured in a room come from {@link EnergyAppRoomTemperatures}.
10
+ *
11
+ * Access to this API requires the `Room` permission
12
+ * ({@link EnergyAppPermissionType}).
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const rooms = energyApp.useRoom();
17
+ *
18
+ * // On boot: read the rooms.
19
+ * applyRooms(await rooms.getRooms());
20
+ *
21
+ * // Afterwards: react to the customer changing a room.
22
+ * rooms.listenForRoomsChanged((all) => applyRooms(all));
23
+ * ```
24
+ */
25
+ export interface EnergyAppRoom {
26
+ /**
27
+ * Returns every room of the home.
28
+ *
29
+ * @returns Promise that resolves to the rooms, in no particular order
30
+ * @throws {EnergyAppPermissionNotGrantedError} If the `Room` permission is not granted.
31
+ */
32
+ getRooms(): Promise<EnyoRoom[]>;
33
+ /**
34
+ * Returns the rooms with the given IDs, e.g. the
35
+ * {@link EnyoHeatpumpHeatingCurveOptimizationSettings.roomIds} of a
36
+ * heating circuit. An ID without a room — the customer removed it — is
37
+ * left out.
38
+ *
39
+ * @param roomIds - IDs of the rooms to return
40
+ * @returns Promise that resolves to the rooms found, in the order of `roomIds`
41
+ * @throws {EnergyAppPermissionNotGrantedError} If the `Room` permission is not granted.
42
+ */
43
+ getRoomsById(roomIds: string[]): Promise<EnyoRoom[]>;
44
+ /**
45
+ * Registers a listener called with every room of the home whenever the
46
+ * customer creates, changes or removes one.
47
+ *
48
+ * @param listener - Callback invoked with all rooms after the change
49
+ * @returns A listener ID that can be passed to {@link removeListener}
50
+ * @throws {EnergyAppPermissionNotGrantedError} If the `Room` permission is not granted.
51
+ */
52
+ listenForRoomsChanged(listener: EnyoRoomsChangedListener): string;
53
+ /**
54
+ * Removes a listener registered with {@link listenForRoomsChanged}.
55
+ *
56
+ * @param listenerId - The ID returned when the listener was registered
57
+ */
58
+ removeListener(listenerId: string): void;
59
+ }
@@ -301,6 +301,7 @@ var EnyoHeatpumpHeatingControlReferenceEnum;
301
301
  */
302
302
  exports.HEATPUMP_CURVE_OPTIMIZATION_MAX_LOWERING_K = 3;
303
303
  /**
304
- * The target temperature of a room whose customer has not set one, in °C.
304
+ * The target temperature of a room whose customer has not set one, in °C
305
+ * ({@link EnyoRoom.targetTemperatureC}).
305
306
  */
306
307
  exports.HEATPUMP_CURVE_OPTIMIZATION_DEFAULT_TARGET_C = 21;
@@ -442,36 +442,10 @@ export interface EnyoHeatpumpRoomTemperatureSensorReference {
442
442
  */
443
443
  export declare const HEATPUMP_CURVE_OPTIMIZATION_MAX_LOWERING_K = 3;
444
444
  /**
445
- * The target temperature of a room whose customer has not set one, in °C.
445
+ * The target temperature of a room whose customer has not set one, in °C
446
+ * ({@link EnyoRoom.targetTemperatureC}).
446
447
  */
447
448
  export declare const HEATPUMP_CURVE_OPTIMIZATION_DEFAULT_TARGET_C = 21;
448
- /**
449
- * One room a heating circuit heats, as the host resolves it for the heating
450
- * curve optimisation: the room's target temperature and the temperature
451
- * sensors assigned to that room. Read-only for apps — the host fills it from
452
- * {@link EnyoHeatpumpHeatingCurveOptimizationSettings.roomIds} and the rooms
453
- * the customer set up in the enyo app.
454
- */
455
- export interface EnyoHeatpumpCurveOptimizationRoom {
456
- /** ID of the room in the enyo app. */
457
- roomId: string;
458
- /** The room's name, as the customer called it — for logs and diagnostics. */
459
- name?: string;
460
- /**
461
- * The room temperature in °C the customer wants in this room — a setpoint
462
- * of the energy manager's controller, **not** written to the heatpump.
463
- * {@link HEATPUMP_CURVE_OPTIMIZATION_DEFAULT_TARGET_C} until the customer
464
- * sets one. Raising it means the customer is too cold there, and the
465
- * energy manager reacts at once.
466
- */
467
- targetRoomTemperatureC: number;
468
- /**
469
- * The temperature sensors assigned to this room. Empty when the room has
470
- * none: such a room is part of the circuit but cannot be judged, and the
471
- * energy manager leaves it out. With several, the coldest counts.
472
- */
473
- sensors: EnyoHeatpumpRoomTemperatureSensorReference[];
474
- }
475
449
  /**
476
450
  * The range of {@link EnyoDataBusSetHeatpumpHeatingCurveOffsetV1.data}
477
451
  * `offsetK` a heating circuit accepts, in Kelvin of flow temperature relative
@@ -487,7 +461,7 @@ export interface EnyoHeatpumpHeatingCurveOffsetRange {
487
461
  /**
488
462
  * What the customer chose for the room-temperature based optimisation of one
489
463
  * heating circuit's heating curve: the energy manager lowers the curve as far
490
- * as possible without any of the {@link rooms} falling below its own target,
464
+ * as possible without any of the circuit's rooms falling below its own target,
491
465
  * using {@link EnyoDataBusSetHeatpumpHeatingCurveOffsetV1}.
492
466
  *
493
467
  * **Customer-owned.** Set in the enyo app, never by an integration: the host
@@ -503,18 +477,17 @@ export interface EnyoHeatpumpHeatingCurveOptimizationSettings {
503
477
  */
504
478
  enabled: boolean;
505
479
  /**
506
- * IDs of the rooms (as set up in the enyo app) this circuit heats — chosen
507
- * by the customer, whether or not a room has a temperature sensor.
480
+ * IDs of the rooms ({@link EnyoRoom.id}) this circuit heats — chosen by the
481
+ * customer, whether or not a room has a temperature sensor. Read the rooms
482
+ * with {@link EnergyAppRoom.getRoomsById}; {@link curveOptimizationRooms}
483
+ * picks the ones the curve is judged by, and
484
+ * {@link EnergyAppRoomTemperatures} gives their measured temperatures. The
485
+ * room furthest below its target decides how low the curve may go; rooms
486
+ * without a sensor are left out, and without any room that has one the
487
+ * optimisation cannot run. The
488
+ * curve is lowered by at most {@link HEATPUMP_CURVE_OPTIMIZATION_MAX_LOWERING_K}.
508
489
  */
509
490
  roomIds?: string[];
510
- /**
511
- * Those rooms resolved by the host: each with its target and its sensors.
512
- * The room furthest below its target decides how low the curve may go;
513
- * rooms without a sensor are left out. Without any room that has a sensor
514
- * the optimisation cannot run. The curve is lowered by at most
515
- * {@link HEATPUMP_CURVE_OPTIMIZATION_MAX_LOWERING_K}. Read-only for apps.
516
- */
517
- rooms?: EnyoHeatpumpCurveOptimizationRoom[];
518
491
  }
519
492
  /**
520
493
  * A domestic-hot-water zone of the installation.
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });