@enyo-energy/energy-app-sdk 1.31.0 → 1.32.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 +39 -1
- package/dist/cjs/energy-app-package-definition.d.cts +142 -0
- package/dist/cjs/implementations/onboarding-v2/package-onboarding-guide-validators.cjs +213 -0
- package/dist/cjs/implementations/onboarding-v2/package-onboarding-guide-validators.d.cts +75 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/types/enyo-data-bus-value.cjs +31 -0
- package/dist/cjs/types/enyo-data-bus-value.d.cts +54 -3
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/energy-app-package-definition.d.ts +142 -0
- package/dist/implementations/onboarding-v2/package-onboarding-guide-validators.d.ts +75 -0
- package/dist/implementations/onboarding-v2/package-onboarding-guide-validators.js +207 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/enyo-data-bus-value.d.ts +54 -3
- package/dist/types/enyo-data-bus-value.js +31 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@ The official TypeScript SDK for building Energy Apps on the enyo platform. Creat
|
|
|
33
33
|
- [NetworkDeviceManager](#networkdevicemanager)
|
|
34
34
|
- [Startup pattern](#startup-pattern)
|
|
35
35
|
- [Package Files](#package-files)
|
|
36
|
+
- [Printable Manual Setup Guide](#printable-manual-setup-guide)
|
|
36
37
|
- [Firmware Update Registry](#firmware-update-registry)
|
|
37
38
|
- [Declaring firmware](#declaring-firmware)
|
|
38
39
|
- [Firmware modes](#firmware-modes)
|
|
@@ -2851,6 +2852,43 @@ It checks that names are unique slugs, that paths stay inside the package and ca
|
|
|
2851
2852
|
extension, and that any declared MIME type is well formed; two names pointing at one
|
|
2852
2853
|
path is a warning.
|
|
2853
2854
|
|
|
2855
|
+
## Printable Manual Setup Guide
|
|
2856
|
+
|
|
2857
|
+
A package can declare **one optional** onboarding guide (v2) as static data in its package
|
|
2858
|
+
definition. The enyo CLI reads it during `enyo release`, and enyo renders it as a printable
|
|
2859
|
+
PDF installation manual. It is separate from the interactive guides an app returns from
|
|
2860
|
+
`useOnboardingV2().registerOnboardingGuidesHandler()`: it never takes part in an onboarding
|
|
2861
|
+
run, so it needs no `name` and doesn't conflict with those guides in any way.
|
|
2862
|
+
|
|
2863
|
+
Paper cannot scan the network or ask the app anything, so the guide must describe a
|
|
2864
|
+
**manual setup**:
|
|
2865
|
+
|
|
2866
|
+
- `startVariant: EnyoOnboardingV2StartVariant.ManualSetup` and `requiresNetworkScan: false`
|
|
2867
|
+
- no `network-scan` / `device-test` actions, no `device-select` / `eebus-device-select`
|
|
2868
|
+
blocks, no `dynamic` blocks, no `start-variant` targets
|
|
2869
|
+
- no `applianceId`, `vendorId` or `modelIds`
|
|
2870
|
+
- plain JSON only, and every image block `file` declared in `files`
|
|
2871
|
+
|
|
2872
|
+
```typescript
|
|
2873
|
+
import {
|
|
2874
|
+
defineEnergyAppPackage,
|
|
2875
|
+
definePublicFile,
|
|
2876
|
+
validatePackageOnboardingGuide
|
|
2877
|
+
} from '@enyo-energy/energy-app-sdk';
|
|
2878
|
+
import {manualSetupGuide} from './src/onboarding/manual-setup.guide.js';
|
|
2879
|
+
|
|
2880
|
+
const packageDef = defineEnergyAppPackage({
|
|
2881
|
+
// ...
|
|
2882
|
+
files: [definePublicFile({name: 'modbus-menu', path: './assets/modbus-menu.png'})],
|
|
2883
|
+
onboardingGuide: manualSetupGuide
|
|
2884
|
+
});
|
|
2885
|
+
|
|
2886
|
+
const {ok, errors, warnings} = validatePackageOnboardingGuide(packageDef);
|
|
2887
|
+
```
|
|
2888
|
+
|
|
2889
|
+
The complete authoring guide, with the reason behind each rule and a full example, is the
|
|
2890
|
+
JSDoc of `EnergyAppPackageDefinition.onboardingGuide`.
|
|
2891
|
+
|
|
2854
2892
|
## Firmware Update Registry
|
|
2855
2893
|
|
|
2856
2894
|
Ship firmware images with your package and hand them to devices at runtime. You declare the files by local path in the package definition, the enyo CLI uploads them during `enyo release`, and the app reaches them through `energyApp.useFirmwareRegistry()`. The release tarball never carries the bytes.
|
|
@@ -4279,7 +4317,7 @@ Each event carries the **complete** picture of the slot — render it as it arri
|
|
|
4279
4317
|
|
|
4280
4318
|
New reason types came with this surface, so a skipped row can say *why* instead of falling back to a generic "scheduled optimization": `SessionComplete`, `AppliancePaused`, `NothingConnected`, `DeadlinePassed`, `WaitingForCheaperSlot`, `AboveOwnPriceLimit`, `BelowMinPower`, `OtherApplianceTurn`, `SupplyExhausted`, `OutsideSchedule` — grouped by the new `SessionState` and `Contention` reason categories. `ApplianceInitiatedDraw` and `PowerOffered` came with the waterfall states above.
|
|
4281
4319
|
|
|
4282
|
-
**Battery vs. charging car.** While a car charges, the house battery is either held out of it or allowed to help, according to the owner's `batteryEvDischargeMode`. Two reason types (category `BatteryState`) say which: `BatteryReservedFromEv` for the hold (`Discharge 0`) and `BatterySupportsEvCharging` for the release (`mode=Auto`). Both carry `context.batteryToEv` (`EnyoDataBusCommandReasonBatteryToEvContext`) with the owner's `mode`, the hold `trigger` (`EnyoBatteryToEvHoldTriggerEnum`: `OwnerBlocked`, `SocLimitReached`, `AllowanceSpent`, `NoMeasurement`), `socLimitPercent`, `allowanceWh` and `remainingWh`. The energy manager re-checks a hold periodically by briefly releasing the battery; that re-check sets `batteryToEv.probe: true` and should not be listed as its own entry in a command history.
|
|
4320
|
+
**Battery vs. charging car.** While a car charges, the house battery is either held out of it or allowed to help, according to the owner's `batteryEvDischargeMode`. Two reason types (category `BatteryState`) say which: `BatteryReservedFromEv` for the hold (`Discharge 0`) and `BatterySupportsEvCharging` for the release (`mode=Auto`). Both carry `context.batteryToEv` (`EnyoDataBusCommandReasonBatteryToEvContext`) with the owner's `mode`, the hold `trigger` (`EnyoBatteryToEvHoldTriggerEnum`: `OwnerBlocked`, `SocLimitReached`, `AllowanceSpent`, `NoMeasurement`), `socLimitPercent`, `allowanceWh` and `remainingWh`. The energy manager re-checks a hold periodically by briefly releasing the battery; that re-check sets `batteryToEv.probe: true` and should not be listed as its own entry in a command history. The car's side of the same decision is stated on the charger's command with `EvChargingFromHomeBattery` (also category `BatteryState`): `powerW` is the battery's share of the charging setpoint (never the whole setpoint), `socPercent` the battery fleet's SoC, `context.batteryToEv` the owner's mode and the session's `socLimitPercent` / `allowanceWh` / `remainingWh` (never `trigger` or `probe`), and, under `Intelligent` only, `electricityPricePerKwh` is what the stored energy costs.
|
|
4283
4321
|
|
|
4284
4322
|
**Battery first on PV surplus.** When a surplus consumer (EV on PV, heating rod, heat pump boost, surplus automation) is held back or reduced because the battery is charged first up to the owner's `batteryPvPriorityTargetSocPercent`, the energy manager states `BatteryPvPriority` (category `PvSurplus`) on that consumer: `context.batteryPvPriority.targetSocPercent` for the target, `socPercent` for the pack's current SoC and `powerW` for the surplus the battery is taking — e.g. "The battery charges to 40 % first (now 28 %)."
|
|
4285
4323
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EnergyAppPermissionType } from "./energy-app-permission.type.cjs";
|
|
2
2
|
import { EnergyAppModelFeatureEnum } from "./energy-app-model-feature.enum.cjs";
|
|
3
3
|
import type { EnyoFileTranslation } from "./types/enyo-file.cjs";
|
|
4
|
+
import type { EnyoOnboardingV2Guide } from "./types/enyo-onboarding-v2.cjs";
|
|
4
5
|
export type EnergyAppPackageLanguage = 'de' | 'en';
|
|
5
6
|
export declare enum EnergyAppPackageCategory {
|
|
6
7
|
Inverter = "inverter",
|
|
@@ -658,6 +659,147 @@ export interface EnergyAppPackageDefinition {
|
|
|
658
659
|
* Only relevant when {@link firmware} is declared.
|
|
659
660
|
*/
|
|
660
661
|
firmwareMode?: EnergyAppPackageFirmwareMode;
|
|
662
|
+
/**
|
|
663
|
+
* The package's static **manual setup** onboarding guide (v2). It is
|
|
664
|
+
* optional and there is at most one. The enyo CLI reads it at release time
|
|
665
|
+
* and ships it with the package version, where enyo renders it as a
|
|
666
|
+
* printable PDF installation manual.
|
|
667
|
+
*
|
|
668
|
+
* ## What it is for
|
|
669
|
+
*
|
|
670
|
+
* The guides an app returns from
|
|
671
|
+
* {@link EnergyAppOnboardingV2.registerOnboardingGuidesHandler} are
|
|
672
|
+
* interactive and only exist on a device where the app is running. This
|
|
673
|
+
* guide is the opposite: a fixed document that exists as soon as the
|
|
674
|
+
* package is released. An installer can read it on paper before the
|
|
675
|
+
* device is even powered. It is written with the same v2 guide model, so
|
|
676
|
+
* an app that already has an interactive manual setup flow can usually
|
|
677
|
+
* declare the same steps here.
|
|
678
|
+
*
|
|
679
|
+
* It does not take part in onboarding runs. It is never offered by the
|
|
680
|
+
* host, never addressed by name and never compared against the guides from
|
|
681
|
+
* the runtime handler, so there are no naming or precedence rules between
|
|
682
|
+
* the two.
|
|
683
|
+
*
|
|
684
|
+
* ## What it may contain
|
|
685
|
+
*
|
|
686
|
+
* Paper cannot scan the network or ask the app anything, so the guide is
|
|
687
|
+
* limited to a manual setup the installer can follow by reading. The
|
|
688
|
+
* following rules are enforced by `validatePackageOnboardingGuide()`, and
|
|
689
|
+
* the CLI refuses the release if any of them is broken:
|
|
690
|
+
*
|
|
691
|
+
* | Rule | Why |
|
|
692
|
+
* |---|---|
|
|
693
|
+
* | `startVariant` is {@link EnyoOnboardingV2StartVariant.ManualSetup} | The document describes a manual setup, nothing else. |
|
|
694
|
+
* | `requiresNetworkScan: false` is set explicitly | The default is `true`, and a printed manual has no scan to rely on. |
|
|
695
|
+
* | No `network-scan` or `device-test` action blocks | Both act on devices found by a scan. |
|
|
696
|
+
* | No `device-select` or `eebus-device-select` blocks | Both pick from devices the host discovered. |
|
|
697
|
+
* | No `dynamic` blocks | Their value is resolved by the app at runtime and does not exist when the PDF is rendered. |
|
|
698
|
+
* | No `start-variant` targets | They lead into another, scan-based guide that is not part of the document. |
|
|
699
|
+
* | No `applianceId`, `vendorId` or `modelIds` | A document is not bound to an appliance, and enyo derives vendor and models from the package. |
|
|
700
|
+
* | Plain JSON only (no functions, `Date`s, `undefined` in arrays, class instances) | The CLI serializes the guide into the release. |
|
|
701
|
+
* | Every image block `file` is declared in {@link files} | Otherwise the PDF has a hole where the image belongs. |
|
|
702
|
+
*
|
|
703
|
+
* Everything else is allowed. Content blocks (text, headline, bullets,
|
|
704
|
+
* image, hint, link) render as they are. Interactive blocks (choice,
|
|
705
|
+
* select, input, credentials, auth, additional-setup and the
|
|
706
|
+
* `connection-check` / `ocpp-connect` actions) render as instructions,
|
|
707
|
+
* and their outcomes and transitions become the branches of the document
|
|
708
|
+
* ("if the device is not reachable, continue with …"). Give every
|
|
709
|
+
* outcome a label that reads well on paper.
|
|
710
|
+
*
|
|
711
|
+
* `name` is optional and has no meaning here.
|
|
712
|
+
*
|
|
713
|
+
* ## How to write one
|
|
714
|
+
*
|
|
715
|
+
* 1. Put the guide in its own file (for example
|
|
716
|
+
* `src/onboarding/manual-setup.guide.ts`) and build it with
|
|
717
|
+
* `defineOnboardingGuideV2()`, the `onboardingV2Block.*` and
|
|
718
|
+
* `onboardingV2Target.*` factories, and the `onContinueV2()` /
|
|
719
|
+
* `onOutcomeV2()` transition helpers.
|
|
720
|
+
* 2. Keep the file free of runtime dependencies. The CLI loads it while
|
|
721
|
+
* loading this definition, outside the app sandbox, so it must not
|
|
722
|
+
* touch the SDK instance, storage, the network or environment state.
|
|
723
|
+
* 3. Import it here and assign it to `onboardingGuide`.
|
|
724
|
+
* 4. Check it locally with `validatePackageOnboardingGuide(definition)`.
|
|
725
|
+
* 5. Release. The CLI validates the guide again and aborts on errors.
|
|
726
|
+
*
|
|
727
|
+
* ```typescript
|
|
728
|
+
* // src/onboarding/manual-setup.guide.ts
|
|
729
|
+
* import {
|
|
730
|
+
* defineOnboardingGuideV2,
|
|
731
|
+
* EnyoDeviceTestOutcomeEnum,
|
|
732
|
+
* EnyoOnboardingV2InputValueType,
|
|
733
|
+
* EnyoOnboardingV2StartVariant,
|
|
734
|
+
* onboardingV2Block,
|
|
735
|
+
* onboardingV2Target,
|
|
736
|
+
* onContinueV2,
|
|
737
|
+
* onOutcomeV2,
|
|
738
|
+
* } from '@enyo-energy/energy-app-sdk';
|
|
739
|
+
*
|
|
740
|
+
* const t = (de: string, en: string) => [
|
|
741
|
+
* {language: 'de' as const, value: de},
|
|
742
|
+
* {language: 'en' as const, value: en},
|
|
743
|
+
* ];
|
|
744
|
+
*
|
|
745
|
+
* export const manualSetupGuide = defineOnboardingGuideV2({
|
|
746
|
+
* title: t('Wechselrichter manuell einrichten', 'Set up the inverter manually'),
|
|
747
|
+
* startVariant: EnyoOnboardingV2StartVariant.ManualSetup,
|
|
748
|
+
* requiresNetworkScan: false,
|
|
749
|
+
* startStepId: 'prepare',
|
|
750
|
+
* steps: [
|
|
751
|
+
* {
|
|
752
|
+
* id: 'prepare',
|
|
753
|
+
* name: 'modbus-aktivieren',
|
|
754
|
+
* title: t('Modbus TCP aktivieren', 'Enable Modbus TCP'),
|
|
755
|
+
* blocks: [
|
|
756
|
+
* onboardingV2Block.text('intro', t(
|
|
757
|
+
* 'Aktiviere Modbus TCP im Webinterface des Wechselrichters.',
|
|
758
|
+
* 'Enable Modbus TCP in the inverter web interface.',
|
|
759
|
+
* )),
|
|
760
|
+
* onboardingV2Block.imageFile('menu', 'modbus-menu', t('Menü', 'Menu')),
|
|
761
|
+
* ],
|
|
762
|
+
* transitions: [onContinueV2(onboardingV2Target.step('ip'))],
|
|
763
|
+
* },
|
|
764
|
+
* {
|
|
765
|
+
* id: 'ip',
|
|
766
|
+
* name: 'ip-eingeben',
|
|
767
|
+
* title: t('IP-Adresse eingeben', 'Enter the IP address'),
|
|
768
|
+
* blocks: [
|
|
769
|
+
* onboardingV2Block.input(
|
|
770
|
+
* 'device-ip',
|
|
771
|
+
* EnyoOnboardingV2InputValueType.IpAddress,
|
|
772
|
+
* t('IP-Adresse des Wechselrichters', 'Inverter IP address'),
|
|
773
|
+
* t('Verbindung prüfen', 'Check connection'),
|
|
774
|
+
* [
|
|
775
|
+
* {id: 'ok', value: EnyoDeviceTestOutcomeEnum.AppliancesCreated, label: t('Verbunden', 'Connected')},
|
|
776
|
+
* {id: 'unreachable', value: EnyoDeviceTestOutcomeEnum.Unreachable, label: t('Nicht erreichbar', 'Unreachable')},
|
|
777
|
+
* ],
|
|
778
|
+
* {placeholder: t('z. B. 192.168.1.42', 'e.g. 192.168.1.42')},
|
|
779
|
+
* ),
|
|
780
|
+
* ],
|
|
781
|
+
* transitions: [
|
|
782
|
+
* onOutcomeV2('device-ip', 'ok', onboardingV2Target.success()),
|
|
783
|
+
* onOutcomeV2('device-ip', 'unreachable', onboardingV2Target.step('prepare')),
|
|
784
|
+
* ],
|
|
785
|
+
* },
|
|
786
|
+
* ],
|
|
787
|
+
* });
|
|
788
|
+
*
|
|
789
|
+
* // energy-app.package.ts
|
|
790
|
+
* import {defineEnergyAppPackage, definePublicFile} from '@enyo-energy/energy-app-sdk';
|
|
791
|
+
* import {manualSetupGuide} from './src/onboarding/manual-setup.guide.js';
|
|
792
|
+
*
|
|
793
|
+
* export default defineEnergyAppPackage({
|
|
794
|
+
* // ...
|
|
795
|
+
* files: [definePublicFile({name: 'modbus-menu', path: './assets/modbus-menu.png'})],
|
|
796
|
+
* onboardingGuide: manualSetupGuide,
|
|
797
|
+
* });
|
|
798
|
+
* ```
|
|
799
|
+
*
|
|
800
|
+
* Omit the field for packages that have no manual setup to document.
|
|
801
|
+
*/
|
|
802
|
+
onboardingGuide?: EnyoOnboardingV2Guide;
|
|
661
803
|
}
|
|
662
804
|
/**
|
|
663
805
|
* Defines an Energy App package with automatic SDK version injection.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Client-side validator for the static manual setup guide declared in a
|
|
4
|
+
* package definition ({@link EnergyAppPackageDefinition.onboardingGuide}).
|
|
5
|
+
*
|
|
6
|
+
* The guide leaves the app repository as data: the enyo CLI reads it at release
|
|
7
|
+
* time and enyo renders it as a printable PDF. Nothing on paper can scan the
|
|
8
|
+
* network or ask the app for a value, so on top of the regular graph checks of
|
|
9
|
+
* {@link validateOnboardingGuideV2} this rejects everything that only works in
|
|
10
|
+
* an interactive run — scans, device pickers, device tests, dynamic values —
|
|
11
|
+
* and everything the CLI cannot serialize.
|
|
12
|
+
*
|
|
13
|
+
* Use {@link validatePackageOnboardingGuide} for the non-throwing result, or
|
|
14
|
+
* {@link assertValidPackageOnboardingGuide} to throw on failure.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.PackageOnboardingGuideValidationError = exports.PACKAGE_ONBOARDING_GUIDE_START_VARIANT = void 0;
|
|
18
|
+
exports.validatePackageOnboardingGuide = validatePackageOnboardingGuide;
|
|
19
|
+
exports.assertValidPackageOnboardingGuide = assertValidPackageOnboardingGuide;
|
|
20
|
+
const enyo_onboarding_v2_js_1 = require("../../types/enyo-onboarding-v2.cjs");
|
|
21
|
+
const onboarding_v2_validators_js_1 = require("./onboarding-v2-validators.cjs");
|
|
22
|
+
/**
|
|
23
|
+
* The only start variant a package's static guide may use. The guide documents
|
|
24
|
+
* a setup the installer performs by hand.
|
|
25
|
+
*/
|
|
26
|
+
exports.PACKAGE_ONBOARDING_GUIDE_START_VARIANT = enyo_onboarding_v2_js_1.EnyoOnboardingV2StartVariant.ManualSetup;
|
|
27
|
+
/**
|
|
28
|
+
* Thrown by {@link assertValidPackageOnboardingGuide} when a package's static
|
|
29
|
+
* onboarding guide fails validation. The message lists every blocking error so
|
|
30
|
+
* callers can surface them directly.
|
|
31
|
+
*/
|
|
32
|
+
class PackageOnboardingGuideValidationError extends Error {
|
|
33
|
+
/** The individual blocking errors that caused the failure. */
|
|
34
|
+
errors;
|
|
35
|
+
/**
|
|
36
|
+
* @param errors - The blocking validation errors.
|
|
37
|
+
*/
|
|
38
|
+
constructor(errors) {
|
|
39
|
+
super(`Invalid package onboarding guide:\n- ${errors.join('\n- ')}`);
|
|
40
|
+
this.name = 'PackageOnboardingGuideValidationError';
|
|
41
|
+
this.errors = errors;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.PackageOnboardingGuideValidationError = PackageOnboardingGuideValidationError;
|
|
45
|
+
/** Action kinds that act on scanned devices and therefore cannot be printed. */
|
|
46
|
+
const FORBIDDEN_ACTIONS = new Set([
|
|
47
|
+
enyo_onboarding_v2_js_1.EnyoOnboardingV2ActionKind.NetworkScan,
|
|
48
|
+
enyo_onboarding_v2_js_1.EnyoOnboardingV2ActionKind.DeviceTest,
|
|
49
|
+
]);
|
|
50
|
+
/**
|
|
51
|
+
* Block types that only exist in an interactive run, with the reason shown to
|
|
52
|
+
* the author.
|
|
53
|
+
*/
|
|
54
|
+
const FORBIDDEN_BLOCKS = new Map([
|
|
55
|
+
[enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.DeviceSelect, 'it picks from devices the host discovered'],
|
|
56
|
+
[enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.EebusDeviceSelect, 'it picks from devices the host discovered'],
|
|
57
|
+
[enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Dynamic, 'its value is resolved by the app at runtime and does not exist when the PDF is rendered'],
|
|
58
|
+
]);
|
|
59
|
+
/**
|
|
60
|
+
* Validates the static manual setup guide of a package: the regular v2 graph
|
|
61
|
+
* checks (with image references resolved against the package's `files`), the
|
|
62
|
+
* manual-setup-only rules, and that the guide is plain JSON the CLI can ship.
|
|
63
|
+
*
|
|
64
|
+
* A package without an `onboardingGuide` is trivially valid.
|
|
65
|
+
*
|
|
66
|
+
* @param definition - The package definition, or just its `onboardingGuide`
|
|
67
|
+
* and `files`.
|
|
68
|
+
* @returns The {@link PackageOnboardingGuideValidationResult}.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const {ok, errors} = validatePackageOnboardingGuide(packageDef);
|
|
73
|
+
* if (!ok) throw new Error(errors.join('\n'));
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
function validatePackageOnboardingGuide(definition) {
|
|
77
|
+
const errors = [];
|
|
78
|
+
const warnings = [];
|
|
79
|
+
const guide = definition.onboardingGuide;
|
|
80
|
+
if (guide === undefined)
|
|
81
|
+
return { ok: true, errors, warnings };
|
|
82
|
+
if (guide === null || typeof guide !== 'object' || Array.isArray(guide)) {
|
|
83
|
+
errors.push('`onboardingGuide` must be a single guide object — a package declares at most one.');
|
|
84
|
+
return { ok: false, errors, warnings };
|
|
85
|
+
}
|
|
86
|
+
// Not plain data means not a guide shape the graph checks can walk safely.
|
|
87
|
+
collectNonJsonValues(guide, 'onboardingGuide', errors);
|
|
88
|
+
if (errors.length > 0)
|
|
89
|
+
return { ok: false, errors, warnings };
|
|
90
|
+
const base = (0, onboarding_v2_validators_js_1.validateOnboardingGuideV2)(guide, { files: definition.files });
|
|
91
|
+
errors.push(...base.errors);
|
|
92
|
+
warnings.push(...base.warnings);
|
|
93
|
+
validateManualSetupOnly(guide, errors);
|
|
94
|
+
return { ok: errors.length === 0, errors, warnings };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Like {@link validatePackageOnboardingGuide}, but throws
|
|
98
|
+
* {@link PackageOnboardingGuideValidationError} when there are blocking errors.
|
|
99
|
+
* Warnings never throw.
|
|
100
|
+
*
|
|
101
|
+
* @param definition - The package definition, or just its `onboardingGuide`
|
|
102
|
+
* and `files`.
|
|
103
|
+
* @returns The declared guide, or `undefined` when the package declares none.
|
|
104
|
+
* @throws {PackageOnboardingGuideValidationError} When validation produces any error.
|
|
105
|
+
*/
|
|
106
|
+
function assertValidPackageOnboardingGuide(definition) {
|
|
107
|
+
const { ok, errors } = validatePackageOnboardingGuide(definition);
|
|
108
|
+
if (!ok)
|
|
109
|
+
throw new PackageOnboardingGuideValidationError(errors);
|
|
110
|
+
return definition.onboardingGuide;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Applies the rules that make a guide printable: manual setup variant, no
|
|
114
|
+
* scan, nothing that depends on discovered devices or runtime values, no link
|
|
115
|
+
* into another guide, and no appliance or catalog binding.
|
|
116
|
+
*
|
|
117
|
+
* @param guide - The guide being validated.
|
|
118
|
+
* @param errors - Collector for blocking problems.
|
|
119
|
+
*/
|
|
120
|
+
function validateManualSetupOnly(guide, errors) {
|
|
121
|
+
if (guide.startVariant !== exports.PACKAGE_ONBOARDING_GUIDE_START_VARIANT) {
|
|
122
|
+
errors.push(`\`startVariant\` must be "${exports.PACKAGE_ONBOARDING_GUIDE_START_VARIANT}" — the package guide ` +
|
|
123
|
+
`documents a manual setup, got "${guide.startVariant}".`);
|
|
124
|
+
}
|
|
125
|
+
if (guide.requiresNetworkScan !== false) {
|
|
126
|
+
errors.push('`requiresNetworkScan` must be set to `false` — it defaults to `true`, and a printed ' +
|
|
127
|
+
'manual has no scan to rely on.');
|
|
128
|
+
}
|
|
129
|
+
if (guide.applianceId !== undefined) {
|
|
130
|
+
errors.push('`applianceId` must not be set — a printed manual is not bound to an appliance.');
|
|
131
|
+
}
|
|
132
|
+
if (guide.vendorId !== undefined || guide.modelIds !== undefined) {
|
|
133
|
+
errors.push('`vendorId` / `modelIds` must not be set — enyo derives them from the package and its ' +
|
|
134
|
+
'`compatibility` list.');
|
|
135
|
+
}
|
|
136
|
+
for (const [i, step] of (guide.steps ?? []).entries()) {
|
|
137
|
+
const at = `steps[${i}] (${step.name || '?'})`;
|
|
138
|
+
for (const block of step.blocks ?? []) {
|
|
139
|
+
const reason = FORBIDDEN_BLOCKS.get(block.type);
|
|
140
|
+
if (reason) {
|
|
141
|
+
errors.push(`${at}: ${block.type} block "${block.id}" is not allowed in the package guide — ${reason}.`);
|
|
142
|
+
}
|
|
143
|
+
else if (block.type === enyo_onboarding_v2_js_1.EnyoOnboardingV2BlockType.Action && FORBIDDEN_ACTIONS.has(block.action)) {
|
|
144
|
+
errors.push(`${at}: ${block.action} action "${block.id}" is not allowed in the package guide — ` +
|
|
145
|
+
'it acts on devices found by a scan.');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
for (const transition of step.transitions ?? []) {
|
|
149
|
+
if (transition.target?.type === enyo_onboarding_v2_js_1.EnyoOnboardingV2TargetType.StartVariant) {
|
|
150
|
+
errors.push(`${at}: transition "${transition.id}" leads to start variant "${transition.target.variant}" — ` +
|
|
151
|
+
'the package guide cannot hand over to another guide.');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Walks a value and reports everything that does not survive a JSON round trip
|
|
158
|
+
* unchanged: functions, symbols, bigints, non-finite numbers, `undefined` array
|
|
159
|
+
* entries, and objects that are not plain objects (class instances, `Date`,
|
|
160
|
+
* `Map`, …). `undefined` object properties are fine — they are simply dropped.
|
|
161
|
+
*
|
|
162
|
+
* @param value - The value to inspect.
|
|
163
|
+
* @param path - Human-readable location of `value`, used in messages.
|
|
164
|
+
* @param errors - Collector for blocking problems.
|
|
165
|
+
* @param seen - Objects already on the current path, to report cycles.
|
|
166
|
+
*/
|
|
167
|
+
function collectNonJsonValues(value, path, errors, seen = new Set()) {
|
|
168
|
+
switch (typeof value) {
|
|
169
|
+
case 'string':
|
|
170
|
+
case 'boolean':
|
|
171
|
+
return;
|
|
172
|
+
case 'number':
|
|
173
|
+
if (!Number.isFinite(value))
|
|
174
|
+
errors.push(`${path}: ${value} is not representable in JSON.`);
|
|
175
|
+
return;
|
|
176
|
+
case 'undefined':
|
|
177
|
+
return;
|
|
178
|
+
case 'function':
|
|
179
|
+
case 'symbol':
|
|
180
|
+
case 'bigint':
|
|
181
|
+
errors.push(`${path}: a ${typeof value} cannot be serialized into the release — use plain data only.`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (value === null)
|
|
185
|
+
return;
|
|
186
|
+
const obj = value;
|
|
187
|
+
if (seen.has(obj)) {
|
|
188
|
+
errors.push(`${path}: circular reference — the guide must be a tree of plain data.`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (Array.isArray(obj)) {
|
|
192
|
+
seen.add(obj);
|
|
193
|
+
obj.forEach((entry, i) => {
|
|
194
|
+
if (entry === undefined)
|
|
195
|
+
errors.push(`${path}[${i}]: undefined array entries become null in JSON.`);
|
|
196
|
+
else
|
|
197
|
+
collectNonJsonValues(entry, `${path}[${i}]`, errors, seen);
|
|
198
|
+
});
|
|
199
|
+
seen.delete(obj);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const proto = Object.getPrototypeOf(obj);
|
|
203
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
204
|
+
const kind = obj.constructor?.name ?? 'non-plain object';
|
|
205
|
+
errors.push(`${path}: ${kind} is not plain data — use object literals, strings, numbers and booleans.`);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
seen.add(obj);
|
|
209
|
+
for (const [key, entry] of Object.entries(obj)) {
|
|
210
|
+
collectNonJsonValues(entry, `${path}.${key}`, errors, seen);
|
|
211
|
+
}
|
|
212
|
+
seen.delete(obj);
|
|
213
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side validator for the static manual setup guide declared in a
|
|
3
|
+
* package definition ({@link EnergyAppPackageDefinition.onboardingGuide}).
|
|
4
|
+
*
|
|
5
|
+
* The guide leaves the app repository as data: the enyo CLI reads it at release
|
|
6
|
+
* time and enyo renders it as a printable PDF. Nothing on paper can scan the
|
|
7
|
+
* network or ask the app for a value, so on top of the regular graph checks of
|
|
8
|
+
* {@link validateOnboardingGuideV2} this rejects everything that only works in
|
|
9
|
+
* an interactive run — scans, device pickers, device tests, dynamic values —
|
|
10
|
+
* and everything the CLI cannot serialize.
|
|
11
|
+
*
|
|
12
|
+
* Use {@link validatePackageOnboardingGuide} for the non-throwing result, or
|
|
13
|
+
* {@link assertValidPackageOnboardingGuide} to throw on failure.
|
|
14
|
+
*/
|
|
15
|
+
import type { EnergyAppPackageDefinition } from '../../energy-app-package-definition.cjs';
|
|
16
|
+
import { EnyoOnboardingV2StartVariant } from '../../types/enyo-onboarding-v2.cjs';
|
|
17
|
+
import type { EnyoOnboardingV2Guide } from '../../types/enyo-onboarding-v2.cjs';
|
|
18
|
+
/**
|
|
19
|
+
* The only start variant a package's static guide may use. The guide documents
|
|
20
|
+
* a setup the installer performs by hand.
|
|
21
|
+
*/
|
|
22
|
+
export declare const PACKAGE_ONBOARDING_GUIDE_START_VARIANT = EnyoOnboardingV2StartVariant.ManualSetup;
|
|
23
|
+
/**
|
|
24
|
+
* Thrown by {@link assertValidPackageOnboardingGuide} when a package's static
|
|
25
|
+
* onboarding guide fails validation. The message lists every blocking error so
|
|
26
|
+
* callers can surface them directly.
|
|
27
|
+
*/
|
|
28
|
+
export declare class PackageOnboardingGuideValidationError extends Error {
|
|
29
|
+
/** The individual blocking errors that caused the failure. */
|
|
30
|
+
readonly errors: string[];
|
|
31
|
+
/**
|
|
32
|
+
* @param errors - The blocking validation errors.
|
|
33
|
+
*/
|
|
34
|
+
constructor(errors: string[]);
|
|
35
|
+
}
|
|
36
|
+
/** The outcome of validating a package's static onboarding guide. */
|
|
37
|
+
export interface PackageOnboardingGuideValidationResult {
|
|
38
|
+
/** True when there are no blocking `errors` (warnings are still allowed). */
|
|
39
|
+
ok: boolean;
|
|
40
|
+
/** Blocking problems — these must be fixed before releasing. */
|
|
41
|
+
errors: string[];
|
|
42
|
+
/** Advisory problems — allowed, but usually worth reviewing. */
|
|
43
|
+
warnings: string[];
|
|
44
|
+
}
|
|
45
|
+
/** The parts of a package definition the static guide is validated against. */
|
|
46
|
+
export type PackageOnboardingGuideSource = Pick<EnergyAppPackageDefinition, 'onboardingGuide' | 'files'>;
|
|
47
|
+
/**
|
|
48
|
+
* Validates the static manual setup guide of a package: the regular v2 graph
|
|
49
|
+
* checks (with image references resolved against the package's `files`), the
|
|
50
|
+
* manual-setup-only rules, and that the guide is plain JSON the CLI can ship.
|
|
51
|
+
*
|
|
52
|
+
* A package without an `onboardingGuide` is trivially valid.
|
|
53
|
+
*
|
|
54
|
+
* @param definition - The package definition, or just its `onboardingGuide`
|
|
55
|
+
* and `files`.
|
|
56
|
+
* @returns The {@link PackageOnboardingGuideValidationResult}.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const {ok, errors} = validatePackageOnboardingGuide(packageDef);
|
|
61
|
+
* if (!ok) throw new Error(errors.join('\n'));
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function validatePackageOnboardingGuide(definition: PackageOnboardingGuideSource): PackageOnboardingGuideValidationResult;
|
|
65
|
+
/**
|
|
66
|
+
* Like {@link validatePackageOnboardingGuide}, but throws
|
|
67
|
+
* {@link PackageOnboardingGuideValidationError} when there are blocking errors.
|
|
68
|
+
* Warnings never throw.
|
|
69
|
+
*
|
|
70
|
+
* @param definition - The package definition, or just its `onboardingGuide`
|
|
71
|
+
* and `files`.
|
|
72
|
+
* @returns The declared guide, or `undefined` when the package declares none.
|
|
73
|
+
* @throws {PackageOnboardingGuideValidationError} When validation produces any error.
|
|
74
|
+
*/
|
|
75
|
+
export declare function assertValidPackageOnboardingGuide(definition: PackageOnboardingGuideSource): EnyoOnboardingV2Guide | undefined;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -115,6 +115,7 @@ __exportStar(require("./packages/energy-app-onboarding.cjs"), exports);
|
|
|
115
115
|
__exportStar(require("./types/enyo-onboarding-v2.cjs"), exports);
|
|
116
116
|
__exportStar(require("./implementations/onboarding-v2/define-onboarding-guide-v2.cjs"), exports);
|
|
117
117
|
__exportStar(require("./implementations/onboarding-v2/onboarding-v2-validators.cjs"), exports);
|
|
118
|
+
__exportStar(require("./implementations/onboarding-v2/package-onboarding-guide-validators.cjs"), exports);
|
|
118
119
|
__exportStar(require("./types/enyo-onboarding-v2-provider.cjs"), exports);
|
|
119
120
|
__exportStar(require("./packages/energy-app-onboarding-v2.cjs"), exports);
|
|
120
121
|
__exportStar(require("./implementations/onboarding-v2/onboarding-v2-provider-validators.cjs"), exports);
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -99,6 +99,7 @@ export * from './packages/energy-app-onboarding.cjs';
|
|
|
99
99
|
export * from './types/enyo-onboarding-v2.cjs';
|
|
100
100
|
export * from './implementations/onboarding-v2/define-onboarding-guide-v2.cjs';
|
|
101
101
|
export * from './implementations/onboarding-v2/onboarding-v2-validators.cjs';
|
|
102
|
+
export * from './implementations/onboarding-v2/package-onboarding-guide-validators.cjs';
|
|
102
103
|
export * from './types/enyo-onboarding-v2-provider.cjs';
|
|
103
104
|
export * from './packages/energy-app-onboarding-v2.cjs';
|
|
104
105
|
export * from './implementations/onboarding-v2/onboarding-v2-provider-validators.cjs';
|
|
@@ -263,6 +263,37 @@ var EnyoDataBusCommandReasonTypeEnum;
|
|
|
263
263
|
* {@link EnyoDataBusCommandReasonCategoryEnum.BatteryState}.
|
|
264
264
|
*/
|
|
265
265
|
EnyoDataBusCommandReasonTypeEnum["BatterySupportsEvCharging"] = "battery-supports-ev-charging";
|
|
266
|
+
/**
|
|
267
|
+
* The car charges on energy from the house battery, within the owner's
|
|
268
|
+
* battery-to-EV setting (`batteryEvDischargeMode`).
|
|
269
|
+
*
|
|
270
|
+
* Stated on the CHARGER's command by the decision maker that routed the
|
|
271
|
+
* battery's energy into the car — never inferred from the battery's
|
|
272
|
+
* measured discharge. The battery states its own side of the same decision
|
|
273
|
+
* with {@link BatterySupportsEvCharging} (released) or
|
|
274
|
+
* {@link BatteryReservedFromEv} (held); this reason is the car's side, and
|
|
275
|
+
* the two are sent together.
|
|
276
|
+
*
|
|
277
|
+
* Fields:
|
|
278
|
+
* - {@link EnyoDataBusCommandReason.powerW}: the battery's share of the
|
|
279
|
+
* charging setpoint, in W. The rest of the setpoint is solar. Never the
|
|
280
|
+
* whole setpoint.
|
|
281
|
+
* - {@link EnyoDataBusCommandReason.socPercent}: the battery fleet's current
|
|
282
|
+
* SoC.
|
|
283
|
+
* - {@link EnyoDataBusCommandReasonContext.batteryToEv}: the owner's mode and
|
|
284
|
+
* where the session stands against it — `socLimitPercent` under
|
|
285
|
+
* `SocLimit`, `allowanceWh` under `FixedWh`, `remainingWh` under both.
|
|
286
|
+
* `trigger` and `probe` are never set here: they describe a battery hold.
|
|
287
|
+
* - {@link EnyoDataBusCommandReason.electricityPricePerKwh}: under
|
|
288
|
+
* `Intelligent` only, what the stored energy costs, which the car's own
|
|
289
|
+
* price limit was compared against. Absent under `SocLimit` / `FixedWh`,
|
|
290
|
+
* which feed the car at any stored price.
|
|
291
|
+
*
|
|
292
|
+
* Told apart from {@link BatteryCapacityAvailable}, which says only that a
|
|
293
|
+
* battery can supply power and names no owner setting. Category
|
|
294
|
+
* {@link EnyoDataBusCommandReasonCategoryEnum.BatteryState}.
|
|
295
|
+
*/
|
|
296
|
+
EnyoDataBusCommandReasonTypeEnum["EvChargingFromHomeBattery"] = "ev-charging-from-home-battery";
|
|
266
297
|
/**
|
|
267
298
|
* The appliance was switched off a moment ago and is held off for its
|
|
268
299
|
* declared minimum off-time, so it is not cycled on and off. Stated by the
|
|
@@ -279,6 +279,37 @@ export declare enum EnyoDataBusCommandReasonTypeEnum {
|
|
|
279
279
|
* {@link EnyoDataBusCommandReasonCategoryEnum.BatteryState}.
|
|
280
280
|
*/
|
|
281
281
|
BatterySupportsEvCharging = "battery-supports-ev-charging",
|
|
282
|
+
/**
|
|
283
|
+
* The car charges on energy from the house battery, within the owner's
|
|
284
|
+
* battery-to-EV setting (`batteryEvDischargeMode`).
|
|
285
|
+
*
|
|
286
|
+
* Stated on the CHARGER's command by the decision maker that routed the
|
|
287
|
+
* battery's energy into the car — never inferred from the battery's
|
|
288
|
+
* measured discharge. The battery states its own side of the same decision
|
|
289
|
+
* with {@link BatterySupportsEvCharging} (released) or
|
|
290
|
+
* {@link BatteryReservedFromEv} (held); this reason is the car's side, and
|
|
291
|
+
* the two are sent together.
|
|
292
|
+
*
|
|
293
|
+
* Fields:
|
|
294
|
+
* - {@link EnyoDataBusCommandReason.powerW}: the battery's share of the
|
|
295
|
+
* charging setpoint, in W. The rest of the setpoint is solar. Never the
|
|
296
|
+
* whole setpoint.
|
|
297
|
+
* - {@link EnyoDataBusCommandReason.socPercent}: the battery fleet's current
|
|
298
|
+
* SoC.
|
|
299
|
+
* - {@link EnyoDataBusCommandReasonContext.batteryToEv}: the owner's mode and
|
|
300
|
+
* where the session stands against it — `socLimitPercent` under
|
|
301
|
+
* `SocLimit`, `allowanceWh` under `FixedWh`, `remainingWh` under both.
|
|
302
|
+
* `trigger` and `probe` are never set here: they describe a battery hold.
|
|
303
|
+
* - {@link EnyoDataBusCommandReason.electricityPricePerKwh}: under
|
|
304
|
+
* `Intelligent` only, what the stored energy costs, which the car's own
|
|
305
|
+
* price limit was compared against. Absent under `SocLimit` / `FixedWh`,
|
|
306
|
+
* which feed the car at any stored price.
|
|
307
|
+
*
|
|
308
|
+
* Told apart from {@link BatteryCapacityAvailable}, which says only that a
|
|
309
|
+
* battery can supply power and names no owner setting. Category
|
|
310
|
+
* {@link EnyoDataBusCommandReasonCategoryEnum.BatteryState}.
|
|
311
|
+
*/
|
|
312
|
+
EvChargingFromHomeBattery = "ev-charging-from-home-battery",
|
|
282
313
|
/**
|
|
283
314
|
* The appliance was switched off a moment ago and is held off for its
|
|
284
315
|
* declared minimum off-time, so it is not cycled on and off. Stated by the
|
|
@@ -675,7 +706,10 @@ export interface EnyoDataBusCommandReasonContext {
|
|
|
675
706
|
* The owner's battery-to-EV setting (`batteryEvDischargeMode`) and where
|
|
676
707
|
* the charging session stands against it. Set on
|
|
677
708
|
* {@link EnyoDataBusCommandReasonTypeEnum.BatteryReservedFromEv} and
|
|
678
|
-
* {@link EnyoDataBusCommandReasonTypeEnum.BatterySupportsEvCharging}
|
|
709
|
+
* {@link EnyoDataBusCommandReasonTypeEnum.BatterySupportsEvCharging} (the
|
|
710
|
+
* battery's command), and on
|
|
711
|
+
* {@link EnyoDataBusCommandReasonTypeEnum.EvChargingFromHomeBattery} (the
|
|
712
|
+
* charger's command).
|
|
679
713
|
*/
|
|
680
714
|
batteryToEv?: EnyoDataBusCommandReasonBatteryToEvContext;
|
|
681
715
|
/**
|
|
@@ -962,8 +996,9 @@ export declare enum EnyoBatteryToEvHoldTriggerEnum {
|
|
|
962
996
|
/**
|
|
963
997
|
* The owner's battery-to-EV setting (`batteryEvDischargeMode`) and where
|
|
964
998
|
* the current charging session stands against it — the structured backing of
|
|
965
|
-
* {@link EnyoDataBusCommandReasonTypeEnum.BatteryReservedFromEv}
|
|
966
|
-
* {@link EnyoDataBusCommandReasonTypeEnum.BatterySupportsEvCharging}
|
|
999
|
+
* {@link EnyoDataBusCommandReasonTypeEnum.BatteryReservedFromEv},
|
|
1000
|
+
* {@link EnyoDataBusCommandReasonTypeEnum.BatterySupportsEvCharging} and
|
|
1001
|
+
* {@link EnyoDataBusCommandReasonTypeEnum.EvChargingFromHomeBattery}.
|
|
967
1002
|
*
|
|
968
1003
|
* All energy values are in watt-hours (Wh).
|
|
969
1004
|
*
|
|
@@ -994,6 +1029,20 @@ export declare enum EnyoBatteryToEvHoldTriggerEnum {
|
|
|
994
1029
|
* }},
|
|
995
1030
|
* };
|
|
996
1031
|
*
|
|
1032
|
+
* // The charger's side of the same decision: 2.4 kW of the car's setpoint is
|
|
1033
|
+
* // the battery's, which may go on down to 50 %:
|
|
1034
|
+
* const charging: EnyoDataBusCommandReason = {
|
|
1035
|
+
* type: EnyoDataBusCommandReasonTypeEnum.EvChargingFromHomeBattery,
|
|
1036
|
+
* category: EnyoDataBusCommandReasonCategoryEnum.BatteryState,
|
|
1037
|
+
* powerW: 2400,
|
|
1038
|
+
* socPercent: 79,
|
|
1039
|
+
* context: {batteryToEv: {
|
|
1040
|
+
* mode: EnergyManagerBatteryEvDischargeModeEnum.SocLimit,
|
|
1041
|
+
* socLimitPercent: 50,
|
|
1042
|
+
* remainingWh: 3895,
|
|
1043
|
+
* }},
|
|
1044
|
+
* };
|
|
1045
|
+
*
|
|
997
1046
|
* // The periodic re-check of an existing hold — not a new decision:
|
|
998
1047
|
* const probe: EnyoDataBusCommandReason = {
|
|
999
1048
|
* type: EnyoDataBusCommandReasonTypeEnum.BatterySupportsEvCharging,
|
|
@@ -1014,6 +1063,7 @@ export interface EnyoDataBusCommandReasonBatteryToEvContext {
|
|
|
1014
1063
|
/**
|
|
1015
1064
|
* Why the battery is held. Set on
|
|
1016
1065
|
* {@link EnyoDataBusCommandReasonTypeEnum.BatteryReservedFromEv} only.
|
|
1066
|
+
* Never set on {@link EnyoDataBusCommandReasonTypeEnum.EvChargingFromHomeBattery}.
|
|
1017
1067
|
*/
|
|
1018
1068
|
trigger?: EnyoBatteryToEvHoldTriggerEnum;
|
|
1019
1069
|
/**
|
|
@@ -1036,6 +1086,7 @@ export interface EnyoDataBusCommandReasonBatteryToEvContext {
|
|
|
1036
1086
|
* `true` for a short re-check of an existing hold: the battery is offered
|
|
1037
1087
|
* `Auto` to see whether it still drains into the car. Not a change of
|
|
1038
1088
|
* decision — consumers SHOULD NOT list it as a separate history entry.
|
|
1089
|
+
* Never set on {@link EnyoDataBusCommandReasonTypeEnum.EvChargingFromHomeBattery}.
|
|
1039
1090
|
*/
|
|
1040
1091
|
probe?: boolean;
|
|
1041
1092
|
}
|