@matter/protocol 0.16.0-alpha.0-20251213-e83db3732 → 0.16.0-alpha.0-20251217-038f88085
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/LICENSE +1 -1
- package/dist/cjs/action/client/ClientInteraction.d.ts +12 -5
- package/dist/cjs/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/cjs/action/client/ClientInteraction.js +39 -15
- package/dist/cjs/action/client/ClientInteraction.js.map +1 -1
- package/dist/cjs/action/client/ClientRead.d.ts +10 -0
- package/dist/cjs/action/client/ClientRead.d.ts.map +1 -0
- package/dist/cjs/action/client/ClientRead.js +22 -0
- package/dist/cjs/action/client/ClientRead.js.map +6 -0
- package/dist/cjs/action/client/ClientRequest.d.ts +20 -0
- package/dist/cjs/action/client/ClientRequest.d.ts.map +1 -0
- package/dist/cjs/action/client/ClientRequest.js +22 -0
- package/dist/cjs/action/client/ClientRequest.js.map +6 -0
- package/dist/cjs/action/client/ClientWrite.d.ts +10 -0
- package/dist/cjs/action/client/ClientWrite.d.ts.map +1 -0
- package/dist/cjs/action/client/ClientWrite.js +22 -0
- package/dist/cjs/action/client/ClientWrite.js.map +6 -0
- package/dist/cjs/action/client/QueuedClientInteraction.d.ts +49 -0
- package/dist/cjs/action/client/QueuedClientInteraction.d.ts.map +1 -0
- package/dist/cjs/action/client/QueuedClientInteraction.js +160 -0
- package/dist/cjs/action/client/QueuedClientInteraction.js.map +6 -0
- package/dist/cjs/action/client/index.d.ts +4 -0
- package/dist/cjs/action/client/index.d.ts.map +1 -1
- package/dist/cjs/action/client/index.js +4 -0
- package/dist/cjs/action/client/index.js.map +1 -1
- package/dist/cjs/action/client/subscription/ClientSubscribe.d.ts +2 -1
- package/dist/cjs/action/client/subscription/ClientSubscribe.d.ts.map +1 -1
- package/dist/cjs/action/client/subscription/ClientSubscriptionHandler.d.ts.map +1 -1
- package/dist/cjs/action/client/subscription/ClientSubscriptionHandler.js +14 -3
- package/dist/cjs/action/client/subscription/ClientSubscriptionHandler.js.map +1 -1
- package/dist/cjs/action/client/subscription/SustainedSubscription.d.ts +1 -1
- package/dist/cjs/action/client/subscription/SustainedSubscription.d.ts.map +1 -1
- package/dist/cjs/action/client/subscription/SustainedSubscription.js +1 -4
- package/dist/cjs/action/client/subscription/SustainedSubscription.js.map +1 -1
- package/dist/cjs/action/request/Invoke.d.ts +7 -1
- package/dist/cjs/action/request/Invoke.d.ts.map +1 -1
- package/dist/cjs/action/request/Invoke.js +0 -3
- 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 -2
- package/dist/cjs/action/request/Read.js.map +1 -1
- package/dist/cjs/action/request/Specifier.d.ts +1 -1
- package/dist/cjs/action/request/Specifier.d.ts.map +1 -1
- package/dist/cjs/action/request/Specifier.js +3 -0
- package/dist/cjs/action/request/Specifier.js.map +1 -1
- package/dist/cjs/action/request/Write.d.ts +1 -0
- package/dist/cjs/action/request/Write.d.ts.map +1 -1
- package/dist/cjs/action/request/Write.js +10 -2
- package/dist/cjs/action/request/Write.js.map +1 -1
- package/dist/cjs/action/response/ReadResult.d.ts +1 -1
- package/dist/cjs/action/response/ReadResult.d.ts.map +1 -1
- package/dist/cjs/cluster/client/ClusterClientTypes.d.ts +37 -8
- package/dist/cjs/cluster/client/ClusterClientTypes.d.ts.map +1 -1
- package/dist/cjs/cluster/client/index.d.ts +0 -3
- package/dist/cjs/cluster/client/index.d.ts.map +1 -1
- package/dist/cjs/cluster/client/index.js +0 -3
- package/dist/cjs/cluster/client/index.js.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.js +3 -2
- package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
- package/dist/cjs/interaction/SubscriptionClient.d.ts.map +1 -1
- package/dist/cjs/interaction/SubscriptionClient.js +2 -1
- package/dist/cjs/interaction/SubscriptionClient.js.map +1 -1
- package/dist/cjs/interaction/index.d.ts +1 -1
- package/dist/cjs/interaction/index.d.ts.map +1 -1
- package/dist/cjs/interaction/index.js +1 -1
- package/dist/cjs/interaction/index.js.map +1 -1
- package/dist/cjs/peer/CommissioningError.d.ts +13 -0
- package/dist/cjs/peer/CommissioningError.d.ts.map +1 -0
- package/dist/cjs/peer/CommissioningError.js +32 -0
- package/dist/cjs/peer/CommissioningError.js.map +6 -0
- package/dist/cjs/peer/ControllerCommissioner.d.ts +2 -3
- package/dist/cjs/peer/ControllerCommissioner.d.ts.map +1 -1
- package/dist/cjs/peer/ControllerCommissioner.js +20 -13
- package/dist/cjs/peer/ControllerCommissioner.js.map +2 -2
- package/dist/cjs/peer/ControllerCommissioningFlow.d.ts +7 -16
- package/dist/cjs/peer/ControllerCommissioningFlow.d.ts.map +1 -1
- package/dist/cjs/peer/ControllerCommissioningFlow.js +395 -178
- package/dist/cjs/peer/ControllerCommissioningFlow.js.map +1 -1
- package/dist/cjs/peer/ControllerDiscovery.d.ts +4 -0
- package/dist/cjs/peer/ControllerDiscovery.d.ts.map +1 -1
- package/dist/cjs/peer/ControllerDiscovery.js +6 -3
- package/dist/cjs/peer/ControllerDiscovery.js.map +1 -1
- package/dist/cjs/peer/InteractionQueue.d.ts +2 -2
- package/dist/cjs/peer/InteractionQueue.d.ts.map +1 -1
- package/dist/cjs/peer/InteractionQueue.js +1 -1
- package/dist/cjs/peer/InteractionQueue.js.map +1 -1
- package/dist/cjs/peer/PeerAddressStore.d.ts +0 -9
- package/dist/cjs/peer/PeerAddressStore.d.ts.map +1 -1
- package/dist/cjs/peer/PeerAddressStore.js.map +1 -1
- package/dist/cjs/peer/PeerSet.d.ts +0 -2
- package/dist/cjs/peer/PeerSet.d.ts.map +1 -1
- package/dist/cjs/peer/PeerSet.js +32 -18
- package/dist/cjs/peer/PeerSet.js.map +1 -1
- package/dist/cjs/peer/PhysicalDeviceProperties.js +1 -1
- package/dist/cjs/peer/PhysicalDeviceProperties.js.map +1 -1
- package/dist/cjs/peer/index.d.ts +1 -0
- package/dist/cjs/peer/index.d.ts.map +1 -1
- package/dist/cjs/peer/index.js +1 -0
- package/dist/cjs/peer/index.js.map +1 -1
- package/dist/cjs/protocol/DeviceCommissioner.d.ts.map +1 -1
- package/dist/cjs/protocol/DeviceCommissioner.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/ExchangeProvider.d.ts +13 -4
- package/dist/cjs/protocol/ExchangeProvider.d.ts.map +1 -1
- package/dist/cjs/protocol/ExchangeProvider.js +5 -3
- package/dist/cjs/protocol/ExchangeProvider.js.map +1 -1
- package/dist/cjs/session/NodeSession.d.ts +5 -2
- package/dist/cjs/session/NodeSession.d.ts.map +1 -1
- package/dist/cjs/session/NodeSession.js +5 -4
- package/dist/cjs/session/NodeSession.js.map +1 -1
- package/dist/cjs/session/Session.d.ts +5 -3
- package/dist/cjs/session/Session.d.ts.map +1 -1
- package/dist/cjs/session/Session.js +8 -4
- package/dist/cjs/session/Session.js.map +1 -1
- package/dist/cjs/session/SessionManager.d.ts +8 -0
- package/dist/cjs/session/SessionManager.d.ts.map +1 -1
- package/dist/cjs/session/SessionManager.js +16 -2
- package/dist/cjs/session/SessionManager.js.map +1 -1
- package/dist/esm/action/client/ClientInteraction.d.ts +12 -5
- package/dist/esm/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/esm/action/client/ClientInteraction.js +42 -16
- package/dist/esm/action/client/ClientInteraction.js.map +1 -1
- package/dist/esm/action/client/ClientRead.d.ts +10 -0
- package/dist/esm/action/client/ClientRead.d.ts.map +1 -0
- package/dist/esm/action/client/ClientRead.js +6 -0
- package/dist/esm/action/client/ClientRead.js.map +6 -0
- package/dist/esm/action/client/ClientRequest.d.ts +20 -0
- package/dist/esm/action/client/ClientRequest.d.ts.map +1 -0
- package/dist/esm/action/client/ClientRequest.js +6 -0
- package/dist/esm/action/client/ClientRequest.js.map +6 -0
- package/dist/esm/action/client/ClientWrite.d.ts +10 -0
- package/dist/esm/action/client/ClientWrite.d.ts.map +1 -0
- package/dist/esm/action/client/ClientWrite.js +6 -0
- package/dist/esm/action/client/ClientWrite.js.map +6 -0
- package/dist/esm/action/client/QueuedClientInteraction.d.ts +49 -0
- package/dist/esm/action/client/QueuedClientInteraction.d.ts.map +1 -0
- package/dist/esm/action/client/QueuedClientInteraction.js +140 -0
- package/dist/esm/action/client/QueuedClientInteraction.js.map +6 -0
- package/dist/esm/action/client/index.d.ts +4 -0
- package/dist/esm/action/client/index.d.ts.map +1 -1
- package/dist/esm/action/client/index.js +4 -0
- package/dist/esm/action/client/index.js.map +1 -1
- package/dist/esm/action/client/subscription/ClientSubscribe.d.ts +2 -1
- package/dist/esm/action/client/subscription/ClientSubscribe.d.ts.map +1 -1
- package/dist/esm/action/client/subscription/ClientSubscriptionHandler.d.ts.map +1 -1
- package/dist/esm/action/client/subscription/ClientSubscriptionHandler.js +14 -3
- package/dist/esm/action/client/subscription/ClientSubscriptionHandler.js.map +1 -1
- package/dist/esm/action/client/subscription/SustainedSubscription.d.ts +1 -1
- package/dist/esm/action/client/subscription/SustainedSubscription.d.ts.map +1 -1
- package/dist/esm/action/client/subscription/SustainedSubscription.js +1 -4
- package/dist/esm/action/client/subscription/SustainedSubscription.js.map +1 -1
- package/dist/esm/action/request/Invoke.d.ts +7 -1
- package/dist/esm/action/request/Invoke.d.ts.map +1 -1
- package/dist/esm/action/request/Invoke.js +0 -3
- 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 -2
- package/dist/esm/action/request/Read.js.map +1 -1
- package/dist/esm/action/request/Specifier.d.ts +1 -1
- package/dist/esm/action/request/Specifier.d.ts.map +1 -1
- package/dist/esm/action/request/Specifier.js +3 -0
- package/dist/esm/action/request/Specifier.js.map +1 -1
- package/dist/esm/action/request/Write.d.ts +1 -0
- package/dist/esm/action/request/Write.d.ts.map +1 -1
- package/dist/esm/action/request/Write.js +10 -2
- package/dist/esm/action/request/Write.js.map +1 -1
- package/dist/esm/action/response/ReadResult.d.ts +1 -1
- package/dist/esm/action/response/ReadResult.d.ts.map +1 -1
- package/dist/esm/cluster/client/ClusterClientTypes.d.ts +37 -8
- package/dist/esm/cluster/client/ClusterClientTypes.d.ts.map +1 -1
- package/dist/esm/cluster/client/index.d.ts +0 -3
- package/dist/esm/cluster/client/index.d.ts.map +1 -1
- package/dist/esm/cluster/client/index.js +0 -3
- package/dist/esm/cluster/client/index.js.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.js +4 -3
- package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
- package/dist/esm/interaction/SubscriptionClient.d.ts.map +1 -1
- package/dist/esm/interaction/SubscriptionClient.js +2 -1
- package/dist/esm/interaction/SubscriptionClient.js.map +1 -1
- package/dist/esm/interaction/index.d.ts +1 -1
- package/dist/esm/interaction/index.d.ts.map +1 -1
- package/dist/esm/interaction/index.js +1 -1
- package/dist/esm/peer/CommissioningError.d.ts +13 -0
- package/dist/esm/peer/CommissioningError.d.ts.map +1 -0
- package/dist/esm/peer/CommissioningError.js +12 -0
- package/dist/esm/peer/CommissioningError.js.map +6 -0
- package/dist/esm/peer/ControllerCommissioner.d.ts +2 -3
- package/dist/esm/peer/ControllerCommissioner.d.ts.map +1 -1
- package/dist/esm/peer/ControllerCommissioner.js +19 -13
- package/dist/esm/peer/ControllerCommissioner.js.map +2 -2
- package/dist/esm/peer/ControllerCommissioningFlow.d.ts +7 -16
- package/dist/esm/peer/ControllerCommissioningFlow.d.ts.map +1 -1
- package/dist/esm/peer/ControllerCommissioningFlow.js +380 -162
- package/dist/esm/peer/ControllerCommissioningFlow.js.map +1 -1
- package/dist/esm/peer/ControllerDiscovery.d.ts +4 -0
- package/dist/esm/peer/ControllerDiscovery.d.ts.map +1 -1
- package/dist/esm/peer/ControllerDiscovery.js +4 -1
- package/dist/esm/peer/ControllerDiscovery.js.map +1 -1
- package/dist/esm/peer/InteractionQueue.d.ts +2 -2
- package/dist/esm/peer/InteractionQueue.d.ts.map +1 -1
- package/dist/esm/peer/InteractionQueue.js +2 -2
- package/dist/esm/peer/InteractionQueue.js.map +1 -1
- package/dist/esm/peer/PeerAddressStore.d.ts +0 -9
- package/dist/esm/peer/PeerAddressStore.d.ts.map +1 -1
- package/dist/esm/peer/PeerAddressStore.js.map +1 -1
- package/dist/esm/peer/PeerSet.d.ts +0 -2
- package/dist/esm/peer/PeerSet.d.ts.map +1 -1
- package/dist/esm/peer/PeerSet.js +32 -18
- package/dist/esm/peer/PeerSet.js.map +1 -1
- package/dist/esm/peer/PhysicalDeviceProperties.js +1 -1
- package/dist/esm/peer/PhysicalDeviceProperties.js.map +1 -1
- package/dist/esm/peer/index.d.ts +1 -0
- package/dist/esm/peer/index.d.ts.map +1 -1
- package/dist/esm/peer/index.js +1 -0
- package/dist/esm/peer/index.js.map +1 -1
- package/dist/esm/protocol/DeviceCommissioner.d.ts.map +1 -1
- package/dist/esm/protocol/DeviceCommissioner.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/ExchangeProvider.d.ts +13 -4
- package/dist/esm/protocol/ExchangeProvider.d.ts.map +1 -1
- package/dist/esm/protocol/ExchangeProvider.js +5 -3
- package/dist/esm/protocol/ExchangeProvider.js.map +1 -1
- package/dist/esm/session/NodeSession.d.ts +5 -2
- package/dist/esm/session/NodeSession.d.ts.map +1 -1
- package/dist/esm/session/NodeSession.js +5 -4
- package/dist/esm/session/NodeSession.js.map +1 -1
- package/dist/esm/session/Session.d.ts +5 -3
- package/dist/esm/session/Session.d.ts.map +1 -1
- package/dist/esm/session/Session.js +8 -4
- package/dist/esm/session/Session.js.map +1 -1
- package/dist/esm/session/SessionManager.d.ts +8 -0
- package/dist/esm/session/SessionManager.d.ts.map +1 -1
- package/dist/esm/session/SessionManager.js +17 -2
- package/dist/esm/session/SessionManager.js.map +1 -1
- package/package.json +6 -6
- package/src/action/client/ClientInteraction.ts +58 -19
- package/src/action/client/ClientRead.ts +10 -0
- package/src/action/client/ClientRequest.ts +20 -0
- package/src/action/client/ClientWrite.ts +10 -0
- package/src/action/client/QueuedClientInteraction.ts +91 -0
- package/src/action/client/index.ts +4 -0
- package/src/action/client/subscription/ClientSubscribe.ts +2 -1
- package/src/action/client/subscription/ClientSubscriptionHandler.ts +14 -3
- package/src/action/client/subscription/SustainedSubscription.ts +6 -9
- package/src/action/request/Invoke.ts +11 -4
- package/src/action/request/Read.ts +3 -2
- package/src/action/request/Specifier.ts +4 -1
- package/src/action/request/Write.ts +11 -2
- package/src/action/response/ReadResult.ts +1 -1
- package/src/cluster/client/ClusterClientTypes.ts +47 -7
- package/src/cluster/client/index.ts +0 -3
- package/src/interaction/InteractionMessenger.ts +5 -4
- package/src/interaction/SubscriptionClient.ts +2 -1
- package/src/interaction/index.ts +1 -1
- package/src/peer/CommissioningError.ts +13 -0
- package/src/peer/ControllerCommissioner.ts +21 -13
- package/src/peer/ControllerCommissioningFlow.ts +418 -186
- package/src/peer/ControllerDiscovery.ts +4 -1
- package/src/peer/InteractionQueue.ts +2 -2
- package/src/peer/PeerAddressStore.ts +0 -9
- package/src/peer/PeerSet.ts +56 -23
- package/src/peer/PhysicalDeviceProperties.ts +1 -1
- package/src/peer/index.ts +1 -0
- package/src/protocol/DeviceCommissioner.ts +0 -1
- package/src/protocol/ExchangeManager.ts +2 -2
- package/src/protocol/ExchangeProvider.ts +9 -7
- package/src/session/NodeSession.ts +5 -4
- package/src/session/Session.ts +8 -4
- package/src/session/SessionManager.ts +19 -2
- package/dist/cjs/cluster/client/AttributeClient.d.ts +0 -75
- package/dist/cjs/cluster/client/AttributeClient.d.ts.map +0 -1
- package/dist/cjs/cluster/client/AttributeClient.js +0 -209
- package/dist/cjs/cluster/client/AttributeClient.js.map +0 -6
- package/dist/cjs/cluster/client/ClusterClient.d.ts +0 -11
- package/dist/cjs/cluster/client/ClusterClient.d.ts.map +0 -1
- package/dist/cjs/cluster/client/ClusterClient.js +0 -335
- package/dist/cjs/cluster/client/ClusterClient.js.map +0 -6
- package/dist/cjs/cluster/client/EventClient.d.ts +0 -33
- package/dist/cjs/cluster/client/EventClient.d.ts.map +0 -1
- package/dist/cjs/cluster/client/EventClient.js +0 -89
- package/dist/cjs/cluster/client/EventClient.js.map +0 -6
- package/dist/cjs/interaction/InteractionClient.d.ts +0 -375
- package/dist/cjs/interaction/InteractionClient.d.ts.map +0 -1
- package/dist/cjs/interaction/InteractionClient.js +0 -1046
- package/dist/cjs/interaction/InteractionClient.js.map +0 -6
- package/dist/esm/cluster/client/AttributeClient.d.ts +0 -75
- package/dist/esm/cluster/client/AttributeClient.d.ts.map +0 -1
- package/dist/esm/cluster/client/AttributeClient.js +0 -189
- package/dist/esm/cluster/client/AttributeClient.js.map +0 -6
- package/dist/esm/cluster/client/ClusterClient.d.ts +0 -11
- package/dist/esm/cluster/client/ClusterClient.d.ts.map +0 -1
- package/dist/esm/cluster/client/ClusterClient.js +0 -320
- package/dist/esm/cluster/client/ClusterClient.js.map +0 -6
- package/dist/esm/cluster/client/EventClient.d.ts +0 -33
- package/dist/esm/cluster/client/EventClient.d.ts.map +0 -1
- package/dist/esm/cluster/client/EventClient.js +0 -69
- package/dist/esm/cluster/client/EventClient.js.map +0 -6
- package/dist/esm/interaction/InteractionClient.d.ts +0 -375
- package/dist/esm/interaction/InteractionClient.d.ts.map +0 -1
- package/dist/esm/interaction/InteractionClient.js +0 -1047
- package/dist/esm/interaction/InteractionClient.js.map +0 -6
- package/src/cluster/client/AttributeClient.ts +0 -230
- package/src/cluster/client/ClusterClient.ts +0 -433
- package/src/cluster/client/EventClient.ts +0 -99
- package/src/interaction/InteractionClient.ts +0 -1614
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { ClientInteraction } from "#action/client/ClientInteraction.js";
|
|
8
|
+
import { ClientRead } from "#action/client/ClientRead.js";
|
|
9
|
+
import { Invoke } from "#action/request/Invoke.js";
|
|
10
|
+
import { Read } from "#action/request/Read.js";
|
|
7
11
|
import { Certificate } from "#certificate/kinds/Certificate.js";
|
|
8
12
|
import { BasicInformation } from "#clusters/basic-information";
|
|
9
13
|
import { Descriptor } from "#clusters/descriptor";
|
|
@@ -16,9 +20,9 @@ import {
|
|
|
16
20
|
ChannelType,
|
|
17
21
|
Diagnostic,
|
|
18
22
|
Duration,
|
|
23
|
+
ImplementationError,
|
|
19
24
|
Instant,
|
|
20
25
|
Logger,
|
|
21
|
-
MatterError,
|
|
22
26
|
Millis,
|
|
23
27
|
Minutes,
|
|
24
28
|
repackErrorAs,
|
|
@@ -33,17 +37,17 @@ import {
|
|
|
33
37
|
ClusterType,
|
|
34
38
|
EndpointNumber,
|
|
35
39
|
FabricIndex,
|
|
40
|
+
Status,
|
|
36
41
|
StatusResponseError,
|
|
37
42
|
TypeFromPartialBitSchema,
|
|
38
43
|
TypeFromSchema,
|
|
39
44
|
VendorId,
|
|
40
45
|
} from "#types";
|
|
41
46
|
import { CertificateAuthority } from "../certificate/CertificateAuthority.js";
|
|
42
|
-
import { ClusterClient } from "../cluster/client/ClusterClient.js";
|
|
43
47
|
import { ClusterClientObj } from "../cluster/client/ClusterClientTypes.js";
|
|
44
48
|
import { TlvCertSigningRequest } from "../common/OperationalCredentialsTypes.js";
|
|
45
49
|
import { Fabric } from "../fabric/Fabric.js";
|
|
46
|
-
import {
|
|
50
|
+
import { CommissioningError } from "./CommissioningError.js";
|
|
47
51
|
import { PeerAddress } from "./PeerAddress.js";
|
|
48
52
|
|
|
49
53
|
const logger = Logger.get("ControllerCommissioner");
|
|
@@ -146,12 +150,6 @@ type CollectedCommissioningData = {
|
|
|
146
150
|
fabricIndex?: FabricIndex;
|
|
147
151
|
};
|
|
148
152
|
|
|
149
|
-
/**
|
|
150
|
-
* Error that throws when Commissioning fails and a process cannot be continued, and no more specific error
|
|
151
|
-
* information is available.
|
|
152
|
-
*/
|
|
153
|
-
export class CommissioningError extends MatterError {}
|
|
154
|
-
|
|
155
153
|
/** The number of fabrics that can be commissioned is already reached. */
|
|
156
154
|
export class MaximumCommissionedFabricsReachedError extends CommissioningError {}
|
|
157
155
|
|
|
@@ -176,9 +174,6 @@ export class ThreadNetworkSetupFailedError extends CommissioningError {}
|
|
|
176
174
|
/** Error that throws when the NodeId is already used in the fabric. */
|
|
177
175
|
export class NodeIdConflictError extends CommissioningError {}
|
|
178
176
|
|
|
179
|
-
/** Error that throws when the device could not be discovered using the provided details. */
|
|
180
|
-
export class CommissionableDeviceDiscoveryFailedError extends CommissioningError {}
|
|
181
|
-
|
|
182
177
|
/** Error that throws when the device could not be connected using the operational discovery and no session could be created. */
|
|
183
178
|
export class OperativeConnectionFailedError extends CommissioningError {}
|
|
184
179
|
|
|
@@ -187,17 +182,19 @@ class RecoverableCommissioningError extends CommissioningError {}
|
|
|
187
182
|
|
|
188
183
|
const DEFAULT_FAILSAFE_TIME = Minutes.one;
|
|
189
184
|
|
|
185
|
+
const RootEndpointNumber = EndpointNumber(0);
|
|
186
|
+
|
|
190
187
|
/**
|
|
191
188
|
* Class to abstract the Device commission flow in a step wise way as defined in Specs. The specs are not 100%
|
|
192
189
|
*/
|
|
193
190
|
export class ControllerCommissioningFlow {
|
|
194
|
-
protected
|
|
191
|
+
protected interaction: ClientInteraction;
|
|
195
192
|
protected readonly ca: CertificateAuthority;
|
|
196
193
|
protected readonly fabric: Fabric;
|
|
197
194
|
protected readonly transitionToCase: (
|
|
198
195
|
peerAddress: PeerAddress,
|
|
199
196
|
supportsConcurrentConnections: boolean,
|
|
200
|
-
) => Promise<
|
|
197
|
+
) => Promise<ClientInteraction | undefined>;
|
|
201
198
|
protected readonly commissioningOptions: ControllerCommissioningFlowOptions;
|
|
202
199
|
protected readonly commissioningSteps = new Array<CommissioningStep>();
|
|
203
200
|
protected readonly commissioningStepResults = new Map<string, CommissioningStepResult>();
|
|
@@ -210,8 +207,8 @@ export class ControllerCommissioningFlow {
|
|
|
210
207
|
#defaultFailSafeTime = DEFAULT_FAILSAFE_TIME;
|
|
211
208
|
|
|
212
209
|
constructor(
|
|
213
|
-
/**
|
|
214
|
-
|
|
210
|
+
/** ClientInteraction for the initiated PASE session */
|
|
211
|
+
interaction: ClientInteraction,
|
|
215
212
|
|
|
216
213
|
/** CertificateAuthority of the controller. */
|
|
217
214
|
ca: CertificateAuthority,
|
|
@@ -226,9 +223,9 @@ export class ControllerCommissioningFlow {
|
|
|
226
223
|
transitionToCase: (
|
|
227
224
|
peerAddress: PeerAddress,
|
|
228
225
|
supportsConcurrentConnections: boolean,
|
|
229
|
-
) => Promise<
|
|
226
|
+
) => Promise<ClientInteraction | undefined>,
|
|
230
227
|
) {
|
|
231
|
-
this.
|
|
228
|
+
this.interaction = interaction;
|
|
232
229
|
this.ca = ca;
|
|
233
230
|
this.fabric = fabric;
|
|
234
231
|
this.transitionToCase = transitionToCase;
|
|
@@ -327,28 +324,65 @@ export class ControllerCommissioningFlow {
|
|
|
327
324
|
}
|
|
328
325
|
|
|
329
326
|
/**
|
|
330
|
-
*
|
|
327
|
+
* Convenience method to get a number of attributes in a read and return exactly these values in the order as
|
|
328
|
+
* defined in the request
|
|
331
329
|
*/
|
|
332
|
-
#
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if (
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
330
|
+
async #readConcreteAttributeValues(request: ClientRead) {
|
|
331
|
+
const attributeMap = new Map<string, any>();
|
|
332
|
+
if (request.attributeRequests === undefined) {
|
|
333
|
+
throw new ImplementationError("Can only handle Attribute reads");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
for (const { endpointId, clusterId, attributeId } of request.attributeRequests) {
|
|
337
|
+
if (endpointId === undefined || clusterId === undefined) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
attributeMap.set(`${endpointId}-${clusterId}-${attributeId}`, undefined);
|
|
341
|
+
}
|
|
342
|
+
for await (const data of this.interaction.read(request)) {
|
|
343
|
+
for (const entry of data) {
|
|
344
|
+
if (entry.kind !== "attr-value") {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const {
|
|
348
|
+
path: { endpointId, clusterId, attributeId },
|
|
349
|
+
value,
|
|
350
|
+
} = entry;
|
|
351
|
+
const key = `${endpointId}-${clusterId}-${attributeId}`;
|
|
352
|
+
if (!attributeMap.has(key)) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
attributeMap.set(key, value);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return [...attributeMap.values()];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// TODO improve response typing
|
|
362
|
+
async #invokeCommand<const C extends ClusterType>(
|
|
363
|
+
request: Invoke.ConcreteCommandRequest<C>,
|
|
364
|
+
options: Omit<Invoke.Definition, "commands"> = {},
|
|
365
|
+
) {
|
|
366
|
+
for await (const data of this.interaction.invoke(
|
|
367
|
+
Invoke({
|
|
368
|
+
commands: [request],
|
|
369
|
+
...options,
|
|
370
|
+
}),
|
|
371
|
+
)) {
|
|
372
|
+
for (const entry of data) {
|
|
373
|
+
// We send only one command, so we only get one response back
|
|
374
|
+
switch (entry.kind) {
|
|
375
|
+
case "cmd-status":
|
|
376
|
+
if (entry.status !== Status.Success) {
|
|
377
|
+
throw StatusResponseError.create(entry.status, undefined, entry.clusterStatus);
|
|
378
|
+
}
|
|
379
|
+
return;
|
|
380
|
+
|
|
381
|
+
case "cmd-response":
|
|
382
|
+
return entry.data;
|
|
383
|
+
}
|
|
344
384
|
}
|
|
345
385
|
}
|
|
346
|
-
logger.debug(
|
|
347
|
-
`Creating new cluster client for cluster ${cluster.name} (endpoint ${endpointId}, isFeatureSpecific ${isFeatureSpecific})`,
|
|
348
|
-
);
|
|
349
|
-
const client = ClusterClient(cluster, endpointId, this.interactionClient);
|
|
350
|
-
this.#clusterClients.set(cluster.id, client);
|
|
351
|
-
return client;
|
|
352
386
|
}
|
|
353
387
|
|
|
354
388
|
/**
|
|
@@ -415,7 +449,7 @@ export class ControllerCommissioningFlow {
|
|
|
415
449
|
});
|
|
416
450
|
|
|
417
451
|
// Care about Network commissioning only when we are on BLE, because else we are already on IP network
|
|
418
|
-
if (this.
|
|
452
|
+
if (this.interaction.channelType === ChannelType.BLE) {
|
|
419
453
|
this.commissioningSteps.push({
|
|
420
454
|
stepNumber: 16,
|
|
421
455
|
subStepNumber: 1,
|
|
@@ -442,7 +476,7 @@ export class ControllerCommissioningFlow {
|
|
|
442
476
|
}
|
|
443
477
|
} else {
|
|
444
478
|
logger.info(
|
|
445
|
-
`Skipping NetworkCommissioning steps because the device is already on IP network (${this.
|
|
479
|
+
`Skipping NetworkCommissioning steps because the device is already on IP network (${this.interaction.channelType})`,
|
|
446
480
|
);
|
|
447
481
|
}
|
|
448
482
|
|
|
@@ -533,31 +567,62 @@ export class ControllerCommissioningFlow {
|
|
|
533
567
|
* Initial Step to receive some common data used by other steps
|
|
534
568
|
*/
|
|
535
569
|
async #getInitialData() {
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
570
|
+
const [
|
|
571
|
+
supportedFabrics,
|
|
572
|
+
commissionedFabrics,
|
|
573
|
+
rootPartsList,
|
|
574
|
+
rootServerList,
|
|
575
|
+
vendorId,
|
|
576
|
+
productId,
|
|
577
|
+
productName,
|
|
578
|
+
supportsConcurrentConnection,
|
|
579
|
+
] = await this.#readConcreteAttributeValues(
|
|
580
|
+
Read(
|
|
581
|
+
Read.Attribute({
|
|
582
|
+
endpoint: RootEndpointNumber,
|
|
583
|
+
cluster: OperationalCredentials.Complete,
|
|
584
|
+
attributes: ["supportedFabrics", "commissionedFabrics"],
|
|
585
|
+
}),
|
|
586
|
+
Read.Attribute({
|
|
587
|
+
endpoint: RootEndpointNumber,
|
|
588
|
+
cluster: Descriptor.Complete,
|
|
589
|
+
attributes: ["partsList", "serverList"],
|
|
590
|
+
}),
|
|
591
|
+
Read.Attribute({
|
|
592
|
+
endpoint: RootEndpointNumber,
|
|
593
|
+
cluster: BasicInformation.Complete,
|
|
594
|
+
attributes: ["vendorId", "productId", "productName"],
|
|
595
|
+
}),
|
|
596
|
+
Read.Attribute({
|
|
597
|
+
endpoint: RootEndpointNumber,
|
|
598
|
+
cluster: GeneralCommissioning.Complete,
|
|
599
|
+
attributes: ["supportsConcurrentConnection"],
|
|
600
|
+
}),
|
|
601
|
+
),
|
|
602
|
+
);
|
|
603
|
+
|
|
539
604
|
if (commissionedFabrics >= supportedFabrics) {
|
|
540
605
|
throw new MaximumCommissionedFabricsReachedError(
|
|
541
606
|
`Commissioned fabrics (${commissionedFabrics}) exceed supported fabrics (${supportedFabrics}). Please remove some fabrics before commissioning.`,
|
|
542
607
|
);
|
|
543
608
|
}
|
|
544
609
|
|
|
545
|
-
|
|
546
|
-
this.collectedCommissioningData.
|
|
547
|
-
this.collectedCommissioningData.rootServerList = await descriptorClient.getServerListAttribute();
|
|
610
|
+
this.collectedCommissioningData.rootPartsList = rootPartsList;
|
|
611
|
+
this.collectedCommissioningData.rootServerList = rootServerList;
|
|
548
612
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
613
|
+
this.collectedCommissioningData.vendorId = vendorId;
|
|
614
|
+
this.collectedCommissioningData.productId = productId;
|
|
615
|
+
this.collectedCommissioningData.productName = productName;
|
|
616
|
+
this.collectedCommissioningData.supportsConcurrentConnection = supportsConcurrentConnection;
|
|
617
|
+
|
|
618
|
+
const networkData = this.interaction.read(
|
|
619
|
+
Read(
|
|
620
|
+
Read.Attribute({
|
|
621
|
+
cluster: NetworkCommissioning.Complete,
|
|
622
|
+
attributes: ["featureMap", "networks"],
|
|
623
|
+
}),
|
|
624
|
+
),
|
|
625
|
+
);
|
|
561
626
|
const networkFeatures = new Array<{
|
|
562
627
|
endpointId: number;
|
|
563
628
|
value: TypeFromPartialBitSchema<typeof NetworkCommissioning.Complete.features>;
|
|
@@ -566,28 +631,33 @@ export class ControllerCommissioningFlow {
|
|
|
566
631
|
endpointId: number;
|
|
567
632
|
value: TypeFromSchema<typeof NetworkCommissioning.TlvNetworkInfo>[];
|
|
568
633
|
}>();
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
634
|
+
|
|
635
|
+
for await (const data of networkData) {
|
|
636
|
+
for (const entry of data) {
|
|
637
|
+
if (entry.kind !== "attr-value") {
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
const {
|
|
641
|
+
path: { endpointId, attributeId },
|
|
642
|
+
value,
|
|
643
|
+
} = entry;
|
|
644
|
+
if (attributeId === NetworkCommissioning.Complete.attributes.featureMap.id) {
|
|
645
|
+
networkFeatures.push({
|
|
646
|
+
endpointId,
|
|
647
|
+
value: value as TypeFromPartialBitSchema<typeof NetworkCommissioning.Complete.features>,
|
|
648
|
+
});
|
|
649
|
+
} else if (attributeId === NetworkCommissioning.Complete.attributes.networks.id) {
|
|
650
|
+
networkStatus.push({
|
|
651
|
+
endpointId,
|
|
652
|
+
value: value as TypeFromSchema<typeof NetworkCommissioning.TlvNetworkInfo>[],
|
|
653
|
+
});
|
|
654
|
+
}
|
|
577
655
|
}
|
|
578
656
|
}
|
|
657
|
+
|
|
579
658
|
this.collectedCommissioningData.networkFeatures = networkFeatures;
|
|
580
659
|
this.collectedCommissioningData.networkStatus = networkStatus;
|
|
581
660
|
|
|
582
|
-
const basicInfoClient = this.#getClusterClient(BasicInformation.Cluster);
|
|
583
|
-
this.collectedCommissioningData.vendorId = await basicInfoClient.getVendorIdAttribute();
|
|
584
|
-
this.collectedCommissioningData.productId = await basicInfoClient.getProductIdAttribute();
|
|
585
|
-
this.collectedCommissioningData.productName = await basicInfoClient.getProductNameAttribute();
|
|
586
|
-
|
|
587
|
-
const generalCommissioningClient = this.#getClusterClient(GeneralCommissioning.Cluster);
|
|
588
|
-
this.collectedCommissioningData.supportsConcurrentConnection =
|
|
589
|
-
await generalCommissioningClient.getSupportsConcurrentConnectionAttribute();
|
|
590
|
-
|
|
591
661
|
return {
|
|
592
662
|
code: CommissioningStepResultCode.Success,
|
|
593
663
|
breadcrumb: this.lastBreadcrumb,
|
|
@@ -604,9 +674,17 @@ export class ControllerCommissioningFlow {
|
|
|
604
674
|
* Attribute”) prior to invoking the ArmFailSafe command.
|
|
605
675
|
*/
|
|
606
676
|
async #armFailsafe(time?: Duration) {
|
|
607
|
-
const client = this.#getClusterClient(GeneralCommissioning.Cluster);
|
|
608
677
|
if (this.collectedCommissioningData.basicCommissioningInfo === undefined) {
|
|
609
|
-
const basicCommissioningInfo = await
|
|
678
|
+
const [basicCommissioningInfo] = await this.#readConcreteAttributeValues(
|
|
679
|
+
Read(
|
|
680
|
+
Read.Attribute({
|
|
681
|
+
endpoint: RootEndpointNumber,
|
|
682
|
+
cluster: GeneralCommissioning.Complete,
|
|
683
|
+
attributes: ["basicCommissioningInfo"],
|
|
684
|
+
}),
|
|
685
|
+
),
|
|
686
|
+
);
|
|
687
|
+
|
|
610
688
|
this.collectedCommissioningData.basicCommissioningInfo = basicCommissioningInfo;
|
|
611
689
|
this.#defaultFailSafeTime = Seconds(basicCommissioningInfo.failSafeExpiryLengthSeconds);
|
|
612
690
|
this.#commissioningStartedTime = Time.nowMs;
|
|
@@ -617,9 +695,14 @@ export class ControllerCommissioningFlow {
|
|
|
617
695
|
const expiryLength = time ?? this.#defaultFailSafeTime;
|
|
618
696
|
this.#ensureGeneralCommissioningSuccess(
|
|
619
697
|
"armFailSafe",
|
|
620
|
-
await
|
|
621
|
-
|
|
622
|
-
|
|
698
|
+
await this.#invokeCommand({
|
|
699
|
+
endpoint: RootEndpointNumber,
|
|
700
|
+
cluster: GeneralCommissioning.Complete,
|
|
701
|
+
command: "armFailSafe",
|
|
702
|
+
fields: {
|
|
703
|
+
breadcrumb: this.lastBreadcrumb,
|
|
704
|
+
expiryLengthSeconds: Seconds.of(expiryLength),
|
|
705
|
+
},
|
|
623
706
|
}),
|
|
624
707
|
);
|
|
625
708
|
this.#currentFailSafeEndTime = Timestamp(Time.nowMs + expiryLength);
|
|
@@ -637,24 +720,28 @@ export class ControllerCommissioningFlow {
|
|
|
637
720
|
}
|
|
638
721
|
|
|
639
722
|
async #ensureFailsafeTimerFor(maxProcessingTime: Duration) {
|
|
640
|
-
const minFailsafeTime = this.
|
|
723
|
+
const minFailsafeTime = this.interaction.maximumPeerResponseTime(maxProcessingTime);
|
|
641
724
|
|
|
642
725
|
const timeLeft = this.#failSafeTimeLeft;
|
|
643
726
|
if (timeLeft < minFailsafeTime) {
|
|
644
727
|
logger.debug(`Failsafe timer has only ${timeLeft}s left, re-arming for at least ${minFailsafeTime}`);
|
|
645
728
|
await this.#armFailsafe(Duration.max(minFailsafeTime, this.#defaultFailSafeTime));
|
|
646
729
|
} else {
|
|
647
|
-
logger.debug(`Failsafe timer is already set for at least ${timeLeft}s`);
|
|
730
|
+
logger.debug(`Failsafe timer is already set for at least ${Seconds.of(timeLeft)}s`);
|
|
648
731
|
}
|
|
649
732
|
}
|
|
650
733
|
|
|
651
734
|
async #resetFailsafeTimer() {
|
|
652
735
|
if (this.#currentFailSafeEndTime === undefined) return;
|
|
653
736
|
try {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
737
|
+
await this.#invokeCommand({
|
|
738
|
+
endpoint: RootEndpointNumber,
|
|
739
|
+
cluster: GeneralCommissioning.Complete,
|
|
740
|
+
command: "armFailSafe",
|
|
741
|
+
fields: {
|
|
742
|
+
breadcrumb: this.lastBreadcrumb,
|
|
743
|
+
expiryLengthSeconds: 0,
|
|
744
|
+
},
|
|
658
745
|
});
|
|
659
746
|
this.#currentFailSafeEndTime = undefined; // No failsafe active anymore
|
|
660
747
|
} catch (error) {
|
|
@@ -682,8 +769,16 @@ export class ControllerCommissioningFlow {
|
|
|
682
769
|
);
|
|
683
770
|
|
|
684
771
|
if (hasRadioNetwork) {
|
|
685
|
-
|
|
686
|
-
|
|
772
|
+
let [locationCapability] = await this.#readConcreteAttributeValues(
|
|
773
|
+
Read(
|
|
774
|
+
Read.Attribute({
|
|
775
|
+
endpoint: RootEndpointNumber,
|
|
776
|
+
cluster: GeneralCommissioning.Complete,
|
|
777
|
+
attributes: ["locationCapability"],
|
|
778
|
+
}),
|
|
779
|
+
),
|
|
780
|
+
);
|
|
781
|
+
|
|
687
782
|
if (locationCapability === GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor) {
|
|
688
783
|
locationCapability = this.commissioningOptions.regulatoryLocation;
|
|
689
784
|
} else {
|
|
@@ -694,13 +789,20 @@ export class ControllerCommissioningFlow {
|
|
|
694
789
|
);
|
|
695
790
|
}
|
|
696
791
|
let countryCode = this.commissioningOptions.regulatoryCountryCode;
|
|
697
|
-
const regulatoryResult = await
|
|
792
|
+
const regulatoryResult = await this.#invokeCommand(
|
|
698
793
|
{
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
794
|
+
endpoint: RootEndpointNumber,
|
|
795
|
+
cluster: GeneralCommissioning.Complete,
|
|
796
|
+
command: "setRegulatoryConfig",
|
|
797
|
+
fields: {
|
|
798
|
+
breadcrumb: this.lastBreadcrumb++,
|
|
799
|
+
newRegulatoryConfig: locationCapability,
|
|
800
|
+
countryCode,
|
|
801
|
+
},
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
702
805
|
},
|
|
703
|
-
{ useExtendedFailSafeMessageResponseTimeout: true },
|
|
704
806
|
);
|
|
705
807
|
if (
|
|
706
808
|
regulatoryResult.errorCode === GeneralCommissioning.CommissioningError.ValueOutsideRange &&
|
|
@@ -712,13 +814,20 @@ export class ControllerCommissioningFlow {
|
|
|
712
814
|
countryCode = "XX";
|
|
713
815
|
this.#ensureGeneralCommissioningSuccess(
|
|
714
816
|
"setRegulatoryConfig",
|
|
715
|
-
await
|
|
817
|
+
await this.#invokeCommand(
|
|
716
818
|
{
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
819
|
+
endpoint: RootEndpointNumber,
|
|
820
|
+
cluster: GeneralCommissioning.Complete,
|
|
821
|
+
command: "setRegulatoryConfig",
|
|
822
|
+
fields: {
|
|
823
|
+
breadcrumb: this.lastBreadcrumb,
|
|
824
|
+
newRegulatoryConfig: locationCapability,
|
|
825
|
+
countryCode,
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
720
830
|
},
|
|
721
|
-
{ useExtendedFailSafeMessageResponseTimeout: true },
|
|
722
831
|
),
|
|
723
832
|
);
|
|
724
833
|
} else {
|
|
@@ -766,28 +875,50 @@ export class ControllerCommissioningFlow {
|
|
|
766
875
|
* (see Section 6.2.3, “Device Attestation Procedure”).
|
|
767
876
|
*/
|
|
768
877
|
async #deviceAttestation() {
|
|
769
|
-
const
|
|
770
|
-
const { certificate: deviceAttestation } = await operationalCredentialsClusterClient.certificateChainRequest(
|
|
878
|
+
const { certificate: deviceAttestation } = await this.#invokeCommand(
|
|
771
879
|
{
|
|
772
|
-
|
|
880
|
+
endpoint: RootEndpointNumber,
|
|
881
|
+
cluster: OperationalCredentials.Complete,
|
|
882
|
+
command: "certificateChainRequest",
|
|
883
|
+
fields: {
|
|
884
|
+
certificateType: OperationalCredentials.CertificateChainType.DacCertificate,
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
773
889
|
},
|
|
774
|
-
{ useExtendedFailSafeMessageResponseTimeout: true },
|
|
775
890
|
);
|
|
891
|
+
|
|
776
892
|
// TODO: extract device public key from deviceAttestation
|
|
777
|
-
const { certificate: productAttestation } = await
|
|
893
|
+
const { certificate: productAttestation } = await this.#invokeCommand(
|
|
778
894
|
{
|
|
779
|
-
|
|
895
|
+
endpoint: RootEndpointNumber,
|
|
896
|
+
cluster: OperationalCredentials.Complete,
|
|
897
|
+
command: "certificateChainRequest",
|
|
898
|
+
fields: {
|
|
899
|
+
certificateType: OperationalCredentials.CertificateChainType.PaiCertificate,
|
|
900
|
+
},
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
780
904
|
},
|
|
781
|
-
{ useExtendedFailSafeMessageResponseTimeout: true },
|
|
782
905
|
);
|
|
906
|
+
|
|
783
907
|
// TODO: validate deviceAttestation and productAttestation
|
|
784
|
-
const { attestationElements, attestationSignature } =
|
|
785
|
-
|
|
786
|
-
|
|
908
|
+
const { attestationElements, attestationSignature } = await this.#invokeCommand(
|
|
909
|
+
{
|
|
910
|
+
endpoint: RootEndpointNumber,
|
|
911
|
+
cluster: OperationalCredentials.Complete,
|
|
912
|
+
command: "attestationRequest",
|
|
913
|
+
fields: {
|
|
787
914
|
attestationNonce: this.fabric.crypto.randomBytes(32),
|
|
788
915
|
},
|
|
789
|
-
|
|
790
|
-
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
919
|
+
},
|
|
920
|
+
);
|
|
921
|
+
|
|
791
922
|
// TODO: validate attestationSignature using device public key
|
|
792
923
|
if (
|
|
793
924
|
deviceAttestation.byteLength === 0 ||
|
|
@@ -823,12 +954,18 @@ export class ControllerCommissioningFlow {
|
|
|
823
954
|
* DCL contains the name and other information of the Commissioner’s manufacturer.
|
|
824
955
|
*/
|
|
825
956
|
async #certificates() {
|
|
826
|
-
const
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
957
|
+
const { nocsrElements, attestationSignature: csrSignature } = await this.#invokeCommand(
|
|
958
|
+
{
|
|
959
|
+
endpoint: RootEndpointNumber,
|
|
960
|
+
cluster: OperationalCredentials.Complete,
|
|
961
|
+
command: "csrRequest",
|
|
962
|
+
fields: { csrNonce: this.fabric.crypto.randomBytes(32) },
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
966
|
+
},
|
|
967
|
+
);
|
|
968
|
+
|
|
832
969
|
if (nocsrElements.byteLength === 0 || csrSignature.byteLength === 0) {
|
|
833
970
|
// TODO: validate the data really
|
|
834
971
|
throw new UnexpectedDataError("Invalid response from device");
|
|
@@ -837,27 +974,42 @@ export class ControllerCommissioningFlow {
|
|
|
837
974
|
const { certSigningRequest } = TlvCertSigningRequest.decode(nocsrElements);
|
|
838
975
|
const operationalPublicKey = await Certificate.getPublicKeyFromCsr(this.ca.crypto, certSigningRequest);
|
|
839
976
|
|
|
840
|
-
await
|
|
977
|
+
await this.#invokeCommand(
|
|
841
978
|
{
|
|
842
|
-
|
|
979
|
+
endpoint: RootEndpointNumber,
|
|
980
|
+
cluster: OperationalCredentials.Complete,
|
|
981
|
+
command: "addTrustedRootCertificate",
|
|
982
|
+
fields: {
|
|
983
|
+
rootCaCertificate: this.ca.rootCert,
|
|
984
|
+
},
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
843
988
|
},
|
|
844
|
-
{ useExtendedFailSafeMessageResponseTimeout: true },
|
|
845
989
|
);
|
|
990
|
+
|
|
846
991
|
const peerOperationalCert = await this.ca.generateNoc(
|
|
847
992
|
operationalPublicKey,
|
|
848
993
|
this.fabric.fabricId,
|
|
849
|
-
this.
|
|
994
|
+
this.interaction.address.nodeId,
|
|
850
995
|
);
|
|
851
996
|
|
|
852
|
-
const addNocResponse = await
|
|
997
|
+
const addNocResponse = await this.#invokeCommand(
|
|
853
998
|
{
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
999
|
+
endpoint: RootEndpointNumber,
|
|
1000
|
+
cluster: OperationalCredentials.Complete,
|
|
1001
|
+
command: "addNoc",
|
|
1002
|
+
fields: {
|
|
1003
|
+
nocValue: peerOperationalCert,
|
|
1004
|
+
icacValue: this.ca.icacCert ?? new Uint8Array(0),
|
|
1005
|
+
ipkValue: this.fabric.identityProtectionKey,
|
|
1006
|
+
adminVendorId: this.fabric.rootVendorId,
|
|
1007
|
+
caseAdminSubject: this.fabric.rootNodeId,
|
|
1008
|
+
},
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
859
1012
|
},
|
|
860
|
-
{ useExtendedFailSafeMessageResponseTimeout: true },
|
|
861
1013
|
);
|
|
862
1014
|
|
|
863
1015
|
this.#ensureOperationalCredentialsSuccess("addNoc", addNocResponse);
|
|
@@ -887,13 +1039,17 @@ export class ControllerCommissioningFlow {
|
|
|
887
1039
|
breadcrumb: this.lastBreadcrumb,
|
|
888
1040
|
};
|
|
889
1041
|
}
|
|
890
|
-
const operationalCredentialCluster = this.#getClusterClient(OperationalCredentials.Cluster);
|
|
891
1042
|
try {
|
|
892
1043
|
this.#ensureOperationalCredentialsSuccess(
|
|
893
1044
|
"updateFabricLabel",
|
|
894
|
-
await
|
|
895
|
-
|
|
896
|
-
|
|
1045
|
+
await this.#invokeCommand({
|
|
1046
|
+
endpoint: RootEndpointNumber,
|
|
1047
|
+
cluster: OperationalCredentials.Complete,
|
|
1048
|
+
command: "updateFabricLabel",
|
|
1049
|
+
fields: {
|
|
1050
|
+
label: this.fabric.label,
|
|
1051
|
+
fabricIndex,
|
|
1052
|
+
},
|
|
897
1053
|
}),
|
|
898
1054
|
);
|
|
899
1055
|
} catch (error) {
|
|
@@ -915,7 +1071,7 @@ export class ControllerCommissioningFlow {
|
|
|
915
1071
|
* its desired access control policies.
|
|
916
1072
|
*/
|
|
917
1073
|
async #configureAccessControlLists() {
|
|
918
|
-
// Standard entry is
|
|
1074
|
+
// Standard entry is enough in our case
|
|
919
1075
|
|
|
920
1076
|
return {
|
|
921
1077
|
code: CommissioningStepResultCode.Skipped,
|
|
@@ -1018,27 +1174,39 @@ export class ControllerCommissioningFlow {
|
|
|
1018
1174
|
}
|
|
1019
1175
|
|
|
1020
1176
|
logger.debug("Configuring WiFi network ...");
|
|
1021
|
-
const networkCommissioningClusterClient = this.#getClusterClient(
|
|
1022
|
-
NetworkCommissioning.Cluster.with("WiFiNetworkInterface"),
|
|
1023
|
-
EndpointNumber(0),
|
|
1024
|
-
true,
|
|
1025
|
-
);
|
|
1026
1177
|
const ssid = Bytes.fromString(this.commissioningOptions.wifiNetwork.wifiSsid);
|
|
1027
1178
|
const credentials = Bytes.fromString(this.commissioningOptions.wifiNetwork.wifiCredentials);
|
|
1028
1179
|
|
|
1180
|
+
const [scanMaxTimeSeconds, connectMaxTimeSeconds] = await this.#readConcreteAttributeValues(
|
|
1181
|
+
Read(
|
|
1182
|
+
Read.Attribute({
|
|
1183
|
+
endpoint: RootEndpointNumber,
|
|
1184
|
+
cluster: NetworkCommissioning.Complete,
|
|
1185
|
+
attributes: ["scanMaxTimeSeconds", "connectMaxTimeSeconds"],
|
|
1186
|
+
}),
|
|
1187
|
+
),
|
|
1188
|
+
);
|
|
1189
|
+
|
|
1029
1190
|
// Only Scan when the device supports concurrent connections
|
|
1030
1191
|
if (this.collectedCommissioningData.supportsConcurrentConnection !== false) {
|
|
1031
|
-
|
|
1032
|
-
await this.#ensureFailsafeTimerFor(
|
|
1192
|
+
// TODO add message transmission time
|
|
1193
|
+
await this.#ensureFailsafeTimerFor(Seconds(scanMaxTimeSeconds));
|
|
1033
1194
|
|
|
1034
|
-
const { networkingStatus, wiFiScanResults, debugText } =
|
|
1035
|
-
|
|
1036
|
-
|
|
1195
|
+
const { networkingStatus, wiFiScanResults, debugText } = await this.#invokeCommand(
|
|
1196
|
+
{
|
|
1197
|
+
endpoint: RootEndpointNumber,
|
|
1198
|
+
cluster: NetworkCommissioning.Complete,
|
|
1199
|
+
command: "scanNetworks",
|
|
1200
|
+
fields: {
|
|
1037
1201
|
ssid,
|
|
1038
1202
|
breadcrumb: this.lastBreadcrumb++,
|
|
1039
1203
|
},
|
|
1040
|
-
|
|
1041
|
-
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
expectedProcessingTime: Seconds(scanMaxTimeSeconds),
|
|
1207
|
+
},
|
|
1208
|
+
);
|
|
1209
|
+
|
|
1042
1210
|
if (networkingStatus !== NetworkCommissioning.NetworkCommissioningStatus.Success) {
|
|
1043
1211
|
throw new WifiNetworkSetupFailedError(`Commissionee failed to scan for WiFi networks: ${debugText}`);
|
|
1044
1212
|
}
|
|
@@ -1053,14 +1221,22 @@ export class ControllerCommissioningFlow {
|
|
|
1053
1221
|
networkingStatus: addNetworkingStatus,
|
|
1054
1222
|
debugText: addDebugText,
|
|
1055
1223
|
networkIndex,
|
|
1056
|
-
} = await
|
|
1224
|
+
} = await this.#invokeCommand(
|
|
1225
|
+
{
|
|
1226
|
+
endpoint: RootEndpointNumber,
|
|
1227
|
+
cluster: NetworkCommissioning.Complete,
|
|
1228
|
+
command: "addOrUpdateWiFiNetwork",
|
|
1229
|
+
fields: {
|
|
1230
|
+
ssid,
|
|
1231
|
+
credentials,
|
|
1232
|
+
breadcrumb: this.lastBreadcrumb++,
|
|
1233
|
+
},
|
|
1234
|
+
},
|
|
1057
1235
|
{
|
|
1058
|
-
|
|
1059
|
-
credentials,
|
|
1060
|
-
breadcrumb: this.lastBreadcrumb++,
|
|
1236
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
1061
1237
|
},
|
|
1062
|
-
{ useExtendedFailSafeMessageResponseTimeout: true },
|
|
1063
1238
|
);
|
|
1239
|
+
|
|
1064
1240
|
if (addNetworkingStatus !== NetworkCommissioning.NetworkCommissioningStatus.Success) {
|
|
1065
1241
|
throw new WifiNetworkSetupFailedError(`Commissionee failed to add WiFi network: ${addDebugText}`);
|
|
1066
1242
|
}
|
|
@@ -1071,7 +1247,15 @@ export class ControllerCommissioningFlow {
|
|
|
1071
1247
|
`Commissionee added WiFi network ${this.commissioningOptions.wifiNetwork.wifiSsid} with network index ${networkIndex}`,
|
|
1072
1248
|
);
|
|
1073
1249
|
|
|
1074
|
-
const updatedNetworks = await
|
|
1250
|
+
const [updatedNetworks] = await this.#readConcreteAttributeValues(
|
|
1251
|
+
Read(
|
|
1252
|
+
Read.Attribute({
|
|
1253
|
+
endpoint: RootEndpointNumber,
|
|
1254
|
+
cluster: NetworkCommissioning.Complete,
|
|
1255
|
+
attributes: ["networks"],
|
|
1256
|
+
}),
|
|
1257
|
+
),
|
|
1258
|
+
);
|
|
1075
1259
|
if (updatedNetworks[networkIndex] === undefined) {
|
|
1076
1260
|
throw new WifiNetworkSetupFailedError(`Commissionee did not return network with index ${networkIndex}`);
|
|
1077
1261
|
}
|
|
@@ -1089,15 +1273,22 @@ export class ControllerCommissioningFlow {
|
|
|
1089
1273
|
};
|
|
1090
1274
|
}
|
|
1091
1275
|
|
|
1092
|
-
|
|
1093
|
-
await this.#ensureFailsafeTimerFor(
|
|
1276
|
+
// TODO Add retransmission time
|
|
1277
|
+
await this.#ensureFailsafeTimerFor(Seconds(connectMaxTimeSeconds));
|
|
1094
1278
|
|
|
1095
|
-
const connectResult = await
|
|
1279
|
+
const connectResult = await this.#invokeCommand(
|
|
1096
1280
|
{
|
|
1097
|
-
|
|
1098
|
-
|
|
1281
|
+
endpoint: RootEndpointNumber,
|
|
1282
|
+
cluster: NetworkCommissioning.Complete,
|
|
1283
|
+
command: "connectNetwork",
|
|
1284
|
+
fields: {
|
|
1285
|
+
networkId: networkId,
|
|
1286
|
+
breadcrumb: this.lastBreadcrumb++,
|
|
1287
|
+
},
|
|
1288
|
+
},
|
|
1289
|
+
{
|
|
1290
|
+
expectedProcessingTime: Seconds(connectMaxTimeSeconds),
|
|
1099
1291
|
},
|
|
1100
|
-
{ expectedProcessingTime: connectMaxTime },
|
|
1101
1292
|
);
|
|
1102
1293
|
|
|
1103
1294
|
if (connectResult.networkingStatus !== NetworkCommissioning.NetworkCommissioningStatus.Success) {
|
|
@@ -1165,22 +1356,34 @@ export class ControllerCommissioningFlow {
|
|
|
1165
1356
|
}
|
|
1166
1357
|
|
|
1167
1358
|
logger.debug("Configuring Thread network ...");
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1359
|
+
const [scanMaxTimeSeconds, connectMaxTimeSeconds] = await this.#readConcreteAttributeValues(
|
|
1360
|
+
Read(
|
|
1361
|
+
Read.Attribute({
|
|
1362
|
+
endpoint: RootEndpointNumber,
|
|
1363
|
+
cluster: NetworkCommissioning.Complete,
|
|
1364
|
+
attributes: ["scanMaxTimeSeconds", "connectMaxTimeSeconds"],
|
|
1365
|
+
}),
|
|
1366
|
+
),
|
|
1172
1367
|
);
|
|
1173
1368
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1369
|
+
if (!this.commissioningOptions.threadNetwork?.networkName) {
|
|
1370
|
+
logger.info("Thread network name is not configured. Skip scanning for it.");
|
|
1371
|
+
} else if (this.collectedCommissioningData.supportsConcurrentConnection !== false) {
|
|
1372
|
+
// Only Scan when the device supports concurrent connections
|
|
1373
|
+
await this.#ensureFailsafeTimerFor(Seconds(scanMaxTimeSeconds));
|
|
1374
|
+
|
|
1375
|
+
const { networkingStatus, threadScanResults, debugText } = (await this.#invokeCommand(
|
|
1376
|
+
{
|
|
1377
|
+
endpoint: RootEndpointNumber,
|
|
1378
|
+
cluster: NetworkCommissioning.Complete,
|
|
1379
|
+
command: "scanNetworks",
|
|
1380
|
+
fields: { breadcrumb: this.lastBreadcrumb++ },
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
expectedProcessingTime: Seconds(scanMaxTimeSeconds),
|
|
1384
|
+
},
|
|
1385
|
+
)) as NetworkCommissioning.ScanNetworksResponse;
|
|
1178
1386
|
|
|
1179
|
-
const { networkingStatus, threadScanResults, debugText } =
|
|
1180
|
-
await networkCommissioningClusterClient.scanNetworks(
|
|
1181
|
-
{ breadcrumb: this.lastBreadcrumb++ },
|
|
1182
|
-
{ expectedProcessingTime: scanMaxTime },
|
|
1183
|
-
);
|
|
1184
1387
|
if (networkingStatus !== NetworkCommissioning.NetworkCommissioningStatus.Success) {
|
|
1185
1388
|
throw new ThreadNetworkSetupFailedError(
|
|
1186
1389
|
`Commissionee failed to scan for Thread networks: ${debugText}`,
|
|
@@ -1212,13 +1415,21 @@ export class ControllerCommissioningFlow {
|
|
|
1212
1415
|
networkingStatus: addNetworkingStatus,
|
|
1213
1416
|
debugText: addDebugText,
|
|
1214
1417
|
networkIndex,
|
|
1215
|
-
} = await
|
|
1418
|
+
} = await this.#invokeCommand(
|
|
1216
1419
|
{
|
|
1217
|
-
|
|
1218
|
-
|
|
1420
|
+
endpoint: RootEndpointNumber,
|
|
1421
|
+
cluster: NetworkCommissioning.Complete,
|
|
1422
|
+
command: "addOrUpdateThreadNetwork",
|
|
1423
|
+
fields: {
|
|
1424
|
+
operationalDataset: Bytes.fromHex(this.commissioningOptions.threadNetwork.operationalDataset),
|
|
1425
|
+
breadcrumb: this.lastBreadcrumb++,
|
|
1426
|
+
},
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
1219
1430
|
},
|
|
1220
|
-
{ useExtendedFailSafeMessageResponseTimeout: true },
|
|
1221
1431
|
);
|
|
1432
|
+
|
|
1222
1433
|
if (addNetworkingStatus !== NetworkCommissioning.NetworkCommissioningStatus.Success) {
|
|
1223
1434
|
throw new ThreadNetworkSetupFailedError(`Commissionee failed to add Thread network: ${addDebugText}`);
|
|
1224
1435
|
}
|
|
@@ -1229,7 +1440,16 @@ export class ControllerCommissioningFlow {
|
|
|
1229
1440
|
`Commissionee added Thread network ${this.commissioningOptions.threadNetwork.networkName} with network index ${networkIndex}`,
|
|
1230
1441
|
);
|
|
1231
1442
|
|
|
1232
|
-
const updatedNetworks = await
|
|
1443
|
+
const updatedNetworks = await this.#readConcreteAttributeValues(
|
|
1444
|
+
Read(
|
|
1445
|
+
Read.Attribute({
|
|
1446
|
+
endpoint: RootEndpointNumber,
|
|
1447
|
+
cluster: NetworkCommissioning.Complete,
|
|
1448
|
+
attributes: ["networks"],
|
|
1449
|
+
}),
|
|
1450
|
+
),
|
|
1451
|
+
);
|
|
1452
|
+
|
|
1233
1453
|
if (updatedNetworks[networkIndex] === undefined) {
|
|
1234
1454
|
throw new ThreadNetworkSetupFailedError(`Commissionee did not return network with index ${networkIndex}`);
|
|
1235
1455
|
}
|
|
@@ -1246,15 +1466,21 @@ export class ControllerCommissioningFlow {
|
|
|
1246
1466
|
};
|
|
1247
1467
|
}
|
|
1248
1468
|
|
|
1249
|
-
|
|
1250
|
-
await this.#ensureFailsafeTimerFor(connectMaxTime);
|
|
1469
|
+
await this.#ensureFailsafeTimerFor(Seconds(connectMaxTimeSeconds));
|
|
1251
1470
|
|
|
1252
|
-
const connectResult = await
|
|
1471
|
+
const connectResult = await this.#invokeCommand(
|
|
1472
|
+
{
|
|
1473
|
+
endpoint: RootEndpointNumber,
|
|
1474
|
+
cluster: NetworkCommissioning.Complete,
|
|
1475
|
+
command: "connectNetwork",
|
|
1476
|
+
fields: {
|
|
1477
|
+
networkId: networkId,
|
|
1478
|
+
breadcrumb: this.lastBreadcrumb++,
|
|
1479
|
+
},
|
|
1480
|
+
},
|
|
1253
1481
|
{
|
|
1254
|
-
|
|
1255
|
-
breadcrumb: this.lastBreadcrumb++,
|
|
1482
|
+
expectedProcessingTime: Seconds(connectMaxTimeSeconds),
|
|
1256
1483
|
},
|
|
1257
|
-
{ expectedProcessingTime: connectMaxTime },
|
|
1258
1484
|
);
|
|
1259
1485
|
|
|
1260
1486
|
if (connectResult.networkingStatus !== NetworkCommissioning.NetworkCommissioningStatus.Success) {
|
|
@@ -1314,10 +1540,10 @@ export class ControllerCommissioningFlow {
|
|
|
1314
1540
|
reArmFailsafeInterval.start();
|
|
1315
1541
|
}
|
|
1316
1542
|
|
|
1317
|
-
let transitionResult:
|
|
1543
|
+
let transitionResult: ClientInteraction | undefined;
|
|
1318
1544
|
try {
|
|
1319
1545
|
transitionResult = await this.transitionToCase(
|
|
1320
|
-
this.
|
|
1546
|
+
this.interaction.address,
|
|
1321
1547
|
// Assume concurrent connections are supported if not know (which should not be the case when we came here)
|
|
1322
1548
|
isConcurrentFlow,
|
|
1323
1549
|
);
|
|
@@ -1335,7 +1561,7 @@ export class ControllerCommissioningFlow {
|
|
|
1335
1561
|
};
|
|
1336
1562
|
}
|
|
1337
1563
|
|
|
1338
|
-
this.
|
|
1564
|
+
this.interaction = transitionResult;
|
|
1339
1565
|
this.#clusterClients.clear();
|
|
1340
1566
|
|
|
1341
1567
|
logger.debug("Successfully reconnected with device ...");
|
|
@@ -1354,12 +1580,18 @@ export class ControllerCommissioningFlow {
|
|
|
1354
1580
|
* the commissioning process.
|
|
1355
1581
|
*/
|
|
1356
1582
|
async #completeCommissioning() {
|
|
1357
|
-
const generalCommissioningClusterClient = this.#getClusterClient(GeneralCommissioning.Cluster);
|
|
1358
1583
|
this.#ensureGeneralCommissioningSuccess(
|
|
1359
1584
|
"commissioningComplete",
|
|
1360
|
-
await
|
|
1361
|
-
|
|
1362
|
-
|
|
1585
|
+
await this.#invokeCommand(
|
|
1586
|
+
{
|
|
1587
|
+
endpoint: RootEndpointNumber,
|
|
1588
|
+
cluster: GeneralCommissioning.Complete,
|
|
1589
|
+
command: "commissioningComplete",
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
useExtendedFailSafeMessageResponseTimeout: true,
|
|
1593
|
+
},
|
|
1594
|
+
),
|
|
1363
1595
|
);
|
|
1364
1596
|
this.#currentFailSafeEndTime = undefined; // gets deactivated when successful
|
|
1365
1597
|
|