@matter/protocol 0.16.0-alpha.0-20251004-92135c7df → 0.16.0-alpha.0-20251011-d8942d7d5
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/action/client/ClientInteraction.d.ts +1 -0
- package/dist/cjs/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/cjs/action/client/ClientInteraction.js +6 -2
- package/dist/cjs/action/client/ClientInteraction.js.map +1 -1
- package/dist/cjs/action/protocols.d.ts +10 -11
- package/dist/cjs/action/protocols.d.ts.map +1 -1
- package/dist/cjs/action/request/Invoke.d.ts.map +1 -1
- package/dist/cjs/action/request/Invoke.js +3 -0
- package/dist/cjs/action/request/Invoke.js.map +1 -1
- package/dist/cjs/action/request/Read.d.ts.map +1 -1
- package/dist/cjs/action/request/Read.js +3 -0
- package/dist/cjs/action/request/Read.js.map +1 -1
- package/dist/cjs/action/request/Write.d.ts.map +1 -1
- package/dist/cjs/action/request/Write.js +3 -0
- package/dist/cjs/action/request/Write.js.map +1 -1
- package/dist/cjs/action/server/AttributeSubscriptionResponse.d.ts +14 -13
- package/dist/cjs/action/server/AttributeSubscriptionResponse.d.ts.map +1 -1
- package/dist/cjs/action/server/AttributeSubscriptionResponse.js +18 -18
- package/dist/cjs/action/server/AttributeSubscriptionResponse.js.map +1 -1
- package/dist/cjs/action/server/ServerInteraction.d.ts +1 -0
- package/dist/cjs/action/server/ServerInteraction.d.ts.map +1 -1
- package/dist/cjs/action/server/ServerInteraction.js +3 -0
- package/dist/cjs/action/server/ServerInteraction.js.map +1 -1
- package/dist/cjs/fabric/Fabric.d.ts +2 -4
- package/dist/cjs/fabric/Fabric.d.ts.map +1 -1
- package/dist/cjs/fabric/Fabric.js.map +1 -1
- package/dist/cjs/fabric/FabricAuthority.d.ts +0 -1
- package/dist/cjs/fabric/FabricAuthority.d.ts.map +1 -1
- package/dist/cjs/fabric/FabricAuthority.js +2 -3
- package/dist/cjs/fabric/FabricAuthority.js.map +1 -1
- package/dist/cjs/fabric/TestFabric.d.ts.map +1 -1
- package/dist/cjs/fabric/TestFabric.js +2 -1
- package/dist/cjs/fabric/TestFabric.js.map +1 -1
- package/dist/cjs/interaction/InteractionClient.js +1 -1
- package/dist/cjs/interaction/InteractionClient.js.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.js +1 -1
- package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
- package/dist/cjs/mdns/MdnsService.d.ts +1 -1
- package/dist/cjs/mdns/MdnsService.d.ts.map +1 -1
- package/dist/cjs/mdns/MdnsService.js +1 -1
- package/dist/cjs/mdns/MdnsService.js.map +1 -1
- package/dist/cjs/peer/ControllerCommissioner.d.ts.map +1 -1
- package/dist/cjs/peer/ControllerCommissioner.js +26 -21
- package/dist/cjs/peer/ControllerCommissioner.js.map +1 -1
- package/dist/cjs/peer/PeerSet.d.ts +5 -6
- package/dist/cjs/peer/PeerSet.d.ts.map +1 -1
- package/dist/cjs/peer/PeerSet.js +19 -6
- package/dist/cjs/peer/PeerSet.js.map +1 -1
- package/dist/cjs/protocol/ExchangeManager.js +2 -2
- package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
- package/dist/cjs/protocol/MessageExchange.d.ts.map +1 -1
- package/dist/cjs/protocol/MessageExchange.js +3 -2
- package/dist/cjs/protocol/MessageExchange.js.map +1 -1
- package/dist/cjs/session/SessionManager.d.ts.map +1 -1
- package/dist/cjs/session/SessionManager.js +17 -9
- package/dist/cjs/session/SessionManager.js.map +1 -1
- package/dist/esm/action/client/ClientInteraction.d.ts +1 -0
- package/dist/esm/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/esm/action/client/ClientInteraction.js +6 -2
- package/dist/esm/action/client/ClientInteraction.js.map +1 -1
- package/dist/esm/action/protocols.d.ts +10 -11
- package/dist/esm/action/protocols.d.ts.map +1 -1
- package/dist/esm/action/request/Invoke.d.ts.map +1 -1
- package/dist/esm/action/request/Invoke.js +3 -0
- package/dist/esm/action/request/Invoke.js.map +1 -1
- package/dist/esm/action/request/Read.d.ts.map +1 -1
- package/dist/esm/action/request/Read.js +3 -0
- package/dist/esm/action/request/Read.js.map +1 -1
- package/dist/esm/action/request/Write.d.ts.map +1 -1
- package/dist/esm/action/request/Write.js +3 -0
- package/dist/esm/action/request/Write.js.map +1 -1
- package/dist/esm/action/server/AttributeSubscriptionResponse.d.ts +14 -13
- package/dist/esm/action/server/AttributeSubscriptionResponse.d.ts.map +1 -1
- package/dist/esm/action/server/AttributeSubscriptionResponse.js +18 -18
- package/dist/esm/action/server/AttributeSubscriptionResponse.js.map +1 -1
- package/dist/esm/action/server/ServerInteraction.d.ts +1 -0
- package/dist/esm/action/server/ServerInteraction.d.ts.map +1 -1
- package/dist/esm/action/server/ServerInteraction.js +3 -0
- package/dist/esm/action/server/ServerInteraction.js.map +1 -1
- package/dist/esm/fabric/Fabric.d.ts +2 -4
- package/dist/esm/fabric/Fabric.d.ts.map +1 -1
- package/dist/esm/fabric/Fabric.js.map +1 -1
- package/dist/esm/fabric/FabricAuthority.d.ts +0 -1
- package/dist/esm/fabric/FabricAuthority.d.ts.map +1 -1
- package/dist/esm/fabric/FabricAuthority.js +2 -3
- package/dist/esm/fabric/FabricAuthority.js.map +1 -1
- package/dist/esm/fabric/TestFabric.d.ts.map +1 -1
- package/dist/esm/fabric/TestFabric.js +3 -2
- package/dist/esm/fabric/TestFabric.js.map +1 -1
- package/dist/esm/interaction/InteractionClient.js +1 -1
- package/dist/esm/interaction/InteractionClient.js.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.js +1 -1
- package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
- package/dist/esm/mdns/MdnsService.d.ts +1 -1
- package/dist/esm/mdns/MdnsService.d.ts.map +1 -1
- package/dist/esm/mdns/MdnsService.js +1 -1
- package/dist/esm/mdns/MdnsService.js.map +1 -1
- package/dist/esm/peer/ControllerCommissioner.d.ts.map +1 -1
- package/dist/esm/peer/ControllerCommissioner.js +26 -21
- package/dist/esm/peer/ControllerCommissioner.js.map +1 -1
- package/dist/esm/peer/PeerSet.d.ts +5 -6
- package/dist/esm/peer/PeerSet.d.ts.map +1 -1
- package/dist/esm/peer/PeerSet.js +19 -6
- package/dist/esm/peer/PeerSet.js.map +1 -1
- package/dist/esm/protocol/ExchangeManager.js +2 -2
- package/dist/esm/protocol/ExchangeManager.js.map +1 -1
- package/dist/esm/protocol/MessageExchange.d.ts.map +1 -1
- package/dist/esm/protocol/MessageExchange.js +3 -2
- package/dist/esm/protocol/MessageExchange.js.map +1 -1
- package/dist/esm/session/SessionManager.d.ts.map +1 -1
- package/dist/esm/session/SessionManager.js +19 -10
- package/dist/esm/session/SessionManager.js.map +1 -1
- package/package.json +6 -6
- package/src/action/client/ClientInteraction.ts +6 -2
- package/src/action/protocols.ts +11 -10
- package/src/action/request/Invoke.ts +4 -0
- package/src/action/request/Read.ts +4 -0
- package/src/action/request/Write.ts +4 -0
- package/src/action/server/AttributeSubscriptionResponse.ts +30 -27
- package/src/action/server/ServerInteraction.ts +5 -0
- package/src/fabric/Fabric.ts +1 -1
- package/src/fabric/FabricAuthority.ts +2 -2
- package/src/fabric/TestFabric.ts +2 -1
- package/src/interaction/InteractionClient.ts +1 -1
- package/src/interaction/InteractionMessenger.ts +1 -1
- package/src/mdns/MdnsService.ts +1 -1
- package/src/peer/ControllerCommissioner.ts +31 -23
- package/src/peer/PeerSet.ts +23 -7
- package/src/protocol/ExchangeManager.ts +2 -2
- package/src/protocol/MessageExchange.ts +3 -2
- package/src/session/SessionManager.ts +35 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matter/protocol",
|
|
3
|
-
"version": "0.16.0-alpha.0-
|
|
3
|
+
"version": "0.16.0-alpha.0-20251011-d8942d7d5",
|
|
4
4
|
"description": "Low-level APIs for Matter interaction",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iot",
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
"#*": "./src/*"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@matter/general": "0.16.0-alpha.0-
|
|
44
|
-
"@matter/model": "0.16.0-alpha.0-
|
|
45
|
-
"@matter/types": "0.16.0-alpha.0-
|
|
43
|
+
"@matter/general": "0.16.0-alpha.0-20251011-d8942d7d5",
|
|
44
|
+
"@matter/model": "0.16.0-alpha.0-20251011-d8942d7d5",
|
|
45
|
+
"@matter/types": "0.16.0-alpha.0-20251011-d8942d7d5"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@matter/tools": "0.16.0-alpha.0-
|
|
49
|
-
"@matter/testing": "0.16.0-alpha.0-
|
|
48
|
+
"@matter/tools": "0.16.0-alpha.0-20251011-d8942d7d5",
|
|
49
|
+
"@matter/testing": "0.16.0-alpha.0-20251011-d8942d7d5"
|
|
50
50
|
},
|
|
51
51
|
"files": [
|
|
52
52
|
"dist/**/*",
|
|
@@ -194,9 +194,9 @@ export class ClientInteraction<SessionT extends InteractionSession = Interaction
|
|
|
194
194
|
messenger = await InteractionClientMessenger.create(this.#exchanges);
|
|
195
195
|
|
|
196
196
|
await messenger.sendSubscribeRequest({
|
|
197
|
-
...request,
|
|
198
197
|
minIntervalFloorSeconds: Seconds.of(DEFAULT_MIN_INTERVAL_FLOOR),
|
|
199
|
-
maxIntervalCeilingSeconds: Seconds.of(DEFAULT_MIN_INTERVAL_FLOOR),
|
|
198
|
+
maxIntervalCeilingSeconds: Seconds.of(DEFAULT_MIN_INTERVAL_FLOOR), // TODO use better max fallback
|
|
199
|
+
...request,
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
await this.#handleSubscriptionResponse(request, readChunks(messenger));
|
|
@@ -211,6 +211,10 @@ export class ClientInteraction<SessionT extends InteractionSession = Interaction
|
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
cancelSubscription(id: number) {
|
|
215
|
+
this.#subscriptions.get(id)?.close();
|
|
216
|
+
}
|
|
217
|
+
|
|
214
218
|
async #handleSubscriptionResponse(request: Subscribe, result: ReadResult) {
|
|
215
219
|
if (request.updated) {
|
|
216
220
|
await request.updated(result);
|
package/src/action/protocols.ts
CHANGED
|
@@ -64,12 +64,9 @@ export interface NodeProtocol extends CollectionProtocol<EndpointProtocol> {
|
|
|
64
64
|
eventHandler: OccurrenceManager;
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
67
|
+
* Emitted when attributes change.
|
|
68
68
|
*/
|
|
69
|
-
|
|
70
|
-
[endpointId: EndpointNumber, clusterId: ClusterId, changes: AttributeId[], version: number],
|
|
71
|
-
MaybePromise
|
|
72
|
-
>;
|
|
69
|
+
attrsChanged: Observable<NodeProtocol.AttrChange, MaybePromise>;
|
|
73
70
|
|
|
74
71
|
/**
|
|
75
72
|
* Inspects an Attribute- or Event path and log in human-readable form if possible
|
|
@@ -77,6 +74,15 @@ export interface NodeProtocol extends CollectionProtocol<EndpointProtocol> {
|
|
|
77
74
|
inspectPath(path: AttributePath | EventPath | CommandPath): string;
|
|
78
75
|
}
|
|
79
76
|
|
|
77
|
+
export namespace NodeProtocol {
|
|
78
|
+
export type AttrChange = [
|
|
79
|
+
endpointId: EndpointNumber,
|
|
80
|
+
clusterId: ClusterId,
|
|
81
|
+
changes: AttributeId[],
|
|
82
|
+
version: number,
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
|
|
80
86
|
/**
|
|
81
87
|
* Protocol contract for a specific endpoint.
|
|
82
88
|
*/
|
|
@@ -115,11 +121,6 @@ export interface ClusterProtocol {
|
|
|
115
121
|
*/
|
|
116
122
|
location: AccessControl.Location;
|
|
117
123
|
|
|
118
|
-
/**
|
|
119
|
-
* The cluster datasource state change event
|
|
120
|
-
*/
|
|
121
|
-
stateChanged: Observable<[changes: AttributeId[], version: number], MaybePromise>;
|
|
122
|
-
|
|
123
124
|
/**
|
|
124
125
|
* Read-only state of the cluster
|
|
125
126
|
*/
|
|
@@ -30,6 +30,10 @@ export function Invoke(options: Invoke.Definition, ...commands: CommandData[]):
|
|
|
30
30
|
export function Invoke(...commands: CommandData[]): Invoke;
|
|
31
31
|
|
|
32
32
|
export function Invoke(optionsOrData: Invoke.Definition | CommandData, ...commands: CommandData[]): Invoke {
|
|
33
|
+
if (optionsOrData === undefined) {
|
|
34
|
+
throw new MalformedRequestError(`Invocation requires at least one command`);
|
|
35
|
+
}
|
|
36
|
+
|
|
33
37
|
let options;
|
|
34
38
|
if ("commands" in optionsOrData) {
|
|
35
39
|
options = optionsOrData;
|
|
@@ -34,6 +34,10 @@ export function Read(options: Read.Options, ...selectors: Read.Selector[]): Read
|
|
|
34
34
|
export function Read(...selectors: Read.Selector[]): Read;
|
|
35
35
|
|
|
36
36
|
export function Read(optionsOrSelector: Read.Options | Read.Selector, ...selectors: Read.Selector[]): Read {
|
|
37
|
+
if (optionsOrSelector === undefined) {
|
|
38
|
+
throw new MalformedRequestError(`Read action designates no attributes or events`);
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
let options;
|
|
38
42
|
if ("kind" in optionsOrSelector) {
|
|
39
43
|
selectors = [optionsOrSelector, ...selectors];
|
|
@@ -30,6 +30,10 @@ export function Write(options: Write.Options, ...data: Write.Attribute[]): Write
|
|
|
30
30
|
export function Write(...data: Write.Attribute[]): Write;
|
|
31
31
|
|
|
32
32
|
export function Write(optionsOrData: Write.Options | Write.Attribute, ...data: Write.Attribute[]): Write {
|
|
33
|
+
if (optionsOrData === undefined) {
|
|
34
|
+
throw new MalformedRequestError(`Write action must have options or data`);
|
|
35
|
+
}
|
|
36
|
+
|
|
33
37
|
let options;
|
|
34
38
|
if ("kind" in optionsOrData) {
|
|
35
39
|
data = [optionsOrData, ...data];
|
|
@@ -11,75 +11,78 @@ import { InternalError } from "#general";
|
|
|
11
11
|
import { AttributeId, AttributePath, ClusterId, EndpointNumber } from "#types";
|
|
12
12
|
import { AttributeReadResponse } from "./AttributeReadResponse.js";
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
export namespace DirtyState {
|
|
15
|
+
export type ForCluster = {
|
|
16
|
+
[clusterId: ClusterId]: Set<AttributeId>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type ForNode = {
|
|
20
|
+
[endpointId: EndpointNumber]: ForCluster;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
25
|
+
* A specialization of {@link AttributeReadResponse} that processes a read/subscribe request with a filter applied to
|
|
26
|
+
* the attributes. Only processes attributes that match the filter.
|
|
24
27
|
*/
|
|
25
28
|
export class AttributeSubscriptionResponse<
|
|
26
29
|
SessionT extends InteractionSession = InteractionSession,
|
|
27
30
|
> extends AttributeReadResponse<SessionT> {
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
+
#dirty: DirtyState.ForNode;
|
|
32
|
+
#currentEndpointDirty?: DirtyState.ForCluster;
|
|
33
|
+
#currentClusterDirty?: Set<number>;
|
|
31
34
|
|
|
32
|
-
constructor(node: NodeProtocol, session: SessionT, filter:
|
|
35
|
+
constructor(node: NodeProtocol, session: SessionT, filter: DirtyState.ForNode) {
|
|
33
36
|
super(node, session);
|
|
34
|
-
this.#
|
|
37
|
+
this.#dirty = filter;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
get
|
|
38
|
-
return this.#
|
|
40
|
+
get dirty() {
|
|
41
|
+
return this.#dirty;
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
/** Guarded accessor for this.#currentEndpointFilter. This should never be undefined */
|
|
42
|
-
protected get
|
|
43
|
-
if (!this.#
|
|
45
|
+
protected get currentEndpointDirty() {
|
|
46
|
+
if (!this.#currentEndpointDirty) {
|
|
44
47
|
throw new InternalError("currentEndpointFilter is not set. Should never happen");
|
|
45
48
|
}
|
|
46
|
-
return this.#
|
|
49
|
+
return this.#currentEndpointDirty;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
/** Guarded accessor for this.#currentCLusterFilter. This should never be undefined */
|
|
50
|
-
protected get
|
|
51
|
-
if (!this.#
|
|
53
|
+
protected get currentClusterDirty() {
|
|
54
|
+
if (!this.#currentClusterDirty) {
|
|
52
55
|
throw new InternalError("currentClusterFilter is not set. Should never happen");
|
|
53
56
|
}
|
|
54
|
-
return this.#
|
|
57
|
+
return this.#currentClusterDirty;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
protected override addConcrete(path: ReadResult.ConcreteAttributePath) {
|
|
58
61
|
const { endpointId, clusterId, attributeId } = path;
|
|
59
|
-
if (this.#
|
|
62
|
+
if (this.#dirty[endpointId]?.[clusterId]?.has(attributeId) === undefined) {
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
62
65
|
super.addConcrete(path);
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
protected override *readEndpointForWildcard(endpoint: EndpointProtocol, path: AttributePath) {
|
|
66
|
-
this.#
|
|
67
|
-
if (this.#
|
|
69
|
+
this.#currentEndpointDirty = this.#dirty[endpoint.id];
|
|
70
|
+
if (this.#currentEndpointDirty === undefined) {
|
|
68
71
|
return;
|
|
69
72
|
}
|
|
70
73
|
yield* super.readEndpointForWildcard(endpoint, path);
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
protected override readClusterForWildcard(cluster: ClusterProtocol, path: AttributePath) {
|
|
74
|
-
this.#
|
|
75
|
-
if (this.#
|
|
77
|
+
this.#currentClusterDirty = this.currentEndpointDirty[cluster.type.id];
|
|
78
|
+
if (this.#currentClusterDirty === undefined) {
|
|
76
79
|
return;
|
|
77
80
|
}
|
|
78
81
|
super.readClusterForWildcard(cluster, path);
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
protected override readAttributeForWildcard(attribute: AttributeTypeProtocol, path: AttributePath) {
|
|
82
|
-
if (!this.
|
|
85
|
+
if (!this.currentClusterDirty.has(attribute.id)) {
|
|
83
86
|
return;
|
|
84
87
|
}
|
|
85
88
|
super.readAttributeForWildcard(attribute, path);
|
|
@@ -65,6 +65,11 @@ export class ServerInteraction<SessionT extends InteractionSession = Interaction
|
|
|
65
65
|
throw new NotImplementedError();
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
cancelSubscription(_id: number): void {
|
|
69
|
+
// TODO
|
|
70
|
+
throw new NotImplementedError();
|
|
71
|
+
}
|
|
72
|
+
|
|
68
73
|
write<T extends Write>(request: T, session: SessionT): WriteResult<T> {
|
|
69
74
|
// TODO - validate request
|
|
70
75
|
|
package/src/fabric/Fabric.ts
CHANGED
|
@@ -51,7 +51,6 @@ export interface FabricAuthorityContext {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export const DEFAULT_ADMIN_VENDOR_ID = VendorId(0xfff1);
|
|
54
|
-
export const DEFAULT_FABRIC_ID = FabricId(1);
|
|
55
54
|
|
|
56
55
|
/**
|
|
57
56
|
* Manages fabrics controlled locally associated with a specific CA.
|
|
@@ -144,10 +143,11 @@ export class FabricAuthority {
|
|
|
144
143
|
.setRootVendorId(this.#config.adminVendorId ?? DEFAULT_ADMIN_VENDOR_ID)
|
|
145
144
|
.setLabel(this.#config.adminFabricLabel);
|
|
146
145
|
|
|
146
|
+
const fabricId = this.#config.fabricId ?? FabricId(this.#fabrics.crypto.randomBigInt(8));
|
|
147
147
|
await fabricBuilder.setOperationalCert(
|
|
148
148
|
await this.#ca.generateNoc(
|
|
149
149
|
fabricBuilder.publicKey,
|
|
150
|
-
|
|
150
|
+
fabricId,
|
|
151
151
|
rootNodeId,
|
|
152
152
|
this.#config.caseAuthenticatedTags,
|
|
153
153
|
),
|
package/src/fabric/TestFabric.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { CertificateAuthority } from "#certificate/CertificateAuthority.js";
|
|
8
8
|
import { ImplementationError, MockCrypto } from "#general";
|
|
9
|
-
import { FabricIndex, VendorId } from "#types";
|
|
9
|
+
import { FabricId, FabricIndex, VendorId } from "#types";
|
|
10
10
|
import { FabricAuthority } from "./FabricAuthority.js";
|
|
11
11
|
import { FabricManager } from "./FabricManager.js";
|
|
12
12
|
|
|
@@ -54,6 +54,7 @@ export namespace TestFabric {
|
|
|
54
54
|
adminFabricLabel: `mock-fabric-${index}`,
|
|
55
55
|
adminVendorId: VendorId(0xfff1),
|
|
56
56
|
fabricIndex: FabricIndex(index),
|
|
57
|
+
fabricId: FabricId(1),
|
|
57
58
|
},
|
|
58
59
|
fabrics,
|
|
59
60
|
});
|
|
@@ -116,7 +116,7 @@ export class InteractionClientProvider {
|
|
|
116
116
|
operationalAddress?: ServerAddressUdp;
|
|
117
117
|
},
|
|
118
118
|
): Promise<InteractionClient> {
|
|
119
|
-
await this.#peers.
|
|
119
|
+
await this.#peers.connect(address, options);
|
|
120
120
|
|
|
121
121
|
return this.getInteractionClient(address, options);
|
|
122
122
|
}
|
|
@@ -301,7 +301,7 @@ export class InteractionServerMessenger extends InteractionMessenger {
|
|
|
301
301
|
}
|
|
302
302
|
default:
|
|
303
303
|
throw new StatusResponseError(
|
|
304
|
-
`Unsupported message type ${message.payloadHeader.messageType}`,
|
|
304
|
+
`Unsupported message type ${MessageType[message.payloadHeader.messageType]} (${message.payloadHeader.messageType})`,
|
|
305
305
|
Status.InvalidAction,
|
|
306
306
|
);
|
|
307
307
|
}
|
package/src/mdns/MdnsService.ts
CHANGED
|
@@ -313,32 +313,40 @@ export class ControllerCommissioner {
|
|
|
313
313
|
if (device !== undefined) {
|
|
314
314
|
logger.info(`Establish PASE to device`, MdnsClient.discoveryDataDiagnostics(device));
|
|
315
315
|
}
|
|
316
|
-
if (address.type === "udp") {
|
|
317
|
-
const { ip } = address;
|
|
318
316
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
`IPv${isIpv6Address ? "6" : "4"} interface not initialized. Cannot use ${ip} for commissioning.`,
|
|
317
|
+
switch (address.type) {
|
|
318
|
+
case "udp":
|
|
319
|
+
const { ip } = address;
|
|
320
|
+
|
|
321
|
+
const isIpv6Address = isIPv6(ip);
|
|
322
|
+
const paseInterface = this.#context.transports.interfaceFor(
|
|
323
|
+
ChannelType.UDP,
|
|
324
|
+
isIpv6Address ? "::" : "0.0.0.0",
|
|
328
325
|
);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
326
|
+
if (paseInterface === undefined) {
|
|
327
|
+
// mainly IPv6 address when IPv4 is disabled
|
|
328
|
+
throw new PairRetransmissionLimitReachedError(
|
|
329
|
+
`IPv${isIpv6Address ? "6" : "4"} interface not initialized. Cannot use ${ip} for commissioning.`,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
paseChannel = await paseInterface.openChannel(address);
|
|
333
|
+
break;
|
|
334
|
+
|
|
335
|
+
case "ble":
|
|
336
|
+
const ble = this.#context.transports.interfaceFor(ChannelType.BLE);
|
|
337
|
+
if (!ble) {
|
|
338
|
+
throw new PairRetransmissionLimitReachedError(
|
|
339
|
+
`BLE interface not initialized. Cannot use ${address.peripheralAddress} for commissioning.`,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
// TODO Have a Timeout mechanism here for connections
|
|
343
|
+
paseChannel = await ble.openChannel(address);
|
|
344
|
+
break;
|
|
345
|
+
|
|
346
|
+
default:
|
|
347
|
+
throw new ImplementationError(
|
|
348
|
+
`Unsupported address type ${(address as ServerAddress).type} for Matter protocol`,
|
|
336
349
|
);
|
|
337
|
-
}
|
|
338
|
-
// TODO Have a Timeout mechanism here for connections
|
|
339
|
-
paseChannel = await ble.openChannel(address);
|
|
340
|
-
} else {
|
|
341
|
-
throw new ImplementationError(`Unsupported PASE address type ${address.type}`);
|
|
342
350
|
}
|
|
343
351
|
|
|
344
352
|
// Do PASE paring
|
package/src/peer/PeerSet.ts
CHANGED
|
@@ -258,10 +258,14 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
|
|
|
258
258
|
return this.#interactionQueue;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
async connect(address: PeerAddress, options: PeerConnectionOptions & { operationalAddress?: ServerAddressUdp }) {
|
|
262
|
+
await this.#ensureConnection(address, { ...options, allowUnknownPeer: true });
|
|
263
|
+
}
|
|
264
|
+
|
|
261
265
|
/**
|
|
262
266
|
* Ensure there is a channel to the designated peer.
|
|
263
267
|
*/
|
|
264
|
-
async ensureConnection(
|
|
268
|
+
async #ensureConnection(
|
|
265
269
|
address: PeerAddress,
|
|
266
270
|
options: PeerConnectionOptions & {
|
|
267
271
|
allowUnknownPeer?: boolean;
|
|
@@ -306,6 +310,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
|
|
|
306
310
|
|
|
307
311
|
/**
|
|
308
312
|
* Obtain an exchange provider for the designated peer.
|
|
313
|
+
* TODO enhance PeerConnectionOptions.discoveryOptions.discoveryData with "addresses" for known operational addresses
|
|
309
314
|
*/
|
|
310
315
|
async exchangeProviderFor(addressOrChannel: PeerAddress | MessageChannel, options: PeerConnectionOptions = {}) {
|
|
311
316
|
if (addressOrChannel instanceof MessageChannel) {
|
|
@@ -325,7 +330,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
|
|
|
325
330
|
|
|
326
331
|
if (!initiallyConnected && !this.#channels.hasChannel(address)) {
|
|
327
332
|
// We got an uninitialized node, so do the first connection as usual
|
|
328
|
-
await this
|
|
333
|
+
await this.#ensureConnection(address, {
|
|
329
334
|
discoveryOptions: { discoveryType: NodeDiscoveryType.None },
|
|
330
335
|
caseAuthenticatedTags,
|
|
331
336
|
});
|
|
@@ -376,11 +381,17 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
|
|
|
376
381
|
|
|
377
382
|
/**
|
|
378
383
|
* Terminate any active peer connection.
|
|
384
|
+
* Also handles unknown peers
|
|
379
385
|
*/
|
|
380
386
|
async disconnect(peer: PeerAddress | OperationalPeer, sendSessionClose = true) {
|
|
381
|
-
|
|
387
|
+
let address = this.get(peer)?.address; // Check known Peers
|
|
382
388
|
if (address === undefined) {
|
|
383
|
-
|
|
389
|
+
// We did not find a ClientNode for this peer, so check if it is a PeerAddress
|
|
390
|
+
if ("nodeId" in peer && "fabricIndex" in peer) {
|
|
391
|
+
address = peer;
|
|
392
|
+
} else {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
384
395
|
}
|
|
385
396
|
|
|
386
397
|
await this.#sessions.removeAllSessionsForNode(address, sendSessionClose);
|
|
@@ -720,6 +731,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
|
|
|
720
731
|
const unsecureSession = this.#sessions.createInsecureSession({
|
|
721
732
|
// Use the session parameters from MDNS announcements when available and rest is assumed to be fallbacks
|
|
722
733
|
sessionParameters: {
|
|
734
|
+
...sessionParameters,
|
|
723
735
|
idleInterval: discoveryData?.SII ?? sessionParameters?.idleInterval,
|
|
724
736
|
activeInterval: discoveryData?.SAI ?? sessionParameters?.activeInterval,
|
|
725
737
|
activeThreshold: discoveryData?.SAT ?? sessionParameters?.activeThreshold,
|
|
@@ -817,7 +829,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
|
|
|
817
829
|
|
|
818
830
|
async #addOrUpdatePeer(
|
|
819
831
|
address: PeerAddress,
|
|
820
|
-
operationalServerAddress
|
|
832
|
+
operationalServerAddress?: ServerAddressUdp,
|
|
821
833
|
discoveryData?: DiscoveryData,
|
|
822
834
|
) {
|
|
823
835
|
let peer = this.#peersByAddress.get(address);
|
|
@@ -825,7 +837,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
|
|
|
825
837
|
peer = { address, dataStore: await this.#store.createNodeStore(address) };
|
|
826
838
|
this.#peers.add(peer);
|
|
827
839
|
}
|
|
828
|
-
peer.operationalAddress = operationalServerAddress;
|
|
840
|
+
peer.operationalAddress = operationalServerAddress ?? peer.operationalAddress;
|
|
829
841
|
if (discoveryData !== undefined) {
|
|
830
842
|
peer.discoveryData = {
|
|
831
843
|
...peer.discoveryData,
|
|
@@ -835,7 +847,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
|
|
|
835
847
|
await this.#store.updatePeer(peer);
|
|
836
848
|
|
|
837
849
|
// If we got a new channel and have a running discovery we can end it
|
|
838
|
-
if (this.#runningPeerDiscoveries.has(address)) {
|
|
850
|
+
if (peer.operationalAddress !== undefined && this.#runningPeerDiscoveries.has(address)) {
|
|
839
851
|
logger.info(`Found ${address} during discovery, cancel discovery.`);
|
|
840
852
|
// We are currently discovering this node, so we need to update the discovery data
|
|
841
853
|
const { mdnsClient: mdnsScanner } = this.#runningPeerDiscoveries.get(address) ?? {};
|
|
@@ -845,6 +857,10 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
|
|
|
845
857
|
}
|
|
846
858
|
}
|
|
847
859
|
|
|
860
|
+
addKnownPeer(address: PeerAddress, operationalServerAddress?: ServerAddressUdp, discoveryData?: DiscoveryData) {
|
|
861
|
+
return this.#addOrUpdatePeer(address, operationalServerAddress, discoveryData);
|
|
862
|
+
}
|
|
863
|
+
|
|
848
864
|
#getLastOperationalAddress(address: PeerAddress) {
|
|
849
865
|
return this.#peersByAddress.get(address)?.operationalAddress;
|
|
850
866
|
}
|
|
@@ -335,9 +335,10 @@ export class ExchangeManager {
|
|
|
335
335
|
return;
|
|
336
336
|
}
|
|
337
337
|
const { session } = exchange;
|
|
338
|
+
this.#exchanges.delete(exchangeIndex);
|
|
338
339
|
if (NodeSession.is(session) && session.closingAfterExchangeFinished) {
|
|
339
340
|
logger.debug(
|
|
340
|
-
`Exchange index ${exchangeIndex} Session ${session.name} is already marked for closure. Close session now.`,
|
|
341
|
+
`Exchange index ${exchangeIndex} on Session ${session.name} is already marked for closure. Close session now.`,
|
|
341
342
|
);
|
|
342
343
|
try {
|
|
343
344
|
await this.#closeSession(session);
|
|
@@ -345,7 +346,6 @@ export class ExchangeManager {
|
|
|
345
346
|
logger.error(`Error closing session ${session.name}. Ignoring.`, error);
|
|
346
347
|
}
|
|
347
348
|
}
|
|
348
|
-
this.#exchanges.delete(exchangeIndex);
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
async #closeSession(session: NodeSession) {
|
|
@@ -198,6 +198,7 @@ export class MessageExchange {
|
|
|
198
198
|
|
|
199
199
|
logger.debug(
|
|
200
200
|
"New exchange",
|
|
201
|
+
isInitiator ? "»" : "«",
|
|
201
202
|
Diagnostic.dict({
|
|
202
203
|
channel: channel.name,
|
|
203
204
|
protocol: this.#protocolId,
|
|
@@ -507,7 +508,7 @@ export class MessageExchange {
|
|
|
507
508
|
if (finalWaitTime > 0) {
|
|
508
509
|
this.#retransmissionCounter--; // We will not resubmit the message again
|
|
509
510
|
logger.debug(
|
|
510
|
-
`Message ${message.packetHeader.messageId}: Wait additional ${finalWaitTime}
|
|
511
|
+
`Message ${message.packetHeader.messageId}: Wait additional ${Duration.format(finalWaitTime)} for processing time and peer resubmissions after all our resubmissions`,
|
|
511
512
|
);
|
|
512
513
|
this.#retransmissionTimer = Time.getTimer(
|
|
513
514
|
`Message wait time after resubmissions ${message.packetHeader.messageId}`,
|
|
@@ -537,7 +538,7 @@ export class MessageExchange {
|
|
|
537
538
|
this.context.retry(this.#retransmissionCounter);
|
|
538
539
|
const resubmissionBackoffTime = this.channel.getMrpResubmissionBackOffTime(this.#retransmissionCounter);
|
|
539
540
|
logger.debug(
|
|
540
|
-
`Resubmit message ${message.packetHeader.messageId} (retransmission attempt ${this.#retransmissionCounter}, backoff time ${resubmissionBackoffTime}
|
|
541
|
+
`Resubmit message ${message.packetHeader.messageId} (retransmission attempt ${this.#retransmissionCounter}, backoff time ${Duration.format(resubmissionBackoffTime)}))`,
|
|
541
542
|
);
|
|
542
543
|
|
|
543
544
|
this.channel
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
ObserverGroup,
|
|
24
24
|
StorageContext,
|
|
25
25
|
StorageManager,
|
|
26
|
+
toHex,
|
|
26
27
|
} from "#general";
|
|
27
28
|
import { Subscription } from "#interaction/Subscription.js";
|
|
28
29
|
import { Specification } from "#model";
|
|
@@ -73,6 +74,7 @@ type ResumptionStorageRecord = {
|
|
|
73
74
|
sharedSecret: Bytes;
|
|
74
75
|
resumptionId: Bytes;
|
|
75
76
|
fabricId: FabricId;
|
|
77
|
+
fabricIndex: FabricIndex;
|
|
76
78
|
peerNodeId: NodeId;
|
|
77
79
|
sessionParameters: {
|
|
78
80
|
idleInterval: Duration;
|
|
@@ -557,21 +559,21 @@ export class SessionManager {
|
|
|
557
559
|
([
|
|
558
560
|
address,
|
|
559
561
|
{ sharedSecret, resumptionId, peerNodeId, fabric, sessionParameters, caseAuthenticatedTags },
|
|
560
|
-
]) =>
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
562
|
+
]): ResumptionStorageRecord => ({
|
|
563
|
+
nodeId: address.nodeId,
|
|
564
|
+
sharedSecret,
|
|
565
|
+
resumptionId,
|
|
566
|
+
fabricId: fabric.fabricId,
|
|
567
|
+
fabricIndex: fabric.fabricIndex,
|
|
568
|
+
peerNodeId: peerNodeId,
|
|
569
|
+
sessionParameters: {
|
|
570
|
+
...sessionParameters,
|
|
571
|
+
supportedTransports: sessionParameters.supportedTransports
|
|
572
|
+
? SupportedTransportsSchema.encode(sessionParameters.supportedTransports)
|
|
573
|
+
: undefined,
|
|
574
|
+
},
|
|
575
|
+
caseAuthenticatedTags,
|
|
576
|
+
}),
|
|
575
577
|
),
|
|
576
578
|
);
|
|
577
579
|
}
|
|
@@ -590,6 +592,7 @@ export class SessionManager {
|
|
|
590
592
|
sharedSecret,
|
|
591
593
|
resumptionId,
|
|
592
594
|
fabricId,
|
|
595
|
+
fabricIndex,
|
|
593
596
|
peerNodeId,
|
|
594
597
|
sessionParameters: {
|
|
595
598
|
idleInterval,
|
|
@@ -604,19 +607,27 @@ export class SessionManager {
|
|
|
604
607
|
} = {},
|
|
605
608
|
caseAuthenticatedTags,
|
|
606
609
|
}) => {
|
|
607
|
-
const fabric = this.#context.fabrics.find(
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
peerNodeId,
|
|
613
|
-
"for fabric index",
|
|
614
|
-
fabric?.fabricIndex,
|
|
610
|
+
const fabric = this.#context.fabrics.find(
|
|
611
|
+
fabric =>
|
|
612
|
+
fabric.fabricId === fabricId &&
|
|
613
|
+
// Backward compatibility logic: fabricIndex was added later (0.15.5), so it might be undefined in older records
|
|
614
|
+
(fabricIndex === undefined || fabric.fabricIndex === fabricIndex),
|
|
615
615
|
);
|
|
616
616
|
if (!fabric) {
|
|
617
|
-
logger.
|
|
617
|
+
logger.warn(
|
|
618
|
+
`Ignoring resumption record for fabric 0x${toHex(fabricId)} and index ${fabricIndex} because we cannot find a matching fabric`,
|
|
619
|
+
);
|
|
618
620
|
return;
|
|
619
621
|
}
|
|
622
|
+
logger.info(
|
|
623
|
+
"restoring resumption record for node",
|
|
624
|
+
fabric.addressOf(nodeId).toString(),
|
|
625
|
+
"and peer node",
|
|
626
|
+
fabric.addressOf(peerNodeId).toString(),
|
|
627
|
+
"for fabric id",
|
|
628
|
+
`0x${toHex(fabric.fabricId)}`,
|
|
629
|
+
`(0x${toHex(fabric.rootVendorId)}, "${fabric?.label}")`,
|
|
630
|
+
);
|
|
620
631
|
this.#resumptionRecords.set(fabric.addressOf(nodeId), {
|
|
621
632
|
sharedSecret,
|
|
622
633
|
resumptionId,
|