Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DataReport chunking #176

Merged
merged 3 commits into from
Jan 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ Matter is a new secure / reliable / local / standard protocol for smart devices
To know more about Matter: https://csa-iot.org/all-solutions/matter/

node-matter is compatible with (or will soon be):
- *iOS 16.2 - Home app*: fully working, [screenshot](https://github.com/mfucci/node-matter/issues/103#issuecomment-1374301293)
- *Alexa*: fully working, [screenshot](https://github.com/mfucci/node-matter/issues/159#issuecomment-1374323476)
- *Android - Home app*: nearly working, [thread](https://github.com/mfucci/node-matter/issues/140)
- *Home Assistant*: was working, we are on it, [thread](https://github.com/mfucci/node-matter/issues/11)
- **iOS - Home app**: fully working, [screenshot](https://github.com/mfucci/node-matter/issues/103#issuecomment-1374301293)
- **Android - Home app**: fully working, [thread](https://github.com/mfucci/node-matter/issues/140#issuecomment-1374417228)
- **Alexa**: fully working, [screenshot](https://github.com/mfucci/node-matter/issues/159#issuecomment-1374323476)
- **Home Assistant**: was working, we are on it, [thread](https://github.com/mfucci/node-matter/issues/11)

## Installation

Expand Down
27 changes: 15 additions & 12 deletions src/matter/interaction/InteractionMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,23 @@ export const TlvReadRequest = TlvObject({
interactionModelRevision: TlvField(0xFF, TlvUInt8),
});

export const TlvAttributeReport = TlvObject({
value: TlvField(1, TlvObject({
version: TlvField(0, TlvUInt32),
path: TlvField(1, TlvList({
endpointId: TlvField(2, TlvUInt16),
clusterId: TlvField(3, TlvUInt32),
id: TlvField(4, TlvUInt32),
})),
value: TlvField(2, TlvAny),
})),
});

export const TlvDataReport = TlvObject({
subscriptionId: TlvOptionalField(0, TlvUInt32),
values: TlvField(1, TlvArray(TlvObject({
value: TlvField(1, TlvObject({
version: TlvField(0, TlvUInt32),
path: TlvField(1, TlvList({
endpointId: TlvField(2, TlvUInt16),
clusterId: TlvField(3, TlvUInt32),
id: TlvField(4, TlvUInt32),
})),
value: TlvField(2, TlvAny),
})),
}))),
isFabricFiltered: TlvOptionalField(4, TlvBoolean),
values: TlvField(1, TlvArray(TlvAttributeReport)),
moreChunkedMessages: TlvOptionalField(3, TlvBoolean),
suppressResponse: TlvOptionalField(4, TlvBoolean),
interactionModelRevision: TlvField(0xFF, TlvUInt8),
});

Expand Down
34 changes: 33 additions & 1 deletion src/matter/interaction/InteractionMessenger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Logger } from "../../log/Logger";
import { MessageExchange } from "../common/MessageExchange";
import { MatterController } from "../MatterController";
import { MatterDevice } from "../MatterDevice";
import { TlvInvokeRequest, TlvInvokeResponse, TlvReadRequest, TlvDataReport, TlvSubscribeRequest, TlvSubscribeResponse, StatusCode, TlvStatusResponse, TlvTimedRequest } from "./InteractionMessages";
import { TlvInvokeRequest, TlvInvokeResponse, TlvReadRequest, TlvDataReport, TlvSubscribeRequest, TlvSubscribeResponse, StatusCode, TlvStatusResponse, TlvTimedRequest, TlvAttributeReport } from "./InteractionMessages";
import { ByteArray, TlvSchema, TypeFromSchema } from "@project-chip/matter.js";

export const enum MessageType {
Expand All @@ -32,6 +32,8 @@ export type InvokeRequest = TypeFromSchema<typeof TlvInvokeRequest>;
export type InvokeResponse = TypeFromSchema<typeof TlvInvokeResponse>;
export type TimedRequest = TypeFromSchema<typeof TlvTimedRequest>;

const MAX_SPDU_LENGTH = 1024;

const logger = Logger.get("InteractionMessenger");

class InteractionMessenger<ContextT> {
Expand Down Expand Up @@ -124,6 +126,36 @@ export class InteractionServerMessenger extends InteractionMessenger<MatterDevic
}

async sendDataReport(dataReport: DataReport) {
const messageBytes = TlvDataReport.encode(dataReport);
if (messageBytes.length > MAX_SPDU_LENGTH) {
// DataReport is too long, it needs to be sent in chunked
const attributeReportsToSend = [...dataReport.values];
dataReport.values.length = 0;
dataReport.moreChunkedMessages = true;

const emptyDataReportBytes = TlvDataReport.encode(dataReport);

let messageSize = emptyDataReportBytes.length;
while (true) {
const attributeReport = attributeReportsToSend.pop();
if (attributeReport === undefined) {
// No more chunks to send
dataReport.moreChunkedMessages = undefined;
break;
}
const attributeReportBytes = TlvAttributeReport.encode(attributeReport).length;
if (messageSize + attributeReportBytes > MAX_SPDU_LENGTH) {
// Report doesn't fit, sending this chunk
await this.exchange.send(MessageType.ReportData, TlvDataReport.encode(dataReport));
await this.waitForSuccess();
dataReport.values.length = 0;
messageSize = emptyDataReportBytes.length;
}
messageSize += attributeReportBytes;
dataReport.values.push(attributeReport);
}
}

await this.exchange.send(MessageType.ReportData, TlvDataReport.encode(dataReport));
}
}
Expand Down
7 changes: 0 additions & 7 deletions src/matter/interaction/InteractionServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ export class InteractionServer implements ProtocolHandler<MatterDevice> {
});

return {
isFabricFiltered: true,
interactionModelRevision: 1,
values: values.map(({ path, value, version, schema }) => ({
value: {
Expand Down Expand Up @@ -179,12 +178,6 @@ export class InteractionServer implements ProtocolHandler<MatterDevice> {
logger.debug(`Subscribe to ${attributeRequests.map(path => this.resolveAttributeName(path)).join(", ")}`);
let attributes = this.getAttributes(attributeRequests);

// Temporay hack until attribution subscription is handled correctly
if (attributes.length > 1) {
// If subscription to multiple attributes is requested, only returns the onoff attribute to handle */*/* subscription.
attributes = [{ path: { endpointId: 1, clusterId: 6, id: 0 }, attribute: this.attributes.get("1/6/0") as AttributeServer<any>}];
}

if (attributeRequests.length === 0) throw new Error("Invalid subscription request");
if (minIntervalFloorSeconds < 0) throw new Error("minIntervalFloorSeconds should be greater or equal to 0");
if (maxIntervalCeilingSeconds < 0) throw new Error("maxIntervalCeilingSeconds should be greater or equal to 1");
Expand Down
1 change: 0 additions & 1 deletion test/matter/interaction/InteractionProtocolTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ const READ_REQUEST: ReadRequest = {

const READ_RESPONSE: DataReport = {
interactionModelRevision: 1,
isFabricFiltered: true,
values: [
{ value: {
path: {
Expand Down