@enyo-energy/energy-app-sdk 0.0.199 → 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 +40 -8
- package/dist/cjs/implementations/onboarding-v2/onboarding-v2-validators.cjs +63 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/packages/energy-app-charge.d.cts +86 -2
- package/dist/cjs/types/enyo-charge.d.cts +112 -3
- package/dist/cjs/types/enyo-onboarding-v2.cjs +21 -0
- package/dist/cjs/types/enyo-onboarding-v2.d.cts +63 -1
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/implementations/onboarding-v2/onboarding-v2-validators.js +64 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/packages/energy-app-charge.d.ts +86 -2
- package/dist/types/enyo-charge.d.ts +112 -3
- package/dist/types/enyo-onboarding-v2.d.ts +63 -1
- package/dist/types/enyo-onboarding-v2.js +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -712,20 +712,52 @@ Manage charging sessions:
|
|
|
712
712
|
```typescript
|
|
713
713
|
const charging = energyApp.useCharge();
|
|
714
714
|
|
|
715
|
-
//
|
|
716
|
-
const
|
|
717
|
-
|
|
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
|
-
//
|
|
723
|
-
const
|
|
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
|
-
//
|
|
726
|
-
await charging.
|
|
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
|
|
@@ -147,6 +147,8 @@ function validateOnboardingGuideV2(guide, context) {
|
|
|
147
147
|
`"${enyo_onboarding_v2_js_1.EnyoOnboardingV2PauseReason.EnyoTodo}" when enyo takes the setup over.`);
|
|
148
148
|
}
|
|
149
149
|
validateNetworkScanFlag(guide, warnings);
|
|
150
|
+
validateApplianceBinding(guide, errors, warnings);
|
|
151
|
+
validateUserNotification(guide, warnings);
|
|
150
152
|
return { ok: errors.length === 0, errors, warnings };
|
|
151
153
|
}
|
|
152
154
|
/**
|
|
@@ -645,6 +647,67 @@ function validateAdditionalSetupBlocks(step, at, errors, warnings) {
|
|
|
645
647
|
}
|
|
646
648
|
}
|
|
647
649
|
}
|
|
650
|
+
/**
|
|
651
|
+
* Checks {@link EnyoOnboardingV2Guide.applianceId} against the guide's
|
|
652
|
+
* {@link EnyoOnboardingV2Guide.startVariant}.
|
|
653
|
+
*
|
|
654
|
+
* The binding is required on {@link EnyoOnboardingV2StartVariant.Maintenance}
|
|
655
|
+
* and meaningless everywhere else. A maintenance run services an appliance that
|
|
656
|
+
* already exists, so a guide that does not name one cannot be bound to anything
|
|
657
|
+
* — that is a blocking error, not advice. The reverse is only a warning: an
|
|
658
|
+
* installation guide carrying an appliance id is harmless, the host ignores it,
|
|
659
|
+
* but it is a sign the wrong variant was chosen.
|
|
660
|
+
*
|
|
661
|
+
* @param guide - The guide being validated.
|
|
662
|
+
* @param errors - Collector for blocking problems.
|
|
663
|
+
* @param warnings - Collector for advisory problems.
|
|
664
|
+
*/
|
|
665
|
+
function validateApplianceBinding(guide, errors, warnings) {
|
|
666
|
+
const isMaintenance = guide.startVariant === enyo_onboarding_v2_js_1.EnyoOnboardingV2StartVariant.Maintenance;
|
|
667
|
+
const applianceId = typeof guide.applianceId === 'string' ? guide.applianceId.trim() : undefined;
|
|
668
|
+
if (isMaintenance) {
|
|
669
|
+
if (!applianceId) {
|
|
670
|
+
errors.push('`applianceId` is required on a `' +
|
|
671
|
+
`${enyo_onboarding_v2_js_1.EnyoOnboardingV2StartVariant.Maintenance}` +
|
|
672
|
+
'` guide — a maintenance run services an appliance that already exists, so the ' +
|
|
673
|
+
'guide must name it.');
|
|
674
|
+
}
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
if (applianceId) {
|
|
678
|
+
warnings.push('`applianceId` is set on a `' +
|
|
679
|
+
`${guide.startVariant}` +
|
|
680
|
+
'` guide and is ignored — only a `' +
|
|
681
|
+
`${enyo_onboarding_v2_js_1.EnyoOnboardingV2StartVariant.Maintenance}` +
|
|
682
|
+
'` guide binds an existing appliance.');
|
|
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
|
+
}
|
|
648
711
|
/**
|
|
649
712
|
* Checks {@link EnyoOnboardingV2Guide.requiresNetworkScan} against what the guide
|
|
650
713
|
* actually does.
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -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);
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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.
|
|
@@ -39,6 +39,27 @@ var EnyoOnboardingV2StartVariant;
|
|
|
39
39
|
EnyoOnboardingV2StartVariant["DeviceFoundConfig"] = "device-found-config";
|
|
40
40
|
/** Manual setup is required by the user (e.g. enter OCPP URL). */
|
|
41
41
|
EnyoOnboardingV2StartVariant["ManualSetup"] = "manual-setup";
|
|
42
|
+
/**
|
|
43
|
+
* Maintenance on an appliance that already exists — the only variant that
|
|
44
|
+
* does not start from an installation.
|
|
45
|
+
*
|
|
46
|
+
* The other three all describe a device on its way *into* the system: not
|
|
47
|
+
* found yet, found but unconfigured, or set up by hand. This one starts from
|
|
48
|
+
* the opposite situation — the appliance is installed, known and running,
|
|
49
|
+
* and the installer is coming back to it to service, reconfigure or
|
|
50
|
+
* reconnect it.
|
|
51
|
+
*
|
|
52
|
+
* Because the appliance is the subject of the run rather than its result, a
|
|
53
|
+
* maintenance guide MUST name it: see
|
|
54
|
+
* {@link EnyoOnboardingV2Guide.applianceId}, which
|
|
55
|
+
* {@link validateOnboardingGuideV2} requires on this variant and on no
|
|
56
|
+
* other. That binding is what fills the `applianceId` the host passes to
|
|
57
|
+
* {@link EnyoOnboardingV2DynamicRequest} and
|
|
58
|
+
* {@link EnyoOnboardingV2AdditionalSetupRequest} — on an installation
|
|
59
|
+
* variant those are populated only once an appliance happens to exist,
|
|
60
|
+
* whereas here they are known from the first step.
|
|
61
|
+
*/
|
|
62
|
+
EnyoOnboardingV2StartVariant["Maintenance"] = "maintenance";
|
|
42
63
|
})(EnyoOnboardingV2StartVariant || (exports.EnyoOnboardingV2StartVariant = EnyoOnboardingV2StartVariant = {}));
|
|
43
64
|
/**
|
|
44
65
|
* Why an onboarding run left the flow through a {@link EnyoOnboardingV2TargetType.Pause}
|
|
@@ -32,7 +32,28 @@ export declare enum EnyoOnboardingV2StartVariant {
|
|
|
32
32
|
/** The device is on the network but still needs configuration. */
|
|
33
33
|
DeviceFoundConfig = "device-found-config",
|
|
34
34
|
/** Manual setup is required by the user (e.g. enter OCPP URL). */
|
|
35
|
-
ManualSetup = "manual-setup"
|
|
35
|
+
ManualSetup = "manual-setup",
|
|
36
|
+
/**
|
|
37
|
+
* Maintenance on an appliance that already exists — the only variant that
|
|
38
|
+
* does not start from an installation.
|
|
39
|
+
*
|
|
40
|
+
* The other three all describe a device on its way *into* the system: not
|
|
41
|
+
* found yet, found but unconfigured, or set up by hand. This one starts from
|
|
42
|
+
* the opposite situation — the appliance is installed, known and running,
|
|
43
|
+
* and the installer is coming back to it to service, reconfigure or
|
|
44
|
+
* reconnect it.
|
|
45
|
+
*
|
|
46
|
+
* Because the appliance is the subject of the run rather than its result, a
|
|
47
|
+
* maintenance guide MUST name it: see
|
|
48
|
+
* {@link EnyoOnboardingV2Guide.applianceId}, which
|
|
49
|
+
* {@link validateOnboardingGuideV2} requires on this variant and on no
|
|
50
|
+
* other. That binding is what fills the `applianceId` the host passes to
|
|
51
|
+
* {@link EnyoOnboardingV2DynamicRequest} and
|
|
52
|
+
* {@link EnyoOnboardingV2AdditionalSetupRequest} — on an installation
|
|
53
|
+
* variant those are populated only once an appliance happens to exist,
|
|
54
|
+
* whereas here they are known from the first step.
|
|
55
|
+
*/
|
|
56
|
+
Maintenance = "maintenance"
|
|
36
57
|
}
|
|
37
58
|
/**
|
|
38
59
|
* Why an onboarding run left the flow through a {@link EnyoOnboardingV2TargetType.Pause}
|
|
@@ -863,6 +884,47 @@ export interface EnyoOnboardingV2Guide {
|
|
|
863
884
|
startStepId: string;
|
|
864
885
|
/** The graph nodes (a set, not an ordered list). */
|
|
865
886
|
steps: EnyoOnboardingV2Step[];
|
|
887
|
+
/**
|
|
888
|
+
* The appliance this guide is bound to — **required on**
|
|
889
|
+
* {@link EnyoOnboardingV2StartVariant.Maintenance}, and meaningless on every
|
|
890
|
+
* other variant.
|
|
891
|
+
*
|
|
892
|
+
* A maintenance run services an appliance that already exists, so the
|
|
893
|
+
* appliance is an input to the run rather than something it produces: the
|
|
894
|
+
* host binds the run to this id and passes it on as
|
|
895
|
+
* {@link EnyoOnboardingV2DynamicRequest.applianceId} and
|
|
896
|
+
* {@link EnyoOnboardingV2AdditionalSetupRequest.applianceId} from the first
|
|
897
|
+
* step onwards. The installation variants have nothing to bind — their
|
|
898
|
+
* appliance is created by the run, if it is created at all — and a guide
|
|
899
|
+
* that sets this anyway is warned about and the value ignored.
|
|
900
|
+
*
|
|
901
|
+
* Optional in the type because one interface serves all four variants; the
|
|
902
|
+
* variant-dependent requirement is enforced by
|
|
903
|
+
* {@link validateOnboardingGuideV2} instead of by the compiler.
|
|
904
|
+
*/
|
|
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;
|
|
866
928
|
/** Optional vendor binding (catalog id). Usually set at publish time. */
|
|
867
929
|
vendorId?: string;
|
|
868
930
|
/** Optional model bindings (catalog ids). Usually set at publish time. */
|
package/dist/cjs/version.cjs
CHANGED
|
@@ -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 = '
|
|
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
|
package/dist/cjs/version.d.cts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* {@link EnyoOnboardingV2PauseReason.EnyoTodo}) — both count, so a guide that
|
|
14
14
|
* legitimately ends in a hand-off is not nagged about a missing success path.
|
|
15
15
|
*/
|
|
16
|
-
import { EnyoOnboardingV2ActionKind, EnyoOnboardingV2EebusPairOutcome, EnyoOnboardingV2BlockType, EnyoOnboardingV2DeviceSelection, EnyoOnboardingV2SetupFieldType, EnyoOnboardingV2InputValueType, EnyoOnboardingV2OcppConnectOutcome, EnyoOnboardingV2PauseReason, EnyoOnboardingV2TargetType, EnyoOnboardingV2TransitionSourceKind, } from '../../types/enyo-onboarding-v2.js';
|
|
16
|
+
import { EnyoOnboardingV2ActionKind, EnyoOnboardingV2EebusPairOutcome, EnyoOnboardingV2BlockType, EnyoOnboardingV2DeviceSelection, EnyoOnboardingV2SetupFieldType, EnyoOnboardingV2InputValueType, EnyoOnboardingV2OcppConnectOutcome, EnyoOnboardingV2PauseReason, EnyoOnboardingV2StartVariant, EnyoOnboardingV2TargetType, EnyoOnboardingV2TransitionSourceKind, } from '../../types/enyo-onboarding-v2.js';
|
|
17
17
|
import { EnyoDeviceTestOutcomeEnum } from '../../types/enyo-device-test.js';
|
|
18
18
|
import { ENYO_ONBOARDING_V2_SETUP_FAILED_OUTCOME } from '../../types/enyo-onboarding-v2-additional-setup.js';
|
|
19
19
|
import { isImagePublicFile } from '../files/public-file-validators.js';
|
|
@@ -141,6 +141,8 @@ export function validateOnboardingGuideV2(guide, context) {
|
|
|
141
141
|
`"${EnyoOnboardingV2PauseReason.EnyoTodo}" when enyo takes the setup over.`);
|
|
142
142
|
}
|
|
143
143
|
validateNetworkScanFlag(guide, warnings);
|
|
144
|
+
validateApplianceBinding(guide, errors, warnings);
|
|
145
|
+
validateUserNotification(guide, warnings);
|
|
144
146
|
return { ok: errors.length === 0, errors, warnings };
|
|
145
147
|
}
|
|
146
148
|
/**
|
|
@@ -639,6 +641,67 @@ function validateAdditionalSetupBlocks(step, at, errors, warnings) {
|
|
|
639
641
|
}
|
|
640
642
|
}
|
|
641
643
|
}
|
|
644
|
+
/**
|
|
645
|
+
* Checks {@link EnyoOnboardingV2Guide.applianceId} against the guide's
|
|
646
|
+
* {@link EnyoOnboardingV2Guide.startVariant}.
|
|
647
|
+
*
|
|
648
|
+
* The binding is required on {@link EnyoOnboardingV2StartVariant.Maintenance}
|
|
649
|
+
* and meaningless everywhere else. A maintenance run services an appliance that
|
|
650
|
+
* already exists, so a guide that does not name one cannot be bound to anything
|
|
651
|
+
* — that is a blocking error, not advice. The reverse is only a warning: an
|
|
652
|
+
* installation guide carrying an appliance id is harmless, the host ignores it,
|
|
653
|
+
* but it is a sign the wrong variant was chosen.
|
|
654
|
+
*
|
|
655
|
+
* @param guide - The guide being validated.
|
|
656
|
+
* @param errors - Collector for blocking problems.
|
|
657
|
+
* @param warnings - Collector for advisory problems.
|
|
658
|
+
*/
|
|
659
|
+
function validateApplianceBinding(guide, errors, warnings) {
|
|
660
|
+
const isMaintenance = guide.startVariant === EnyoOnboardingV2StartVariant.Maintenance;
|
|
661
|
+
const applianceId = typeof guide.applianceId === 'string' ? guide.applianceId.trim() : undefined;
|
|
662
|
+
if (isMaintenance) {
|
|
663
|
+
if (!applianceId) {
|
|
664
|
+
errors.push('`applianceId` is required on a `' +
|
|
665
|
+
`${EnyoOnboardingV2StartVariant.Maintenance}` +
|
|
666
|
+
'` guide — a maintenance run services an appliance that already exists, so the ' +
|
|
667
|
+
'guide must name it.');
|
|
668
|
+
}
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if (applianceId) {
|
|
672
|
+
warnings.push('`applianceId` is set on a `' +
|
|
673
|
+
`${guide.startVariant}` +
|
|
674
|
+
'` guide and is ignored — only a `' +
|
|
675
|
+
`${EnyoOnboardingV2StartVariant.Maintenance}` +
|
|
676
|
+
'` guide binds an existing appliance.');
|
|
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
|
+
}
|
|
642
705
|
/**
|
|
643
706
|
* Checks {@link EnyoOnboardingV2Guide.requiresNetworkScan} against what the guide
|
|
644
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
|
-
/**
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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.
|
|
@@ -32,7 +32,28 @@ export declare enum EnyoOnboardingV2StartVariant {
|
|
|
32
32
|
/** The device is on the network but still needs configuration. */
|
|
33
33
|
DeviceFoundConfig = "device-found-config",
|
|
34
34
|
/** Manual setup is required by the user (e.g. enter OCPP URL). */
|
|
35
|
-
ManualSetup = "manual-setup"
|
|
35
|
+
ManualSetup = "manual-setup",
|
|
36
|
+
/**
|
|
37
|
+
* Maintenance on an appliance that already exists — the only variant that
|
|
38
|
+
* does not start from an installation.
|
|
39
|
+
*
|
|
40
|
+
* The other three all describe a device on its way *into* the system: not
|
|
41
|
+
* found yet, found but unconfigured, or set up by hand. This one starts from
|
|
42
|
+
* the opposite situation — the appliance is installed, known and running,
|
|
43
|
+
* and the installer is coming back to it to service, reconfigure or
|
|
44
|
+
* reconnect it.
|
|
45
|
+
*
|
|
46
|
+
* Because the appliance is the subject of the run rather than its result, a
|
|
47
|
+
* maintenance guide MUST name it: see
|
|
48
|
+
* {@link EnyoOnboardingV2Guide.applianceId}, which
|
|
49
|
+
* {@link validateOnboardingGuideV2} requires on this variant and on no
|
|
50
|
+
* other. That binding is what fills the `applianceId` the host passes to
|
|
51
|
+
* {@link EnyoOnboardingV2DynamicRequest} and
|
|
52
|
+
* {@link EnyoOnboardingV2AdditionalSetupRequest} — on an installation
|
|
53
|
+
* variant those are populated only once an appliance happens to exist,
|
|
54
|
+
* whereas here they are known from the first step.
|
|
55
|
+
*/
|
|
56
|
+
Maintenance = "maintenance"
|
|
36
57
|
}
|
|
37
58
|
/**
|
|
38
59
|
* Why an onboarding run left the flow through a {@link EnyoOnboardingV2TargetType.Pause}
|
|
@@ -863,6 +884,47 @@ export interface EnyoOnboardingV2Guide {
|
|
|
863
884
|
startStepId: string;
|
|
864
885
|
/** The graph nodes (a set, not an ordered list). */
|
|
865
886
|
steps: EnyoOnboardingV2Step[];
|
|
887
|
+
/**
|
|
888
|
+
* The appliance this guide is bound to — **required on**
|
|
889
|
+
* {@link EnyoOnboardingV2StartVariant.Maintenance}, and meaningless on every
|
|
890
|
+
* other variant.
|
|
891
|
+
*
|
|
892
|
+
* A maintenance run services an appliance that already exists, so the
|
|
893
|
+
* appliance is an input to the run rather than something it produces: the
|
|
894
|
+
* host binds the run to this id and passes it on as
|
|
895
|
+
* {@link EnyoOnboardingV2DynamicRequest.applianceId} and
|
|
896
|
+
* {@link EnyoOnboardingV2AdditionalSetupRequest.applianceId} from the first
|
|
897
|
+
* step onwards. The installation variants have nothing to bind — their
|
|
898
|
+
* appliance is created by the run, if it is created at all — and a guide
|
|
899
|
+
* that sets this anyway is warned about and the value ignored.
|
|
900
|
+
*
|
|
901
|
+
* Optional in the type because one interface serves all four variants; the
|
|
902
|
+
* variant-dependent requirement is enforced by
|
|
903
|
+
* {@link validateOnboardingGuideV2} instead of by the compiler.
|
|
904
|
+
*/
|
|
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;
|
|
866
928
|
/** Optional vendor binding (catalog id). Usually set at publish time. */
|
|
867
929
|
vendorId?: string;
|
|
868
930
|
/** Optional model bindings (catalog ids). Usually set at publish time. */
|
|
@@ -36,6 +36,27 @@ export var EnyoOnboardingV2StartVariant;
|
|
|
36
36
|
EnyoOnboardingV2StartVariant["DeviceFoundConfig"] = "device-found-config";
|
|
37
37
|
/** Manual setup is required by the user (e.g. enter OCPP URL). */
|
|
38
38
|
EnyoOnboardingV2StartVariant["ManualSetup"] = "manual-setup";
|
|
39
|
+
/**
|
|
40
|
+
* Maintenance on an appliance that already exists — the only variant that
|
|
41
|
+
* does not start from an installation.
|
|
42
|
+
*
|
|
43
|
+
* The other three all describe a device on its way *into* the system: not
|
|
44
|
+
* found yet, found but unconfigured, or set up by hand. This one starts from
|
|
45
|
+
* the opposite situation — the appliance is installed, known and running,
|
|
46
|
+
* and the installer is coming back to it to service, reconfigure or
|
|
47
|
+
* reconnect it.
|
|
48
|
+
*
|
|
49
|
+
* Because the appliance is the subject of the run rather than its result, a
|
|
50
|
+
* maintenance guide MUST name it: see
|
|
51
|
+
* {@link EnyoOnboardingV2Guide.applianceId}, which
|
|
52
|
+
* {@link validateOnboardingGuideV2} requires on this variant and on no
|
|
53
|
+
* other. That binding is what fills the `applianceId` the host passes to
|
|
54
|
+
* {@link EnyoOnboardingV2DynamicRequest} and
|
|
55
|
+
* {@link EnyoOnboardingV2AdditionalSetupRequest} — on an installation
|
|
56
|
+
* variant those are populated only once an appliance happens to exist,
|
|
57
|
+
* whereas here they are known from the first step.
|
|
58
|
+
*/
|
|
59
|
+
EnyoOnboardingV2StartVariant["Maintenance"] = "maintenance";
|
|
39
60
|
})(EnyoOnboardingV2StartVariant || (EnyoOnboardingV2StartVariant = {}));
|
|
40
61
|
/**
|
|
41
62
|
* Why an onboarding run left the flow through a {@link EnyoOnboardingV2TargetType.Pause}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED