From e4544738da76bf07addf8806fc5492edac72cd53 Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Fri, 26 Aug 2022 14:29:04 -1000 Subject: [PATCH 01/10] Add Descriptor cluster --- src/Main.ts | 6 ++-- src/interaction/cluster/DescriptorCluster.ts | 34 ++++++++++++++++++++ src/interaction/model/Device.ts | 5 ++- src/interaction/model/Endpoint.ts | 11 +++++++ 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 src/interaction/cluster/DescriptorCluster.ts diff --git a/src/Main.ts b/src/Main.ts index 3969c85b..1c467cd6 100644 --- a/src/Main.ts +++ b/src/Main.ts @@ -63,13 +63,13 @@ class Main { new CasePairing(), )) .addProtocolHandler(Protocol.INTERACTION_MODEL, new InteractionProtocol(new Device([ - new Endpoint(0x00, "MA-rootdevice", [ + new Endpoint(0x00, "MA-rootdevice", 0x0016, [ new BasicCluster({ vendorName, vendorId, productName, productId }), new GeneralCommissioningCluster(), new OperationalCredentialsCluster({devicePrivateKey: DevicePrivateKey, deviceCertificate: DeviceCertificate, deviceIntermediateCertificate: ProductIntermediateCertificate, certificateDeclaration: CertificateDeclaration}), ]), - new Endpoint(0x01, "MA-OnOff", [ - new OnOffCluster(commandExecutor("on"), commandExecutor("off")), + new Endpoint(0x01, "MA-OnOff", 0x0100, [ + new OnOffCluster(commandExecutor("on"), commandExecutor("off")), ]), ]))) .start() diff --git a/src/interaction/cluster/DescriptorCluster.ts b/src/interaction/cluster/DescriptorCluster.ts new file mode 100644 index 00000000..82d87e9a --- /dev/null +++ b/src/interaction/cluster/DescriptorCluster.ts @@ -0,0 +1,34 @@ +/** + * @license + * Copyright 2022 Marco Fucci di Napoli (mfucci@gmail.com) + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ArrayT, Field, ObjectT, UnsignedIntT } from "../../codec/TlvObjectCodec"; +import { Attribute } from "../model/Attribute"; +import { Cluster } from "../model/Cluster"; +import { Endpoint } from "../model/Endpoint"; + +const CLUSTER_ID = 0x1d; + +export class DescriptorCluster extends Cluster { + constructor(endpoint: Endpoint, allEndpoints: Endpoint[]) { + super( + CLUSTER_ID, + "Descriptor", + [], + [ + new Attribute(0, "DeviceList", ArrayT(ObjectT({ + type: Field(0, UnsignedIntT), + revision: Field(1, UnsignedIntT), + })), [{ + type: endpoint.type, + revision: 1, + }]), + new Attribute(1, "ServerList", ArrayT(UnsignedIntT), [CLUSTER_ID, ...endpoint.getClusterIds()]), + new Attribute(3, "ClientList", ArrayT(UnsignedIntT), []), + new Attribute(4, "PartsList", ArrayT(UnsignedIntT), endpoint.id === 0 ? allEndpoints.map(endpoint => endpoint.id).filter(endpointId => endpointId !== 0) : []), + ], + ); + } +} diff --git a/src/interaction/model/Device.ts b/src/interaction/model/Device.ts index 6991c9e0..42d50cff 100644 --- a/src/interaction/model/Device.ts +++ b/src/interaction/model/Device.ts @@ -24,7 +24,10 @@ export class Device { private readonly endpointsMap = new Map(); constructor(endpoints: Endpoint[]) { - endpoints.forEach(endpoint => this.endpointsMap.set(endpoint.id, endpoint)); + endpoints.forEach(endpoint => { + endpoint.addDescriptorCluster(endpoints); + this.endpointsMap.set(endpoint.id, endpoint); + }); } getAttributeValues({endpointId, clusterId, attributeId}: AttributePath) { diff --git a/src/interaction/model/Endpoint.ts b/src/interaction/model/Endpoint.ts index 990a5c8b..6f527a7d 100644 --- a/src/interaction/model/Endpoint.ts +++ b/src/interaction/model/Endpoint.ts @@ -6,6 +6,7 @@ import { Element } from "../../codec/TlvCodec"; import { Session } from "../../session/Session"; +import { DescriptorCluster } from "../cluster/DescriptorCluster"; import { Cluster } from "./Cluster"; export class Endpoint { @@ -14,11 +15,17 @@ export class Endpoint { constructor( readonly id: number, readonly name: string, + readonly type: number, clusters: Cluster[], ) { clusters.forEach(cluster => this.clustersMap.set(cluster.id, cluster)); } + addDescriptorCluster(endpoints: Endpoint[]) { + const descriptorCluster = new DescriptorCluster(this, endpoints); + this.clustersMap.set(descriptorCluster.id, descriptorCluster); + } + getAttributeValue(clusterId?: number, attributeId?: number) { // If clusterId is not provided, iterate over all clusters var clusterIds = (clusterId === undefined) ? [...this.clustersMap.keys()] : [ clusterId ]; @@ -30,6 +37,10 @@ export class Endpoint { }) } + getClusterIds() { + return [...this.clustersMap.keys()]; + } + async invoke(session: Session, clusterId: number, commandId: number, args: Element) { return this.clustersMap.get(clusterId)?.invoke(session, commandId, args); } From 705da79c09a4e61499cd80cb7cee7d09b57a07ea Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Tue, 30 Aug 2022 07:30:04 -1000 Subject: [PATCH 02/10] Handle MDNS requests from localhost --- src/util/Network.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/Network.ts b/src/util/Network.ts index 6d14283b..aa086ed3 100644 --- a/src/util/Network.ts +++ b/src/util/Network.ts @@ -20,8 +20,8 @@ export function getIpMacAddresses(): {ip: string, mac: string}[] { const interfaces = networkInterfaces(); for (const name in interfaces) { const netInterfaces = interfaces[name] as NetworkInterfaceInfo[]; - for (const {internal, family, mac, address} of netInterfaces) { - if (internal || family !== "IPv4") continue; + for (const {family, mac, address} of netInterfaces) { + if (family !== "IPv4") continue; result.push({ip: address, mac}); } } @@ -33,8 +33,8 @@ export function getIpMacOnInterface(remoteAddress: string): {ip: string, mac: st const interfaces = networkInterfaces(); for (const name in interfaces) { const netInterfaces = interfaces[name] as NetworkInterfaceInfo[]; - for (const {internal, family, mac, address, netmask} of netInterfaces) { - if (internal || family !== "IPv4") continue; + for (const {family, mac, address, netmask} of netInterfaces) { + if (family !== "IPv4") continue; const netmaskNumber = ipToNumber(netmask); const ipNumber = ipToNumber(address); if ((ipNumber & netmaskNumber) !== (remoteAddressNumber & netmaskNumber)) continue; From bbbb030296ed97fe34a94a06195a4808269e95c1 Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Tue, 30 Aug 2022 12:44:22 -1000 Subject: [PATCH 03/10] Placeholder for subscribe --- src/interaction/InteractionMessages.ts | 47 +++++++++++++++++++++---- src/interaction/InteractionMessenger.ts | 14 ++++++-- src/interaction/InteractionProtocol.ts | 15 +++++++- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/interaction/InteractionMessages.ts b/src/interaction/InteractionMessages.ts index 8008bb90..1bbd130e 100644 --- a/src/interaction/InteractionMessages.ts +++ b/src/interaction/InteractionMessages.ts @@ -5,14 +5,16 @@ */ import { TlvType } from "../codec/TlvCodec"; -import { AnyT, ArrayT, BooleanT, Field, ObjectT, OptionalField, UnsignedIntT } from "../codec/TlvObjectCodec"; +import { AnyT, ArrayT, BooleanT, Field, ObjectT, OptionalField, UnsignedIntT, UnsignedLongT } from "../codec/TlvObjectCodec"; + +const AttributePathT = ObjectT({ + endpointId: OptionalField(2, UnsignedIntT), + clusterId: OptionalField(3, UnsignedIntT), + attributeId: OptionalField(4, UnsignedIntT), +}, TlvType.List); export const ReadRequestT = ObjectT({ - attributes: Field(0, ArrayT(ObjectT({ - endpointId: OptionalField(2, UnsignedIntT), - clusterId: OptionalField(3, UnsignedIntT), - attributeId: OptionalField(4, UnsignedIntT), - }, TlvType.List))), + attributes: Field(0, ArrayT(AttributePathT)), isFabricFiltered: Field(3, BooleanT), interactionModelRevision: Field(0xFF, UnsignedIntT), }); @@ -33,6 +35,39 @@ export const ReadResponseT = ObjectT({ interactionModelRevision: Field(0xFF, UnsignedIntT), }); +export const SubscribeRequestT = ObjectT({ + keepSubscriptions: Field(0, BooleanT), + minIntervalFloorSeconds: Field(1, UnsignedIntT), + maxIntervalCeilingSeconds: Field(2, UnsignedIntT), + attributeRequests: OptionalField(3, ArrayT(AttributePathT)), + eventRequests: OptionalField(4, ArrayT(ObjectT({ + node: Field(0, UnsignedIntT), + endpoint: Field(1, UnsignedIntT), + cluster: Field(2, UnsignedIntT), + event: Field(3, UnsignedIntT), + isUrgent: Field(4, BooleanT), + }, TlvType.List))), + eventFilters: OptionalField(5, ArrayT(ObjectT({ + node: Field(0, UnsignedIntT), + eventMin: Field(1, UnsignedLongT), + }, TlvType.List))), + isFabricFiltered: Field(7, BooleanT), + dataVersionFilters: OptionalField(8, ArrayT(ObjectT({ + path: Field(0, ObjectT({ + node: Field(0, UnsignedIntT), + endpoint: Field(1, UnsignedIntT), + cluster: Field(2, UnsignedIntT), + }, TlvType.List)), + dataVersion: Field(1, UnsignedIntT), + }))), +}); + +export const SubscribeResponseT = ObjectT({ + subscriptionId: Field(0, UnsignedIntT), + minIntervalFloorSeconds: Field(1, UnsignedIntT), + maxIntervalCeilingSeconds: Field(2, UnsignedIntT), +}); + export const InvokeRequestT = ObjectT({ suppressResponse: Field(0, BooleanT), timedRequest: Field(1, BooleanT), diff --git a/src/interaction/InteractionMessenger.ts b/src/interaction/InteractionMessenger.ts index a5e3161b..e7396558 100644 --- a/src/interaction/InteractionMessenger.ts +++ b/src/interaction/InteractionMessenger.ts @@ -6,7 +6,7 @@ import { JsType, TlvObjectCodec } from "../codec/TlvObjectCodec"; import { MessageExchange } from "../server/MessageExchange"; -import { InvokeRequestT, InvokeResponseT, ReadRequestT, ReadResponseT } from "./InteractionMessages"; +import { InvokeRequestT, InvokeResponseT, ReadRequestT, ReadResponseT, SubscribeRequestT, SubscribeResponseT } from "./InteractionMessages"; export const enum MessageType { StatusResponse = 0x01, @@ -21,16 +21,19 @@ export const enum MessageType { TimedRequest = 0x0a, } -export type InvokeRequest = JsType; -export type InvokeResponse = JsType; export type ReadRequest = JsType; export type ReadResponse = JsType; +export type SubscribeRequest = JsType; +export type SubscribeResponse = JsType; +export type InvokeRequest = JsType; +export type InvokeResponse = JsType; export class InteractionMessenger { constructor( private readonly exchange: MessageExchange, private readonly handleReadRequest: (request: ReadRequest) => ReadResponse, + private readonly handleSubscribeRequest: (request: SubscribeRequest) => SubscribeResponse, private readonly handleInvokeRequest: (request: InvokeRequest) => Promise, ) {} @@ -42,6 +45,11 @@ export class InteractionMessenger { const readResponse = this.handleReadRequest(readRequest); this.exchange.send(MessageType.ReportData, TlvObjectCodec.encode(readResponse, ReadResponseT)); break; + case MessageType.SubscribeRequest: + const subscribeRequest = TlvObjectCodec.decode(message.payload, SubscribeRequestT); + const subscribeResponse = this.handleSubscribeRequest(subscribeRequest); + this.exchange.send(MessageType.ReportData, TlvObjectCodec.encode(subscribeResponse, SubscribeResponseT)); + break; case MessageType.InvokeCommandRequest: const invokeRequest = TlvObjectCodec.decode(message.payload, InvokeRequestT); const invokeResponse = await this.handleInvokeRequest(invokeRequest); diff --git a/src/interaction/InteractionProtocol.ts b/src/interaction/InteractionProtocol.ts index 79d4b7af..75460c1d 100644 --- a/src/interaction/InteractionProtocol.ts +++ b/src/interaction/InteractionProtocol.ts @@ -7,7 +7,7 @@ import { Device } from "./model/Device"; import { ProtocolHandler } from "../server/MatterServer"; import { MessageExchange } from "../server/MessageExchange"; -import { InteractionMessenger, InvokeRequest, InvokeResponse, ReadRequest, ReadResponse } from "./InteractionMessenger"; +import { InteractionMessenger, InvokeRequest, InvokeResponse, ReadRequest, ReadResponse, SubscribeRequest, SubscribeResponse } from "./InteractionMessenger"; export class InteractionProtocol implements ProtocolHandler { constructor( @@ -18,6 +18,7 @@ export class InteractionProtocol implements ProtocolHandler { const messenger = new InteractionMessenger( exchange, readRequest => this.handleReadRequest(exchange, readRequest), + subscribeRequest => this.handleSubscribeRequest(exchange, subscribeRequest), invokeRequest => this.handleInvokeRequest(exchange, invokeRequest), ); await messenger.handleRequest(); @@ -33,6 +34,18 @@ export class InteractionProtocol implements ProtocolHandler { }; } + handleSubscribeRequest(exchange: MessageExchange, { minIntervalFloorSeconds, maxIntervalCeilingSeconds }: SubscribeRequest): SubscribeResponse { + console.log(`Received subscribe request from ${exchange.channel.getName()}`); + + // TODO: implement this + + return { + subscriptionId: 0, + minIntervalFloorSeconds, + maxIntervalCeilingSeconds, + }; + } + async handleInvokeRequest(exchange: MessageExchange, {invokes}: InvokeRequest): Promise { console.log(`Received invoke request from ${exchange.channel.getName()}: ${invokes.map(({path: {endpointId, clusterId, commandId}}) => `${endpointId}/${clusterId}/${commandId}`).join(", ")}`); From ae81095bbfeb7c9b4ee471114561a7bef44108f4 Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Tue, 30 Aug 2022 17:06:20 -1000 Subject: [PATCH 04/10] Fix unit tests and ensure to run them on PR --- .github/node.js.yml | 31 +++++++++++++++++++ src/Devices.ts | 4 +++ src/Main.ts | 5 +-- src/interaction/cluster/DescriptorCluster.ts | 2 +- src/interaction/model/Endpoint.ts | 3 +- test/codec/DnsTest.ts | 2 +- ...agerTest.ts => InteractionProtocolTest.ts} | 3 +- test/session/SecureSessionTest.ts | 3 +- test/tsconfig.json | 12 +++++++ 9 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 .github/node.js.yml create mode 100644 src/Devices.ts rename test/interaction/{InteractionManagerTest.ts => InteractionProtocolTest.ts} (96%) create mode 100644 test/tsconfig.json diff --git a/.github/node.js.yml b/.github/node.js.yml new file mode 100644 index 00000000..7b69169c --- /dev/null +++ b/.github/node.js.yml @@ -0,0 +1,31 @@ +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +name: Node.js CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x, 14.x, 16.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - run: npm ci + - run: npm run build --if-present + - run: npm test diff --git a/src/Devices.ts b/src/Devices.ts new file mode 100644 index 00000000..3b42475e --- /dev/null +++ b/src/Devices.ts @@ -0,0 +1,4 @@ +export const DEVICE = { + ROOT: { name: "MA-rootdevice", code: 0x0016 }, + ON_OFF_LIGHT: { name: "MA-onofflight", code: 0x0100 }, +} \ No newline at end of file diff --git a/src/Main.ts b/src/Main.ts index 1c467cd6..379109e9 100644 --- a/src/Main.ts +++ b/src/Main.ts @@ -20,6 +20,7 @@ import { GeneralCommissioningCluster } from "./interaction/cluster/GeneralCommis import { OperationalCredentialsCluster } from "./interaction/cluster/OperationalCredentialsCluster"; import { OnOffCluster } from "./interaction/cluster/OnOffCluster"; import { execSync } from "child_process"; +import { DEVICE } from "./Devices"; const commandArguments = process.argv.slice(2); @@ -63,12 +64,12 @@ class Main { new CasePairing(), )) .addProtocolHandler(Protocol.INTERACTION_MODEL, new InteractionProtocol(new Device([ - new Endpoint(0x00, "MA-rootdevice", 0x0016, [ + new Endpoint(0x00, DEVICE.ROOT, [ new BasicCluster({ vendorName, vendorId, productName, productId }), new GeneralCommissioningCluster(), new OperationalCredentialsCluster({devicePrivateKey: DevicePrivateKey, deviceCertificate: DeviceCertificate, deviceIntermediateCertificate: ProductIntermediateCertificate, certificateDeclaration: CertificateDeclaration}), ]), - new Endpoint(0x01, "MA-OnOff", 0x0100, [ + new Endpoint(0x01, DEVICE.ON_OFF_LIGHT, [ new OnOffCluster(commandExecutor("on"), commandExecutor("off")), ]), ]))) diff --git a/src/interaction/cluster/DescriptorCluster.ts b/src/interaction/cluster/DescriptorCluster.ts index 82d87e9a..b185ddb8 100644 --- a/src/interaction/cluster/DescriptorCluster.ts +++ b/src/interaction/cluster/DescriptorCluster.ts @@ -22,7 +22,7 @@ export class DescriptorCluster extends Cluster { type: Field(0, UnsignedIntT), revision: Field(1, UnsignedIntT), })), [{ - type: endpoint.type, + type: endpoint.device.code, revision: 1, }]), new Attribute(1, "ServerList", ArrayT(UnsignedIntT), [CLUSTER_ID, ...endpoint.getClusterIds()]), diff --git a/src/interaction/model/Endpoint.ts b/src/interaction/model/Endpoint.ts index 6f527a7d..7adef580 100644 --- a/src/interaction/model/Endpoint.ts +++ b/src/interaction/model/Endpoint.ts @@ -14,8 +14,7 @@ export class Endpoint { constructor( readonly id: number, - readonly name: string, - readonly type: number, + readonly device: {name: string, code: number}, clusters: Cluster[], ) { clusters.forEach(cluster => this.clustersMap.set(cluster.id, cluster)); diff --git a/test/codec/DnsTest.ts b/test/codec/DnsTest.ts index 2d1ac66f..6ee1d2ed 100644 --- a/test/codec/DnsTest.ts +++ b/test/codec/DnsTest.ts @@ -26,7 +26,7 @@ const DNS_RESPONSE: DnsMessage = { ], } -const RESULT = Buffer.from("000080000000000400000004095f7365727669636573075f646e732d7364045f756470056c6f63616c00000c0001000000780014075f6d6174746572045f746370056c6f63616c00095f7365727669636573075f646e732d7364045f756470056c6f63616c00000c000100000078002c125f4944353539414633363135343941394132045f737562075f6d6174746572045f746370056c6f63616c00075f6d6174746572045f746370056c6f63616c00000c000100000078003621443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c00125f4944353539414633363135343941394132045f737562075f6d6174746572045f746370056c6f63616c00000c000100000078003621443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c001044434136333241303239354630303030056c6f63616c0000010001000000780004c0a8c82e1044434136333241303239354630303030056c6f63616c00001c0001000000780010fe800000000000009580b7336f549f4321443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c000021000100000078001e0000000015a41044434136333241303239354630303030056c6f63616c0021443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c0000100001000000780015085349493d35303030075341493d33303003543d31", "hex"); +const RESULT = Buffer.from("000084000000000400000004095f7365727669636573075f646e732d7364045f756470056c6f63616c00000c0001000000780014075f6d6174746572045f746370056c6f63616c00095f7365727669636573075f646e732d7364045f756470056c6f63616c00000c000100000078002c125f4944353539414633363135343941394132045f737562075f6d6174746572045f746370056c6f63616c00075f6d6174746572045f746370056c6f63616c00000c000100000078003621443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c00125f4944353539414633363135343941394132045f737562075f6d6174746572045f746370056c6f63616c00000c000100000078003621443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c001044434136333241303239354630303030056c6f63616c0000010001000000780004c0a8c82e1044434136333241303239354630303030056c6f63616c00001c0001000000780010fe800000000000009580b7336f549f4321443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c000021000100000078001e0000000015a41044434136333241303239354630303030056c6f63616c0021443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c0000100001000000780015085349493d35303030075341493d33303003543d31", "hex"); const ENCODED = Buffer.from("000000000003000200000001026c62075f646e732d7364045f756470056c6f63616c00000c00010f5f636f6d70616e696f6e2d6c696e6b045f746370c01c000c0001085f686f6d656b6974c037000c0001c027000c000100001194000a074b69746368656ec027c042000c00010000119400272441423645433741312d333837422d353235332d413835342d394441353236333535363746c04200002905a00000119400120004000e0099929387b033db4275a6a31b2d", "hex"); diff --git a/test/interaction/InteractionManagerTest.ts b/test/interaction/InteractionProtocolTest.ts similarity index 96% rename from test/interaction/InteractionManagerTest.ts rename to test/interaction/InteractionProtocolTest.ts index 574dad1f..aede8d1e 100644 --- a/test/interaction/InteractionManagerTest.ts +++ b/test/interaction/InteractionProtocolTest.ts @@ -13,6 +13,7 @@ import { ReadRequest, ReadResponse } from "../../src/interaction/InteractionMess import { Device } from "../../src/interaction/model/Device"; import { Endpoint } from "../../src/interaction/model/Endpoint"; import { MessageExchange } from "../../src/server/MessageExchange"; +import { DEVICE } from "../../src/Devices"; const READ_REQUEST: ReadRequest = { interactionModelRevision: 1, @@ -61,7 +62,7 @@ describe("InteractionProtocol", () => { context("handleReadRequest", () => { it("replies with attribute values", () => { const interactionProtocol = new InteractionProtocol(new Device([ - new Endpoint(0, "root", [ + new Endpoint(0, DEVICE.ROOT, [ new BasicCluster({vendorName: "vendor", vendorId: 1, productName: "product", productId: 2}), ]) ])); diff --git a/test/session/SecureSessionTest.ts b/test/session/SecureSessionTest.ts index 27b7b337..eeea11aa 100644 --- a/test/session/SecureSessionTest.ts +++ b/test/session/SecureSessionTest.ts @@ -6,6 +6,7 @@ import assert from "assert"; import { Message, MessageCodec, SessionType } from "../../src/codec/MessageCodec"; +import { MatterServer } from "../../src/server/MatterServer"; import { SecureSession } from "../../src/session/SecureSession"; import { UNDEFINED_NODE_ID } from "../../src/session/SessionManager"; @@ -33,7 +34,7 @@ const MESSAGE: Message = { const ENCRYPTED_BYTES = Buffer.from("1f9c4e278a2e2a755ebb4fcb9478211efb09aa9518fcafb56d74f135544636037c16fb6b62347794da0c5bde142e1a8b1cc96575e9e55471c08b58f7640b7d7f4173c8ff967c39e9961f30a29cb1f64f68df4b5bc1e742587f778eeb9ec586c162ff384558596792a2c1e43c150cd0e9ec1484c50950f17cd6c084d07caed94ce45c20004210cbde48da44ebcf7d931657f03e07e3ea29ae41868b804bf39e628323cd025507773f07268301aa1e77a82927fce041241839cee4114f6307b6befe3befde87a2d3f13eeef96b27b36e788d907b44bef2d195aa802692f4f12acc015aede3cd29da272d1e4b7f3f59683d25bf08f0e29fba2a8a9b", "hex"); describe("SecureSession", () => { - const secureSession = new SecureSession(1, UNDEFINED_NODE_ID, UNDEFINED_NODE_ID, 0x8d4b, Buffer.alloc(0), DECRYPT_KEY, ENCRYPT_KEY, Buffer.alloc(0)); + const secureSession = new SecureSession({} as MatterServer, 1, UNDEFINED_NODE_ID, UNDEFINED_NODE_ID, 0x8d4b, Buffer.alloc(0), DECRYPT_KEY, ENCRYPT_KEY, Buffer.alloc(0)); context("decrypt", () => { it("decrypts a message", () => { diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 00000000..3cfe85d5 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "CommonJS", + "noImplicitAny": true, + "removeComments": true, + "esModuleInterop": true, + "declaration": true, + "strict": true + }, + "include": ["**/*.ts", "../src/**/*.ts"], +} From d09308624446e3fb6a12d75875fd7565fa281034 Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Thu, 1 Sep 2022 10:50:35 -1000 Subject: [PATCH 05/10] Fix message type for SubscribeResponse --- src/interaction/InteractionMessenger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interaction/InteractionMessenger.ts b/src/interaction/InteractionMessenger.ts index e7396558..ba80125c 100644 --- a/src/interaction/InteractionMessenger.ts +++ b/src/interaction/InteractionMessenger.ts @@ -48,7 +48,7 @@ export class InteractionMessenger { case MessageType.SubscribeRequest: const subscribeRequest = TlvObjectCodec.decode(message.payload, SubscribeRequestT); const subscribeResponse = this.handleSubscribeRequest(subscribeRequest); - this.exchange.send(MessageType.ReportData, TlvObjectCodec.encode(subscribeResponse, SubscribeResponseT)); + this.exchange.send(MessageType.SubscribeResponse, TlvObjectCodec.encode(subscribeResponse, SubscribeResponseT)); break; case MessageType.InvokeCommandRequest: const invokeRequest = TlvObjectCodec.decode(message.payload, InvokeRequestT); From 136e0499eb6fb63771fea89f1e7f14e1d542fe4c Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Fri, 26 Aug 2022 14:29:04 -1000 Subject: [PATCH 06/10] Add Descriptor cluster --- src/Main.ts | 6 ++-- src/interaction/cluster/DescriptorCluster.ts | 34 ++++++++++++++++++++ src/interaction/model/Device.ts | 5 ++- src/interaction/model/Endpoint.ts | 11 +++++++ 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 src/interaction/cluster/DescriptorCluster.ts diff --git a/src/Main.ts b/src/Main.ts index 3969c85b..1c467cd6 100644 --- a/src/Main.ts +++ b/src/Main.ts @@ -63,13 +63,13 @@ class Main { new CasePairing(), )) .addProtocolHandler(Protocol.INTERACTION_MODEL, new InteractionProtocol(new Device([ - new Endpoint(0x00, "MA-rootdevice", [ + new Endpoint(0x00, "MA-rootdevice", 0x0016, [ new BasicCluster({ vendorName, vendorId, productName, productId }), new GeneralCommissioningCluster(), new OperationalCredentialsCluster({devicePrivateKey: DevicePrivateKey, deviceCertificate: DeviceCertificate, deviceIntermediateCertificate: ProductIntermediateCertificate, certificateDeclaration: CertificateDeclaration}), ]), - new Endpoint(0x01, "MA-OnOff", [ - new OnOffCluster(commandExecutor("on"), commandExecutor("off")), + new Endpoint(0x01, "MA-OnOff", 0x0100, [ + new OnOffCluster(commandExecutor("on"), commandExecutor("off")), ]), ]))) .start() diff --git a/src/interaction/cluster/DescriptorCluster.ts b/src/interaction/cluster/DescriptorCluster.ts new file mode 100644 index 00000000..82d87e9a --- /dev/null +++ b/src/interaction/cluster/DescriptorCluster.ts @@ -0,0 +1,34 @@ +/** + * @license + * Copyright 2022 Marco Fucci di Napoli (mfucci@gmail.com) + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ArrayT, Field, ObjectT, UnsignedIntT } from "../../codec/TlvObjectCodec"; +import { Attribute } from "../model/Attribute"; +import { Cluster } from "../model/Cluster"; +import { Endpoint } from "../model/Endpoint"; + +const CLUSTER_ID = 0x1d; + +export class DescriptorCluster extends Cluster { + constructor(endpoint: Endpoint, allEndpoints: Endpoint[]) { + super( + CLUSTER_ID, + "Descriptor", + [], + [ + new Attribute(0, "DeviceList", ArrayT(ObjectT({ + type: Field(0, UnsignedIntT), + revision: Field(1, UnsignedIntT), + })), [{ + type: endpoint.type, + revision: 1, + }]), + new Attribute(1, "ServerList", ArrayT(UnsignedIntT), [CLUSTER_ID, ...endpoint.getClusterIds()]), + new Attribute(3, "ClientList", ArrayT(UnsignedIntT), []), + new Attribute(4, "PartsList", ArrayT(UnsignedIntT), endpoint.id === 0 ? allEndpoints.map(endpoint => endpoint.id).filter(endpointId => endpointId !== 0) : []), + ], + ); + } +} diff --git a/src/interaction/model/Device.ts b/src/interaction/model/Device.ts index 6991c9e0..42d50cff 100644 --- a/src/interaction/model/Device.ts +++ b/src/interaction/model/Device.ts @@ -24,7 +24,10 @@ export class Device { private readonly endpointsMap = new Map(); constructor(endpoints: Endpoint[]) { - endpoints.forEach(endpoint => this.endpointsMap.set(endpoint.id, endpoint)); + endpoints.forEach(endpoint => { + endpoint.addDescriptorCluster(endpoints); + this.endpointsMap.set(endpoint.id, endpoint); + }); } getAttributeValues({endpointId, clusterId, attributeId}: AttributePath) { diff --git a/src/interaction/model/Endpoint.ts b/src/interaction/model/Endpoint.ts index 990a5c8b..6f527a7d 100644 --- a/src/interaction/model/Endpoint.ts +++ b/src/interaction/model/Endpoint.ts @@ -6,6 +6,7 @@ import { Element } from "../../codec/TlvCodec"; import { Session } from "../../session/Session"; +import { DescriptorCluster } from "../cluster/DescriptorCluster"; import { Cluster } from "./Cluster"; export class Endpoint { @@ -14,11 +15,17 @@ export class Endpoint { constructor( readonly id: number, readonly name: string, + readonly type: number, clusters: Cluster[], ) { clusters.forEach(cluster => this.clustersMap.set(cluster.id, cluster)); } + addDescriptorCluster(endpoints: Endpoint[]) { + const descriptorCluster = new DescriptorCluster(this, endpoints); + this.clustersMap.set(descriptorCluster.id, descriptorCluster); + } + getAttributeValue(clusterId?: number, attributeId?: number) { // If clusterId is not provided, iterate over all clusters var clusterIds = (clusterId === undefined) ? [...this.clustersMap.keys()] : [ clusterId ]; @@ -30,6 +37,10 @@ export class Endpoint { }) } + getClusterIds() { + return [...this.clustersMap.keys()]; + } + async invoke(session: Session, clusterId: number, commandId: number, args: Element) { return this.clustersMap.get(clusterId)?.invoke(session, commandId, args); } From a999ce59df586814a990de62c00cb0436e04e056 Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Tue, 30 Aug 2022 07:30:04 -1000 Subject: [PATCH 07/10] Handle MDNS requests from localhost --- src/util/Network.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/Network.ts b/src/util/Network.ts index 6d14283b..aa086ed3 100644 --- a/src/util/Network.ts +++ b/src/util/Network.ts @@ -20,8 +20,8 @@ export function getIpMacAddresses(): {ip: string, mac: string}[] { const interfaces = networkInterfaces(); for (const name in interfaces) { const netInterfaces = interfaces[name] as NetworkInterfaceInfo[]; - for (const {internal, family, mac, address} of netInterfaces) { - if (internal || family !== "IPv4") continue; + for (const {family, mac, address} of netInterfaces) { + if (family !== "IPv4") continue; result.push({ip: address, mac}); } } @@ -33,8 +33,8 @@ export function getIpMacOnInterface(remoteAddress: string): {ip: string, mac: st const interfaces = networkInterfaces(); for (const name in interfaces) { const netInterfaces = interfaces[name] as NetworkInterfaceInfo[]; - for (const {internal, family, mac, address, netmask} of netInterfaces) { - if (internal || family !== "IPv4") continue; + for (const {family, mac, address, netmask} of netInterfaces) { + if (family !== "IPv4") continue; const netmaskNumber = ipToNumber(netmask); const ipNumber = ipToNumber(address); if ((ipNumber & netmaskNumber) !== (remoteAddressNumber & netmaskNumber)) continue; From 2c3be289bc508b8deb7662adb28720a7a6f4c700 Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Tue, 30 Aug 2022 12:44:22 -1000 Subject: [PATCH 08/10] Placeholder for subscribe --- src/interaction/InteractionMessages.ts | 47 +++++++++++++++++++++---- src/interaction/InteractionMessenger.ts | 14 ++++++-- src/interaction/InteractionProtocol.ts | 15 +++++++- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/interaction/InteractionMessages.ts b/src/interaction/InteractionMessages.ts index 8008bb90..1bbd130e 100644 --- a/src/interaction/InteractionMessages.ts +++ b/src/interaction/InteractionMessages.ts @@ -5,14 +5,16 @@ */ import { TlvType } from "../codec/TlvCodec"; -import { AnyT, ArrayT, BooleanT, Field, ObjectT, OptionalField, UnsignedIntT } from "../codec/TlvObjectCodec"; +import { AnyT, ArrayT, BooleanT, Field, ObjectT, OptionalField, UnsignedIntT, UnsignedLongT } from "../codec/TlvObjectCodec"; + +const AttributePathT = ObjectT({ + endpointId: OptionalField(2, UnsignedIntT), + clusterId: OptionalField(3, UnsignedIntT), + attributeId: OptionalField(4, UnsignedIntT), +}, TlvType.List); export const ReadRequestT = ObjectT({ - attributes: Field(0, ArrayT(ObjectT({ - endpointId: OptionalField(2, UnsignedIntT), - clusterId: OptionalField(3, UnsignedIntT), - attributeId: OptionalField(4, UnsignedIntT), - }, TlvType.List))), + attributes: Field(0, ArrayT(AttributePathT)), isFabricFiltered: Field(3, BooleanT), interactionModelRevision: Field(0xFF, UnsignedIntT), }); @@ -33,6 +35,39 @@ export const ReadResponseT = ObjectT({ interactionModelRevision: Field(0xFF, UnsignedIntT), }); +export const SubscribeRequestT = ObjectT({ + keepSubscriptions: Field(0, BooleanT), + minIntervalFloorSeconds: Field(1, UnsignedIntT), + maxIntervalCeilingSeconds: Field(2, UnsignedIntT), + attributeRequests: OptionalField(3, ArrayT(AttributePathT)), + eventRequests: OptionalField(4, ArrayT(ObjectT({ + node: Field(0, UnsignedIntT), + endpoint: Field(1, UnsignedIntT), + cluster: Field(2, UnsignedIntT), + event: Field(3, UnsignedIntT), + isUrgent: Field(4, BooleanT), + }, TlvType.List))), + eventFilters: OptionalField(5, ArrayT(ObjectT({ + node: Field(0, UnsignedIntT), + eventMin: Field(1, UnsignedLongT), + }, TlvType.List))), + isFabricFiltered: Field(7, BooleanT), + dataVersionFilters: OptionalField(8, ArrayT(ObjectT({ + path: Field(0, ObjectT({ + node: Field(0, UnsignedIntT), + endpoint: Field(1, UnsignedIntT), + cluster: Field(2, UnsignedIntT), + }, TlvType.List)), + dataVersion: Field(1, UnsignedIntT), + }))), +}); + +export const SubscribeResponseT = ObjectT({ + subscriptionId: Field(0, UnsignedIntT), + minIntervalFloorSeconds: Field(1, UnsignedIntT), + maxIntervalCeilingSeconds: Field(2, UnsignedIntT), +}); + export const InvokeRequestT = ObjectT({ suppressResponse: Field(0, BooleanT), timedRequest: Field(1, BooleanT), diff --git a/src/interaction/InteractionMessenger.ts b/src/interaction/InteractionMessenger.ts index a5e3161b..e7396558 100644 --- a/src/interaction/InteractionMessenger.ts +++ b/src/interaction/InteractionMessenger.ts @@ -6,7 +6,7 @@ import { JsType, TlvObjectCodec } from "../codec/TlvObjectCodec"; import { MessageExchange } from "../server/MessageExchange"; -import { InvokeRequestT, InvokeResponseT, ReadRequestT, ReadResponseT } from "./InteractionMessages"; +import { InvokeRequestT, InvokeResponseT, ReadRequestT, ReadResponseT, SubscribeRequestT, SubscribeResponseT } from "./InteractionMessages"; export const enum MessageType { StatusResponse = 0x01, @@ -21,16 +21,19 @@ export const enum MessageType { TimedRequest = 0x0a, } -export type InvokeRequest = JsType; -export type InvokeResponse = JsType; export type ReadRequest = JsType; export type ReadResponse = JsType; +export type SubscribeRequest = JsType; +export type SubscribeResponse = JsType; +export type InvokeRequest = JsType; +export type InvokeResponse = JsType; export class InteractionMessenger { constructor( private readonly exchange: MessageExchange, private readonly handleReadRequest: (request: ReadRequest) => ReadResponse, + private readonly handleSubscribeRequest: (request: SubscribeRequest) => SubscribeResponse, private readonly handleInvokeRequest: (request: InvokeRequest) => Promise, ) {} @@ -42,6 +45,11 @@ export class InteractionMessenger { const readResponse = this.handleReadRequest(readRequest); this.exchange.send(MessageType.ReportData, TlvObjectCodec.encode(readResponse, ReadResponseT)); break; + case MessageType.SubscribeRequest: + const subscribeRequest = TlvObjectCodec.decode(message.payload, SubscribeRequestT); + const subscribeResponse = this.handleSubscribeRequest(subscribeRequest); + this.exchange.send(MessageType.ReportData, TlvObjectCodec.encode(subscribeResponse, SubscribeResponseT)); + break; case MessageType.InvokeCommandRequest: const invokeRequest = TlvObjectCodec.decode(message.payload, InvokeRequestT); const invokeResponse = await this.handleInvokeRequest(invokeRequest); diff --git a/src/interaction/InteractionProtocol.ts b/src/interaction/InteractionProtocol.ts index 79d4b7af..75460c1d 100644 --- a/src/interaction/InteractionProtocol.ts +++ b/src/interaction/InteractionProtocol.ts @@ -7,7 +7,7 @@ import { Device } from "./model/Device"; import { ProtocolHandler } from "../server/MatterServer"; import { MessageExchange } from "../server/MessageExchange"; -import { InteractionMessenger, InvokeRequest, InvokeResponse, ReadRequest, ReadResponse } from "./InteractionMessenger"; +import { InteractionMessenger, InvokeRequest, InvokeResponse, ReadRequest, ReadResponse, SubscribeRequest, SubscribeResponse } from "./InteractionMessenger"; export class InteractionProtocol implements ProtocolHandler { constructor( @@ -18,6 +18,7 @@ export class InteractionProtocol implements ProtocolHandler { const messenger = new InteractionMessenger( exchange, readRequest => this.handleReadRequest(exchange, readRequest), + subscribeRequest => this.handleSubscribeRequest(exchange, subscribeRequest), invokeRequest => this.handleInvokeRequest(exchange, invokeRequest), ); await messenger.handleRequest(); @@ -33,6 +34,18 @@ export class InteractionProtocol implements ProtocolHandler { }; } + handleSubscribeRequest(exchange: MessageExchange, { minIntervalFloorSeconds, maxIntervalCeilingSeconds }: SubscribeRequest): SubscribeResponse { + console.log(`Received subscribe request from ${exchange.channel.getName()}`); + + // TODO: implement this + + return { + subscriptionId: 0, + minIntervalFloorSeconds, + maxIntervalCeilingSeconds, + }; + } + async handleInvokeRequest(exchange: MessageExchange, {invokes}: InvokeRequest): Promise { console.log(`Received invoke request from ${exchange.channel.getName()}: ${invokes.map(({path: {endpointId, clusterId, commandId}}) => `${endpointId}/${clusterId}/${commandId}`).join(", ")}`); From 725d5397844b6b1c3c62f638e4aeee250cdd1c11 Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Thu, 1 Sep 2022 10:50:35 -1000 Subject: [PATCH 09/10] Fix message type for SubscribeResponse --- src/interaction/InteractionMessenger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interaction/InteractionMessenger.ts b/src/interaction/InteractionMessenger.ts index e7396558..ba80125c 100644 --- a/src/interaction/InteractionMessenger.ts +++ b/src/interaction/InteractionMessenger.ts @@ -48,7 +48,7 @@ export class InteractionMessenger { case MessageType.SubscribeRequest: const subscribeRequest = TlvObjectCodec.decode(message.payload, SubscribeRequestT); const subscribeResponse = this.handleSubscribeRequest(subscribeRequest); - this.exchange.send(MessageType.ReportData, TlvObjectCodec.encode(subscribeResponse, SubscribeResponseT)); + this.exchange.send(MessageType.SubscribeResponse, TlvObjectCodec.encode(subscribeResponse, SubscribeResponseT)); break; case MessageType.InvokeCommandRequest: const invokeRequest = TlvObjectCodec.decode(message.payload, InvokeRequestT); From 3351a738d0619ea47151af3fe2ae62c76766dbc9 Mon Sep 17 00:00:00 2001 From: Marco Fucci di Napoli Date: Tue, 30 Aug 2022 17:06:20 -1000 Subject: [PATCH 10/10] Fix unit tests and ensure to run them on PR --- .github/node.js.yml | 31 +++++++++++++++++++ src/Devices.ts | 4 +++ src/Main.ts | 5 +-- src/interaction/cluster/DescriptorCluster.ts | 2 +- src/interaction/model/Endpoint.ts | 3 +- test/codec/DnsTest.ts | 2 +- ...agerTest.ts => InteractionProtocolTest.ts} | 3 +- test/session/SecureSessionTest.ts | 3 +- test/tsconfig.json | 12 +++++++ 9 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 .github/node.js.yml create mode 100644 src/Devices.ts rename test/interaction/{InteractionManagerTest.ts => InteractionProtocolTest.ts} (96%) create mode 100644 test/tsconfig.json diff --git a/.github/node.js.yml b/.github/node.js.yml new file mode 100644 index 00000000..7b69169c --- /dev/null +++ b/.github/node.js.yml @@ -0,0 +1,31 @@ +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +name: Node.js CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x, 14.x, 16.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - run: npm ci + - run: npm run build --if-present + - run: npm test diff --git a/src/Devices.ts b/src/Devices.ts new file mode 100644 index 00000000..3b42475e --- /dev/null +++ b/src/Devices.ts @@ -0,0 +1,4 @@ +export const DEVICE = { + ROOT: { name: "MA-rootdevice", code: 0x0016 }, + ON_OFF_LIGHT: { name: "MA-onofflight", code: 0x0100 }, +} \ No newline at end of file diff --git a/src/Main.ts b/src/Main.ts index 1c467cd6..379109e9 100644 --- a/src/Main.ts +++ b/src/Main.ts @@ -20,6 +20,7 @@ import { GeneralCommissioningCluster } from "./interaction/cluster/GeneralCommis import { OperationalCredentialsCluster } from "./interaction/cluster/OperationalCredentialsCluster"; import { OnOffCluster } from "./interaction/cluster/OnOffCluster"; import { execSync } from "child_process"; +import { DEVICE } from "./Devices"; const commandArguments = process.argv.slice(2); @@ -63,12 +64,12 @@ class Main { new CasePairing(), )) .addProtocolHandler(Protocol.INTERACTION_MODEL, new InteractionProtocol(new Device([ - new Endpoint(0x00, "MA-rootdevice", 0x0016, [ + new Endpoint(0x00, DEVICE.ROOT, [ new BasicCluster({ vendorName, vendorId, productName, productId }), new GeneralCommissioningCluster(), new OperationalCredentialsCluster({devicePrivateKey: DevicePrivateKey, deviceCertificate: DeviceCertificate, deviceIntermediateCertificate: ProductIntermediateCertificate, certificateDeclaration: CertificateDeclaration}), ]), - new Endpoint(0x01, "MA-OnOff", 0x0100, [ + new Endpoint(0x01, DEVICE.ON_OFF_LIGHT, [ new OnOffCluster(commandExecutor("on"), commandExecutor("off")), ]), ]))) diff --git a/src/interaction/cluster/DescriptorCluster.ts b/src/interaction/cluster/DescriptorCluster.ts index 82d87e9a..b185ddb8 100644 --- a/src/interaction/cluster/DescriptorCluster.ts +++ b/src/interaction/cluster/DescriptorCluster.ts @@ -22,7 +22,7 @@ export class DescriptorCluster extends Cluster { type: Field(0, UnsignedIntT), revision: Field(1, UnsignedIntT), })), [{ - type: endpoint.type, + type: endpoint.device.code, revision: 1, }]), new Attribute(1, "ServerList", ArrayT(UnsignedIntT), [CLUSTER_ID, ...endpoint.getClusterIds()]), diff --git a/src/interaction/model/Endpoint.ts b/src/interaction/model/Endpoint.ts index 6f527a7d..7adef580 100644 --- a/src/interaction/model/Endpoint.ts +++ b/src/interaction/model/Endpoint.ts @@ -14,8 +14,7 @@ export class Endpoint { constructor( readonly id: number, - readonly name: string, - readonly type: number, + readonly device: {name: string, code: number}, clusters: Cluster[], ) { clusters.forEach(cluster => this.clustersMap.set(cluster.id, cluster)); diff --git a/test/codec/DnsTest.ts b/test/codec/DnsTest.ts index 2d1ac66f..6ee1d2ed 100644 --- a/test/codec/DnsTest.ts +++ b/test/codec/DnsTest.ts @@ -26,7 +26,7 @@ const DNS_RESPONSE: DnsMessage = { ], } -const RESULT = Buffer.from("000080000000000400000004095f7365727669636573075f646e732d7364045f756470056c6f63616c00000c0001000000780014075f6d6174746572045f746370056c6f63616c00095f7365727669636573075f646e732d7364045f756470056c6f63616c00000c000100000078002c125f4944353539414633363135343941394132045f737562075f6d6174746572045f746370056c6f63616c00075f6d6174746572045f746370056c6f63616c00000c000100000078003621443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c00125f4944353539414633363135343941394132045f737562075f6d6174746572045f746370056c6f63616c00000c000100000078003621443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c001044434136333241303239354630303030056c6f63616c0000010001000000780004c0a8c82e1044434136333241303239354630303030056c6f63616c00001c0001000000780010fe800000000000009580b7336f549f4321443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c000021000100000078001e0000000015a41044434136333241303239354630303030056c6f63616c0021443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c0000100001000000780015085349493d35303030075341493d33303003543d31", "hex"); +const RESULT = Buffer.from("000084000000000400000004095f7365727669636573075f646e732d7364045f756470056c6f63616c00000c0001000000780014075f6d6174746572045f746370056c6f63616c00095f7365727669636573075f646e732d7364045f756470056c6f63616c00000c000100000078002c125f4944353539414633363135343941394132045f737562075f6d6174746572045f746370056c6f63616c00075f6d6174746572045f746370056c6f63616c00000c000100000078003621443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c00125f4944353539414633363135343941394132045f737562075f6d6174746572045f746370056c6f63616c00000c000100000078003621443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c001044434136333241303239354630303030056c6f63616c0000010001000000780004c0a8c82e1044434136333241303239354630303030056c6f63616c00001c0001000000780010fe800000000000009580b7336f549f4321443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c000021000100000078001e0000000015a41044434136333241303239354630303030056c6f63616c0021443535394146333631353439413941322d30303030303030303030303030303039075f6d6174746572045f746370056c6f63616c0000100001000000780015085349493d35303030075341493d33303003543d31", "hex"); const ENCODED = Buffer.from("000000000003000200000001026c62075f646e732d7364045f756470056c6f63616c00000c00010f5f636f6d70616e696f6e2d6c696e6b045f746370c01c000c0001085f686f6d656b6974c037000c0001c027000c000100001194000a074b69746368656ec027c042000c00010000119400272441423645433741312d333837422d353235332d413835342d394441353236333535363746c04200002905a00000119400120004000e0099929387b033db4275a6a31b2d", "hex"); diff --git a/test/interaction/InteractionManagerTest.ts b/test/interaction/InteractionProtocolTest.ts similarity index 96% rename from test/interaction/InteractionManagerTest.ts rename to test/interaction/InteractionProtocolTest.ts index 574dad1f..aede8d1e 100644 --- a/test/interaction/InteractionManagerTest.ts +++ b/test/interaction/InteractionProtocolTest.ts @@ -13,6 +13,7 @@ import { ReadRequest, ReadResponse } from "../../src/interaction/InteractionMess import { Device } from "../../src/interaction/model/Device"; import { Endpoint } from "../../src/interaction/model/Endpoint"; import { MessageExchange } from "../../src/server/MessageExchange"; +import { DEVICE } from "../../src/Devices"; const READ_REQUEST: ReadRequest = { interactionModelRevision: 1, @@ -61,7 +62,7 @@ describe("InteractionProtocol", () => { context("handleReadRequest", () => { it("replies with attribute values", () => { const interactionProtocol = new InteractionProtocol(new Device([ - new Endpoint(0, "root", [ + new Endpoint(0, DEVICE.ROOT, [ new BasicCluster({vendorName: "vendor", vendorId: 1, productName: "product", productId: 2}), ]) ])); diff --git a/test/session/SecureSessionTest.ts b/test/session/SecureSessionTest.ts index 27b7b337..eeea11aa 100644 --- a/test/session/SecureSessionTest.ts +++ b/test/session/SecureSessionTest.ts @@ -6,6 +6,7 @@ import assert from "assert"; import { Message, MessageCodec, SessionType } from "../../src/codec/MessageCodec"; +import { MatterServer } from "../../src/server/MatterServer"; import { SecureSession } from "../../src/session/SecureSession"; import { UNDEFINED_NODE_ID } from "../../src/session/SessionManager"; @@ -33,7 +34,7 @@ const MESSAGE: Message = { const ENCRYPTED_BYTES = Buffer.from("1f9c4e278a2e2a755ebb4fcb9478211efb09aa9518fcafb56d74f135544636037c16fb6b62347794da0c5bde142e1a8b1cc96575e9e55471c08b58f7640b7d7f4173c8ff967c39e9961f30a29cb1f64f68df4b5bc1e742587f778eeb9ec586c162ff384558596792a2c1e43c150cd0e9ec1484c50950f17cd6c084d07caed94ce45c20004210cbde48da44ebcf7d931657f03e07e3ea29ae41868b804bf39e628323cd025507773f07268301aa1e77a82927fce041241839cee4114f6307b6befe3befde87a2d3f13eeef96b27b36e788d907b44bef2d195aa802692f4f12acc015aede3cd29da272d1e4b7f3f59683d25bf08f0e29fba2a8a9b", "hex"); describe("SecureSession", () => { - const secureSession = new SecureSession(1, UNDEFINED_NODE_ID, UNDEFINED_NODE_ID, 0x8d4b, Buffer.alloc(0), DECRYPT_KEY, ENCRYPT_KEY, Buffer.alloc(0)); + const secureSession = new SecureSession({} as MatterServer, 1, UNDEFINED_NODE_ID, UNDEFINED_NODE_ID, 0x8d4b, Buffer.alloc(0), DECRYPT_KEY, ENCRYPT_KEY, Buffer.alloc(0)); context("decrypt", () => { it("decrypts a message", () => { diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 00000000..3cfe85d5 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "CommonJS", + "noImplicitAny": true, + "removeComments": true, + "esModuleInterop": true, + "declaration": true, + "strict": true + }, + "include": ["**/*.ts", "../src/**/*.ts"], +}