@enyo-energy/energy-app-sdk 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -712,20 +712,52 @@ Manage charging sessions:
712
712
  ```typescript
713
713
  const charging = energyApp.useCharge();
714
714
 
715
- // Start charging session
716
- const sessionId = await charging.startCharge({
717
- vehicleId: 'vehicle-123',
715
+ // Open a session when the charger reports a StartTransaction
716
+ const charge = await charging.startCharge({
717
+ applianceId: 'appliance-42',
718
+ transactionId: 'tx-1',
719
+ meterStartValueWh: 1_204_000,
718
720
  connectorId: 1,
719
- maxPowerKw: 22
720
721
  });
721
722
 
722
- // Get active sessions
723
- const sessions = await charging.getActiveSessions();
723
+ // Close it when the charger reports a StopTransaction
724
+ const finished = await charging.stopCharge(charge.id, {
725
+ meterEndValueWh: 1_215_500,
726
+ energyDeliveredWh: 11_500,
727
+ });
728
+ console.log(`${finished.totalEnergyKwh} kWh for ${finished.paidPriceEuroCent} ct`);
729
+
730
+ // The session a charger is running right now, if any
731
+ const active = await charging.findActiveCharge('appliance-42');
724
732
 
725
- // Stop charging
726
- await charging.stopCharge(sessionId);
733
+ // A session that spans several OCPP transactions (reconnect, re-authorization)
734
+ await charging.addTransactionIdToActiveCharge('appliance-42', 'tx-2');
735
+
736
+ // Read the history
737
+ const completed = await charging.list({
738
+ applianceId: 'appliance-42',
739
+ status: EnyoChargeStatus.Completed,
740
+ });
727
741
  ```
728
742
 
743
+ **Energy and cost totals are the host's to compute, never the app's.**
744
+ `stopCharge()` derives `totalEnergyKwh` and `paidPriceEuroCent` from the
745
+ session's meter readings and the electricity tariff in force while the energy
746
+ flowed. Pass readings — `meterEndValueWh`, and `energyDeliveredWh` when the
747
+ charger reports it — and let the host price them. An app that computes its own
748
+ totals makes the customer's bill depend on which app closed the session.
749
+
750
+ The same division applies to the samples in `meterValues`: leave `pricePerKwh`
751
+ and `chargingCostEuro` unset and the host resolves the tariff for each sample's
752
+ 15-minute slot itself. `powerW` and `voltageL1..L3` are optional — write them
753
+ when the charger reports them; when omitted the host keeps whatever it already
754
+ recorded for that timestamp.
755
+
756
+ `startCharge()` is idempotent on `applianceId` + `transactionId`, so a repeated
757
+ StartTransaction returns the running session instead of opening a second one.
758
+ `save()` remains a whole-object update for a session that already exists; it
759
+ offers no uniqueness guarantee, so do not open sessions with it.
760
+
729
761
  React to charging sessions as they happen:
730
762
 
731
763
  ```typescript
@@ -148,6 +148,7 @@ function validateOnboardingGuideV2(guide, context) {
148
148
  }
149
149
  validateNetworkScanFlag(guide, warnings);
150
150
  validateApplianceBinding(guide, errors, warnings);
151
+ validateUserNotification(guide, warnings);
151
152
  return { ok: errors.length === 0, errors, warnings };
152
153
  }
153
154
  /**
@@ -681,6 +682,32 @@ function validateApplianceBinding(guide, errors, warnings) {
681
682
  '` guide binds an existing appliance.');
682
683
  }
683
684
  }
685
+ /**
686
+ * Checks {@link EnyoOnboardingV2Guide.notifyUser} against the guide's
687
+ * {@link EnyoOnboardingV2Guide.startVariant}.
688
+ *
689
+ * The flag asks the host to tell the end customer that a maintenance run is
690
+ * happening on their appliance, so it only means anything on
691
+ * {@link EnyoOnboardingV2StartVariant.Maintenance}. An installation guide has
692
+ * nobody to notify — the appliance does not exist yet and the installer is
693
+ * standing in front of the device — so setting it there is a warning, not an
694
+ * error: the host ignores the value, but it is a sign the wrong variant was
695
+ * chosen. Leaving it unset is always fine; it defaults to `false`.
696
+ *
697
+ * @param guide - The guide being validated.
698
+ * @param warnings - Collector for advisory problems.
699
+ */
700
+ function validateUserNotification(guide, warnings) {
701
+ if (guide.notifyUser === undefined)
702
+ return;
703
+ if (guide.startVariant === enyo_onboarding_v2_js_1.EnyoOnboardingV2StartVariant.Maintenance)
704
+ return;
705
+ warnings.push('`notifyUser` is set on a `' +
706
+ `${guide.startVariant}` +
707
+ '` guide and is ignored — only a `' +
708
+ `${enyo_onboarding_v2_js_1.EnyoOnboardingV2StartVariant.Maintenance}` +
709
+ '` guide notifies the end customer about the run.');
710
+ }
684
711
  /**
685
712
  * Checks {@link EnyoOnboardingV2Guide.requiresNetworkScan} against what the guide
686
713
  * actually does.
@@ -88,6 +88,8 @@ __exportStar(require("./types/enyo-heating-rod-appliance.cjs"), exports);
88
88
  __exportStar(require("./types/enyo-charger-appliance.cjs"), exports);
89
89
  __exportStar(require("./types/enyo-charging-card.cjs"), exports);
90
90
  __exportStar(require("./packages/energy-app-charging-card.cjs"), exports);
91
+ __exportStar(require("./types/enyo-charge.cjs"), exports);
92
+ __exportStar(require("./packages/energy-app-charge.cjs"), exports);
91
93
  __exportStar(require("./types/enyo-battery-appliance.cjs"), exports);
92
94
  __exportStar(require("./types/enyo-heatpump-appliance.cjs"), exports);
93
95
  __exportStar(require("./types/enyo-inverter-appliance.cjs"), exports);
@@ -72,6 +72,8 @@ export * from './types/enyo-heating-rod-appliance.cjs';
72
72
  export * from './types/enyo-charger-appliance.cjs';
73
73
  export * from './types/enyo-charging-card.cjs';
74
74
  export * from './packages/energy-app-charging-card.cjs';
75
+ export * from './types/enyo-charge.cjs';
76
+ export * from './packages/energy-app-charge.cjs';
75
77
  export * from './types/enyo-battery-appliance.cjs';
76
78
  export * from './types/enyo-heatpump-appliance.cjs';
77
79
  export * from './types/enyo-inverter-appliance.cjs';
@@ -1,10 +1,94 @@
1
- import { EnyoCharge, EnyoChargeFilter, EnyoDefaultChargeMode } from "../types/enyo-charge.cjs";
1
+ import { EnyoCharge, EnyoChargeFilter, EnyoDefaultChargeMode, EnyoStartChargeRequest, EnyoStopChargeRequest } from "../types/enyo-charge.cjs";
2
2
  /**
3
3
  * Interface for managing charging sessions in enyo packages.
4
4
  * Provides operations for charge session tracking and management.
5
5
  */
6
6
  export interface EnergyAppCharge {
7
- /** Save or update a charging session in the system */
7
+ /**
8
+ * Opens a charging session for an appliance and returns it.
9
+ *
10
+ * Use this to start a session — not {@link save}. `save` writes whatever
11
+ * object it is given and offers no uniqueness guarantee, so two
12
+ * StartTransactions arriving a few milliseconds apart both pass an app-side
13
+ * "is one already active?" check and create two sessions for one plug-in.
14
+ * This call is handled by the host, which holds the invariant that an
15
+ * appliance has at most one active charge.
16
+ *
17
+ * **Idempotent.** When a session is already active for the same
18
+ * `applianceId` + `transactionId`, that session is returned unchanged
19
+ * rather than a second one being created — a repeated StartTransaction is
20
+ * safe. A session that is active on the appliance under a *different*
21
+ * transaction ID is treated as over: the host finalises it with best-effort
22
+ * end values before opening this one. If it is really the same physical
23
+ * session continuing under a new transaction ID (a reconnect, a
24
+ * re-authorization), use {@link addTransactionIdToActiveCharge} instead.
25
+ *
26
+ * The returned charge carries its `id`, so the session can be updated and
27
+ * later stopped without a {@link findActiveCharge} round-trip first.
28
+ *
29
+ * **Required permission:** `Charge`.
30
+ *
31
+ * @param request - What is known at plug-in time.
32
+ * @returns Promise resolving to the started session — or the session that
33
+ * was already running for this appliance and transaction.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const charge = await energyApp.useCharge().startCharge({
38
+ * applianceId,
39
+ * transactionId: ocppTransactionId,
40
+ * meterStartValueWh: meterValue,
41
+ * connectorId: 1,
42
+ * });
43
+ * ```
44
+ */
45
+ startCharge: (request: EnyoStartChargeRequest) => Promise<EnyoCharge>;
46
+ /**
47
+ * Closes a charging session and returns the final record.
48
+ *
49
+ * The host does the arithmetic, and that is the point of this call: it
50
+ * derives {@link EnyoCharge.totalEnergyKwh} and
51
+ * {@link EnyoCharge.paidPriceEuroCent} from the session's meter readings
52
+ * and the electricity tariff in force while the energy flowed, then moves
53
+ * the session to a terminal status and notifies
54
+ * {@link listenForChargeStopped}. An app must not price a session itself —
55
+ * totals computed app-side would differ between apps, and a customer's bill
56
+ * would depend on which app happened to close the charge.
57
+ *
58
+ * Supply readings, not totals: `meterEndValueWh` and, when the charger
59
+ * reports it, `energyDeliveredWh`. Anything an app writes into
60
+ * `paidPriceEuroCent` via {@link save} is overwritten here.
61
+ *
62
+ * Stopping a session that is already in a terminal status is a no-op and
63
+ * returns the stored record — a duplicate StopTransaction will not re-price
64
+ * a finished charge.
65
+ *
66
+ * **Required permission:** `Charge`.
67
+ *
68
+ * @param chargeId - The session to close, as returned by
69
+ * {@link startCharge} or {@link findActiveCharge}.
70
+ * @param request - How the session ended.
71
+ * @returns Promise resolving to the final charge record, totals included.
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * const finished = await energyApp.useCharge().stopCharge(charge.id, {
76
+ * meterEndValueWh: meterValue,
77
+ * energyDeliveredWh: stopTransaction.energyWh,
78
+ * });
79
+ * console.log(`${finished.totalEnergyKwh} kWh for ${finished.paidPriceEuroCent} ct`);
80
+ * ```
81
+ */
82
+ stopCharge: (chargeId: string, request: EnyoStopChargeRequest) => Promise<EnyoCharge>;
83
+ /**
84
+ * Save or update a charging session in the system.
85
+ *
86
+ * This is a whole-object write for a session that already exists — a
87
+ * changed vehicle, a corrected reading. Use {@link startCharge} to open a
88
+ * session and {@link stopCharge} to close one: calling this without a
89
+ * `chargeId` creates a row with no uniqueness guarantee, and writing
90
+ * terminal values by hand skips the host's energy and cost calculation.
91
+ */
8
92
  save: (charge: Omit<EnyoCharge, 'id'>, chargeId?: string) => Promise<void>;
9
93
  /** Get a list of charging sessions with optional filtering */
10
94
  list: (filter?: EnyoChargeFilter) => Promise<EnyoCharge[]>;
@@ -11,16 +11,37 @@ export declare enum EnyoChargeStatus {
11
11
  Failed = "Failed"
12
12
  }
13
13
  /**
14
- * Meter values recorded during a charging session
14
+ * A single sample recorded during a charging session — the charger's meter
15
+ * reading at one instant, plus whatever electrical detail it reported with it.
16
+ *
17
+ * `timestamp` and `valueWh` are the load-bearing pair: every energy total and
18
+ * every cost in a charge is a delta between two samples' `valueWh`, walked in
19
+ * timestamp order (see `calculateChargeCost()`). The rest is detail the charger
20
+ * may or may not report.
21
+ *
22
+ * **Pricing fields are normally filled in by the host, not the app.** Leave
23
+ * {@link pricePerKwh} and {@link chargingCostEuro} unset and the host resolves
24
+ * the tariff for the sample's 15-minute slot and derives the incremental cost
25
+ * itself; set them and your values are kept. Deriving them app-side means
26
+ * reimplementing the tariff lookup, and getting the cent-native unit of
27
+ * `pricePerKwh` wrong there is a silent factor-100 error in the customer's bill.
15
28
  */
16
29
  export interface ChargeMeterValue {
17
30
  /** Timestamp when the value was recorded */
18
31
  timestamp: Date;
19
32
  /** Energy value in Watt hours */
20
33
  valueWh: number;
21
- /** Price per kWh at the time of measurement */
34
+ /**
35
+ * Price per kWh at the time of measurement, in **Euro cents** (ct/kWh) —
36
+ * not Euro. Usually left unset for the host to resolve from the appliance's
37
+ * electricity tariff; see the note on {@link ChargeMeterValue}.
38
+ */
22
39
  pricePerKwh?: number;
23
- /** Charging cost in Euro for this measurement */
40
+ /**
41
+ * Incremental charging cost in **Euro** for this sample — the energy
42
+ * delivered since the previous sample priced at {@link pricePerKwh}, not a
43
+ * running total. Usually left unset for the host to derive.
44
+ */
24
45
  chargingCostEuro?: number;
25
46
  /** Current on phase 1 in Amperes */
26
47
  currentPhase1: number;
@@ -28,6 +49,23 @@ export interface ChargeMeterValue {
28
49
  currentPhase2?: number;
29
50
  /** Current on phase 3 in Amperes */
30
51
  currentPhase3?: number;
52
+ /**
53
+ * Active charging power in Watt at the moment of the sample.
54
+ *
55
+ * Optional because not every charger reports it and it is derivable from
56
+ * the `valueWh` deltas — but only coarsely, at the sampling interval, which
57
+ * is why a charger that does report it should write it here. When omitted,
58
+ * the host keeps whatever power it already recorded for a sample with this
59
+ * timestamp (its own meter-value stream may have seen one) rather than
60
+ * overwriting it with a zero.
61
+ */
62
+ powerW?: number;
63
+ /** Voltage on phase 1 in Volt, when the charger reports it. */
64
+ voltageL1?: number;
65
+ /** Voltage on phase 2 in Volt, when the charger reports it. */
66
+ voltageL2?: number;
67
+ /** Voltage on phase 3 in Volt, when the charger reports it. */
68
+ voltageL3?: number;
31
69
  }
32
70
  /**
33
71
  * Represents a charging session in the enyo system.
@@ -80,6 +118,77 @@ export interface EnyoCharge {
80
118
  /** Target completion time for the charging session as an ISO 8601 timestamp */
81
119
  completeAtIsoTimestamp?: string;
82
120
  }
121
+ /**
122
+ * What is known when a charging session opens — the input to
123
+ * {@link EnergyAppCharge.startCharge}.
124
+ *
125
+ * Everything here is fact at plug-in time. The totals a session is judged by
126
+ * ({@link EnyoCharge.totalEnergyKwh}, {@link EnyoCharge.paidPriceEuroCent}) are
127
+ * absent on purpose: they are the host's to compute when the session ends, from
128
+ * the meter readings and the tariff in force while the energy flowed.
129
+ */
130
+ export interface EnyoStartChargeRequest {
131
+ /** The appliance (charger) running the session. */
132
+ applianceId: string;
133
+ /**
134
+ * The charger's transaction ID for this session (OCPP `transactionId` or
135
+ * the protocol's equivalent).
136
+ *
137
+ * Together with `applianceId` this identifies the session: two chargers may
138
+ * independently issue the same transaction ID, so it is unique per
139
+ * appliance, not globally. It is also the idempotency key —
140
+ * {@link EnergyAppCharge.startCharge} returns the existing session rather
141
+ * than opening a second one when this pair is already active.
142
+ */
143
+ transactionId: string;
144
+ /** When charging started. Defaults to the moment the host handles the call. */
145
+ startTime?: Date;
146
+ /** The charger's meter reading at session start, in Watt hours. */
147
+ meterStartValueWh?: number;
148
+ /** Connector on the charge point the vehicle is plugged into. */
149
+ connectorId?: number;
150
+ /** The charging card that authorized the session, if any. */
151
+ chargingCardId?: string;
152
+ /** The vehicle being charged, if known. */
153
+ vehicleId?: string;
154
+ /** Number of phases charging over. Defaults to 3. */
155
+ numberOfPhases?: number;
156
+ /** Charging mode to apply. Defaults to the appliance's configured default. */
157
+ chargeMode?: EnyoChargeModeEnum;
158
+ /** Target completion time as an ISO 8601 timestamp, for optimized modes. */
159
+ completeAtIsoTimestamp?: string;
160
+ }
161
+ /**
162
+ * How a charging session ended — the input to
163
+ * {@link EnergyAppCharge.stopCharge}.
164
+ *
165
+ * Meter readings, not totals: give the host what the charger reported and it
166
+ * derives the delivered energy and the price. See
167
+ * {@link EnergyAppCharge.stopCharge}.
168
+ */
169
+ export interface EnyoStopChargeRequest {
170
+ /** When charging ended. Defaults to the moment the host handles the call. */
171
+ endTime?: Date;
172
+ /** The charger's meter reading at session end, in Watt hours. */
173
+ meterEndValueWh?: number;
174
+ /**
175
+ * Energy delivered over the session in Watt hours, when the charger reports
176
+ * it directly (OCPP `StopTransaction`).
177
+ *
178
+ * Preferred over the meter delta when present — a charger that meters its
179
+ * own session knows better than a subtraction across two readings that may
180
+ * span a meter reset.
181
+ */
182
+ energyDeliveredWh?: number;
183
+ /**
184
+ * How the session ended. Defaults to {@link EnyoChargeStatus.Completed};
185
+ * pass {@link EnyoChargeStatus.Failed} for a session cut short by a fault.
186
+ *
187
+ * Both are terminal and both fire the stopped listener — a handler that
188
+ * assumes success will mis-report faulted sessions.
189
+ */
190
+ status?: EnyoChargeStatus;
191
+ }
83
192
  /**
84
193
  * Represents a single entry in a charging schedule.
85
194
  * Defines a time-bound current limit for smart charging.
@@ -903,6 +903,28 @@ export interface EnyoOnboardingV2Guide {
903
903
  * {@link validateOnboardingGuideV2} instead of by the compiler.
904
904
  */
905
905
  applianceId?: string;
906
+ /**
907
+ * Whether the end customer is told about this maintenance run — **only
908
+ * meaningful on** {@link EnyoOnboardingV2StartVariant.Maintenance}, and
909
+ * ignored on every other variant.
910
+ *
911
+ * A maintenance run touches an appliance the customer already lives with:
912
+ * it may take the wallbox offline for a few minutes, change how the
913
+ * inverter behaves, or simply happen while nobody is watching. Set this to
914
+ * `true` when the customer should hear about it — the host sends them a
915
+ * notification for the run — and leave it unset when the work is invisible
916
+ * to them and a message would only be noise.
917
+ *
918
+ * Defaults to `false`: a guide says nothing unless it asks to. The
919
+ * installation variants have no customer to notify — there is no appliance
920
+ * yet, and the installer is standing in front of the device — so a guide
921
+ * that sets this anyway is warned about and the value ignored.
922
+ *
923
+ * Optional in the type because one interface serves all four variants; the
924
+ * variant-dependent meaning is enforced by
925
+ * {@link validateOnboardingGuideV2} instead of by the compiler.
926
+ */
927
+ notifyUser?: boolean;
906
928
  /** Optional vendor binding (catalog id). Usually set at publish time. */
907
929
  vendorId?: string;
908
930
  /** Optional model bindings (catalog ids). Usually set at publish time. */
@@ -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 = '1.0.0';
12
+ exports.SDK_VERSION = '1.1.0';
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 = "1.0.0";
8
+ export declare const SDK_VERSION = "1.1.0";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
@@ -142,6 +142,7 @@ export function validateOnboardingGuideV2(guide, context) {
142
142
  }
143
143
  validateNetworkScanFlag(guide, warnings);
144
144
  validateApplianceBinding(guide, errors, warnings);
145
+ validateUserNotification(guide, warnings);
145
146
  return { ok: errors.length === 0, errors, warnings };
146
147
  }
147
148
  /**
@@ -675,6 +676,32 @@ function validateApplianceBinding(guide, errors, warnings) {
675
676
  '` guide binds an existing appliance.');
676
677
  }
677
678
  }
679
+ /**
680
+ * Checks {@link EnyoOnboardingV2Guide.notifyUser} against the guide's
681
+ * {@link EnyoOnboardingV2Guide.startVariant}.
682
+ *
683
+ * The flag asks the host to tell the end customer that a maintenance run is
684
+ * happening on their appliance, so it only means anything on
685
+ * {@link EnyoOnboardingV2StartVariant.Maintenance}. An installation guide has
686
+ * nobody to notify — the appliance does not exist yet and the installer is
687
+ * standing in front of the device — so setting it there is a warning, not an
688
+ * error: the host ignores the value, but it is a sign the wrong variant was
689
+ * chosen. Leaving it unset is always fine; it defaults to `false`.
690
+ *
691
+ * @param guide - The guide being validated.
692
+ * @param warnings - Collector for advisory problems.
693
+ */
694
+ function validateUserNotification(guide, warnings) {
695
+ if (guide.notifyUser === undefined)
696
+ return;
697
+ if (guide.startVariant === EnyoOnboardingV2StartVariant.Maintenance)
698
+ return;
699
+ warnings.push('`notifyUser` is set on a `' +
700
+ `${guide.startVariant}` +
701
+ '` guide and is ignored — only a `' +
702
+ `${EnyoOnboardingV2StartVariant.Maintenance}` +
703
+ '` guide notifies the end customer about the run.');
704
+ }
678
705
  /**
679
706
  * Checks {@link EnyoOnboardingV2Guide.requiresNetworkScan} against what the guide
680
707
  * actually does.
package/dist/index.d.ts CHANGED
@@ -72,6 +72,8 @@ export * from './types/enyo-heating-rod-appliance.js';
72
72
  export * from './types/enyo-charger-appliance.js';
73
73
  export * from './types/enyo-charging-card.js';
74
74
  export * from './packages/energy-app-charging-card.js';
75
+ export * from './types/enyo-charge.js';
76
+ export * from './packages/energy-app-charge.js';
75
77
  export * from './types/enyo-battery-appliance.js';
76
78
  export * from './types/enyo-heatpump-appliance.js';
77
79
  export * from './types/enyo-inverter-appliance.js';
package/dist/index.js CHANGED
@@ -72,6 +72,8 @@ export * from './types/enyo-heating-rod-appliance.js';
72
72
  export * from './types/enyo-charger-appliance.js';
73
73
  export * from './types/enyo-charging-card.js';
74
74
  export * from './packages/energy-app-charging-card.js';
75
+ export * from './types/enyo-charge.js';
76
+ export * from './packages/energy-app-charge.js';
75
77
  export * from './types/enyo-battery-appliance.js';
76
78
  export * from './types/enyo-heatpump-appliance.js';
77
79
  export * from './types/enyo-inverter-appliance.js';
@@ -1,10 +1,94 @@
1
- import { EnyoCharge, EnyoChargeFilter, EnyoDefaultChargeMode } from "../types/enyo-charge.js";
1
+ import { EnyoCharge, EnyoChargeFilter, EnyoDefaultChargeMode, EnyoStartChargeRequest, EnyoStopChargeRequest } from "../types/enyo-charge.js";
2
2
  /**
3
3
  * Interface for managing charging sessions in enyo packages.
4
4
  * Provides operations for charge session tracking and management.
5
5
  */
6
6
  export interface EnergyAppCharge {
7
- /** Save or update a charging session in the system */
7
+ /**
8
+ * Opens a charging session for an appliance and returns it.
9
+ *
10
+ * Use this to start a session — not {@link save}. `save` writes whatever
11
+ * object it is given and offers no uniqueness guarantee, so two
12
+ * StartTransactions arriving a few milliseconds apart both pass an app-side
13
+ * "is one already active?" check and create two sessions for one plug-in.
14
+ * This call is handled by the host, which holds the invariant that an
15
+ * appliance has at most one active charge.
16
+ *
17
+ * **Idempotent.** When a session is already active for the same
18
+ * `applianceId` + `transactionId`, that session is returned unchanged
19
+ * rather than a second one being created — a repeated StartTransaction is
20
+ * safe. A session that is active on the appliance under a *different*
21
+ * transaction ID is treated as over: the host finalises it with best-effort
22
+ * end values before opening this one. If it is really the same physical
23
+ * session continuing under a new transaction ID (a reconnect, a
24
+ * re-authorization), use {@link addTransactionIdToActiveCharge} instead.
25
+ *
26
+ * The returned charge carries its `id`, so the session can be updated and
27
+ * later stopped without a {@link findActiveCharge} round-trip first.
28
+ *
29
+ * **Required permission:** `Charge`.
30
+ *
31
+ * @param request - What is known at plug-in time.
32
+ * @returns Promise resolving to the started session — or the session that
33
+ * was already running for this appliance and transaction.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const charge = await energyApp.useCharge().startCharge({
38
+ * applianceId,
39
+ * transactionId: ocppTransactionId,
40
+ * meterStartValueWh: meterValue,
41
+ * connectorId: 1,
42
+ * });
43
+ * ```
44
+ */
45
+ startCharge: (request: EnyoStartChargeRequest) => Promise<EnyoCharge>;
46
+ /**
47
+ * Closes a charging session and returns the final record.
48
+ *
49
+ * The host does the arithmetic, and that is the point of this call: it
50
+ * derives {@link EnyoCharge.totalEnergyKwh} and
51
+ * {@link EnyoCharge.paidPriceEuroCent} from the session's meter readings
52
+ * and the electricity tariff in force while the energy flowed, then moves
53
+ * the session to a terminal status and notifies
54
+ * {@link listenForChargeStopped}. An app must not price a session itself —
55
+ * totals computed app-side would differ between apps, and a customer's bill
56
+ * would depend on which app happened to close the charge.
57
+ *
58
+ * Supply readings, not totals: `meterEndValueWh` and, when the charger
59
+ * reports it, `energyDeliveredWh`. Anything an app writes into
60
+ * `paidPriceEuroCent` via {@link save} is overwritten here.
61
+ *
62
+ * Stopping a session that is already in a terminal status is a no-op and
63
+ * returns the stored record — a duplicate StopTransaction will not re-price
64
+ * a finished charge.
65
+ *
66
+ * **Required permission:** `Charge`.
67
+ *
68
+ * @param chargeId - The session to close, as returned by
69
+ * {@link startCharge} or {@link findActiveCharge}.
70
+ * @param request - How the session ended.
71
+ * @returns Promise resolving to the final charge record, totals included.
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * const finished = await energyApp.useCharge().stopCharge(charge.id, {
76
+ * meterEndValueWh: meterValue,
77
+ * energyDeliveredWh: stopTransaction.energyWh,
78
+ * });
79
+ * console.log(`${finished.totalEnergyKwh} kWh for ${finished.paidPriceEuroCent} ct`);
80
+ * ```
81
+ */
82
+ stopCharge: (chargeId: string, request: EnyoStopChargeRequest) => Promise<EnyoCharge>;
83
+ /**
84
+ * Save or update a charging session in the system.
85
+ *
86
+ * This is a whole-object write for a session that already exists — a
87
+ * changed vehicle, a corrected reading. Use {@link startCharge} to open a
88
+ * session and {@link stopCharge} to close one: calling this without a
89
+ * `chargeId` creates a row with no uniqueness guarantee, and writing
90
+ * terminal values by hand skips the host's energy and cost calculation.
91
+ */
8
92
  save: (charge: Omit<EnyoCharge, 'id'>, chargeId?: string) => Promise<void>;
9
93
  /** Get a list of charging sessions with optional filtering */
10
94
  list: (filter?: EnyoChargeFilter) => Promise<EnyoCharge[]>;
@@ -11,16 +11,37 @@ export declare enum EnyoChargeStatus {
11
11
  Failed = "Failed"
12
12
  }
13
13
  /**
14
- * Meter values recorded during a charging session
14
+ * A single sample recorded during a charging session — the charger's meter
15
+ * reading at one instant, plus whatever electrical detail it reported with it.
16
+ *
17
+ * `timestamp` and `valueWh` are the load-bearing pair: every energy total and
18
+ * every cost in a charge is a delta between two samples' `valueWh`, walked in
19
+ * timestamp order (see `calculateChargeCost()`). The rest is detail the charger
20
+ * may or may not report.
21
+ *
22
+ * **Pricing fields are normally filled in by the host, not the app.** Leave
23
+ * {@link pricePerKwh} and {@link chargingCostEuro} unset and the host resolves
24
+ * the tariff for the sample's 15-minute slot and derives the incremental cost
25
+ * itself; set them and your values are kept. Deriving them app-side means
26
+ * reimplementing the tariff lookup, and getting the cent-native unit of
27
+ * `pricePerKwh` wrong there is a silent factor-100 error in the customer's bill.
15
28
  */
16
29
  export interface ChargeMeterValue {
17
30
  /** Timestamp when the value was recorded */
18
31
  timestamp: Date;
19
32
  /** Energy value in Watt hours */
20
33
  valueWh: number;
21
- /** Price per kWh at the time of measurement */
34
+ /**
35
+ * Price per kWh at the time of measurement, in **Euro cents** (ct/kWh) —
36
+ * not Euro. Usually left unset for the host to resolve from the appliance's
37
+ * electricity tariff; see the note on {@link ChargeMeterValue}.
38
+ */
22
39
  pricePerKwh?: number;
23
- /** Charging cost in Euro for this measurement */
40
+ /**
41
+ * Incremental charging cost in **Euro** for this sample — the energy
42
+ * delivered since the previous sample priced at {@link pricePerKwh}, not a
43
+ * running total. Usually left unset for the host to derive.
44
+ */
24
45
  chargingCostEuro?: number;
25
46
  /** Current on phase 1 in Amperes */
26
47
  currentPhase1: number;
@@ -28,6 +49,23 @@ export interface ChargeMeterValue {
28
49
  currentPhase2?: number;
29
50
  /** Current on phase 3 in Amperes */
30
51
  currentPhase3?: number;
52
+ /**
53
+ * Active charging power in Watt at the moment of the sample.
54
+ *
55
+ * Optional because not every charger reports it and it is derivable from
56
+ * the `valueWh` deltas — but only coarsely, at the sampling interval, which
57
+ * is why a charger that does report it should write it here. When omitted,
58
+ * the host keeps whatever power it already recorded for a sample with this
59
+ * timestamp (its own meter-value stream may have seen one) rather than
60
+ * overwriting it with a zero.
61
+ */
62
+ powerW?: number;
63
+ /** Voltage on phase 1 in Volt, when the charger reports it. */
64
+ voltageL1?: number;
65
+ /** Voltage on phase 2 in Volt, when the charger reports it. */
66
+ voltageL2?: number;
67
+ /** Voltage on phase 3 in Volt, when the charger reports it. */
68
+ voltageL3?: number;
31
69
  }
32
70
  /**
33
71
  * Represents a charging session in the enyo system.
@@ -80,6 +118,77 @@ export interface EnyoCharge {
80
118
  /** Target completion time for the charging session as an ISO 8601 timestamp */
81
119
  completeAtIsoTimestamp?: string;
82
120
  }
121
+ /**
122
+ * What is known when a charging session opens — the input to
123
+ * {@link EnergyAppCharge.startCharge}.
124
+ *
125
+ * Everything here is fact at plug-in time. The totals a session is judged by
126
+ * ({@link EnyoCharge.totalEnergyKwh}, {@link EnyoCharge.paidPriceEuroCent}) are
127
+ * absent on purpose: they are the host's to compute when the session ends, from
128
+ * the meter readings and the tariff in force while the energy flowed.
129
+ */
130
+ export interface EnyoStartChargeRequest {
131
+ /** The appliance (charger) running the session. */
132
+ applianceId: string;
133
+ /**
134
+ * The charger's transaction ID for this session (OCPP `transactionId` or
135
+ * the protocol's equivalent).
136
+ *
137
+ * Together with `applianceId` this identifies the session: two chargers may
138
+ * independently issue the same transaction ID, so it is unique per
139
+ * appliance, not globally. It is also the idempotency key —
140
+ * {@link EnergyAppCharge.startCharge} returns the existing session rather
141
+ * than opening a second one when this pair is already active.
142
+ */
143
+ transactionId: string;
144
+ /** When charging started. Defaults to the moment the host handles the call. */
145
+ startTime?: Date;
146
+ /** The charger's meter reading at session start, in Watt hours. */
147
+ meterStartValueWh?: number;
148
+ /** Connector on the charge point the vehicle is plugged into. */
149
+ connectorId?: number;
150
+ /** The charging card that authorized the session, if any. */
151
+ chargingCardId?: string;
152
+ /** The vehicle being charged, if known. */
153
+ vehicleId?: string;
154
+ /** Number of phases charging over. Defaults to 3. */
155
+ numberOfPhases?: number;
156
+ /** Charging mode to apply. Defaults to the appliance's configured default. */
157
+ chargeMode?: EnyoChargeModeEnum;
158
+ /** Target completion time as an ISO 8601 timestamp, for optimized modes. */
159
+ completeAtIsoTimestamp?: string;
160
+ }
161
+ /**
162
+ * How a charging session ended — the input to
163
+ * {@link EnergyAppCharge.stopCharge}.
164
+ *
165
+ * Meter readings, not totals: give the host what the charger reported and it
166
+ * derives the delivered energy and the price. See
167
+ * {@link EnergyAppCharge.stopCharge}.
168
+ */
169
+ export interface EnyoStopChargeRequest {
170
+ /** When charging ended. Defaults to the moment the host handles the call. */
171
+ endTime?: Date;
172
+ /** The charger's meter reading at session end, in Watt hours. */
173
+ meterEndValueWh?: number;
174
+ /**
175
+ * Energy delivered over the session in Watt hours, when the charger reports
176
+ * it directly (OCPP `StopTransaction`).
177
+ *
178
+ * Preferred over the meter delta when present — a charger that meters its
179
+ * own session knows better than a subtraction across two readings that may
180
+ * span a meter reset.
181
+ */
182
+ energyDeliveredWh?: number;
183
+ /**
184
+ * How the session ended. Defaults to {@link EnyoChargeStatus.Completed};
185
+ * pass {@link EnyoChargeStatus.Failed} for a session cut short by a fault.
186
+ *
187
+ * Both are terminal and both fire the stopped listener — a handler that
188
+ * assumes success will mis-report faulted sessions.
189
+ */
190
+ status?: EnyoChargeStatus;
191
+ }
83
192
  /**
84
193
  * Represents a single entry in a charging schedule.
85
194
  * Defines a time-bound current limit for smart charging.
@@ -903,6 +903,28 @@ export interface EnyoOnboardingV2Guide {
903
903
  * {@link validateOnboardingGuideV2} instead of by the compiler.
904
904
  */
905
905
  applianceId?: string;
906
+ /**
907
+ * Whether the end customer is told about this maintenance run — **only
908
+ * meaningful on** {@link EnyoOnboardingV2StartVariant.Maintenance}, and
909
+ * ignored on every other variant.
910
+ *
911
+ * A maintenance run touches an appliance the customer already lives with:
912
+ * it may take the wallbox offline for a few minutes, change how the
913
+ * inverter behaves, or simply happen while nobody is watching. Set this to
914
+ * `true` when the customer should hear about it — the host sends them a
915
+ * notification for the run — and leave it unset when the work is invisible
916
+ * to them and a message would only be noise.
917
+ *
918
+ * Defaults to `false`: a guide says nothing unless it asks to. The
919
+ * installation variants have no customer to notify — there is no appliance
920
+ * yet, and the installer is standing in front of the device — so a guide
921
+ * that sets this anyway is warned about and the value ignored.
922
+ *
923
+ * Optional in the type because one interface serves all four variants; the
924
+ * variant-dependent meaning is enforced by
925
+ * {@link validateOnboardingGuideV2} instead of by the compiler.
926
+ */
927
+ notifyUser?: boolean;
906
928
  /** Optional vendor binding (catalog id). Usually set at publish time. */
907
929
  vendorId?: string;
908
930
  /** Optional model bindings (catalog ids). Usually set at publish time. */
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 = "1.0.0";
8
+ export declare const SDK_VERSION = "1.1.0";
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 = '1.0.0';
8
+ export const SDK_VERSION = '1.1.0';
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": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "enyo Energy App SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",