@enyo-energy/energy-app-sdk 0.0.190 → 0.0.192

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -757,18 +757,33 @@ the update callback cheap.
757
757
 
758
758
  #### `useChargingCard(): EnergyAppChargingCard`
759
759
 
760
- Handle charging authentication:
760
+ Read registered charging cards and pair new RFID cards:
761
761
 
762
762
  ```typescript
763
763
  const chargingCards = energyApp.useChargingCard();
764
764
 
765
- // Validate charging card
766
- const isValid = await chargingCards.validateCard('RFID-12345');
765
+ // List all registered charging cards
766
+ const cards = await chargingCards.list();
767
767
 
768
- // Get card information
769
- const cardInfo = await chargingCards.getCardInfo('RFID-12345');
768
+ // Get a single card
769
+ const card = await chargingCards.getById('card-id');
770
+
771
+ // Handle pairing requests coming from the app
772
+ const listenerId = chargingCards.listenForPairingStarted(async (request) => {
773
+ // Put the reader into pairing mode and wait for a card to be presented.
774
+ // Resolve with the RFID read from the card - the host assigns it to
775
+ // request.chargingCardId and clears its pendingRegistration flag.
776
+ return await charger.enterPairingMode(request.applianceId, request.timeoutMs);
777
+ });
778
+
779
+ chargingCards.removeListener(listenerId);
770
780
  ```
771
781
 
782
+ Reject the promise returned by the pairing listener when no card was presented or
783
+ the charger refused to enter pairing mode - the host then reports the attempt as
784
+ failed and leaves the card pending. A package managing several chargers should
785
+ check `request.applianceId` and reject requests for appliances it does not own.
786
+
772
787
  ### User Features
773
788
 
774
789
  #### `useAuthentication(): EnergyAppAuthentication`
@@ -83,6 +83,8 @@ __exportStar(require("./packages/energy-app-configuration-manager.cjs"), exports
83
83
  __exportStar(require("./types/enyo-air-conditioning-appliance.cjs"), exports);
84
84
  __exportStar(require("./types/enyo-heating-rod-appliance.cjs"), exports);
85
85
  __exportStar(require("./types/enyo-charger-appliance.cjs"), exports);
86
+ __exportStar(require("./types/enyo-charging-card.cjs"), exports);
87
+ __exportStar(require("./packages/energy-app-charging-card.cjs"), exports);
86
88
  __exportStar(require("./types/enyo-battery-appliance.cjs"), exports);
87
89
  __exportStar(require("./types/enyo-heatpump-appliance.cjs"), exports);
88
90
  __exportStar(require("./types/enyo-inverter-appliance.cjs"), exports);
@@ -67,6 +67,8 @@ export * from './packages/energy-app-configuration-manager.cjs';
67
67
  export * from './types/enyo-air-conditioning-appliance.cjs';
68
68
  export * from './types/enyo-heating-rod-appliance.cjs';
69
69
  export * from './types/enyo-charger-appliance.cjs';
70
+ export * from './types/enyo-charging-card.cjs';
71
+ export * from './packages/energy-app-charging-card.cjs';
70
72
  export * from './types/enyo-battery-appliance.cjs';
71
73
  export * from './types/enyo-heatpump-appliance.cjs';
72
74
  export * from './types/enyo-inverter-appliance.cjs';
@@ -1,11 +1,56 @@
1
- import { EnyoChargingCard } from "../types/enyo-charging-card.cjs";
1
+ import { EnyoChargingCard, EnyoChargingCardPairingRequest } from "../types/enyo-charging-card.cjs";
2
2
  /**
3
3
  * Interface for managing charging cards in enyo packages.
4
- * Provides read-only operations for charging card information.
4
+ * Provides read-only operations for charging card information as well as
5
+ * RFID pairing support.
5
6
  */
6
7
  export interface EnergyAppChargingCard {
7
8
  /** Get a list of all registered charging cards */
8
9
  list: () => Promise<EnyoChargingCard[]>;
9
10
  /** Get a specific charging card by its ID */
10
11
  getById: (id: string) => Promise<EnyoChargingCard | null>;
12
+ /**
13
+ * Listen for pairing requests, i.e. the host asking this package to put its
14
+ * RFID reader into pairing mode — typically because a user started adding a
15
+ * new charging card in the app.
16
+ *
17
+ * The listener owns the whole pairing attempt: it enables pairing mode on
18
+ * the charger, waits for a card to be held against the reader and resolves
19
+ * with the RFID identifier that was read (the value stored in
20
+ * {@link EnyoChargingCard.rfid}). The host assigns it to the charging card
21
+ * named by {@link EnyoChargingCardPairingRequest.chargingCardId} and clears
22
+ * that card's {@link EnyoChargingCard.pendingRegistration} flag.
23
+ *
24
+ * Reject the returned promise when no card was presented or the charger
25
+ * refused to enter pairing mode; the host then reports the attempt as
26
+ * failed and leaves the card pending. Honour
27
+ * {@link EnyoChargingCardPairingRequest.timeoutMs} when it is set — the host
28
+ * ignores a result that arrives after the deadline.
29
+ *
30
+ * Only register a listener when the package actually drives an RFID reader.
31
+ * Requests carry an optional
32
+ * {@link EnyoChargingCardPairingRequest.applianceId}; a package managing
33
+ * several chargers should check it and reject requests for appliances it
34
+ * does not own.
35
+ *
36
+ * @param listener - Callback invoked for every pairing request, resolving
37
+ * with the RFID identifier read from the presented card
38
+ * @returns A unique listener ID that can be used to remove the listener
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const chargingCard = energyApp.useChargingCard();
43
+ * const listenerId = chargingCard.listenForPairingStarted(async (request) => {
44
+ * const rfid = await charger.enterPairingMode(request.applianceId, request.timeoutMs);
45
+ * return rfid;
46
+ * });
47
+ * ```
48
+ */
49
+ listenForPairingStarted: (listener: (request: EnyoChargingCardPairingRequest) => Promise<string>) => string;
50
+ /**
51
+ * Removes a previously registered listener.
52
+ *
53
+ * @param listenerId - The ID returned by {@link listenForPairingStarted}
54
+ */
55
+ removeListener: (listenerId: string) => void;
11
56
  }
@@ -98,6 +98,7 @@ export interface EnergyAppDiagnostics {
98
98
  * generatedAtIso: new Date().toISOString(),
99
99
  * announced: [
100
100
  * {
101
+ * id: 'charger:flexible-kwh',
101
102
  * category: EnyoApplianceTypeEnum.Charger,
102
103
  * flexibilityAnnouncementType:
103
104
  * EnyoFlexibilityAnnouncementTypeEnum.FlexibleKwhTarget,
@@ -110,6 +111,8 @@ export interface EnergyAppDiagnostics {
110
111
  * },
111
112
  * },
112
113
  * {
114
+ * id: 'storage:flexible-discharge',
115
+ * applianceId: 'battery-1',
113
116
  * category: EnyoApplianceTypeEnum.Storage,
114
117
  * flexibilityAnnouncementType:
115
118
  * EnyoFlexibilityAnnouncementTypeEnum.FlexibleDischarge,
@@ -124,6 +127,7 @@ export interface EnergyAppDiagnostics {
124
127
  * ],
125
128
  * granted: [
126
129
  * {
130
+ * announcementId: 'charger:flexible-kwh',
127
131
  * category: EnyoApplianceTypeEnum.Charger,
128
132
  * flexibilityAnnouncementType:
129
133
  * EnyoFlexibilityAnnouncementTypeEnum.FlexibleKwhTarget,
@@ -134,6 +138,7 @@ export interface EnergyAppDiagnostics {
134
138
  * reason: 'grid connection point limit',
135
139
  * },
136
140
  * {
141
+ * announcementId: 'storage:flexible-discharge',
137
142
  * category: EnyoApplianceTypeEnum.Storage,
138
143
  * flexibilityAnnouncementType:
139
144
  * EnyoFlexibilityAnnouncementTypeEnum.FlexibleDischarge,
@@ -9,3 +9,24 @@ export interface EnyoChargingCard {
9
9
  rfid?: string;
10
10
  pendingRegistration?: boolean;
11
11
  }
12
+ /**
13
+ * Details of a pairing request handed to a
14
+ * {@link EnergyAppChargingCard.listenForPairingStarted} listener when the
15
+ * host asks a package to put its RFID reader into pairing mode.
16
+ */
17
+ export interface EnyoChargingCardPairingRequest {
18
+ /** ID of the charging card record the scanned RFID should be assigned to */
19
+ chargingCardId: string;
20
+ /**
21
+ * Appliance (charger) whose RFID reader should enter pairing mode. Omitted
22
+ * when the host does not target a specific charger — in that case the
23
+ * package decides which of its readers to use.
24
+ */
25
+ applianceId?: string;
26
+ /**
27
+ * Time budget, in milliseconds, the package has to deliver a scanned card.
28
+ * The host stops waiting once it elapses, so a listener that resolves later
29
+ * has no effect. Omitted when the host does not impose a deadline.
30
+ */
31
+ timeoutMs?: number;
32
+ }
@@ -15,6 +15,15 @@ var EnyoDataBusCommandReasonTypeEnum;
15
15
  EnyoDataBusCommandReasonTypeEnum["PvSurplusAvailable"] = "pv-surplus-available";
16
16
  /** Command issued because PV surplus is unavailable */
17
17
  EnyoDataBusCommandReasonTypeEnum["PvSurplusUnavailable"] = "pv-surplus-unavailable";
18
+ /**
19
+ * Command issued because PV surplus exists but is allocated to another
20
+ * appliance. Belongs to the
21
+ * {@link EnyoDataBusCommandReasonCategoryEnum.PvSurplus} category. Set
22
+ * {@link EnyoDataBusCommandReason.inFavourOfApplianceType} so the
23
+ * end-user text can name the appliance that got the surplus instead of
24
+ * saying "elsewhere".
25
+ */
26
+ EnyoDataBusCommandReasonTypeEnum["PvSurplusAllocatedElsewhere"] = "pv-surplus-allocated-elsewhere";
18
27
  /** Command issued because battery capacity is available */
19
28
  EnyoDataBusCommandReasonTypeEnum["BatteryCapacityAvailable"] = "battery-capacity-available";
20
29
  /** Command issued because battery capacity is unavailable */
@@ -23,6 +23,15 @@ export declare enum EnyoDataBusCommandReasonTypeEnum {
23
23
  PvSurplusAvailable = "pv-surplus-available",
24
24
  /** Command issued because PV surplus is unavailable */
25
25
  PvSurplusUnavailable = "pv-surplus-unavailable",
26
+ /**
27
+ * Command issued because PV surplus exists but is allocated to another
28
+ * appliance. Belongs to the
29
+ * {@link EnyoDataBusCommandReasonCategoryEnum.PvSurplus} category. Set
30
+ * {@link EnyoDataBusCommandReason.inFavourOfApplianceType} so the
31
+ * end-user text can name the appliance that got the surplus instead of
32
+ * saying "elsewhere".
33
+ */
34
+ PvSurplusAllocatedElsewhere = "pv-surplus-allocated-elsewhere",
26
35
  /** Command issued because battery capacity is available */
27
36
  BatteryCapacityAvailable = "battery-capacity-available",
28
37
  /** Command issued because battery capacity is unavailable */
@@ -100,6 +109,16 @@ export interface EnyoDataBusCommandReason {
100
109
  temperatureC?: number;
101
110
  /** Relevant state of charge as a percentage (battery-driven reasons) */
102
111
  socPercent?: number;
112
+ /**
113
+ * The appliance category the decision was made in favour of.
114
+ *
115
+ * Set on reasons that describe a trade-off between appliances — most
116
+ * notably
117
+ * {@link EnyoDataBusCommandReasonTypeEnum.PvSurplusAllocatedElsewhere} —
118
+ * so the end-user text can name the winning appliance (e.g. "the battery")
119
+ * rather than saying "elsewhere".
120
+ */
121
+ inFavourOfApplianceType?: EnyoApplianceTypeEnum;
103
122
  }
104
123
  /**
105
124
  * Whether a grid operator power limitation caps power drawn from the grid
@@ -125,11 +125,49 @@ export interface EnyoFlexibilityAnnouncementContext {
125
125
  heatpumpTargetType?: EnyoFlexibilityHeatpumpTargetTypeEnum;
126
126
  /** For air-conditioning announcements: what the flexibility is aimed at. */
127
127
  airConditioningTargetType?: EnyoFlexibilityAirConditioningTargetTypeEnum;
128
+ /**
129
+ * For storage announcements: the round-trip cost of cycling the battery, in
130
+ * EUR per kWh. The floor a discharge has to beat to be worth licensing.
131
+ */
132
+ storageCycleCostEurPerKwh?: number;
133
+ /**
134
+ * For storage announcements: the physical discharge floor, in Watts. Signed —
135
+ * negative means discharge, so this is the most negative power the storage can
136
+ * physically reach.
137
+ */
138
+ storageMinPowerW?: number;
139
+ /** For storage announcements: the physical charge ceiling, in Watts. */
140
+ storageMaxPowerW?: number;
141
+ /** For storage announcements: usable capacity of the storage, in Wh. */
142
+ storageCapacityWh?: number;
143
+ /**
144
+ * For storage announcements: the discharge power we will actually command, in
145
+ * Watts. May be well inside {@link storageMinPowerW} — today it is `0`, i.e. we
146
+ * license discharge without commanding it.
147
+ */
148
+ storageCommandableDischargeW?: number;
149
+ /**
150
+ * For storage announcements: whether the storage accepts an explicit
151
+ * `Discharge 0` command, i.e. can be told to hold rather than only being left
152
+ * alone.
153
+ */
154
+ storageCanHold?: boolean;
128
155
  }
129
156
  /** Fields shared by every category-scoped flexibility announcement. */
130
157
  export interface EnyoCategoryFlexibilityAnnouncementBase {
158
+ /**
159
+ * What this announcement IS, stable across cycles — e.g. `storage:peak-requirement`.
160
+ * A category speaks with several at once and (category, type) does not tell them
161
+ * apart. REQUIRED: a positional identity changes whenever a neighbour appears.
162
+ */
163
+ id: string;
131
164
  /** The appliance category this demand aggregates (e.g. `Charger`). */
132
165
  category: EnyoApplianceTypeEnum;
166
+ /**
167
+ * The appliance this announcement speaks for, when a category announces per
168
+ * appliance rather than as one aggregate. Absent = the whole category.
169
+ */
170
+ applianceId?: string;
133
171
  /** Optional extra context for the decision maker; absent for most announcements. */
134
172
  context?: EnyoFlexibilityAnnouncementContext;
135
173
  }
@@ -143,6 +181,12 @@ export interface EnyoCategoryRequiredKwhTargetAnnouncement extends EnyoCategoryF
143
181
  kwh: number;
144
182
  /** Power band the delivery may use. */
145
183
  power: EnyoFlexibilityPowerBand;
184
+ /**
185
+ * Price ceiling, when the requirement is economic rather than absolute.
186
+ * Present on the wire today; without it every economic requirement looked
187
+ * like a MUST-RUN in diagnostics.
188
+ */
189
+ priceLimitEuroCent?: number;
146
190
  };
147
191
  }
148
192
  /** Deliver `kwh` flexibly (cost-/PV-optimized), aggregated across the category. */
@@ -224,10 +268,13 @@ export declare enum EnyoCategoryFlexibilityGrantStatusEnum {
224
268
  * A grant is deliberately flatter than the announcement it answers: the decision
225
269
  * maker replies with one allocation — a window, a power envelope, and (for the
226
270
  * energy-target and discharge variants) an amount of energy — regardless of which
227
- * announcement variant produced it. Pairing is by `category` +
228
- * `flexibilityAnnouncementType`, which is unique within a single publish.
271
+ * announcement variant produced it. Pairing is by `announcementId` — the `id` of
272
+ * the announcement being answered — which stays stable across cycles and tells
273
+ * apart the several announcements one category may speak with at once.
229
274
  */
230
275
  export interface EnyoCategoryFlexibilityGrant {
276
+ /** The `id` of the announcement this answers. */
277
+ announcementId: string;
231
278
  /** The appliance category this allocation is for (e.g. `Charger`). */
232
279
  category: EnyoApplianceTypeEnum;
233
280
  /** Which announcement variant this grant answers. */
@@ -270,14 +317,13 @@ export interface EnyoCategoryFlexibilityDiagnostics {
270
317
  /** ISO 8601 timestamp when this snapshot was taken. */
271
318
  generatedAtIso: string;
272
319
  /**
273
- * The flexibility the managers announced, one entry per
274
- * (category, announcement variant).
320
+ * The flexibility the managers announced, one entry per announcement `id`.
275
321
  */
276
322
  announced: EnyoCategoryFlexibilityAnnouncement[];
277
323
  /**
278
324
  * What the decision maker granted in answer to `announced`. Every entry SHOULD
279
- * pair with an `announced` entry by `category` + `flexibilityAnnouncementType`;
280
- * an announcement with no matching grant means the decision had not been taken
325
+ * pair with an `announced` entry by `announcementId`; an announcement with no
326
+ * matching grant means the decision had not been taken
281
327
  * yet when the snapshot was produced.
282
328
  */
283
329
  granted: EnyoCategoryFlexibilityGrant[];
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EnyoHeatpumpApplianceHeatingCircuitTypeEnum = exports.EnyoHeatpumpApplianceAdditionalDeviceEnum = exports.EnyoHeatpumpApplianceConnectionTypeEnum = exports.EnyoHeatpumpApplianceModeEnum = exports.EnyoHeatpumpApplianceAvailableFeaturesEnum = void 0;
3
+ exports.EnyoHeatpumpApplianceHeatingCircuitTypeEnum = exports.EnyoHeatpumpApplianceHeatingRodUsageEnum = exports.EnyoHeatpumpApplianceAdditionalDeviceEnum = exports.EnyoHeatpumpApplianceConnectionTypeEnum = exports.EnyoHeatpumpApplianceModeEnum = exports.EnyoHeatpumpApplianceAvailableFeaturesEnum = void 0;
4
4
  var EnyoHeatpumpApplianceAvailableFeaturesEnum;
5
5
  (function (EnyoHeatpumpApplianceAvailableFeaturesEnum) {
6
6
  /** If the heatpump is capable of domestic hot water*/
7
7
  EnyoHeatpumpApplianceAvailableFeaturesEnum["DomesticHotWater"] = "DomesticHotWater";
8
8
  /** If the heatpump has a heating rod*/
9
9
  EnyoHeatpumpApplianceAvailableFeaturesEnum["HeatingRod"] = "HeatingRod";
10
+ /** If the heating rod of the heatpump can be actively controlled (steered) by the energy manager */
11
+ EnyoHeatpumpApplianceAvailableFeaturesEnum["HeatingRodControllable"] = "HeatingRodControllable";
10
12
  /** If the heatpump supports room overheating via heating circuits */
11
13
  EnyoHeatpumpApplianceAvailableFeaturesEnum["RoomOverheating"] = "RoomOverheating";
12
14
  /** If the heatpump supports buffer tank overheating */
@@ -62,6 +64,18 @@ var EnyoHeatpumpApplianceAdditionalDeviceEnum;
62
64
  /** A solar thermal system is present in the installation */
63
65
  EnyoHeatpumpApplianceAdditionalDeviceEnum["SolarThermal"] = "SolarThermal";
64
66
  })(EnyoHeatpumpApplianceAdditionalDeviceEnum || (exports.EnyoHeatpumpApplianceAdditionalDeviceEnum = EnyoHeatpumpApplianceAdditionalDeviceEnum = {}));
67
+ /**
68
+ * Describes how the heating rod of a heatpump installation is used.
69
+ * Consumers (UI, energy manager) use this to decide whether the heating rod is
70
+ * the sole heat source or only assists the compressor.
71
+ */
72
+ var EnyoHeatpumpApplianceHeatingRodUsageEnum;
73
+ (function (EnyoHeatpumpApplianceHeatingRodUsageEnum) {
74
+ /** The heating rod is the only heat source used (no compressor support) */
75
+ EnyoHeatpumpApplianceHeatingRodUsageEnum["OnlyHeatingRot"] = "OnlyHeatingRot";
76
+ /** The heating rod is used in addition to the compressor to further increase the temperature */
77
+ EnyoHeatpumpApplianceHeatingRodUsageEnum["HeatingRodForTemperatureIncrease"] = "HeatingRodForTemperatureIncrease";
78
+ })(EnyoHeatpumpApplianceHeatingRodUsageEnum || (exports.EnyoHeatpumpApplianceHeatingRodUsageEnum = EnyoHeatpumpApplianceHeatingRodUsageEnum = {}));
65
79
  /**
66
80
  * The type of heat emitter connected to a heating circuit. Influences the
67
81
  * flow temperatures the circuit operates at (floor heating typically runs at
@@ -3,6 +3,8 @@ export declare enum EnyoHeatpumpApplianceAvailableFeaturesEnum {
3
3
  DomesticHotWater = "DomesticHotWater",
4
4
  /** If the heatpump has a heating rod*/
5
5
  HeatingRod = "HeatingRod",
6
+ /** If the heating rod of the heatpump can be actively controlled (steered) by the energy manager */
7
+ HeatingRodControllable = "HeatingRodControllable",
6
8
  /** If the heatpump supports room overheating via heating circuits */
7
9
  RoomOverheating = "RoomOverheating",
8
10
  /** If the heatpump supports buffer tank overheating */
@@ -55,6 +57,17 @@ export declare enum EnyoHeatpumpApplianceAdditionalDeviceEnum {
55
57
  /** A solar thermal system is present in the installation */
56
58
  SolarThermal = "SolarThermal"
57
59
  }
60
+ /**
61
+ * Describes how the heating rod of a heatpump installation is used.
62
+ * Consumers (UI, energy manager) use this to decide whether the heating rod is
63
+ * the sole heat source or only assists the compressor.
64
+ */
65
+ export declare enum EnyoHeatpumpApplianceHeatingRodUsageEnum {
66
+ /** The heating rod is the only heat source used (no compressor support) */
67
+ OnlyHeatingRot = "OnlyHeatingRot",
68
+ /** The heating rod is used in addition to the compressor to further increase the temperature */
69
+ HeatingRodForTemperatureIncrease = "HeatingRodForTemperatureIncrease"
70
+ }
58
71
  /**
59
72
  * The type of heat emitter connected to a heating circuit. Influences the
60
73
  * flow temperatures the circuit operates at (floor heating typically runs at
@@ -124,4 +137,11 @@ export interface EnyoHeatpumpApplianceMetadata {
124
137
  * heatpump (e.g. a heating rod or a solar thermal system).
125
138
  */
126
139
  additionalDevices?: EnyoHeatpumpApplianceAdditionalDeviceEnum[];
140
+ /**
141
+ * How the heating rod of the installation is used (e.g. as the only heat
142
+ * source or only to further increase the temperature on top of the
143
+ * compressor). Only meaningful if the heatpump has a heating rod
144
+ * (see {@link EnyoHeatpumpApplianceAvailableFeaturesEnum.HeatingRod}).
145
+ */
146
+ heatingRodUsage?: EnyoHeatpumpApplianceHeatingRodUsageEnum;
127
147
  }
@@ -9,7 +9,7 @@ exports.getSdkVersion = getSdkVersion;
9
9
  /**
10
10
  * Current version of the enyo Energy App SDK.
11
11
  */
12
- exports.SDK_VERSION = '0.0.190';
12
+ exports.SDK_VERSION = '0.0.192';
13
13
  /**
14
14
  * Gets the current SDK version.
15
15
  * @returns The semantic version string of the SDK
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export declare const SDK_VERSION = "0.0.190";
8
+ export declare const SDK_VERSION = "0.0.192";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
package/dist/index.d.ts CHANGED
@@ -67,6 +67,8 @@ export * from './packages/energy-app-configuration-manager.js';
67
67
  export * from './types/enyo-air-conditioning-appliance.js';
68
68
  export * from './types/enyo-heating-rod-appliance.js';
69
69
  export * from './types/enyo-charger-appliance.js';
70
+ export * from './types/enyo-charging-card.js';
71
+ export * from './packages/energy-app-charging-card.js';
70
72
  export * from './types/enyo-battery-appliance.js';
71
73
  export * from './types/enyo-heatpump-appliance.js';
72
74
  export * from './types/enyo-inverter-appliance.js';
package/dist/index.js CHANGED
@@ -67,6 +67,8 @@ export * from './packages/energy-app-configuration-manager.js';
67
67
  export * from './types/enyo-air-conditioning-appliance.js';
68
68
  export * from './types/enyo-heating-rod-appliance.js';
69
69
  export * from './types/enyo-charger-appliance.js';
70
+ export * from './types/enyo-charging-card.js';
71
+ export * from './packages/energy-app-charging-card.js';
70
72
  export * from './types/enyo-battery-appliance.js';
71
73
  export * from './types/enyo-heatpump-appliance.js';
72
74
  export * from './types/enyo-inverter-appliance.js';
@@ -1,11 +1,56 @@
1
- import { EnyoChargingCard } from "../types/enyo-charging-card.js";
1
+ import { EnyoChargingCard, EnyoChargingCardPairingRequest } from "../types/enyo-charging-card.js";
2
2
  /**
3
3
  * Interface for managing charging cards in enyo packages.
4
- * Provides read-only operations for charging card information.
4
+ * Provides read-only operations for charging card information as well as
5
+ * RFID pairing support.
5
6
  */
6
7
  export interface EnergyAppChargingCard {
7
8
  /** Get a list of all registered charging cards */
8
9
  list: () => Promise<EnyoChargingCard[]>;
9
10
  /** Get a specific charging card by its ID */
10
11
  getById: (id: string) => Promise<EnyoChargingCard | null>;
12
+ /**
13
+ * Listen for pairing requests, i.e. the host asking this package to put its
14
+ * RFID reader into pairing mode — typically because a user started adding a
15
+ * new charging card in the app.
16
+ *
17
+ * The listener owns the whole pairing attempt: it enables pairing mode on
18
+ * the charger, waits for a card to be held against the reader and resolves
19
+ * with the RFID identifier that was read (the value stored in
20
+ * {@link EnyoChargingCard.rfid}). The host assigns it to the charging card
21
+ * named by {@link EnyoChargingCardPairingRequest.chargingCardId} and clears
22
+ * that card's {@link EnyoChargingCard.pendingRegistration} flag.
23
+ *
24
+ * Reject the returned promise when no card was presented or the charger
25
+ * refused to enter pairing mode; the host then reports the attempt as
26
+ * failed and leaves the card pending. Honour
27
+ * {@link EnyoChargingCardPairingRequest.timeoutMs} when it is set — the host
28
+ * ignores a result that arrives after the deadline.
29
+ *
30
+ * Only register a listener when the package actually drives an RFID reader.
31
+ * Requests carry an optional
32
+ * {@link EnyoChargingCardPairingRequest.applianceId}; a package managing
33
+ * several chargers should check it and reject requests for appliances it
34
+ * does not own.
35
+ *
36
+ * @param listener - Callback invoked for every pairing request, resolving
37
+ * with the RFID identifier read from the presented card
38
+ * @returns A unique listener ID that can be used to remove the listener
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const chargingCard = energyApp.useChargingCard();
43
+ * const listenerId = chargingCard.listenForPairingStarted(async (request) => {
44
+ * const rfid = await charger.enterPairingMode(request.applianceId, request.timeoutMs);
45
+ * return rfid;
46
+ * });
47
+ * ```
48
+ */
49
+ listenForPairingStarted: (listener: (request: EnyoChargingCardPairingRequest) => Promise<string>) => string;
50
+ /**
51
+ * Removes a previously registered listener.
52
+ *
53
+ * @param listenerId - The ID returned by {@link listenForPairingStarted}
54
+ */
55
+ removeListener: (listenerId: string) => void;
11
56
  }
@@ -98,6 +98,7 @@ export interface EnergyAppDiagnostics {
98
98
  * generatedAtIso: new Date().toISOString(),
99
99
  * announced: [
100
100
  * {
101
+ * id: 'charger:flexible-kwh',
101
102
  * category: EnyoApplianceTypeEnum.Charger,
102
103
  * flexibilityAnnouncementType:
103
104
  * EnyoFlexibilityAnnouncementTypeEnum.FlexibleKwhTarget,
@@ -110,6 +111,8 @@ export interface EnergyAppDiagnostics {
110
111
  * },
111
112
  * },
112
113
  * {
114
+ * id: 'storage:flexible-discharge',
115
+ * applianceId: 'battery-1',
113
116
  * category: EnyoApplianceTypeEnum.Storage,
114
117
  * flexibilityAnnouncementType:
115
118
  * EnyoFlexibilityAnnouncementTypeEnum.FlexibleDischarge,
@@ -124,6 +127,7 @@ export interface EnergyAppDiagnostics {
124
127
  * ],
125
128
  * granted: [
126
129
  * {
130
+ * announcementId: 'charger:flexible-kwh',
127
131
  * category: EnyoApplianceTypeEnum.Charger,
128
132
  * flexibilityAnnouncementType:
129
133
  * EnyoFlexibilityAnnouncementTypeEnum.FlexibleKwhTarget,
@@ -134,6 +138,7 @@ export interface EnergyAppDiagnostics {
134
138
  * reason: 'grid connection point limit',
135
139
  * },
136
140
  * {
141
+ * announcementId: 'storage:flexible-discharge',
137
142
  * category: EnyoApplianceTypeEnum.Storage,
138
143
  * flexibilityAnnouncementType:
139
144
  * EnyoFlexibilityAnnouncementTypeEnum.FlexibleDischarge,
@@ -9,3 +9,24 @@ export interface EnyoChargingCard {
9
9
  rfid?: string;
10
10
  pendingRegistration?: boolean;
11
11
  }
12
+ /**
13
+ * Details of a pairing request handed to a
14
+ * {@link EnergyAppChargingCard.listenForPairingStarted} listener when the
15
+ * host asks a package to put its RFID reader into pairing mode.
16
+ */
17
+ export interface EnyoChargingCardPairingRequest {
18
+ /** ID of the charging card record the scanned RFID should be assigned to */
19
+ chargingCardId: string;
20
+ /**
21
+ * Appliance (charger) whose RFID reader should enter pairing mode. Omitted
22
+ * when the host does not target a specific charger — in that case the
23
+ * package decides which of its readers to use.
24
+ */
25
+ applianceId?: string;
26
+ /**
27
+ * Time budget, in milliseconds, the package has to deliver a scanned card.
28
+ * The host stops waiting once it elapses, so a listener that resolves later
29
+ * has no effect. Omitted when the host does not impose a deadline.
30
+ */
31
+ timeoutMs?: number;
32
+ }
@@ -23,6 +23,15 @@ export declare enum EnyoDataBusCommandReasonTypeEnum {
23
23
  PvSurplusAvailable = "pv-surplus-available",
24
24
  /** Command issued because PV surplus is unavailable */
25
25
  PvSurplusUnavailable = "pv-surplus-unavailable",
26
+ /**
27
+ * Command issued because PV surplus exists but is allocated to another
28
+ * appliance. Belongs to the
29
+ * {@link EnyoDataBusCommandReasonCategoryEnum.PvSurplus} category. Set
30
+ * {@link EnyoDataBusCommandReason.inFavourOfApplianceType} so the
31
+ * end-user text can name the appliance that got the surplus instead of
32
+ * saying "elsewhere".
33
+ */
34
+ PvSurplusAllocatedElsewhere = "pv-surplus-allocated-elsewhere",
26
35
  /** Command issued because battery capacity is available */
27
36
  BatteryCapacityAvailable = "battery-capacity-available",
28
37
  /** Command issued because battery capacity is unavailable */
@@ -100,6 +109,16 @@ export interface EnyoDataBusCommandReason {
100
109
  temperatureC?: number;
101
110
  /** Relevant state of charge as a percentage (battery-driven reasons) */
102
111
  socPercent?: number;
112
+ /**
113
+ * The appliance category the decision was made in favour of.
114
+ *
115
+ * Set on reasons that describe a trade-off between appliances — most
116
+ * notably
117
+ * {@link EnyoDataBusCommandReasonTypeEnum.PvSurplusAllocatedElsewhere} —
118
+ * so the end-user text can name the winning appliance (e.g. "the battery")
119
+ * rather than saying "elsewhere".
120
+ */
121
+ inFavourOfApplianceType?: EnyoApplianceTypeEnum;
103
122
  }
104
123
  /**
105
124
  * Whether a grid operator power limitation caps power drawn from the grid
@@ -12,6 +12,15 @@ export var EnyoDataBusCommandReasonTypeEnum;
12
12
  EnyoDataBusCommandReasonTypeEnum["PvSurplusAvailable"] = "pv-surplus-available";
13
13
  /** Command issued because PV surplus is unavailable */
14
14
  EnyoDataBusCommandReasonTypeEnum["PvSurplusUnavailable"] = "pv-surplus-unavailable";
15
+ /**
16
+ * Command issued because PV surplus exists but is allocated to another
17
+ * appliance. Belongs to the
18
+ * {@link EnyoDataBusCommandReasonCategoryEnum.PvSurplus} category. Set
19
+ * {@link EnyoDataBusCommandReason.inFavourOfApplianceType} so the
20
+ * end-user text can name the appliance that got the surplus instead of
21
+ * saying "elsewhere".
22
+ */
23
+ EnyoDataBusCommandReasonTypeEnum["PvSurplusAllocatedElsewhere"] = "pv-surplus-allocated-elsewhere";
15
24
  /** Command issued because battery capacity is available */
16
25
  EnyoDataBusCommandReasonTypeEnum["BatteryCapacityAvailable"] = "battery-capacity-available";
17
26
  /** Command issued because battery capacity is unavailable */
@@ -125,11 +125,49 @@ export interface EnyoFlexibilityAnnouncementContext {
125
125
  heatpumpTargetType?: EnyoFlexibilityHeatpumpTargetTypeEnum;
126
126
  /** For air-conditioning announcements: what the flexibility is aimed at. */
127
127
  airConditioningTargetType?: EnyoFlexibilityAirConditioningTargetTypeEnum;
128
+ /**
129
+ * For storage announcements: the round-trip cost of cycling the battery, in
130
+ * EUR per kWh. The floor a discharge has to beat to be worth licensing.
131
+ */
132
+ storageCycleCostEurPerKwh?: number;
133
+ /**
134
+ * For storage announcements: the physical discharge floor, in Watts. Signed —
135
+ * negative means discharge, so this is the most negative power the storage can
136
+ * physically reach.
137
+ */
138
+ storageMinPowerW?: number;
139
+ /** For storage announcements: the physical charge ceiling, in Watts. */
140
+ storageMaxPowerW?: number;
141
+ /** For storage announcements: usable capacity of the storage, in Wh. */
142
+ storageCapacityWh?: number;
143
+ /**
144
+ * For storage announcements: the discharge power we will actually command, in
145
+ * Watts. May be well inside {@link storageMinPowerW} — today it is `0`, i.e. we
146
+ * license discharge without commanding it.
147
+ */
148
+ storageCommandableDischargeW?: number;
149
+ /**
150
+ * For storage announcements: whether the storage accepts an explicit
151
+ * `Discharge 0` command, i.e. can be told to hold rather than only being left
152
+ * alone.
153
+ */
154
+ storageCanHold?: boolean;
128
155
  }
129
156
  /** Fields shared by every category-scoped flexibility announcement. */
130
157
  export interface EnyoCategoryFlexibilityAnnouncementBase {
158
+ /**
159
+ * What this announcement IS, stable across cycles — e.g. `storage:peak-requirement`.
160
+ * A category speaks with several at once and (category, type) does not tell them
161
+ * apart. REQUIRED: a positional identity changes whenever a neighbour appears.
162
+ */
163
+ id: string;
131
164
  /** The appliance category this demand aggregates (e.g. `Charger`). */
132
165
  category: EnyoApplianceTypeEnum;
166
+ /**
167
+ * The appliance this announcement speaks for, when a category announces per
168
+ * appliance rather than as one aggregate. Absent = the whole category.
169
+ */
170
+ applianceId?: string;
133
171
  /** Optional extra context for the decision maker; absent for most announcements. */
134
172
  context?: EnyoFlexibilityAnnouncementContext;
135
173
  }
@@ -143,6 +181,12 @@ export interface EnyoCategoryRequiredKwhTargetAnnouncement extends EnyoCategoryF
143
181
  kwh: number;
144
182
  /** Power band the delivery may use. */
145
183
  power: EnyoFlexibilityPowerBand;
184
+ /**
185
+ * Price ceiling, when the requirement is economic rather than absolute.
186
+ * Present on the wire today; without it every economic requirement looked
187
+ * like a MUST-RUN in diagnostics.
188
+ */
189
+ priceLimitEuroCent?: number;
146
190
  };
147
191
  }
148
192
  /** Deliver `kwh` flexibly (cost-/PV-optimized), aggregated across the category. */
@@ -224,10 +268,13 @@ export declare enum EnyoCategoryFlexibilityGrantStatusEnum {
224
268
  * A grant is deliberately flatter than the announcement it answers: the decision
225
269
  * maker replies with one allocation — a window, a power envelope, and (for the
226
270
  * energy-target and discharge variants) an amount of energy — regardless of which
227
- * announcement variant produced it. Pairing is by `category` +
228
- * `flexibilityAnnouncementType`, which is unique within a single publish.
271
+ * announcement variant produced it. Pairing is by `announcementId` — the `id` of
272
+ * the announcement being answered — which stays stable across cycles and tells
273
+ * apart the several announcements one category may speak with at once.
229
274
  */
230
275
  export interface EnyoCategoryFlexibilityGrant {
276
+ /** The `id` of the announcement this answers. */
277
+ announcementId: string;
231
278
  /** The appliance category this allocation is for (e.g. `Charger`). */
232
279
  category: EnyoApplianceTypeEnum;
233
280
  /** Which announcement variant this grant answers. */
@@ -270,14 +317,13 @@ export interface EnyoCategoryFlexibilityDiagnostics {
270
317
  /** ISO 8601 timestamp when this snapshot was taken. */
271
318
  generatedAtIso: string;
272
319
  /**
273
- * The flexibility the managers announced, one entry per
274
- * (category, announcement variant).
320
+ * The flexibility the managers announced, one entry per announcement `id`.
275
321
  */
276
322
  announced: EnyoCategoryFlexibilityAnnouncement[];
277
323
  /**
278
324
  * What the decision maker granted in answer to `announced`. Every entry SHOULD
279
- * pair with an `announced` entry by `category` + `flexibilityAnnouncementType`;
280
- * an announcement with no matching grant means the decision had not been taken
325
+ * pair with an `announced` entry by `announcementId`; an announcement with no
326
+ * matching grant means the decision had not been taken
281
327
  * yet when the snapshot was produced.
282
328
  */
283
329
  granted: EnyoCategoryFlexibilityGrant[];
@@ -3,6 +3,8 @@ export declare enum EnyoHeatpumpApplianceAvailableFeaturesEnum {
3
3
  DomesticHotWater = "DomesticHotWater",
4
4
  /** If the heatpump has a heating rod*/
5
5
  HeatingRod = "HeatingRod",
6
+ /** If the heating rod of the heatpump can be actively controlled (steered) by the energy manager */
7
+ HeatingRodControllable = "HeatingRodControllable",
6
8
  /** If the heatpump supports room overheating via heating circuits */
7
9
  RoomOverheating = "RoomOverheating",
8
10
  /** If the heatpump supports buffer tank overheating */
@@ -55,6 +57,17 @@ export declare enum EnyoHeatpumpApplianceAdditionalDeviceEnum {
55
57
  /** A solar thermal system is present in the installation */
56
58
  SolarThermal = "SolarThermal"
57
59
  }
60
+ /**
61
+ * Describes how the heating rod of a heatpump installation is used.
62
+ * Consumers (UI, energy manager) use this to decide whether the heating rod is
63
+ * the sole heat source or only assists the compressor.
64
+ */
65
+ export declare enum EnyoHeatpumpApplianceHeatingRodUsageEnum {
66
+ /** The heating rod is the only heat source used (no compressor support) */
67
+ OnlyHeatingRot = "OnlyHeatingRot",
68
+ /** The heating rod is used in addition to the compressor to further increase the temperature */
69
+ HeatingRodForTemperatureIncrease = "HeatingRodForTemperatureIncrease"
70
+ }
58
71
  /**
59
72
  * The type of heat emitter connected to a heating circuit. Influences the
60
73
  * flow temperatures the circuit operates at (floor heating typically runs at
@@ -124,4 +137,11 @@ export interface EnyoHeatpumpApplianceMetadata {
124
137
  * heatpump (e.g. a heating rod or a solar thermal system).
125
138
  */
126
139
  additionalDevices?: EnyoHeatpumpApplianceAdditionalDeviceEnum[];
140
+ /**
141
+ * How the heating rod of the installation is used (e.g. as the only heat
142
+ * source or only to further increase the temperature on top of the
143
+ * compressor). Only meaningful if the heatpump has a heating rod
144
+ * (see {@link EnyoHeatpumpApplianceAvailableFeaturesEnum.HeatingRod}).
145
+ */
146
+ heatingRodUsage?: EnyoHeatpumpApplianceHeatingRodUsageEnum;
127
147
  }
@@ -4,6 +4,8 @@ export var EnyoHeatpumpApplianceAvailableFeaturesEnum;
4
4
  EnyoHeatpumpApplianceAvailableFeaturesEnum["DomesticHotWater"] = "DomesticHotWater";
5
5
  /** If the heatpump has a heating rod*/
6
6
  EnyoHeatpumpApplianceAvailableFeaturesEnum["HeatingRod"] = "HeatingRod";
7
+ /** If the heating rod of the heatpump can be actively controlled (steered) by the energy manager */
8
+ EnyoHeatpumpApplianceAvailableFeaturesEnum["HeatingRodControllable"] = "HeatingRodControllable";
7
9
  /** If the heatpump supports room overheating via heating circuits */
8
10
  EnyoHeatpumpApplianceAvailableFeaturesEnum["RoomOverheating"] = "RoomOverheating";
9
11
  /** If the heatpump supports buffer tank overheating */
@@ -59,6 +61,18 @@ export var EnyoHeatpumpApplianceAdditionalDeviceEnum;
59
61
  /** A solar thermal system is present in the installation */
60
62
  EnyoHeatpumpApplianceAdditionalDeviceEnum["SolarThermal"] = "SolarThermal";
61
63
  })(EnyoHeatpumpApplianceAdditionalDeviceEnum || (EnyoHeatpumpApplianceAdditionalDeviceEnum = {}));
64
+ /**
65
+ * Describes how the heating rod of a heatpump installation is used.
66
+ * Consumers (UI, energy manager) use this to decide whether the heating rod is
67
+ * the sole heat source or only assists the compressor.
68
+ */
69
+ export var EnyoHeatpumpApplianceHeatingRodUsageEnum;
70
+ (function (EnyoHeatpumpApplianceHeatingRodUsageEnum) {
71
+ /** The heating rod is the only heat source used (no compressor support) */
72
+ EnyoHeatpumpApplianceHeatingRodUsageEnum["OnlyHeatingRot"] = "OnlyHeatingRot";
73
+ /** The heating rod is used in addition to the compressor to further increase the temperature */
74
+ EnyoHeatpumpApplianceHeatingRodUsageEnum["HeatingRodForTemperatureIncrease"] = "HeatingRodForTemperatureIncrease";
75
+ })(EnyoHeatpumpApplianceHeatingRodUsageEnum || (EnyoHeatpumpApplianceHeatingRodUsageEnum = {}));
62
76
  /**
63
77
  * The type of heat emitter connected to a heating circuit. Influences the
64
78
  * flow temperatures the circuit operates at (floor heating typically runs at
package/dist/version.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export declare const SDK_VERSION = "0.0.190";
8
+ export declare const SDK_VERSION = "0.0.192";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
package/dist/version.js CHANGED
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export const SDK_VERSION = '0.0.190';
8
+ export const SDK_VERSION = '0.0.192';
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enyo-energy/energy-app-sdk",
3
- "version": "0.0.190",
3
+ "version": "0.0.192",
4
4
  "description": "enyo Energy App SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",