@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.
- package/README.md +45 -0
- package/dist/cjs/energy-app-permission.type.cjs +1 -0
- package/dist/cjs/energy-app-permission.type.d.cts +2 -1
- package/dist/cjs/energy-app.cjs +16 -0
- package/dist/cjs/energy-app.d.cts +15 -0
- package/dist/cjs/enyo-energy-app-sdk.d.cts +3 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/packages/energy-app-command-log.cjs +2 -0
- package/dist/cjs/packages/energy-app-command-log.d.cts +108 -0
- package/dist/cjs/types/enyo-battery-appliance.d.cts +23 -0
- package/dist/cjs/types/enyo-command-log.cjs +50 -0
- package/dist/cjs/types/enyo-command-log.d.cts +151 -0
- package/dist/cjs/types/enyo-data-bus-value.cjs +35 -1
- package/dist/cjs/types/enyo-data-bus-value.d.cts +81 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/energy-app-permission.type.d.ts +2 -1
- package/dist/energy-app-permission.type.js +1 -0
- package/dist/energy-app.d.ts +15 -0
- package/dist/energy-app.js +16 -0
- package/dist/enyo-energy-app-sdk.d.ts +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/packages/energy-app-command-log.d.ts +108 -0
- package/dist/packages/energy-app-command-log.js +1 -0
- package/dist/types/enyo-battery-appliance.d.ts +23 -0
- package/dist/types/enyo-command-log.d.ts +151 -0
- package/dist/types/enyo-command-log.js +47 -0
- package/dist/types/enyo-data-bus-value.d.ts +81 -0
- package/dist/types/enyo-data-bus-value.js +34 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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
|
package/dist/cjs/energy-app.cjs
CHANGED
|
@@ -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
|
}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -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);
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -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,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}.
|