@matter-server/ws-controller 0.2.0-alpha.0-00000000-000000000

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +11 -0
  3. package/dist/esm/controller/AttributeDataCache.d.ts +49 -0
  4. package/dist/esm/controller/AttributeDataCache.d.ts.map +1 -0
  5. package/dist/esm/controller/AttributeDataCache.js +154 -0
  6. package/dist/esm/controller/AttributeDataCache.js.map +6 -0
  7. package/dist/esm/controller/ControllerCommandHandler.d.ts +118 -0
  8. package/dist/esm/controller/ControllerCommandHandler.d.ts.map +1 -0
  9. package/dist/esm/controller/ControllerCommandHandler.js +1015 -0
  10. package/dist/esm/controller/ControllerCommandHandler.js.map +6 -0
  11. package/dist/esm/controller/LegacyDataInjector.d.ts +95 -0
  12. package/dist/esm/controller/LegacyDataInjector.d.ts.map +1 -0
  13. package/dist/esm/controller/LegacyDataInjector.js +196 -0
  14. package/dist/esm/controller/LegacyDataInjector.js.map +6 -0
  15. package/dist/esm/controller/MatterController.d.ts +59 -0
  16. package/dist/esm/controller/MatterController.d.ts.map +1 -0
  17. package/dist/esm/controller/MatterController.js +212 -0
  18. package/dist/esm/controller/MatterController.js.map +6 -0
  19. package/dist/esm/controller/Nodes.d.ts +62 -0
  20. package/dist/esm/controller/Nodes.d.ts.map +1 -0
  21. package/dist/esm/controller/Nodes.js +85 -0
  22. package/dist/esm/controller/Nodes.js.map +6 -0
  23. package/dist/esm/controller/TestNodeCommandHandler.d.ts +84 -0
  24. package/dist/esm/controller/TestNodeCommandHandler.d.ts.map +1 -0
  25. package/dist/esm/controller/TestNodeCommandHandler.js +225 -0
  26. package/dist/esm/controller/TestNodeCommandHandler.js.map +6 -0
  27. package/dist/esm/data/VendorIDs.d.ts +7 -0
  28. package/dist/esm/data/VendorIDs.d.ts.map +1 -0
  29. package/dist/esm/data/VendorIDs.js +1237 -0
  30. package/dist/esm/data/VendorIDs.js.map +6 -0
  31. package/dist/esm/example/send-command.d.ts +7 -0
  32. package/dist/esm/example/send-command.d.ts.map +1 -0
  33. package/dist/esm/example/send-command.js +60 -0
  34. package/dist/esm/example/send-command.js.map +6 -0
  35. package/dist/esm/index.d.ts +21 -0
  36. package/dist/esm/index.d.ts.map +1 -0
  37. package/dist/esm/index.js +26 -0
  38. package/dist/esm/index.js.map +6 -0
  39. package/dist/esm/model/ModelMapper.d.ts +34 -0
  40. package/dist/esm/model/ModelMapper.d.ts.map +1 -0
  41. package/dist/esm/model/ModelMapper.js +62 -0
  42. package/dist/esm/model/ModelMapper.js.map +6 -0
  43. package/dist/esm/package.json +3 -0
  44. package/dist/esm/server/ConfigStorage.d.ts +29 -0
  45. package/dist/esm/server/ConfigStorage.d.ts.map +1 -0
  46. package/dist/esm/server/ConfigStorage.js +84 -0
  47. package/dist/esm/server/ConfigStorage.js.map +6 -0
  48. package/dist/esm/server/Converters.d.ts +53 -0
  49. package/dist/esm/server/Converters.d.ts.map +1 -0
  50. package/dist/esm/server/Converters.js +343 -0
  51. package/dist/esm/server/Converters.js.map +6 -0
  52. package/dist/esm/server/WebSocketControllerHandler.d.ts +21 -0
  53. package/dist/esm/server/WebSocketControllerHandler.d.ts.map +1 -0
  54. package/dist/esm/server/WebSocketControllerHandler.js +767 -0
  55. package/dist/esm/server/WebSocketControllerHandler.js.map +6 -0
  56. package/dist/esm/types/CommandHandler.d.ts +258 -0
  57. package/dist/esm/types/CommandHandler.d.ts.map +1 -0
  58. package/dist/esm/types/CommandHandler.js +6 -0
  59. package/dist/esm/types/CommandHandler.js.map +6 -0
  60. package/dist/esm/types/WebServer.d.ts +12 -0
  61. package/dist/esm/types/WebServer.d.ts.map +1 -0
  62. package/dist/esm/types/WebServer.js +6 -0
  63. package/dist/esm/types/WebServer.js.map +6 -0
  64. package/dist/esm/types/WebSocketMessageTypes.d.ts +478 -0
  65. package/dist/esm/types/WebSocketMessageTypes.d.ts.map +1 -0
  66. package/dist/esm/types/WebSocketMessageTypes.js +77 -0
  67. package/dist/esm/types/WebSocketMessageTypes.js.map +6 -0
  68. package/dist/esm/util/matterVersion.d.ts +12 -0
  69. package/dist/esm/util/matterVersion.d.ts.map +1 -0
  70. package/dist/esm/util/matterVersion.js +32 -0
  71. package/dist/esm/util/matterVersion.js.map +6 -0
  72. package/dist/esm/util/network.d.ts +14 -0
  73. package/dist/esm/util/network.d.ts.map +1 -0
  74. package/dist/esm/util/network.js +63 -0
  75. package/dist/esm/util/network.js.map +6 -0
  76. package/package.json +45 -0
  77. package/src/controller/AttributeDataCache.ts +194 -0
  78. package/src/controller/ControllerCommandHandler.ts +1256 -0
  79. package/src/controller/LegacyDataInjector.ts +314 -0
  80. package/src/controller/MatterController.ts +265 -0
  81. package/src/controller/Nodes.ts +115 -0
  82. package/src/controller/TestNodeCommandHandler.ts +305 -0
  83. package/src/data/VendorIDs.ts +1234 -0
  84. package/src/example/send-command.ts +82 -0
  85. package/src/index.ts +33 -0
  86. package/src/model/ModelMapper.ts +87 -0
  87. package/src/server/ConfigStorage.ts +112 -0
  88. package/src/server/Converters.ts +483 -0
  89. package/src/server/WebSocketControllerHandler.ts +917 -0
  90. package/src/tsconfig.json +7 -0
  91. package/src/types/CommandHandler.ts +270 -0
  92. package/src/types/WebServer.ts +14 -0
  93. package/src/types/WebSocketMessageTypes.ts +525 -0
  94. package/src/util/matterVersion.ts +45 -0
  95. package/src/util/network.ts +85 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { NodeId } from "@matter/main";
7
+ import { ClusterClientObj } from "@matter/main/protocol";
8
+ import { ClusterId, ClusterType, EndpointNumber } from "@matter/main/types";
9
+ import { InteractionClient } from "@project-chip/matter.js/cluster";
10
+ import { PairedNode } from "@project-chip/matter.js/device";
11
+ import { AttributeDataCache } from "./AttributeDataCache.js";
12
+ /**
13
+ * Manages node storage and provides access to nodes and their clients.
14
+ *
15
+ * This class handles:
16
+ * - Storage of PairedNode instances
17
+ * - Node retrieval and existence checking
18
+ * - Access to interaction clients and cluster clients
19
+ * - Attribute data caching
20
+ */
21
+ export declare class Nodes {
22
+ #private;
23
+ /**
24
+ * Get the attribute cache instance.
25
+ */
26
+ get attributeCache(): AttributeDataCache;
27
+ /**
28
+ * Get all node IDs.
29
+ */
30
+ getIds(): NodeId[];
31
+ /**
32
+ * Get a node by ID.
33
+ * @throws ServerError if node not found
34
+ */
35
+ get(nodeId: NodeId): PairedNode;
36
+ /**
37
+ * Check if a node exists.
38
+ */
39
+ has(nodeId: NodeId): boolean;
40
+ /**
41
+ * Add or update a node in storage.
42
+ */
43
+ set(nodeId: NodeId, node: PairedNode): void;
44
+ /**
45
+ * Remove a node from storage and clear its attribute cache.
46
+ */
47
+ delete(nodeId: NodeId): void;
48
+ /**
49
+ * Get the interaction client for a node.
50
+ */
51
+ interactionClientFor(nodeId: NodeId): Promise<InteractionClient>;
52
+ /**
53
+ * Get a cluster client by cluster ID for a specific endpoint on a node.
54
+ * @throws Error if endpoint or cluster not found
55
+ */
56
+ clusterClientByIdFor(nodeId: NodeId, endpointId: EndpointNumber, clusterId: ClusterId): ClusterClientObj<any>;
57
+ /**
58
+ * Get a typed cluster client for a specific endpoint on a node.
59
+ */
60
+ clusterClientFor<const T extends ClusterType>(nodeId: NodeId, endpointId: EndpointNumber, cluster: T): ClusterClientObj<T>;
61
+ }
62
+ //# sourceMappingURL=Nodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Nodes.d.ts","sourceRoot":"","sources":["../../../src/controller/Nodes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D;;;;;;;;GAQG;AACH,qBAAa,KAAK;;IAId;;OAEG;IACH,IAAI,cAAc,IAAI,kBAAkB,CAEvC;IAED;;OAEG;IACH,MAAM,IAAI,MAAM,EAAE;IAIlB;;;OAGG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU;IAQ/B;;OAEG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAI5B;;OAEG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI;IAI3C;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK5B;;OAEG;IACH,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIhE;;;OAGG;IACH,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC;IAkB7G;;OAEG;IACH,gBAAgB,CAAC,KAAK,CAAC,CAAC,SAAS,WAAW,EACxC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,cAAc,EAC1B,OAAO,EAAE,CAAC,GACX,gBAAgB,CAAC,CAAC,CAAC;CAGzB"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { ServerError } from "../types/WebSocketMessageTypes.js";
7
+ import { AttributeDataCache } from "./AttributeDataCache.js";
8
+ class Nodes {
9
+ #nodes = /* @__PURE__ */ new Map();
10
+ #attributeCache = new AttributeDataCache();
11
+ /**
12
+ * Get the attribute cache instance.
13
+ */
14
+ get attributeCache() {
15
+ return this.#attributeCache;
16
+ }
17
+ /**
18
+ * Get all node IDs.
19
+ */
20
+ getIds() {
21
+ return Array.from(this.#nodes.keys());
22
+ }
23
+ /**
24
+ * Get a node by ID.
25
+ * @throws ServerError if node not found
26
+ */
27
+ get(nodeId) {
28
+ const node = this.#nodes.get(nodeId);
29
+ if (node === void 0) {
30
+ throw ServerError.nodeNotExists(nodeId);
31
+ }
32
+ return node;
33
+ }
34
+ /**
35
+ * Check if a node exists.
36
+ */
37
+ has(nodeId) {
38
+ return this.#nodes.has(nodeId);
39
+ }
40
+ /**
41
+ * Add or update a node in storage.
42
+ */
43
+ set(nodeId, node) {
44
+ this.#nodes.set(nodeId, node);
45
+ }
46
+ /**
47
+ * Remove a node from storage and clear its attribute cache.
48
+ */
49
+ delete(nodeId) {
50
+ this.#nodes.delete(nodeId);
51
+ this.#attributeCache.delete(nodeId);
52
+ }
53
+ /**
54
+ * Get the interaction client for a node.
55
+ */
56
+ interactionClientFor(nodeId) {
57
+ return this.get(nodeId).getInteractionClient();
58
+ }
59
+ /**
60
+ * Get a cluster client by cluster ID for a specific endpoint on a node.
61
+ * @throws Error if endpoint or cluster not found
62
+ */
63
+ clusterClientByIdFor(nodeId, endpointId, clusterId) {
64
+ const node = this.get(nodeId);
65
+ const endpoint = endpointId === 0 ? node.getRootEndpoint() : node.getDeviceById(endpointId);
66
+ if (endpoint === void 0) {
67
+ throw new Error(`Endpoint ${endpointId} on node ${nodeId} not found`);
68
+ }
69
+ const client = endpoint.getClusterClientById(clusterId);
70
+ if (client === void 0) {
71
+ throw new Error(`Cluster ${clusterId} on endpoint ${endpointId} on node ${nodeId} not found`);
72
+ }
73
+ return client;
74
+ }
75
+ /**
76
+ * Get a typed cluster client for a specific endpoint on a node.
77
+ */
78
+ clusterClientFor(nodeId, endpointId, cluster) {
79
+ return this.clusterClientByIdFor(nodeId, endpointId, cluster.id);
80
+ }
81
+ }
82
+ export {
83
+ Nodes
84
+ };
85
+ //# sourceMappingURL=Nodes.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/controller/Nodes.ts"],
4
+ "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,SAAS,mBAAmB;AAC5B,SAAS,0BAA0B;AAW5B,MAAM,MAAM;AAAA,EACf,SAAS,oBAAI,IAAwB;AAAA,EACrC,kBAAkB,IAAI,mBAAmB;AAAA;AAAA;AAAA;AAAA,EAKzC,IAAI,iBAAqC;AACrC,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,SAAmB;AACf,WAAO,MAAM,KAAK,KAAK,OAAO,KAAK,CAAC;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,QAA4B;AAC5B,UAAM,OAAO,KAAK,OAAO,IAAI,MAAM;AACnC,QAAI,SAAS,QAAW;AACpB,YAAM,YAAY,cAAc,MAAM;AAAA,IAC1C;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAyB;AACzB,WAAO,KAAK,OAAO,IAAI,MAAM;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAgB,MAAwB;AACxC,SAAK,OAAO,IAAI,QAAQ,IAAI;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,QAAsB;AACzB,SAAK,OAAO,OAAO,MAAM;AACzB,SAAK,gBAAgB,OAAO,MAAM;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,QAA4C;AAC7D,WAAO,KAAK,IAAI,MAAM,EAAE,qBAAqB;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAAqB,QAAgB,YAA4B,WAA6C;AAC1G,UAAM,OAAO,KAAK,IAAI,MAAM;AAE5B,UAAM,WAAW,eAAe,IAAI,KAAK,gBAAgB,IAAI,KAAK,cAAc,UAAU;AAE1F,QAAI,aAAa,QAAW;AACxB,YAAM,IAAI,MAAM,YAAY,UAAU,YAAY,MAAM,YAAY;AAAA,IACxE;AAEA,UAAM,SAAS,SAAS,qBAAqB,SAAS;AAEtD,QAAI,WAAW,QAAW;AACtB,YAAM,IAAI,MAAM,WAAW,SAAS,gBAAgB,UAAU,YAAY,MAAM,YAAY;AAAA,IAChG;AAEA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,iBACI,QACA,YACA,SACmB;AACnB,WAAO,KAAK,qBAAqB,QAAQ,YAAY,QAAQ,EAAE;AAAA,EACnE;AACJ;",
5
+ "names": []
6
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { NodeId, Observable } from "@matter/main";
7
+ import { AttributeResponseStatus, AttributesData, InvokeRequest, MatterNodeData, NodeCommandHandler, ReadAttributeRequest, ReadAttributeResponse, WriteAttributeRequest } from "../types/CommandHandler.js";
8
+ import { MatterNode } from "../types/WebSocketMessageTypes.js";
9
+ /**
10
+ * Command handler for test nodes (imported diagnostic dumps).
11
+ * Test nodes are stored in memory and provide mock responses for commands.
12
+ */
13
+ export declare class TestNodeCommandHandler implements NodeCommandHandler {
14
+ #private;
15
+ /** Observable for node added events */
16
+ readonly nodeAdded: Observable<[nodeId: NodeId, node: MatterNode], void>;
17
+ /** Observable for node removed events */
18
+ readonly nodeRemoved: Observable<[nodeId: NodeId], void>;
19
+ /**
20
+ * Check if a node ID is in the test node range (>= TEST_NODE_START).
21
+ */
22
+ static isTestNodeId(nodeId: number | bigint): boolean;
23
+ /**
24
+ * Check if this handler manages the given node ID.
25
+ */
26
+ hasNode(nodeId: NodeId): boolean;
27
+ /**
28
+ * Get all test node IDs.
29
+ */
30
+ getNodeIds(): NodeId[];
31
+ /**
32
+ * Get all test nodes.
33
+ */
34
+ getNodes(): MatterNode[];
35
+ /**
36
+ * Get a test node by ID.
37
+ */
38
+ getNode(nodeId: NodeId): MatterNode | undefined;
39
+ /**
40
+ * Get full node details in WebSocket API format.
41
+ */
42
+ getNodeDetails(nodeId: NodeId): Promise<MatterNodeData>;
43
+ /**
44
+ * Read multiple attributes from a test node by path strings.
45
+ * Handles wildcards in paths.
46
+ */
47
+ handleReadAttributes(nodeId: NodeId, attributePaths: string[], _fabricFiltered?: boolean): Promise<AttributesData>;
48
+ /**
49
+ * Import test nodes from a diagnostic dump.
50
+ * @param dump JSON string containing the diagnostic dump
51
+ * @returns Array of imported node IDs
52
+ */
53
+ importTestNodes(dump: string): NodeId[];
54
+ /**
55
+ * Read attributes from a test node.
56
+ * Returns values from the stored attributes map.
57
+ */
58
+ handleReadAttribute(data: ReadAttributeRequest): Promise<ReadAttributeResponse>;
59
+ /**
60
+ * Write an attribute to a test node.
61
+ * Logs the write and returns success (no actual write occurs).
62
+ */
63
+ handleWriteAttribute(data: WriteAttributeRequest): Promise<AttributeResponseStatus>;
64
+ /**
65
+ * Invoke a command on a test node.
66
+ * Logs the command and returns null (no actual command execution).
67
+ */
68
+ handleInvoke(data: InvokeRequest): Promise<unknown>;
69
+ /**
70
+ * Get IP addresses for a test node.
71
+ * Returns mock IP addresses.
72
+ */
73
+ getNodeIpAddresses(_nodeId: NodeId, _preferCache?: boolean): Promise<string[]>;
74
+ /**
75
+ * Ping a test node.
76
+ * Returns mock success results.
77
+ */
78
+ pingNode(_nodeId: NodeId, _attempts?: number): Promise<Record<string, boolean>>;
79
+ /**
80
+ * Remove a test node.
81
+ */
82
+ removeNode(nodeId: NodeId): Promise<void>;
83
+ }
84
+ //# sourceMappingURL=TestNodeCommandHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestNodeCommandHandler.d.ts","sourceRoot":"","sources":["../../../src/controller/TestNodeCommandHandler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAU,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,EACH,uBAAuB,EACvB,cAAc,EACd,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAmB,MAAM,mCAAmC,CAAC;AAIhF;;;GAGG;AACH,qBAAa,sBAAuB,YAAW,kBAAkB;;IAG7D,uCAAuC;IACvC,QAAQ,CAAC,SAAS,uDAAwD;IAE1E,yCAAyC;IACzC,QAAQ,CAAC,WAAW,qCAAsC;IAE1D;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO;IAKrD;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,UAAU,IAAI,MAAM,EAAE;IAItB;;OAEG;IACH,QAAQ,IAAI,UAAU,EAAE;IAIxB;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI/C;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAQ7D;;;OAGG;IACG,oBAAoB,CACtB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EAAE,EACxB,eAAe,CAAC,EAAE,OAAO,GAC1B,OAAO,CAAC,cAAc,CAAC;IA6B1B;;;;OAIG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IA6DvC;;;OAGG;IACG,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAkDrF;;;OAGG;IACG,oBAAoB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAezF;;;OAGG;IACG,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAWzD;;;OAGG;IACG,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAIpF;;;OAGG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAIrF;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAYlD"}
@@ -0,0 +1,225 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { Logger, NodeId, Observable } from "@matter/main";
7
+ import { parseBigIntAwareJson, splitAttributePath } from "../server/Converters.js";
8
+ import { TEST_NODE_START } from "../types/WebSocketMessageTypes.js";
9
+ const logger = Logger.get("TestNodeCommandHandler");
10
+ class TestNodeCommandHandler {
11
+ #testNodes = /* @__PURE__ */ new Map();
12
+ /** Observable for node added events */
13
+ nodeAdded = new Observable();
14
+ /** Observable for node removed events */
15
+ nodeRemoved = new Observable();
16
+ /**
17
+ * Check if a node ID is in the test node range (>= TEST_NODE_START).
18
+ */
19
+ static isTestNodeId(nodeId) {
20
+ const bigId = typeof nodeId === "bigint" ? nodeId : BigInt(nodeId);
21
+ return bigId >= TEST_NODE_START;
22
+ }
23
+ /**
24
+ * Check if this handler manages the given node ID.
25
+ */
26
+ hasNode(nodeId) {
27
+ return this.#testNodes.has(BigInt(nodeId));
28
+ }
29
+ /**
30
+ * Get all test node IDs.
31
+ */
32
+ getNodeIds() {
33
+ return Array.from(this.#testNodes.keys()).map((id) => NodeId(id));
34
+ }
35
+ /**
36
+ * Get all test nodes.
37
+ */
38
+ getNodes() {
39
+ return Array.from(this.#testNodes.values());
40
+ }
41
+ /**
42
+ * Get a test node by ID.
43
+ */
44
+ getNode(nodeId) {
45
+ return this.#testNodes.get(BigInt(nodeId));
46
+ }
47
+ /**
48
+ * Get full node details in WebSocket API format.
49
+ */
50
+ async getNodeDetails(nodeId) {
51
+ const testNode = this.#testNodes.get(BigInt(nodeId));
52
+ if (testNode === void 0) {
53
+ throw new Error(`Test node ${nodeId} not found`);
54
+ }
55
+ return testNode;
56
+ }
57
+ /**
58
+ * Read multiple attributes from a test node by path strings.
59
+ * Handles wildcards in paths.
60
+ */
61
+ async handleReadAttributes(nodeId, attributePaths, _fabricFiltered) {
62
+ const testNode = this.#testNodes.get(BigInt(nodeId));
63
+ if (testNode === void 0) {
64
+ throw new Error(`Test node ${nodeId} not found`);
65
+ }
66
+ const result = {};
67
+ for (const path of attributePaths) {
68
+ const { endpointId, clusterId, attributeId } = splitAttributePath(path);
69
+ if (path.includes("*")) {
70
+ for (const [attrPath, value] of Object.entries(testNode.attributes)) {
71
+ const parts = attrPath.split("/").map(Number);
72
+ if ((endpointId === void 0 || parts[0] === endpointId) && (clusterId === void 0 || parts[1] === clusterId) && (attributeId === void 0 || parts[2] === attributeId)) {
73
+ result[attrPath] = value;
74
+ }
75
+ }
76
+ } else {
77
+ result[path] = testNode.attributes[path];
78
+ }
79
+ }
80
+ return result;
81
+ }
82
+ /**
83
+ * Import test nodes from a diagnostic dump.
84
+ * @param dump JSON string containing the diagnostic dump
85
+ * @returns Array of imported node IDs
86
+ */
87
+ importTestNodes(dump) {
88
+ const dumpData = parseBigIntAwareJson(dump);
89
+ let dumpNodes;
90
+ if (dumpData?.data?.node) {
91
+ dumpNodes = [dumpData.data.node];
92
+ } else if (dumpData?.data?.server?.nodes) {
93
+ dumpNodes = Object.values(dumpData.data.server.nodes);
94
+ } else if (dumpData?.data?.nodes) {
95
+ dumpNodes = Object.values(dumpData.data.nodes);
96
+ } else {
97
+ throw new Error("Invalid dump format: cannot find node data");
98
+ }
99
+ let nextTestNodeId = TEST_NODE_START;
100
+ for (const existingId of this.#testNodes.keys()) {
101
+ if (existingId >= nextTestNodeId) {
102
+ nextTestNodeId = existingId + 1n;
103
+ }
104
+ }
105
+ const importedNodeIds = [];
106
+ for (const nodeDict of dumpNodes) {
107
+ const testNodeId = nextTestNodeId++;
108
+ const nodeId = NodeId(testNodeId);
109
+ const testNode = {
110
+ node_id: testNodeId,
111
+ date_commissioned: nodeDict.date_commissioned,
112
+ last_interview: nodeDict.last_interview,
113
+ interview_version: nodeDict.interview_version,
114
+ available: nodeDict.available,
115
+ is_bridge: nodeDict.is_bridge,
116
+ attributes: nodeDict.attributes,
117
+ attribute_subscriptions: []
118
+ };
119
+ this.#testNodes.set(testNodeId, testNode);
120
+ importedNodeIds.push(nodeId);
121
+ logger.info(`Imported test node ${testNodeId} with ${Object.keys(testNode.attributes).length} attributes`);
122
+ this.nodeAdded.emit(nodeId, testNode);
123
+ }
124
+ return importedNodeIds;
125
+ }
126
+ /**
127
+ * Read attributes from a test node.
128
+ * Returns values from the stored attributes map.
129
+ */
130
+ async handleReadAttribute(data) {
131
+ const { nodeId, endpointId, clusterId, attributeId } = data;
132
+ const testNode = this.#testNodes.get(BigInt(nodeId));
133
+ if (testNode === void 0) {
134
+ throw new Error(`Test node ${nodeId} not found`);
135
+ }
136
+ const values = [];
137
+ const hasWildcards = endpointId === void 0 || clusterId === void 0 || attributeId === void 0;
138
+ if (hasWildcards) {
139
+ for (const [attrPath, value] of Object.entries(testNode.attributes)) {
140
+ const parts = attrPath.split("/").map(Number);
141
+ if ((endpointId === void 0 || parts[0] === endpointId) && (clusterId === void 0 || parts[1] === clusterId) && (attributeId === void 0 || parts[2] === attributeId)) {
142
+ values.push({
143
+ endpointId: parts[0],
144
+ clusterId: parts[1],
145
+ attributeId: parts[2],
146
+ dataVersion: 0,
147
+ value
148
+ });
149
+ }
150
+ }
151
+ } else {
152
+ const path = `${endpointId}/${clusterId}/${attributeId}`;
153
+ const value = testNode.attributes[path];
154
+ if (value !== void 0) {
155
+ values.push({
156
+ endpointId,
157
+ clusterId,
158
+ attributeId,
159
+ dataVersion: 0,
160
+ value
161
+ });
162
+ }
163
+ }
164
+ logger.debug(`read_attribute for test node ${nodeId}: ${values.length} values`);
165
+ return { values };
166
+ }
167
+ /**
168
+ * Write an attribute to a test node.
169
+ * Logs the write and returns success (no actual write occurs).
170
+ */
171
+ async handleWriteAttribute(data) {
172
+ const { nodeId, endpointId, clusterId, attributeId, value } = data;
173
+ logger.debug(
174
+ `write_attribute for test node ${nodeId} on ${endpointId}/${clusterId}/${attributeId} - value: ${JSON.stringify(value)}`
175
+ );
176
+ return {
177
+ endpointId,
178
+ clusterId,
179
+ attributeId,
180
+ status: 0
181
+ // Success
182
+ };
183
+ }
184
+ /**
185
+ * Invoke a command on a test node.
186
+ * Logs the command and returns null (no actual command execution).
187
+ */
188
+ async handleInvoke(data) {
189
+ const { nodeId, endpointId, clusterId, commandName, data: payload } = data;
190
+ logger.debug(
191
+ `device_command for test node ${nodeId} on endpoint ${endpointId} - cluster ${clusterId} - command ${commandName} - payload: ${JSON.stringify(payload)}`
192
+ );
193
+ return null;
194
+ }
195
+ /**
196
+ * Get IP addresses for a test node.
197
+ * Returns mock IP addresses.
198
+ */
199
+ async getNodeIpAddresses(_nodeId, _preferCache) {
200
+ return ["0.0.0.0", "0000:1111:2222:3333:4444"];
201
+ }
202
+ /**
203
+ * Ping a test node.
204
+ * Returns mock success results.
205
+ */
206
+ async pingNode(_nodeId, _attempts) {
207
+ return { "0.0.0.0": true, "0000:1111:2222:3333:4444": true };
208
+ }
209
+ /**
210
+ * Remove a test node.
211
+ */
212
+ async removeNode(nodeId) {
213
+ const bigId = BigInt(nodeId);
214
+ if (!this.#testNodes.has(bigId)) {
215
+ throw new Error(`Test node ${nodeId} not found`);
216
+ }
217
+ logger.info(`Removing test node ${nodeId}`);
218
+ this.#testNodes.delete(bigId);
219
+ this.nodeRemoved.emit(nodeId);
220
+ }
221
+ }
222
+ export {
223
+ TestNodeCommandHandler
224
+ };
225
+ //# sourceMappingURL=TestNodeCommandHandler.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/controller/TestNodeCommandHandler.ts"],
4
+ "mappings": "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",
5
+ "names": []
6
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export declare const VendorIds: Record<string, string>;
7
+ //# sourceMappingURL=VendorIDs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VendorIDs.d.ts","sourceRoot":"","sources":["../../../src/data/VendorIDs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA2sC5C,CAAC"}