@enyo-energy/energy-app-sdk 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +45 -0
  2. package/dist/cjs/energy-app-permission.type.cjs +1 -0
  3. package/dist/cjs/energy-app-permission.type.d.cts +2 -1
  4. package/dist/cjs/energy-app.cjs +16 -0
  5. package/dist/cjs/energy-app.d.cts +15 -0
  6. package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
  7. package/dist/cjs/index.cjs +2 -0
  8. package/dist/cjs/index.d.cts +2 -0
  9. package/dist/cjs/packages/energy-app-command-log.cjs +2 -0
  10. package/dist/cjs/packages/energy-app-command-log.d.cts +108 -0
  11. package/dist/cjs/types/enyo-battery-appliance.d.cts +23 -0
  12. package/dist/cjs/types/enyo-command-log.cjs +50 -0
  13. package/dist/cjs/types/enyo-command-log.d.cts +151 -0
  14. package/dist/cjs/types/enyo-data-bus-value.cjs +35 -1
  15. package/dist/cjs/types/enyo-data-bus-value.d.cts +81 -0
  16. package/dist/cjs/version.cjs +1 -1
  17. package/dist/cjs/version.d.cts +1 -1
  18. package/dist/energy-app-permission.type.d.ts +2 -1
  19. package/dist/energy-app-permission.type.js +1 -0
  20. package/dist/energy-app.d.ts +15 -0
  21. package/dist/energy-app.js +16 -0
  22. package/dist/enyo-energy-app-sdk.d.ts +3 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +2 -0
  25. package/dist/packages/energy-app-command-log.d.ts +108 -0
  26. package/dist/packages/energy-app-command-log.js +1 -0
  27. package/dist/types/enyo-battery-appliance.d.ts +23 -0
  28. package/dist/types/enyo-command-log.d.ts +151 -0
  29. package/dist/types/enyo-command-log.js +47 -0
  30. package/dist/types/enyo-data-bus-value.d.ts +81 -0
  31. package/dist/types/enyo-data-bus-value.js +34 -0
  32. package/dist/version.d.ts +1 -1
  33. package/dist/version.js +1 -1
  34. package/package.json +1 -1
package/README.md CHANGED
@@ -367,6 +367,7 @@ Energy Apps use a granular permissions system to control access to system resour
367
367
  - **`Savings`**: Publish and read back day-scoped savings reports
368
368
  - **`EpexSpotPrices`**: Read EPEX SPOT day-ahead wholesale market prices
369
369
  - **`GridFeeRegister`** / **`GridFeeUse`**: Publish / resolve dynamic grid fees (time-variable network charges)
370
+ - **`CommandLog`**: Record and read back the commands apps issued to appliances
370
371
 
371
372
  #### Site & Identity Permissions
372
373
 
@@ -1500,6 +1501,50 @@ Notes worth respecting when producing a report:
1500
1501
 
1501
1502
  Requires the `Savings` permission.
1502
1503
 
1504
+ #### `useCommandLog(): EnergyAppCommandLog`
1505
+
1506
+ Record which register, configuration key or message your app wrote to an appliance — and read those
1507
+ writes back. This is the answer to *"why did the device do that?"*: an appliance's state shows what it
1508
+ is doing now, not that you wrote holding register 40023 four seconds ago because the energy manager cut
1509
+ available power, nor that the write was rejected.
1510
+
1511
+ ```typescript
1512
+ const commandLog = energyApp.useCommandLog();
1513
+
1514
+ await commandLog.logCommand({
1515
+ applianceId,
1516
+ protocol: EnyoCommandProtocolEnum.Modbus,
1517
+ operation: 'write-single-register',
1518
+ target: '40023',
1519
+ previousValue: String(before),
1520
+ newValue: String(limitW),
1521
+ outcome: EnyoCommandOutcomeEnum.Success,
1522
+ reason: 'energy manager reduced available power',
1523
+ });
1524
+
1525
+ // Everything we failed to write to this appliance today.
1526
+ const failures = await commandLog.getCommands({
1527
+ applianceId,
1528
+ outcome: EnyoCommandOutcomeEnum.Failed,
1529
+ fromIso: startOfDay,
1530
+ });
1531
+ ```
1532
+
1533
+ - **Writes, not reads.** Polling a register every two seconds does not belong here; recording reads
1534
+ would bury the writes the log exists for.
1535
+ - **Log the attempt, not only the success.** `Failed`, `TimedOut` and `Rejected` entries are the ones
1536
+ someone goes looking for. `Rejected` is separate from `Failed` on purpose — a device that answered and
1537
+ refused is a different diagnosis from one that could not be reached.
1538
+ - **Fill `reason`.** *What* was written can be reconstructed from the device; *why* cannot.
1539
+ - **Never gate the command on the log.** Log after the write is attempted; a failure to log must not
1540
+ surface as a failure to control the appliance.
1541
+ - **Append-only.** No update, no delete — a log the app can rewrite cannot explain an incident.
1542
+ Retention is the host's.
1543
+ - **Reads span every app** unless you filter by `packageName`: two packages fighting over the same
1544
+ register are invisible from one app's own entries.
1545
+
1546
+ Requires the `CommandLog` permission.
1547
+
1503
1548
  ### Operational Utilities
1504
1549
 
1505
1550
  #### `useOnboardingV2(): EnergyAppOnboardingV2`
@@ -62,4 +62,5 @@ var EnergyAppPermissionTypeEnum;
62
62
  EnergyAppPermissionTypeEnum["FirmwareRegistry"] = "FirmwareRegistry";
63
63
  EnergyAppPermissionTypeEnum["GridFeeRegister"] = "GridFeeRegister";
64
64
  EnergyAppPermissionTypeEnum["GridFeeUse"] = "GridFeeUse";
65
+ EnergyAppPermissionTypeEnum["CommandLog"] = "CommandLog";
65
66
  })(EnergyAppPermissionTypeEnum || (exports.EnergyAppPermissionTypeEnum = EnergyAppPermissionTypeEnum = {}));
@@ -57,7 +57,8 @@ export declare enum EnergyAppPermissionTypeEnum {
57
57
  EpexSpotPrices = "EpexSpotPrices",
58
58
  FirmwareRegistry = "FirmwareRegistry",
59
59
  GridFeeRegister = "GridFeeRegister",
60
- GridFeeUse = "GridFeeUse"
60
+ GridFeeUse = "GridFeeUse",
61
+ CommandLog = "CommandLog"
61
62
  }
62
63
  /**
63
64
  * String union of every permission name, derived from
@@ -465,6 +465,22 @@ class EnergyApp {
465
465
  useGridFee() {
466
466
  return this.energyAppSdk.useGridFee();
467
467
  }
468
+ /**
469
+ * Gets the Command Log API for recording the writes this app issued to an
470
+ * appliance — which Modbus register, which OCPP configuration key or
471
+ * message, with what it was before and whether the device took it.
472
+ *
473
+ * An appliance's state shows what it is doing; the command log shows what
474
+ * was done to it and why, which is what an incident investigation actually
475
+ * needs. Entries are append-only, and reads span every app so that two
476
+ * packages fighting over the same register are visible.
477
+ * @returns The Command Log API instance
478
+ * @throws {EnergyAppPermissionNotGrantedError} If the `CommandLog`
479
+ * permission is not granted.
480
+ */
481
+ useCommandLog() {
482
+ return this.energyAppSdk.useCommandLog();
483
+ }
468
484
  /**
469
485
  * Gets the current SDK version.
470
486
  * @returns The semantic version string of the SDK
@@ -46,6 +46,7 @@ import { EnergyAppSavings } from "./packages/energy-app-savings.cjs";
46
46
  import { EnergyAppDeviceTest } from "./packages/energy-app-device-test.cjs";
47
47
  import { EnergyAppEpexSpotPrice } from "./packages/energy-app-epex-spot-price.cjs";
48
48
  import { EnergyAppGridFee } from "./packages/energy-app-grid-fee.cjs";
49
+ import { EnergyAppCommandLog } from "./packages/energy-app-command-log.cjs";
49
50
  import { UseFetchOptions } from "./types/enyo-fetch.cjs";
50
51
  /**
51
52
  * Concrete implementation of {@link EnyoEnergyAppSdk} that delegates every call
@@ -387,6 +388,20 @@ export declare class EnergyApp implements EnyoEnergyAppSdk {
387
388
  * `GridFeeUse` permission required by the called method is not granted.
388
389
  */
389
390
  useGridFee(): EnergyAppGridFee;
391
+ /**
392
+ * Gets the Command Log API for recording the writes this app issued to an
393
+ * appliance — which Modbus register, which OCPP configuration key or
394
+ * message, with what it was before and whether the device took it.
395
+ *
396
+ * An appliance's state shows what it is doing; the command log shows what
397
+ * was done to it and why, which is what an incident investigation actually
398
+ * needs. Entries are append-only, and reads span every app so that two
399
+ * packages fighting over the same register are visible.
400
+ * @returns The Command Log API instance
401
+ * @throws {EnergyAppPermissionNotGrantedError} If the `CommandLog`
402
+ * permission is not granted.
403
+ */
404
+ useCommandLog(): EnergyAppCommandLog;
390
405
  /**
391
406
  * Gets the current SDK version.
392
407
  * @returns The semantic version string of the SDK
@@ -45,6 +45,7 @@ import { EnergyAppSavings } from "./packages/energy-app-savings.cjs";
45
45
  import { EnergyAppDeviceTest } from "./packages/energy-app-device-test.cjs";
46
46
  import { EnergyAppEpexSpotPrice } from "./packages/energy-app-epex-spot-price.cjs";
47
47
  import { EnergyAppGridFee } from "./packages/energy-app-grid-fee.cjs";
48
+ import { EnergyAppCommandLog } from "./packages/energy-app-command-log.cjs";
48
49
  import { UseFetchOptions } from "./types/enyo-fetch.cjs";
49
50
  export declare enum EnergyAppStateEnum {
50
51
  Launching = "launching",
@@ -166,4 +167,6 @@ export interface EnyoEnergyAppSdk {
166
167
  useEpexSpotPrices: () => EnergyAppEpexSpotPrice;
167
168
  /** Get the Grid Fee API for publishing time-dependent network charges and resolving them to a 15-minute series */
168
169
  useGridFee: () => EnergyAppGridFee;
170
+ /** Get the Command Log API for recording which register, configuration key or message this app wrote to an appliance, and reading those commands back */
171
+ useCommandLog: () => EnergyAppCommandLog;
169
172
  }
@@ -147,6 +147,8 @@ __exportStar(require("./packages/energy-app-device-test.cjs"), exports);
147
147
  __exportStar(require("./implementations/device-test/device-test-validators.cjs"), exports);
148
148
  __exportStar(require("./types/enyo-savings.cjs"), exports);
149
149
  __exportStar(require("./packages/energy-app-savings.cjs"), exports);
150
+ __exportStar(require("./types/enyo-command-log.cjs"), exports);
151
+ __exportStar(require("./packages/energy-app-command-log.cjs"), exports);
150
152
  __exportStar(require("./types/enyo-epex-spot-price.cjs"), exports);
151
153
  __exportStar(require("./packages/energy-app-epex-spot-price.cjs"), exports);
152
154
  __exportStar(require("./types/enyo-automation.cjs"), exports);
@@ -131,6 +131,8 @@ export * from './packages/energy-app-device-test.cjs';
131
131
  export * from './implementations/device-test/device-test-validators.cjs';
132
132
  export * from './types/enyo-savings.cjs';
133
133
  export * from './packages/energy-app-savings.cjs';
134
+ export * from './types/enyo-command-log.cjs';
135
+ export * from './packages/energy-app-command-log.cjs';
134
136
  export * from './types/enyo-epex-spot-price.cjs';
135
137
  export * from './packages/energy-app-epex-spot-price.cjs';
136
138
  export * from './types/enyo-automation.cjs';
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,108 @@
1
+ import { EnyoCommandLogEntry, EnyoCommandLogEntryInput, EnyoCommandLogFilter } from "../types/enyo-command-log.cjs";
2
+ /**
3
+ * Interface for recording and reading back the commands an energy app issued to
4
+ * an appliance.
5
+ *
6
+ * This is the answer to "why did the device do that?". An appliance's own state
7
+ * shows what it is doing now; it does not show that this app wrote holding
8
+ * register 40023 four seconds ago because the energy manager cut the available
9
+ * power, nor that the write was rejected. Without a record of the writes, that
10
+ * has to be reconstructed from log files that may already have rotated away.
11
+ *
12
+ * The unit is a **write, not a read**. Polling a register every two seconds does
13
+ * not belong here — an entry is a command that changed, or tried to change,
14
+ * something on the device. Recording reads would bury the writes.
15
+ *
16
+ * Entries are append-only: there is no update and no delete. A log an app can
17
+ * rewrite cannot be trusted to explain an incident, and retention is the host's
18
+ * to enforce.
19
+ *
20
+ * Access to this API requires the `CommandLog` permission
21
+ * ({@link EnergyAppPermissionType}); {@link EnergyApp.useCommandLog} throws when
22
+ * it has not been granted.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const commandLog = energyApp.useCommandLog();
27
+ *
28
+ * const before = await modbus.readHoldingRegisters(40023, 1);
29
+ * try {
30
+ * await modbus.writeSingleRegister(40023, limitW);
31
+ * await commandLog.logCommand({
32
+ * applianceId,
33
+ * protocol: EnyoCommandProtocolEnum.Modbus,
34
+ * operation: 'write-single-register',
35
+ * target: '40023',
36
+ * previousValue: String(before.readUInt16BE(0)),
37
+ * newValue: String(limitW),
38
+ * outcome: EnyoCommandOutcomeEnum.Success,
39
+ * reason: 'energy manager reduced available power',
40
+ * });
41
+ * } catch (error) {
42
+ * await commandLog.logCommand({
43
+ * applianceId,
44
+ * protocol: EnyoCommandProtocolEnum.Modbus,
45
+ * operation: 'write-single-register',
46
+ * target: '40023',
47
+ * newValue: String(limitW),
48
+ * outcome: EnyoCommandOutcomeEnum.Failed,
49
+ * errorMessage: String(error),
50
+ * reason: 'energy manager reduced available power',
51
+ * });
52
+ * }
53
+ * ```
54
+ */
55
+ export interface EnergyAppCommandLog {
56
+ /**
57
+ * Appends one command to the log.
58
+ *
59
+ * Log the attempt, not only the success: a command that timed out or was
60
+ * rejected is the entry someone will actually go looking for, and an app
61
+ * that only records its successes produces a log where nothing ever went
62
+ * wrong. Set {@link EnyoCommandLogEntry.outcome} accordingly and put the
63
+ * detail in `errorMessage`.
64
+ *
65
+ * Fill {@link EnyoCommandLogEntry.reason} whenever the app knows it. What was
66
+ * written can be reconstructed from the device; why it was written cannot.
67
+ *
68
+ * This call is for auditing and must never gate the command itself — log
69
+ * after the write has been attempted, and do not let a failure to log
70
+ * surface as a failure to control the appliance.
71
+ *
72
+ * Requires the `CommandLog` permission.
73
+ *
74
+ * @param entry - The command to record. `id`, `packageName` and (unless
75
+ * supplied) `timestampIso` are assigned by the host.
76
+ * @returns Promise resolving to the stored entry, including its assigned id
77
+ * @throws {EnergyAppPermissionNotGrantedError} If the `CommandLog` permission
78
+ * is not granted.
79
+ */
80
+ logCommand(entry: EnyoCommandLogEntryInput): Promise<EnyoCommandLogEntry>;
81
+ /**
82
+ * Reads recorded commands back, newest first.
83
+ *
84
+ * Returns entries from **every** app, not just this one, unless
85
+ * {@link EnyoCommandLogFilter.packageName} narrows it. That is deliberate:
86
+ * the failure this log exists to explain is often two apps writing the same
87
+ * register, which is invisible from either app's own entries.
88
+ *
89
+ * Requires the `CommandLog` permission.
90
+ *
91
+ * @param filter - Optional narrowing by appliance, protocol, outcome, time
92
+ * range or package. Fields combine with AND.
93
+ * @returns Promise resolving to the matching entries, newest first
94
+ * @throws {EnergyAppPermissionNotGrantedError} If the `CommandLog` permission
95
+ * is not granted.
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * // Everything we failed to write to this appliance today.
100
+ * const failures = await commandLog.getCommands({
101
+ * applianceId,
102
+ * outcome: EnyoCommandOutcomeEnum.Failed,
103
+ * fromIso: startOfDay,
104
+ * });
105
+ * ```
106
+ */
107
+ getCommands(filter?: EnyoCommandLogFilter): Promise<EnyoCommandLogEntry[]>;
108
+ }
@@ -65,4 +65,27 @@ export interface EnyoBatteryApplianceMetadata {
65
65
  * (0–100). The EMS should not charge the battery above this value.
66
66
  */
67
67
  maxSoC?: number;
68
+ /**
69
+ * Observed round-trip efficiency as a percentage (0–100): of the energy put
70
+ * into the battery, how much comes back out again.
71
+ *
72
+ * A **calculated** value, not a nameplate figure — derived from measured
73
+ * charge and discharge energy over completed cycles. It therefore drifts
74
+ * with cell age, temperature and depth of discharge, and is absent until
75
+ * enough cycles have been observed to compute it. Consumers should treat a
76
+ * missing value as "not yet known" rather than substituting a datasheet
77
+ * number.
78
+ *
79
+ * This is what decides whether a charge/discharge cycle is worth running at
80
+ * all: arbitrage pays only when the price spread exceeds the energy lost to
81
+ * the round trip, so an optimizer that assumes 100% will schedule cycles
82
+ * that lose money.
83
+ *
84
+ * Measure it at the same boundary the battery reports its power and meter
85
+ * values at. An AC-to-AC round trip includes the inverter's conversion
86
+ * losses in both directions and typically lands several points below the
87
+ * DC-to-DC figure for the same pack — quoting one where the other is
88
+ * expected silently misprices every cycle.
89
+ */
90
+ roundTripEfficiencyPercent?: number;
68
91
  }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EnyoCommandOutcomeEnum = exports.EnyoCommandProtocolEnum = void 0;
4
+ /**
5
+ * The protocol a logged command was issued over.
6
+ *
7
+ * Names the transport, not the appliance: the same wallbox may be written to
8
+ * over Modbus TCP for power limits and over OCPP for configuration keys, and an
9
+ * entry is only interpretable once you know which.
10
+ */
11
+ var EnyoCommandProtocolEnum;
12
+ (function (EnyoCommandProtocolEnum) {
13
+ /** Modbus TCP */
14
+ EnyoCommandProtocolEnum["Modbus"] = "modbus";
15
+ /** Modbus RTU (serial) */
16
+ EnyoCommandProtocolEnum["ModbusRtu"] = "modbus-rtu";
17
+ /** OCPP — a configuration change or a call sent to a charge point */
18
+ EnyoCommandProtocolEnum["Ocpp"] = "ocpp";
19
+ /** EEBUS SHIP/SPINE */
20
+ EnyoCommandProtocolEnum["Eebus"] = "eebus";
21
+ /** MQTT publish */
22
+ EnyoCommandProtocolEnum["Mqtt"] = "mqtt";
23
+ /** HTTP request against a vendor API or local endpoint */
24
+ EnyoCommandProtocolEnum["Http"] = "http";
25
+ /** Bluetooth Low Energy GATT write */
26
+ EnyoCommandProtocolEnum["Bluetooth"] = "bluetooth";
27
+ /** UDP datagram */
28
+ EnyoCommandProtocolEnum["Udp"] = "udp";
29
+ /** Anything else — describe it in {@link EnyoCommandLogEntry.operation} */
30
+ EnyoCommandProtocolEnum["Other"] = "other";
31
+ })(EnyoCommandProtocolEnum || (exports.EnyoCommandProtocolEnum = EnyoCommandProtocolEnum = {}));
32
+ /**
33
+ * How a logged command ended.
34
+ *
35
+ * {@link Rejected} is deliberately separate from {@link Failed}: a device that
36
+ * answered and refused is a very different diagnosis from one that could not be
37
+ * reached, and collapsing the two is what makes a log unable to answer "did it
38
+ * hear us?".
39
+ */
40
+ var EnyoCommandOutcomeEnum;
41
+ (function (EnyoCommandOutcomeEnum) {
42
+ /** The device accepted the command. */
43
+ EnyoCommandOutcomeEnum["Success"] = "success";
44
+ /** The command could not be delivered or the device errored. */
45
+ EnyoCommandOutcomeEnum["Failed"] = "failed";
46
+ /** No answer arrived within the app's deadline. */
47
+ EnyoCommandOutcomeEnum["TimedOut"] = "timed-out";
48
+ /** The device answered and refused the command. */
49
+ EnyoCommandOutcomeEnum["Rejected"] = "rejected";
50
+ })(EnyoCommandOutcomeEnum || (exports.EnyoCommandOutcomeEnum = EnyoCommandOutcomeEnum = {}));
@@ -0,0 +1,151 @@
1
+ /**
2
+ * The protocol a logged command was issued over.
3
+ *
4
+ * Names the transport, not the appliance: the same wallbox may be written to
5
+ * over Modbus TCP for power limits and over OCPP for configuration keys, and an
6
+ * entry is only interpretable once you know which.
7
+ */
8
+ export declare enum EnyoCommandProtocolEnum {
9
+ /** Modbus TCP */
10
+ Modbus = "modbus",
11
+ /** Modbus RTU (serial) */
12
+ ModbusRtu = "modbus-rtu",
13
+ /** OCPP — a configuration change or a call sent to a charge point */
14
+ Ocpp = "ocpp",
15
+ /** EEBUS SHIP/SPINE */
16
+ Eebus = "eebus",
17
+ /** MQTT publish */
18
+ Mqtt = "mqtt",
19
+ /** HTTP request against a vendor API or local endpoint */
20
+ Http = "http",
21
+ /** Bluetooth Low Energy GATT write */
22
+ Bluetooth = "bluetooth",
23
+ /** UDP datagram */
24
+ Udp = "udp",
25
+ /** Anything else — describe it in {@link EnyoCommandLogEntry.operation} */
26
+ Other = "other"
27
+ }
28
+ /**
29
+ * How a logged command ended.
30
+ *
31
+ * {@link Rejected} is deliberately separate from {@link Failed}: a device that
32
+ * answered and refused is a very different diagnosis from one that could not be
33
+ * reached, and collapsing the two is what makes a log unable to answer "did it
34
+ * hear us?".
35
+ */
36
+ export declare enum EnyoCommandOutcomeEnum {
37
+ /** The device accepted the command. */
38
+ Success = "success",
39
+ /** The command could not be delivered or the device errored. */
40
+ Failed = "failed",
41
+ /** No answer arrived within the app's deadline. */
42
+ TimedOut = "timed-out",
43
+ /** The device answered and refused the command. */
44
+ Rejected = "rejected"
45
+ }
46
+ /**
47
+ * One recorded command an energy app issued to an appliance.
48
+ *
49
+ * The unit of the log is a **write**, not a read: which register was set, which
50
+ * OCPP configuration key was changed, which message was executed — with what it
51
+ * was before, what it became, and whether the device took it.
52
+ */
53
+ export interface EnyoCommandLogEntry {
54
+ /** Unique identifier of this entry. Assigned by the host. */
55
+ id: string;
56
+ /** The appliance the command was issued to. */
57
+ applianceId: string;
58
+ /** When the command was issued, in ISO format. */
59
+ timestampIso: string;
60
+ /** The package that issued it. Set by the host, not by the caller. */
61
+ packageName: string;
62
+ /** Which protocol carried the command. */
63
+ protocol: EnyoCommandProtocolEnum;
64
+ /**
65
+ * What was done, in the protocol's own vocabulary — `'write-holding-registers'`,
66
+ * `'ChangeConfiguration'`, `'SetChargingProfile'`, `'gatt-write'`.
67
+ *
68
+ * Use the name the protocol uses rather than a prose description, so entries
69
+ * from one protocol group and compare.
70
+ */
71
+ operation: string;
72
+ /**
73
+ * What was addressed: a register address (`'40023'`), an OCPP configuration
74
+ * key (`'MeterValueSampleInterval'`), an MQTT topic, a GATT characteristic
75
+ * UUID. Omit for commands that address nothing in particular.
76
+ */
77
+ target?: string;
78
+ /**
79
+ * The value before the write, rendered as a string. Omit when it was not
80
+ * read back first — do not substitute a value the app merely believed was
81
+ * there, or the log will explain changes that never happened.
82
+ */
83
+ previousValue?: string;
84
+ /** The value written, rendered as a string. */
85
+ newValue?: string;
86
+ /** How the command ended. */
87
+ outcome: EnyoCommandOutcomeEnum;
88
+ /**
89
+ * Why the app issued the command — `'energy manager reduced available power'`,
90
+ * `'user changed charge mode'`.
91
+ *
92
+ * The single most useful field when reading the log back: the *what* can be
93
+ * reconstructed from the device, the *why* cannot.
94
+ */
95
+ reason?: string;
96
+ /**
97
+ * The error or rejection detail, for any outcome other than
98
+ * {@link EnyoCommandOutcomeEnum.Success}.
99
+ */
100
+ errorMessage?: string;
101
+ /** How long the command took, in milliseconds. */
102
+ durationMs?: number;
103
+ /**
104
+ * Anything the flat fields cannot carry — a decoded payload, the full
105
+ * register block written, the OCPP response object.
106
+ *
107
+ * Values above carry strings so entries from different protocols share one
108
+ * shape and can be rendered in one table; structured data belongs here.
109
+ */
110
+ details?: Record<string, unknown>;
111
+ }
112
+ /**
113
+ * What an app passes to {@link EnergyAppCommandLog.logCommand}.
114
+ *
115
+ * `id` and `packageName` are assigned by the host. `timestampIso` may be
116
+ * supplied when the command was issued earlier than it is being logged —
117
+ * a batch flushed after a reconnect — and otherwise defaults to the moment the
118
+ * host handles the call.
119
+ */
120
+ export type EnyoCommandLogEntryInput = Omit<EnyoCommandLogEntry, 'id' | 'packageName' | 'timestampIso'> & {
121
+ timestampIso?: string;
122
+ };
123
+ /**
124
+ * Narrows which entries {@link EnergyAppCommandLog.getCommands} returns.
125
+ *
126
+ * Every field is optional and they combine with AND. With no filter at all, the
127
+ * most recent entries across every appliance are returned.
128
+ */
129
+ export interface EnyoCommandLogFilter {
130
+ /** Only commands issued to this appliance. */
131
+ applianceId?: string;
132
+ /** Only commands carried over this protocol. */
133
+ protocol?: EnyoCommandProtocolEnum;
134
+ /** Only commands that ended this way — e.g. every failure in the last day. */
135
+ outcome?: EnyoCommandOutcomeEnum;
136
+ /** Only commands issued at or after this ISO timestamp (inclusive). */
137
+ fromIso?: string;
138
+ /** Only commands issued before this ISO timestamp (exclusive). */
139
+ untilIso?: string;
140
+ /**
141
+ * Only commands issued by this package. Omit to see every app's commands —
142
+ * a device misbehaving because two apps are writing the same register is
143
+ * invisible from one app's own entries.
144
+ */
145
+ packageName?: string;
146
+ /**
147
+ * Maximum number of entries to return, newest first. The host applies its
148
+ * own upper bound when this is omitted or larger than that bound.
149
+ */
150
+ limit?: number;
151
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EnyoPowerSourceEnum = exports.EnyoHeatpumpControlPurposeEnum = exports.EnyoChargingProfileTypeEnum = exports.EnyoCommandAcknowledgeAnswerEnum = exports.EnyoStorageControlDirectionEnum = exports.EnyoStorageControlModeEnum = exports.EnyoStorageScheduleDirectionEnum = exports.EnyoStorageScheduleModeEnum = exports.EnyoChargingLimitRequestResultEnum = exports.EnyoDataBusMessageEnum = exports.EnyoChargeInitiatorEnum = exports.EnyoChargeModeEnum = exports.EnyoChargingStopReason = exports.EnyoChargingMeterValueContext = exports.EnyoStringStateEnum = exports.EnyoHeatingRodStateEnum = exports.EnyoInverterStateEnum = exports.EnyoBatteryStateEnum = exports.EnyoGridOperatorLimitTypeEnum = exports.EnyoDataBusCommandReasonCategoryEnum = exports.EnyoDataBusCommandReasonTypeEnum = void 0;
3
+ exports.EnyoPowerSourceEnum = exports.EnyoHeatpumpHeatSourceEnum = exports.EnyoHeatpumpControlPurposeEnum = exports.EnyoChargingProfileTypeEnum = exports.EnyoCommandAcknowledgeAnswerEnum = exports.EnyoStorageControlDirectionEnum = exports.EnyoStorageControlModeEnum = exports.EnyoStorageScheduleDirectionEnum = exports.EnyoStorageScheduleModeEnum = exports.EnyoChargingLimitRequestResultEnum = exports.EnyoDataBusMessageEnum = exports.EnyoChargeInitiatorEnum = exports.EnyoChargeModeEnum = exports.EnyoChargingStopReason = exports.EnyoChargingMeterValueContext = exports.EnyoStringStateEnum = exports.EnyoHeatingRodStateEnum = exports.EnyoInverterStateEnum = exports.EnyoBatteryStateEnum = exports.EnyoGridOperatorLimitTypeEnum = exports.EnyoDataBusCommandReasonCategoryEnum = exports.EnyoDataBusCommandReasonTypeEnum = void 0;
4
4
  /**
5
5
  * Enum representing the reason type for why a data bus command was issued.
6
6
  * Used to attach context to commands for logging, debugging, and UI display.
@@ -417,6 +417,40 @@ var EnyoHeatpumpControlPurposeEnum;
417
417
  /** Use the power for space cooling (reversible heatpumps) */
418
418
  EnyoHeatpumpControlPurposeEnum["Cooling"] = "cooling";
419
419
  })(EnyoHeatpumpControlPurposeEnum || (exports.EnyoHeatpumpControlPurposeEnum = EnyoHeatpumpControlPurposeEnum = {}));
420
+ /**
421
+ * Which heat source an announced available-power envelope is meant to be spent
422
+ * on.
423
+ *
424
+ * The two sources are not interchangeable: a compressor moves several units of
425
+ * heat per unit of electricity and its draw is worth shifting in time, while a
426
+ * heating rod is resistive and returns roughly one. Granting PV surplus without
427
+ * saying which to use lets an appliance dump it into the rod, which is the
428
+ * worst available answer for the same Watts.
429
+ *
430
+ * Reported back through {@link EnyoDataBusHeatpumpValuesV1}'s
431
+ * `compressorRunning` / `heatingRodRunning`, so a sender can see whether the
432
+ * target was honoured.
433
+ */
434
+ var EnyoHeatpumpHeatSourceEnum;
435
+ (function (EnyoHeatpumpHeatSourceEnum) {
436
+ /**
437
+ * Spend the power on the compressor. The integrated heating rod should stay
438
+ * off for the duration of this envelope.
439
+ */
440
+ EnyoHeatpumpHeatSourceEnum["Compressor"] = "compressor";
441
+ /**
442
+ * Spend the power on the integrated heating rod — for example to absorb a
443
+ * surplus the compressor's modulation band cannot take, or when the
444
+ * compressor is unavailable.
445
+ */
446
+ EnyoHeatpumpHeatSourceEnum["HeatingRod"] = "heating-rod";
447
+ /**
448
+ * Either source, or both at once. Explicitly permits bivalent operation,
449
+ * where the rod runs alongside the compressor to meet demand the compressor
450
+ * alone cannot.
451
+ */
452
+ EnyoHeatpumpHeatSourceEnum["Both"] = "both";
453
+ })(EnyoHeatpumpHeatSourceEnum || (exports.EnyoHeatpumpHeatSourceEnum = EnyoHeatpumpHeatSourceEnum = {}));
420
454
  /**
421
455
  * Where an announced available-power envelope is expected to originate from.
422
456
  * Used to give appliances (and analytics) insight into whether the granted
@@ -450,6 +450,36 @@ export interface EnyoDataBusHeatpumpValuesV1 extends EnyoDataBusMessage {
450
450
  heatGenerationHeatingWh?: number;
451
451
  /** Power generation for domestic hot water in Wh (meter value)*/
452
452
  heatGenerationDomesticHotWaterWh?: number;
453
+ /**
454
+ * Whether the compressor is drawing power at this moment.
455
+ *
456
+ * Optional context for {@link powerW}: the same 4 kW draw means
457
+ * something different depending on which heat source produced it,
458
+ * and only the appliance knows which. A compressor's consumption
459
+ * moves with its modulation band and is worth shifting in time; a
460
+ * heating rod's does not.
461
+ *
462
+ * Omit when the appliance does not report it — `undefined` means
463
+ * "not known", which is not the same as `false`.
464
+ */
465
+ compressorRunning?: boolean;
466
+ /**
467
+ * Whether the heat pump's integrated heating rod (backup /
468
+ * auxiliary resistive element) is energized at this moment.
469
+ *
470
+ * May be `true` at the same time as {@link compressorRunning}:
471
+ * bivalent operation runs the rod alongside the compressor when the
472
+ * compressor alone cannot meet demand, and that combination is
473
+ * usually the most expensive state the appliance can be in.
474
+ *
475
+ * This is the rod built into the heat pump. A heating rod that is a
476
+ * separate appliance reports its own state through
477
+ * {@link EnyoDataBusHeatingRodValuesV1} instead.
478
+ *
479
+ * Omit when the appliance does not report it — `undefined` means
480
+ * "not known", which is not the same as `false`.
481
+ */
482
+ heatingRodRunning?: boolean;
453
483
  };
454
484
  /** Grid operator power limitation currently applied by the heatpump, if any */
455
485
  gridOperatorLimit?: EnyoGridOperatorLimit;
@@ -2139,6 +2169,39 @@ export declare enum EnyoHeatpumpControlPurposeEnum {
2139
2169
  /** Use the power for space cooling (reversible heatpumps) */
2140
2170
  Cooling = "cooling"
2141
2171
  }
2172
+ /**
2173
+ * Which heat source an announced available-power envelope is meant to be spent
2174
+ * on.
2175
+ *
2176
+ * The two sources are not interchangeable: a compressor moves several units of
2177
+ * heat per unit of electricity and its draw is worth shifting in time, while a
2178
+ * heating rod is resistive and returns roughly one. Granting PV surplus without
2179
+ * saying which to use lets an appliance dump it into the rod, which is the
2180
+ * worst available answer for the same Watts.
2181
+ *
2182
+ * Reported back through {@link EnyoDataBusHeatpumpValuesV1}'s
2183
+ * `compressorRunning` / `heatingRodRunning`, so a sender can see whether the
2184
+ * target was honoured.
2185
+ */
2186
+ export declare enum EnyoHeatpumpHeatSourceEnum {
2187
+ /**
2188
+ * Spend the power on the compressor. The integrated heating rod should stay
2189
+ * off for the duration of this envelope.
2190
+ */
2191
+ Compressor = "compressor",
2192
+ /**
2193
+ * Spend the power on the integrated heating rod — for example to absorb a
2194
+ * surplus the compressor's modulation band cannot take, or when the
2195
+ * compressor is unavailable.
2196
+ */
2197
+ HeatingRod = "heating-rod",
2198
+ /**
2199
+ * Either source, or both at once. Explicitly permits bivalent operation,
2200
+ * where the rod runs alongside the compressor to meet demand the compressor
2201
+ * alone cannot.
2202
+ */
2203
+ Both = "both"
2204
+ }
2142
2205
  /**
2143
2206
  * Where an announced available-power envelope is expected to originate from.
2144
2207
  * Used to give appliances (and analytics) insight into whether the granted
@@ -2184,6 +2247,24 @@ export interface EnyoDataBusSetHeatpumpAvailablePowerV2 extends EnyoDataBusMessa
2184
2247
  * pre-heating). Advisory — the appliance may still apply its own logic.
2185
2248
  */
2186
2249
  purpose?: EnyoHeatpumpControlPurposeEnum;
2250
+ /**
2251
+ * Which heat source the announced power should be spent on — the
2252
+ * compressor, the integrated heating rod, or either.
2253
+ *
2254
+ * Where {@link purpose} says *what for*, this says *with what*. Set it
2255
+ * when the distinction matters to the sender: PV surplus is normally
2256
+ * worth far more through the compressor, while a rod is the right sink
2257
+ * for a surplus the compressor cannot absorb.
2258
+ *
2259
+ * Omit to leave the choice to the appliance — omission is not the same
2260
+ * as {@link EnyoHeatpumpHeatSourceEnum.Both}, which actively permits
2261
+ * running the rod alongside the compressor.
2262
+ *
2263
+ * Advisory, like {@link purpose}: the appliance may override it when
2264
+ * safety or comfort requires, for example to run a legionella cycle or
2265
+ * a defrost that needs the rod.
2266
+ */
2267
+ desiredTarget?: EnyoHeatpumpHeatSourceEnum;
2187
2268
  /**
2188
2269
  * Optional breakdown of the announced envelope by source (PV / battery /
2189
2270
  * grid). When present, the shares should sum to {@link data.powerW}.