@enyo-energy/energy-app-sdk 0.0.135 → 0.0.136
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/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/packages/eebus/eebus-feature-catalog.cjs +2 -0
- package/dist/cjs/packages/eebus/eebus-feature-catalog.d.cts +90 -0
- package/dist/cjs/packages/eebus/energy-app-eebus.d.cts +19 -5
- package/dist/cjs/types/enyo-eebus-features.cjs +17 -0
- package/dist/cjs/types/enyo-eebus-features.d.cts +131 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/packages/eebus/eebus-feature-catalog.d.ts +90 -0
- package/dist/packages/eebus/eebus-feature-catalog.js +1 -0
- package/dist/packages/eebus/energy-app-eebus.d.ts +19 -5
- package/dist/types/enyo-eebus-features.d.ts +131 -0
- package/dist/types/enyo-eebus-features.js +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -52,6 +52,7 @@ __exportStar(require("./packages/energy-app-energy-prices.cjs"), exports);
|
|
|
52
52
|
__exportStar(require("./packages/energy-app-modbus-rtu.cjs"), exports);
|
|
53
53
|
__exportStar(require("./types/enyo-eebus.cjs"), exports);
|
|
54
54
|
__exportStar(require("./types/enyo-eebus-use-cases.cjs"), exports);
|
|
55
|
+
__exportStar(require("./types/enyo-eebus-features.cjs"), exports);
|
|
55
56
|
__exportStar(require("./packages/energy-app-eebus.cjs"), exports);
|
|
56
57
|
__exportStar(require("./types/enyo-mqtt.cjs"), exports);
|
|
57
58
|
__exportStar(require("./packages/energy-app-mqtt.cjs"), exports);
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -36,6 +36,7 @@ export * from './packages/energy-app-energy-prices.cjs';
|
|
|
36
36
|
export * from './packages/energy-app-modbus-rtu.cjs';
|
|
37
37
|
export * from './types/enyo-eebus.cjs';
|
|
38
38
|
export * from './types/enyo-eebus-use-cases.cjs';
|
|
39
|
+
export * from './types/enyo-eebus-features.cjs';
|
|
39
40
|
export * from './packages/energy-app-eebus.cjs';
|
|
40
41
|
export * from './types/enyo-mqtt.cjs';
|
|
41
42
|
export * from './packages/energy-app-mqtt.cjs';
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { EebusRemoteFeatureCatalog } from '../../types/enyo-eebus-features.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Per-peer SPINE entity/feature catalog for paired EEbus remotes.
|
|
4
|
+
*
|
|
5
|
+
* Exposes the lib's `RemoteDevice` view — the set of entities and
|
|
6
|
+
* features the peer actually advertises via
|
|
7
|
+
* `NodeManagement.DetailedDiscoveryData`, kept in sync by the SDK as the
|
|
8
|
+
* remote emits `NodeManagement.NotifyChange` events.
|
|
9
|
+
*
|
|
10
|
+
* Use this service in preference to {@link EebusIdentityService.getSupportedUseCases}
|
|
11
|
+
* when gating package behaviour on remote capability. Non-certified peers
|
|
12
|
+
* and simulators routinely under-populate `NodeManagement.UseCaseData`
|
|
13
|
+
* while still exposing the matching SPINE features (e.g. a `LoadControl`
|
|
14
|
+
* server with `loadControlLimitDescriptionData` of
|
|
15
|
+
* `limitDirection: 'consume'` but no corresponding
|
|
16
|
+
* `limitationOfPowerConsumption` use case). Feature-level gates work on
|
|
17
|
+
* these peers; use-case gates do not.
|
|
18
|
+
*
|
|
19
|
+
* Identity, like the use-case list, is observable rather than one-shot:
|
|
20
|
+
* remotes add or remove entities and features after a firmware update or
|
|
21
|
+
* a runtime mode change. Always pair {@link get} with
|
|
22
|
+
* {@link onFeaturesChanged} for any package that reacts to peer
|
|
23
|
+
* capabilities, otherwise the package will keep operating against a
|
|
24
|
+
* stale snapshot.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // Feature-based LPC gate that works on peers with incomplete UseCaseData
|
|
29
|
+
* const catalog = await eebus.features.get(ski);
|
|
30
|
+
* const loadControl = catalog.entities
|
|
31
|
+
* .flatMap(e => e.features)
|
|
32
|
+
* .find(f =>
|
|
33
|
+
* f.type === 'LoadControl'
|
|
34
|
+
* && f.role === 'server'
|
|
35
|
+
* && f.supportedFunctions.some(s => s.function === 'loadControlLimitListData'),
|
|
36
|
+
* );
|
|
37
|
+
* if (loadControl) {
|
|
38
|
+
* await eebus.useCases.lpc(ski).setConsumptionLimit({ value: 11000, isActive: true });
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* // React to the peer adding or removing features at runtime
|
|
42
|
+
* const listenerId = eebus.features.onFeaturesChanged(ski, next => {
|
|
43
|
+
* refreshCapabilityGates(next);
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export interface EebusFeatureCatalog {
|
|
48
|
+
/**
|
|
49
|
+
* Get the current SPINE entity/feature catalog snapshot for a remote node.
|
|
50
|
+
*
|
|
51
|
+
* The snapshot reflects the most recent `NodeManagement.DetailedDiscoveryData`
|
|
52
|
+
* state the SDK has observed; it does not trigger a re-fetch from the
|
|
53
|
+
* remote. To observe live additions, removals, and updates use
|
|
54
|
+
* {@link onFeaturesChanged}.
|
|
55
|
+
*
|
|
56
|
+
* If the peer identified by `ski` is not paired or not currently
|
|
57
|
+
* connected, the returned snapshot resolves with `found: false` and an
|
|
58
|
+
* empty `entities` list rather than throwing — so packages can use the
|
|
59
|
+
* call as a capability gate without wrapping it in `try`/`catch`.
|
|
60
|
+
*
|
|
61
|
+
* @param ski Subject Key Identifier of the remote node
|
|
62
|
+
* @returns The current entity/feature catalog snapshot
|
|
63
|
+
*/
|
|
64
|
+
get: (ski: string) => Promise<EebusRemoteFeatureCatalog>;
|
|
65
|
+
/**
|
|
66
|
+
* Subscribe to feature/entity catalog changes for a remote node.
|
|
67
|
+
*
|
|
68
|
+
* The listener is invoked with the full updated snapshot whenever the
|
|
69
|
+
* lib emits `featureAdded`, `featureUpdated`, `featureRemoved`,
|
|
70
|
+
* `entityAdded`, or `entityRemoved` for the peer. The payload shape
|
|
71
|
+
* matches {@link get} so subscribers can replace their cached catalog
|
|
72
|
+
* on every event without diffing.
|
|
73
|
+
*
|
|
74
|
+
* Subscriptions are scoped to the peer identified by `ski`. If the
|
|
75
|
+
* peer disconnects, the listener remains registered and resumes
|
|
76
|
+
* delivering events when the peer reconnects — packages do not need
|
|
77
|
+
* to re-subscribe after a transient disconnect.
|
|
78
|
+
*
|
|
79
|
+
* @param ski Subject Key Identifier of the remote node
|
|
80
|
+
* @param listener Callback invoked with the full updated catalog snapshot
|
|
81
|
+
* @returns Listener ID that can be passed to {@link removeListener} to cancel
|
|
82
|
+
*/
|
|
83
|
+
onFeaturesChanged: (ski: string, listener: (catalog: EebusRemoteFeatureCatalog) => void) => string;
|
|
84
|
+
/**
|
|
85
|
+
* Remove a feature-catalog listener previously registered via
|
|
86
|
+
* {@link onFeaturesChanged}.
|
|
87
|
+
* @param listenerId The ID returned by the registration method
|
|
88
|
+
*/
|
|
89
|
+
removeListener: (listenerId: string) => void;
|
|
90
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { EebusDeviceManagement } from './eebus-device-management.cjs';
|
|
2
|
+
import { EebusFeatureCatalog } from './eebus-feature-catalog.cjs';
|
|
2
3
|
import { EebusIdentityService } from './eebus-identity-service.cjs';
|
|
3
4
|
import { EebusSpineLowLevel } from './eebus-spine-low-level.cjs';
|
|
4
5
|
import { EebusUseCaseRegistry } from './eebus-use-case-registry.cjs';
|
|
5
6
|
export { EebusDeviceManagement } from './eebus-device-management.cjs';
|
|
7
|
+
export { EebusFeatureCatalog } from './eebus-feature-catalog.cjs';
|
|
6
8
|
export { EebusIdentityService } from './eebus-identity-service.cjs';
|
|
7
9
|
export { EebusSpineLowLevel } from './eebus-spine-low-level.cjs';
|
|
8
10
|
export { EebusUseCaseRegistry } from './eebus-use-case-registry.cjs';
|
|
@@ -16,10 +18,11 @@ export { EebusSetpointClient } from './eebus-setpoint-client.cjs';
|
|
|
16
18
|
/**
|
|
17
19
|
* Interface for EEbus (SHIP/SPINE) device communication in enyo packages.
|
|
18
20
|
*
|
|
19
|
-
* The API is split into
|
|
21
|
+
* The API is split into five orthogonal concerns, each its own sub-interface:
|
|
20
22
|
*
|
|
21
23
|
* - {@link devices} — SHIP-level device lifecycle: discovery, pairing, connection
|
|
22
24
|
* - {@link identity} — NID: observable per-node identity, diagnosis state, use-case discovery
|
|
25
|
+
* - {@link features} — observable per-peer SPINE entity/feature catalog
|
|
23
26
|
* - {@link useCases} — typed use-case clients: LPC, LPP, MGCP, MPC, OHPCF, Setpoint, Hvac
|
|
24
27
|
* - {@link spine} — low-level SPINE escape hatch for features not yet wrapped
|
|
25
28
|
*
|
|
@@ -41,16 +44,21 @@ export { EebusSetpointClient } from './eebus-setpoint-client.cjs';
|
|
|
41
44
|
* console.log(`${identity.brandName} ${identity.deviceName} v${identity.softwareRevision}`);
|
|
42
45
|
* eebus.identity.onIdentityChanged(device.ski, next => updateStatusBadge(next));
|
|
43
46
|
*
|
|
44
|
-
* // 3.
|
|
45
|
-
* const
|
|
46
|
-
*
|
|
47
|
+
* // 3. Feature catalog — gate behaviour on what the peer actually advertises
|
|
48
|
+
* const catalog = await eebus.features.get(device.ski);
|
|
49
|
+
* const hasLpcServer = catalog.entities
|
|
50
|
+
* .flatMap(e => e.features)
|
|
51
|
+
* .some(f => f.type === 'LoadControl' && f.role === 'server');
|
|
52
|
+
*
|
|
53
|
+
* // 4. Use cases — typed, role-aware
|
|
54
|
+
* if (hasLpcServer) {
|
|
47
55
|
* await eebus.useCases.lpc(device.ski).setConsumptionLimit({
|
|
48
56
|
* value: 11000,
|
|
49
57
|
* isActive: true,
|
|
50
58
|
* });
|
|
51
59
|
* }
|
|
52
60
|
*
|
|
53
|
-
* //
|
|
61
|
+
* // 5. Escape hatch — raw SPINE for unmodelled features
|
|
54
62
|
* const dp = await eebus.spine.readData(device.ski, 'DeviceConfiguration', 'keyValueListData');
|
|
55
63
|
* ```
|
|
56
64
|
*/
|
|
@@ -59,6 +67,12 @@ export interface EnergyAppEebus {
|
|
|
59
67
|
devices: EebusDeviceManagement;
|
|
60
68
|
/** EEBUS Node Identification (NID) — observable identity + use-case discovery */
|
|
61
69
|
identity: EebusIdentityService;
|
|
70
|
+
/**
|
|
71
|
+
* Observable per-peer SPINE entity/feature catalog. Prefer feature-level
|
|
72
|
+
* gates from this catalog over use-case gates from {@link identity} when
|
|
73
|
+
* the peer is known to under-populate `NodeManagement.UseCaseData`.
|
|
74
|
+
*/
|
|
75
|
+
features: EebusFeatureCatalog;
|
|
62
76
|
/** Typed use-case clients for the implemented EEBUS use cases */
|
|
63
77
|
useCases: EebusUseCaseRegistry;
|
|
64
78
|
/** Low-level SPINE escape hatch for features not yet wrapped by a typed client */
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Types describing the SPINE feature/entity catalog advertised by a remote
|
|
4
|
+
* EEbus peer.
|
|
5
|
+
*
|
|
6
|
+
* The catalog is the **authoritative source of truth** for what a peer can
|
|
7
|
+
* actually do. It is derived from the lib's `RemoteDevice` view, which is
|
|
8
|
+
* itself kept in sync with `NodeManagement.DetailedDiscoveryData` replies
|
|
9
|
+
* and `NodeManagement.NotifyChange` events from the remote.
|
|
10
|
+
*
|
|
11
|
+
* Prefer feature/role gates based on this catalog over use-case gates from
|
|
12
|
+
* {@link EebusUseCaseSupport}: many non-certified peers and simulators
|
|
13
|
+
* implement working SPINE features (e.g. a `LoadControl` server with a
|
|
14
|
+
* `loadControlLimitDescriptionData` of `limitDirection: 'consume'`) without
|
|
15
|
+
* advertising the matching use case in `NodeManagement.UseCaseData`.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types describing the SPINE feature/entity catalog advertised by a remote
|
|
3
|
+
* EEbus peer.
|
|
4
|
+
*
|
|
5
|
+
* The catalog is the **authoritative source of truth** for what a peer can
|
|
6
|
+
* actually do. It is derived from the lib's `RemoteDevice` view, which is
|
|
7
|
+
* itself kept in sync with `NodeManagement.DetailedDiscoveryData` replies
|
|
8
|
+
* and `NodeManagement.NotifyChange` events from the remote.
|
|
9
|
+
*
|
|
10
|
+
* Prefer feature/role gates based on this catalog over use-case gates from
|
|
11
|
+
* {@link EebusUseCaseSupport}: many non-certified peers and simulators
|
|
12
|
+
* implement working SPINE features (e.g. a `LoadControl` server with a
|
|
13
|
+
* `loadControlLimitDescriptionData` of `limitDirection: 'consume'`) without
|
|
14
|
+
* advertising the matching use case in `NodeManagement.UseCaseData`.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* SPINE role under which a feature is advertised by the remote node.
|
|
18
|
+
*
|
|
19
|
+
* The set is intentionally widened with `string` so that lib upgrades that
|
|
20
|
+
* introduce new SPINE role variants do not break consumer code that
|
|
21
|
+
* pattern-matches on the role.
|
|
22
|
+
*/
|
|
23
|
+
export type EebusFeatureRole = 'client' | 'server' | 'special' | string;
|
|
24
|
+
/**
|
|
25
|
+
* SPINE address triple identifying a feature within the EEbus topology.
|
|
26
|
+
*
|
|
27
|
+
* - {@link entity} addresses the entity within the remote node (e.g. `[1]`
|
|
28
|
+
* for a flat node, `[1, 1]` when the node nests sub-entities such as a
|
|
29
|
+
* compressor under a heat-pump appliance).
|
|
30
|
+
* - {@link feature} addresses the feature within that entity.
|
|
31
|
+
* - {@link device} optionally carries the remote node's SPINE
|
|
32
|
+
* `NetworkAddressDeviceID`. Usually omitted because the surrounding
|
|
33
|
+
* {@link EebusRemoteFeatureCatalog.deviceAddress} already provides it.
|
|
34
|
+
*/
|
|
35
|
+
export interface EebusFeatureAddress {
|
|
36
|
+
/** SPINE entity address — a sequence of integers identifying the entity within the node */
|
|
37
|
+
entity: number[];
|
|
38
|
+
/** SPINE feature address within the entity */
|
|
39
|
+
feature: number;
|
|
40
|
+
/** Optional SPINE `NetworkAddressDeviceID` of the remote node */
|
|
41
|
+
device?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A SPINE function the remote advertises as supported on a feature,
|
|
45
|
+
* together with the operations (read / write) the remote permits on it.
|
|
46
|
+
*
|
|
47
|
+
* The operation payloads are intentionally opaque (`object`) — SPINE
|
|
48
|
+
* permits per-function filters and bindings whose shape varies by
|
|
49
|
+
* function and is irrelevant to most callers. Pass them through verbatim
|
|
50
|
+
* when forwarding to {@link EebusSpineLowLevel}.
|
|
51
|
+
*/
|
|
52
|
+
export interface EebusSupportedFunction {
|
|
53
|
+
/** SPINE function/data-set name (e.g. `'loadControlLimitListData'`, `'measurementListData'`) */
|
|
54
|
+
function: string;
|
|
55
|
+
/** Operations the remote permits on this function */
|
|
56
|
+
possibleOperations: {
|
|
57
|
+
/** Present when the remote permits reads; payload mirrors the SPINE `read` operation parameters */
|
|
58
|
+
read?: object;
|
|
59
|
+
/** Present when the remote permits writes; payload mirrors the SPINE `write` operation parameters */
|
|
60
|
+
write?: object;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A single SPINE feature advertised by a remote entity.
|
|
65
|
+
*
|
|
66
|
+
* The {@link type} is a wire string (e.g. `'LoadControl'`,
|
|
67
|
+
* `'Measurement'`, `'DeviceClassification'`) rather than a closed enum
|
|
68
|
+
* so that lib upgrades that introduce new SPINE feature types do not
|
|
69
|
+
* break existing packages — see {@link EebusSpineLowLevel} for the same
|
|
70
|
+
* rationale applied to the low-level escape hatch.
|
|
71
|
+
*/
|
|
72
|
+
export interface EebusRemoteFeature {
|
|
73
|
+
/** SPINE address triple for this feature */
|
|
74
|
+
address: EebusFeatureAddress;
|
|
75
|
+
/** SPINE feature type wire string (e.g. `'LoadControl'`, `'Measurement'`) */
|
|
76
|
+
type: string;
|
|
77
|
+
/** SPINE role under which the feature is advertised */
|
|
78
|
+
role: EebusFeatureRole;
|
|
79
|
+
/** Functions the remote advertises as supported on this feature */
|
|
80
|
+
supportedFunctions: EebusSupportedFunction[];
|
|
81
|
+
/** Optional manufacturer-provided label */
|
|
82
|
+
label?: string;
|
|
83
|
+
/** Optional manufacturer-provided description */
|
|
84
|
+
description?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A SPINE entity advertised by a remote node.
|
|
88
|
+
*
|
|
89
|
+
* A node usually exposes a `DeviceInformation` entity (the node itself)
|
|
90
|
+
* plus one or more application-specific entities (e.g. `EVSE`, `EV`,
|
|
91
|
+
* `HeatPumpAppliance`, `Compressor`). Sub-entities are flattened into
|
|
92
|
+
* this list with their full {@link address} path preserved (e.g. `[1, 1]`
|
|
93
|
+
* for a compressor under a heat-pump appliance), so consumers can
|
|
94
|
+
* reconstruct the hierarchy when needed.
|
|
95
|
+
*/
|
|
96
|
+
export interface EebusRemoteEntity {
|
|
97
|
+
/** SPINE entity address — a sequence of integers identifying the entity (e.g. `[1]` or `[1, 1]`) */
|
|
98
|
+
address: number[];
|
|
99
|
+
/** SPINE entity type wire string (e.g. `'HeatPumpAppliance'`, `'Compressor'`, `'EVSE'`) */
|
|
100
|
+
type: string;
|
|
101
|
+
/** Optional manufacturer-provided label */
|
|
102
|
+
label?: string;
|
|
103
|
+
/** Optional manufacturer-provided description */
|
|
104
|
+
description?: string;
|
|
105
|
+
/** Features advertised on this entity */
|
|
106
|
+
features: EebusRemoteFeature[];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Snapshot of the full SPINE entity/feature catalog advertised by a
|
|
110
|
+
* remote EEbus peer.
|
|
111
|
+
*
|
|
112
|
+
* Returned by {@link EebusFeatureCatalog.get} and delivered to
|
|
113
|
+
* {@link EebusFeatureCatalog.onFeaturesChanged} listeners. The shape is
|
|
114
|
+
* intentionally identical between the two so that change subscribers can
|
|
115
|
+
* drop and replace their cached catalog on every event without diffing.
|
|
116
|
+
*
|
|
117
|
+
* When the peer identified by {@link ski} is not known (never paired or
|
|
118
|
+
* not currently connected), {@link found} is `false` and {@link entities}
|
|
119
|
+
* is an empty array — the call resolves cleanly rather than throwing, so
|
|
120
|
+
* packages can use the snapshot as a feature gate without try/catch.
|
|
121
|
+
*/
|
|
122
|
+
export interface EebusRemoteFeatureCatalog {
|
|
123
|
+
/** Subject Key Identifier of the remote node this catalog describes */
|
|
124
|
+
ski: string;
|
|
125
|
+
/** Whether the peer is currently known to the SDK (paired AND reachable) */
|
|
126
|
+
found: boolean;
|
|
127
|
+
/** Remote node's SPINE `NetworkAddressDeviceID`, when known */
|
|
128
|
+
deviceAddress?: string;
|
|
129
|
+
/** SPINE entities advertised by the remote node, flattened with their address path preserved */
|
|
130
|
+
entities: EebusRemoteEntity[];
|
|
131
|
+
}
|
package/dist/cjs/version.cjs
CHANGED
|
@@ -9,7 +9,7 @@ exports.getSdkVersion = getSdkVersion;
|
|
|
9
9
|
/**
|
|
10
10
|
* Current version of the enyo Energy App SDK.
|
|
11
11
|
*/
|
|
12
|
-
exports.SDK_VERSION = '0.0.
|
|
12
|
+
exports.SDK_VERSION = '0.0.136';
|
|
13
13
|
/**
|
|
14
14
|
* Gets the current SDK version.
|
|
15
15
|
* @returns The semantic version string of the SDK
|
package/dist/cjs/version.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export * from './packages/energy-app-energy-prices.js';
|
|
|
36
36
|
export * from './packages/energy-app-modbus-rtu.js';
|
|
37
37
|
export * from './types/enyo-eebus.js';
|
|
38
38
|
export * from './types/enyo-eebus-use-cases.js';
|
|
39
|
+
export * from './types/enyo-eebus-features.js';
|
|
39
40
|
export * from './packages/energy-app-eebus.js';
|
|
40
41
|
export * from './types/enyo-mqtt.js';
|
|
41
42
|
export * from './packages/energy-app-mqtt.js';
|
package/dist/index.js
CHANGED
|
@@ -36,6 +36,7 @@ export * from './packages/energy-app-energy-prices.js';
|
|
|
36
36
|
export * from './packages/energy-app-modbus-rtu.js';
|
|
37
37
|
export * from './types/enyo-eebus.js';
|
|
38
38
|
export * from './types/enyo-eebus-use-cases.js';
|
|
39
|
+
export * from './types/enyo-eebus-features.js';
|
|
39
40
|
export * from './packages/energy-app-eebus.js';
|
|
40
41
|
export * from './types/enyo-mqtt.js';
|
|
41
42
|
export * from './packages/energy-app-mqtt.js';
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { EebusRemoteFeatureCatalog } from '../../types/enyo-eebus-features.js';
|
|
2
|
+
/**
|
|
3
|
+
* Per-peer SPINE entity/feature catalog for paired EEbus remotes.
|
|
4
|
+
*
|
|
5
|
+
* Exposes the lib's `RemoteDevice` view — the set of entities and
|
|
6
|
+
* features the peer actually advertises via
|
|
7
|
+
* `NodeManagement.DetailedDiscoveryData`, kept in sync by the SDK as the
|
|
8
|
+
* remote emits `NodeManagement.NotifyChange` events.
|
|
9
|
+
*
|
|
10
|
+
* Use this service in preference to {@link EebusIdentityService.getSupportedUseCases}
|
|
11
|
+
* when gating package behaviour on remote capability. Non-certified peers
|
|
12
|
+
* and simulators routinely under-populate `NodeManagement.UseCaseData`
|
|
13
|
+
* while still exposing the matching SPINE features (e.g. a `LoadControl`
|
|
14
|
+
* server with `loadControlLimitDescriptionData` of
|
|
15
|
+
* `limitDirection: 'consume'` but no corresponding
|
|
16
|
+
* `limitationOfPowerConsumption` use case). Feature-level gates work on
|
|
17
|
+
* these peers; use-case gates do not.
|
|
18
|
+
*
|
|
19
|
+
* Identity, like the use-case list, is observable rather than one-shot:
|
|
20
|
+
* remotes add or remove entities and features after a firmware update or
|
|
21
|
+
* a runtime mode change. Always pair {@link get} with
|
|
22
|
+
* {@link onFeaturesChanged} for any package that reacts to peer
|
|
23
|
+
* capabilities, otherwise the package will keep operating against a
|
|
24
|
+
* stale snapshot.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // Feature-based LPC gate that works on peers with incomplete UseCaseData
|
|
29
|
+
* const catalog = await eebus.features.get(ski);
|
|
30
|
+
* const loadControl = catalog.entities
|
|
31
|
+
* .flatMap(e => e.features)
|
|
32
|
+
* .find(f =>
|
|
33
|
+
* f.type === 'LoadControl'
|
|
34
|
+
* && f.role === 'server'
|
|
35
|
+
* && f.supportedFunctions.some(s => s.function === 'loadControlLimitListData'),
|
|
36
|
+
* );
|
|
37
|
+
* if (loadControl) {
|
|
38
|
+
* await eebus.useCases.lpc(ski).setConsumptionLimit({ value: 11000, isActive: true });
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* // React to the peer adding or removing features at runtime
|
|
42
|
+
* const listenerId = eebus.features.onFeaturesChanged(ski, next => {
|
|
43
|
+
* refreshCapabilityGates(next);
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export interface EebusFeatureCatalog {
|
|
48
|
+
/**
|
|
49
|
+
* Get the current SPINE entity/feature catalog snapshot for a remote node.
|
|
50
|
+
*
|
|
51
|
+
* The snapshot reflects the most recent `NodeManagement.DetailedDiscoveryData`
|
|
52
|
+
* state the SDK has observed; it does not trigger a re-fetch from the
|
|
53
|
+
* remote. To observe live additions, removals, and updates use
|
|
54
|
+
* {@link onFeaturesChanged}.
|
|
55
|
+
*
|
|
56
|
+
* If the peer identified by `ski` is not paired or not currently
|
|
57
|
+
* connected, the returned snapshot resolves with `found: false` and an
|
|
58
|
+
* empty `entities` list rather than throwing — so packages can use the
|
|
59
|
+
* call as a capability gate without wrapping it in `try`/`catch`.
|
|
60
|
+
*
|
|
61
|
+
* @param ski Subject Key Identifier of the remote node
|
|
62
|
+
* @returns The current entity/feature catalog snapshot
|
|
63
|
+
*/
|
|
64
|
+
get: (ski: string) => Promise<EebusRemoteFeatureCatalog>;
|
|
65
|
+
/**
|
|
66
|
+
* Subscribe to feature/entity catalog changes for a remote node.
|
|
67
|
+
*
|
|
68
|
+
* The listener is invoked with the full updated snapshot whenever the
|
|
69
|
+
* lib emits `featureAdded`, `featureUpdated`, `featureRemoved`,
|
|
70
|
+
* `entityAdded`, or `entityRemoved` for the peer. The payload shape
|
|
71
|
+
* matches {@link get} so subscribers can replace their cached catalog
|
|
72
|
+
* on every event without diffing.
|
|
73
|
+
*
|
|
74
|
+
* Subscriptions are scoped to the peer identified by `ski`. If the
|
|
75
|
+
* peer disconnects, the listener remains registered and resumes
|
|
76
|
+
* delivering events when the peer reconnects — packages do not need
|
|
77
|
+
* to re-subscribe after a transient disconnect.
|
|
78
|
+
*
|
|
79
|
+
* @param ski Subject Key Identifier of the remote node
|
|
80
|
+
* @param listener Callback invoked with the full updated catalog snapshot
|
|
81
|
+
* @returns Listener ID that can be passed to {@link removeListener} to cancel
|
|
82
|
+
*/
|
|
83
|
+
onFeaturesChanged: (ski: string, listener: (catalog: EebusRemoteFeatureCatalog) => void) => string;
|
|
84
|
+
/**
|
|
85
|
+
* Remove a feature-catalog listener previously registered via
|
|
86
|
+
* {@link onFeaturesChanged}.
|
|
87
|
+
* @param listenerId The ID returned by the registration method
|
|
88
|
+
*/
|
|
89
|
+
removeListener: (listenerId: string) => void;
|
|
90
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { EebusDeviceManagement } from './eebus-device-management.js';
|
|
2
|
+
import { EebusFeatureCatalog } from './eebus-feature-catalog.js';
|
|
2
3
|
import { EebusIdentityService } from './eebus-identity-service.js';
|
|
3
4
|
import { EebusSpineLowLevel } from './eebus-spine-low-level.js';
|
|
4
5
|
import { EebusUseCaseRegistry } from './eebus-use-case-registry.js';
|
|
5
6
|
export { EebusDeviceManagement } from './eebus-device-management.js';
|
|
7
|
+
export { EebusFeatureCatalog } from './eebus-feature-catalog.js';
|
|
6
8
|
export { EebusIdentityService } from './eebus-identity-service.js';
|
|
7
9
|
export { EebusSpineLowLevel } from './eebus-spine-low-level.js';
|
|
8
10
|
export { EebusUseCaseRegistry } from './eebus-use-case-registry.js';
|
|
@@ -16,10 +18,11 @@ export { EebusSetpointClient } from './eebus-setpoint-client.js';
|
|
|
16
18
|
/**
|
|
17
19
|
* Interface for EEbus (SHIP/SPINE) device communication in enyo packages.
|
|
18
20
|
*
|
|
19
|
-
* The API is split into
|
|
21
|
+
* The API is split into five orthogonal concerns, each its own sub-interface:
|
|
20
22
|
*
|
|
21
23
|
* - {@link devices} — SHIP-level device lifecycle: discovery, pairing, connection
|
|
22
24
|
* - {@link identity} — NID: observable per-node identity, diagnosis state, use-case discovery
|
|
25
|
+
* - {@link features} — observable per-peer SPINE entity/feature catalog
|
|
23
26
|
* - {@link useCases} — typed use-case clients: LPC, LPP, MGCP, MPC, OHPCF, Setpoint, Hvac
|
|
24
27
|
* - {@link spine} — low-level SPINE escape hatch for features not yet wrapped
|
|
25
28
|
*
|
|
@@ -41,16 +44,21 @@ export { EebusSetpointClient } from './eebus-setpoint-client.js';
|
|
|
41
44
|
* console.log(`${identity.brandName} ${identity.deviceName} v${identity.softwareRevision}`);
|
|
42
45
|
* eebus.identity.onIdentityChanged(device.ski, next => updateStatusBadge(next));
|
|
43
46
|
*
|
|
44
|
-
* // 3.
|
|
45
|
-
* const
|
|
46
|
-
*
|
|
47
|
+
* // 3. Feature catalog — gate behaviour on what the peer actually advertises
|
|
48
|
+
* const catalog = await eebus.features.get(device.ski);
|
|
49
|
+
* const hasLpcServer = catalog.entities
|
|
50
|
+
* .flatMap(e => e.features)
|
|
51
|
+
* .some(f => f.type === 'LoadControl' && f.role === 'server');
|
|
52
|
+
*
|
|
53
|
+
* // 4. Use cases — typed, role-aware
|
|
54
|
+
* if (hasLpcServer) {
|
|
47
55
|
* await eebus.useCases.lpc(device.ski).setConsumptionLimit({
|
|
48
56
|
* value: 11000,
|
|
49
57
|
* isActive: true,
|
|
50
58
|
* });
|
|
51
59
|
* }
|
|
52
60
|
*
|
|
53
|
-
* //
|
|
61
|
+
* // 5. Escape hatch — raw SPINE for unmodelled features
|
|
54
62
|
* const dp = await eebus.spine.readData(device.ski, 'DeviceConfiguration', 'keyValueListData');
|
|
55
63
|
* ```
|
|
56
64
|
*/
|
|
@@ -59,6 +67,12 @@ export interface EnergyAppEebus {
|
|
|
59
67
|
devices: EebusDeviceManagement;
|
|
60
68
|
/** EEBUS Node Identification (NID) — observable identity + use-case discovery */
|
|
61
69
|
identity: EebusIdentityService;
|
|
70
|
+
/**
|
|
71
|
+
* Observable per-peer SPINE entity/feature catalog. Prefer feature-level
|
|
72
|
+
* gates from this catalog over use-case gates from {@link identity} when
|
|
73
|
+
* the peer is known to under-populate `NodeManagement.UseCaseData`.
|
|
74
|
+
*/
|
|
75
|
+
features: EebusFeatureCatalog;
|
|
62
76
|
/** Typed use-case clients for the implemented EEBUS use cases */
|
|
63
77
|
useCases: EebusUseCaseRegistry;
|
|
64
78
|
/** Low-level SPINE escape hatch for features not yet wrapped by a typed client */
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types describing the SPINE feature/entity catalog advertised by a remote
|
|
3
|
+
* EEbus peer.
|
|
4
|
+
*
|
|
5
|
+
* The catalog is the **authoritative source of truth** for what a peer can
|
|
6
|
+
* actually do. It is derived from the lib's `RemoteDevice` view, which is
|
|
7
|
+
* itself kept in sync with `NodeManagement.DetailedDiscoveryData` replies
|
|
8
|
+
* and `NodeManagement.NotifyChange` events from the remote.
|
|
9
|
+
*
|
|
10
|
+
* Prefer feature/role gates based on this catalog over use-case gates from
|
|
11
|
+
* {@link EebusUseCaseSupport}: many non-certified peers and simulators
|
|
12
|
+
* implement working SPINE features (e.g. a `LoadControl` server with a
|
|
13
|
+
* `loadControlLimitDescriptionData` of `limitDirection: 'consume'`) without
|
|
14
|
+
* advertising the matching use case in `NodeManagement.UseCaseData`.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* SPINE role under which a feature is advertised by the remote node.
|
|
18
|
+
*
|
|
19
|
+
* The set is intentionally widened with `string` so that lib upgrades that
|
|
20
|
+
* introduce new SPINE role variants do not break consumer code that
|
|
21
|
+
* pattern-matches on the role.
|
|
22
|
+
*/
|
|
23
|
+
export type EebusFeatureRole = 'client' | 'server' | 'special' | string;
|
|
24
|
+
/**
|
|
25
|
+
* SPINE address triple identifying a feature within the EEbus topology.
|
|
26
|
+
*
|
|
27
|
+
* - {@link entity} addresses the entity within the remote node (e.g. `[1]`
|
|
28
|
+
* for a flat node, `[1, 1]` when the node nests sub-entities such as a
|
|
29
|
+
* compressor under a heat-pump appliance).
|
|
30
|
+
* - {@link feature} addresses the feature within that entity.
|
|
31
|
+
* - {@link device} optionally carries the remote node's SPINE
|
|
32
|
+
* `NetworkAddressDeviceID`. Usually omitted because the surrounding
|
|
33
|
+
* {@link EebusRemoteFeatureCatalog.deviceAddress} already provides it.
|
|
34
|
+
*/
|
|
35
|
+
export interface EebusFeatureAddress {
|
|
36
|
+
/** SPINE entity address — a sequence of integers identifying the entity within the node */
|
|
37
|
+
entity: number[];
|
|
38
|
+
/** SPINE feature address within the entity */
|
|
39
|
+
feature: number;
|
|
40
|
+
/** Optional SPINE `NetworkAddressDeviceID` of the remote node */
|
|
41
|
+
device?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A SPINE function the remote advertises as supported on a feature,
|
|
45
|
+
* together with the operations (read / write) the remote permits on it.
|
|
46
|
+
*
|
|
47
|
+
* The operation payloads are intentionally opaque (`object`) — SPINE
|
|
48
|
+
* permits per-function filters and bindings whose shape varies by
|
|
49
|
+
* function and is irrelevant to most callers. Pass them through verbatim
|
|
50
|
+
* when forwarding to {@link EebusSpineLowLevel}.
|
|
51
|
+
*/
|
|
52
|
+
export interface EebusSupportedFunction {
|
|
53
|
+
/** SPINE function/data-set name (e.g. `'loadControlLimitListData'`, `'measurementListData'`) */
|
|
54
|
+
function: string;
|
|
55
|
+
/** Operations the remote permits on this function */
|
|
56
|
+
possibleOperations: {
|
|
57
|
+
/** Present when the remote permits reads; payload mirrors the SPINE `read` operation parameters */
|
|
58
|
+
read?: object;
|
|
59
|
+
/** Present when the remote permits writes; payload mirrors the SPINE `write` operation parameters */
|
|
60
|
+
write?: object;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A single SPINE feature advertised by a remote entity.
|
|
65
|
+
*
|
|
66
|
+
* The {@link type} is a wire string (e.g. `'LoadControl'`,
|
|
67
|
+
* `'Measurement'`, `'DeviceClassification'`) rather than a closed enum
|
|
68
|
+
* so that lib upgrades that introduce new SPINE feature types do not
|
|
69
|
+
* break existing packages — see {@link EebusSpineLowLevel} for the same
|
|
70
|
+
* rationale applied to the low-level escape hatch.
|
|
71
|
+
*/
|
|
72
|
+
export interface EebusRemoteFeature {
|
|
73
|
+
/** SPINE address triple for this feature */
|
|
74
|
+
address: EebusFeatureAddress;
|
|
75
|
+
/** SPINE feature type wire string (e.g. `'LoadControl'`, `'Measurement'`) */
|
|
76
|
+
type: string;
|
|
77
|
+
/** SPINE role under which the feature is advertised */
|
|
78
|
+
role: EebusFeatureRole;
|
|
79
|
+
/** Functions the remote advertises as supported on this feature */
|
|
80
|
+
supportedFunctions: EebusSupportedFunction[];
|
|
81
|
+
/** Optional manufacturer-provided label */
|
|
82
|
+
label?: string;
|
|
83
|
+
/** Optional manufacturer-provided description */
|
|
84
|
+
description?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A SPINE entity advertised by a remote node.
|
|
88
|
+
*
|
|
89
|
+
* A node usually exposes a `DeviceInformation` entity (the node itself)
|
|
90
|
+
* plus one or more application-specific entities (e.g. `EVSE`, `EV`,
|
|
91
|
+
* `HeatPumpAppliance`, `Compressor`). Sub-entities are flattened into
|
|
92
|
+
* this list with their full {@link address} path preserved (e.g. `[1, 1]`
|
|
93
|
+
* for a compressor under a heat-pump appliance), so consumers can
|
|
94
|
+
* reconstruct the hierarchy when needed.
|
|
95
|
+
*/
|
|
96
|
+
export interface EebusRemoteEntity {
|
|
97
|
+
/** SPINE entity address — a sequence of integers identifying the entity (e.g. `[1]` or `[1, 1]`) */
|
|
98
|
+
address: number[];
|
|
99
|
+
/** SPINE entity type wire string (e.g. `'HeatPumpAppliance'`, `'Compressor'`, `'EVSE'`) */
|
|
100
|
+
type: string;
|
|
101
|
+
/** Optional manufacturer-provided label */
|
|
102
|
+
label?: string;
|
|
103
|
+
/** Optional manufacturer-provided description */
|
|
104
|
+
description?: string;
|
|
105
|
+
/** Features advertised on this entity */
|
|
106
|
+
features: EebusRemoteFeature[];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Snapshot of the full SPINE entity/feature catalog advertised by a
|
|
110
|
+
* remote EEbus peer.
|
|
111
|
+
*
|
|
112
|
+
* Returned by {@link EebusFeatureCatalog.get} and delivered to
|
|
113
|
+
* {@link EebusFeatureCatalog.onFeaturesChanged} listeners. The shape is
|
|
114
|
+
* intentionally identical between the two so that change subscribers can
|
|
115
|
+
* drop and replace their cached catalog on every event without diffing.
|
|
116
|
+
*
|
|
117
|
+
* When the peer identified by {@link ski} is not known (never paired or
|
|
118
|
+
* not currently connected), {@link found} is `false` and {@link entities}
|
|
119
|
+
* is an empty array — the call resolves cleanly rather than throwing, so
|
|
120
|
+
* packages can use the snapshot as a feature gate without try/catch.
|
|
121
|
+
*/
|
|
122
|
+
export interface EebusRemoteFeatureCatalog {
|
|
123
|
+
/** Subject Key Identifier of the remote node this catalog describes */
|
|
124
|
+
ski: string;
|
|
125
|
+
/** Whether the peer is currently known to the SDK (paired AND reachable) */
|
|
126
|
+
found: boolean;
|
|
127
|
+
/** Remote node's SPINE `NetworkAddressDeviceID`, when known */
|
|
128
|
+
deviceAddress?: string;
|
|
129
|
+
/** SPINE entities advertised by the remote node, flattened with their address path preserved */
|
|
130
|
+
entities: EebusRemoteEntity[];
|
|
131
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types describing the SPINE feature/entity catalog advertised by a remote
|
|
3
|
+
* EEbus peer.
|
|
4
|
+
*
|
|
5
|
+
* The catalog is the **authoritative source of truth** for what a peer can
|
|
6
|
+
* actually do. It is derived from the lib's `RemoteDevice` view, which is
|
|
7
|
+
* itself kept in sync with `NodeManagement.DetailedDiscoveryData` replies
|
|
8
|
+
* and `NodeManagement.NotifyChange` events from the remote.
|
|
9
|
+
*
|
|
10
|
+
* Prefer feature/role gates based on this catalog over use-case gates from
|
|
11
|
+
* {@link EebusUseCaseSupport}: many non-certified peers and simulators
|
|
12
|
+
* implement working SPINE features (e.g. a `LoadControl` server with a
|
|
13
|
+
* `loadControlLimitDescriptionData` of `limitDirection: 'consume'`) without
|
|
14
|
+
* advertising the matching use case in `NodeManagement.UseCaseData`.
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED