Skip to content

Commit 06694f6

Browse files
authored
fix: General fixes (#19)
1 parent e060ea7 commit 06694f6

8 files changed

Lines changed: 64 additions & 38 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,4 @@ dist
131131

132132
# ZoH
133133
*.save
134+
/temp

package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zigbee-on-host",
3-
"version": "0.1.2",
3+
"version": "0.1.3",
44
"description": "ZigBee stack designed to run on a host and communicate with a radio co-processor (RCP)",
55
"engines": {
66
"node": ">=20.17.0"

src/dev/minimal-adapter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class MinimalAdapter {
103103
const hostname = pathUrl.hostname;
104104
const port = Number.parseInt(pathUrl.port, 10);
105105

106-
logger.debug(`Opening TCP socket with ${hostname}:${port}`, NS);
106+
logger.debug(() => `Opening TCP socket with ${hostname}:${port}`, NS);
107107

108108
this.socketPort = new Socket();
109109

@@ -121,7 +121,7 @@ export class MinimalAdapter {
121121
};
122122

123123
this.socketPort!.on("connect", () => {
124-
logger.debug("Socket connected", NS);
124+
logger.debug(() => "Socket connected", NS);
125125
});
126126
this.socketPort!.on("ready", (): void => {
127127
logger.info("Socket ready", NS);

src/drivers/ot-rcp-driver.ts

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,6 @@ export type Backup = {
636636
// const SPINEL_ENCRYPTER_EXTRA_DATA_SIZE = 0;
637637
// const SPINEL_FRAME_BUFFER_SIZE = SPINEL_FRAME_MAX_SIZE + SPINEL_ENCRYPTER_EXTRA_DATA_SIZE;
638638

639-
const CONFIG_COMMAND_TIMEOUT = 10000;
640639
const CONFIG_TID_MASK = 0x0e;
641640
const CONFIG_HIGHWATER_MARK = HDLC_TX_CHUNK_SIZE * 4;
642641
/** The number of OctetDurations until a route discovery expires. */
@@ -923,7 +922,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
923922

924923
await new Promise<SpinelFrame>((resolve, reject) => {
925924
this.resetWaiter = {
926-
timer: setTimeout(() => reject(new Error(`Timeout after ${CONFIG_COMMAND_TIMEOUT * 3}`)), CONFIG_COMMAND_TIMEOUT * 3),
925+
timer: setTimeout(() => reject(new Error("Reset timeout after 5000ms")), 5000),
927926
resolve,
928927
};
929928
});
@@ -1087,7 +1086,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
10871086
macHeader.destination16! !== ZigbeeMACConsts.BCAST_ADDR &&
10881087
macHeader.destination16! !== ZigbeeConsts.COORDINATOR_ADDRESS
10891088
) {
1090-
logger.debug(`<-~- MAC Ignoring frame intended for device ${macHeader.destination16}`, NS);
1089+
logger.debug(() => `<-~- MAC Ignoring frame intended for device ${macHeader.destination16}`, NS);
10911090
return;
10921091
}
10931092

@@ -1103,7 +1102,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
11031102
const [nwkGPHeader, nwkGPHOutOffset] = decodeZigbeeNWKGPHeader(macPayload, nwkGPFCFOutOffset, nwkGPFCF);
11041103

11051104
if (nwkGPHeader.sourceId === undefined) {
1106-
logger.debug("<-~- NWKGP Ignoring frame without sourceId", NS);
1105+
logger.debug(() => "<-~- NWKGP Ignoring frame without sourceId", NS);
11071106
return;
11081107
}
11091108

@@ -1119,16 +1118,26 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
11191118

11201119
this.processZigbeeNWKGPDataFrame(nwkGPPayload, macHeader, nwkGPHeader, metadata?.rssi ?? 0);
11211120
} else {
1122-
logger.debug(`<-~- NWKGP Ignoring frame with type ${nwkGPHeader.frameControl.frameType}`, NS);
1121+
logger.debug(() => `<-~- NWKGP Ignoring frame with type ${nwkGPHeader.frameControl.frameType}`, NS);
11231122
return;
11241123
}
11251124
} else {
1126-
logger.debug(`<-x- NWKGP Invalid frame addressing ${macFCF.destAddrMode} (${macHeader.destination16})`, NS);
1125+
logger.debug(() => `<-x- NWKGP Invalid frame addressing ${macFCF.destAddrMode} (${macHeader.destination16})`, NS);
11271126
return;
11281127
}
11291128
} else {
11301129
const [nwkFCF, nwkFCFOutOffset] = decodeZigbeeNWKFrameControl(macPayload, 0);
11311130
const [nwkHeader, nwkHOutOffset] = decodeZigbeeNWKHeader(macPayload, nwkFCFOutOffset, nwkFCF);
1131+
1132+
if (
1133+
macHeader.destination16 !== undefined &&
1134+
macHeader.destination16 >= ZigbeeConsts.BCAST_MIN &&
1135+
nwkHeader.source16 === ZigbeeConsts.COORDINATOR_ADDRESS
1136+
) {
1137+
logger.debug(() => "<-~- NWK Ignoring frame from coordinator (broadcast loopback)", NS);
1138+
return;
1139+
}
1140+
11321141
const nwkPayload = decodeZigbeeNWKPayload(
11331142
macPayload,
11341143
nwkHOutOffset,
@@ -1143,7 +1152,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
11431152
const [apsFCF, apsFCFOutOffset] = decodeZigbeeAPSFrameControl(nwkPayload, 0);
11441153
const [apsHeader, apsHOutOffset] = decodeZigbeeAPSHeader(nwkPayload, apsFCFOutOffset, apsFCF);
11451154

1146-
if (apsHeader.frameControl.ackRequest) {
1155+
if (apsHeader.frameControl.ackRequest && nwkHeader.source16 !== ZigbeeConsts.COORDINATOR_ADDRESS) {
11471156
await this.onZigbeeAPSACKRequest(macHeader, nwkHeader, apsHeader);
11481157
}
11491158

@@ -1210,18 +1219,15 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
12101219
});
12111220
}
12121221

1213-
public async getProperty(propertyId: SpinelPropertyId): ReturnType<typeof this.sendCommand> {
1222+
public async getProperty(propertyId: SpinelPropertyId, timeout = 10000): ReturnType<typeof this.sendCommand> {
12141223
const [data] = writePropertyId(propertyId, 0);
12151224

1216-
return await this.sendCommand(SpinelCommandId.PROP_VALUE_GET, data, true, CONFIG_COMMAND_TIMEOUT);
1225+
return await this.sendCommand(SpinelCommandId.PROP_VALUE_GET, data, true, timeout);
12171226
}
12181227

1219-
public async setProperty(payload: Buffer): Promise<[respPropertyId: SpinelPropertyId, data: Buffer]> {
1220-
const response = await this.sendCommand(SpinelCommandId.PROP_VALUE_SET, payload, true, CONFIG_COMMAND_TIMEOUT);
1221-
const [respPropertyId, outOffset] = getPackedUInt(response.payload, 0);
1222-
const data = response.payload.subarray(outOffset);
1223-
1224-
return [respPropertyId, data];
1228+
public async setProperty(payload: Buffer, timeout = 10000): Promise<void> {
1229+
// LAST_STATUS checked in `onFrame`
1230+
await this.sendCommand(SpinelCommandId.PROP_VALUE_SET, payload, true, timeout);
12251231
}
12261232

12271233
// #endregion
@@ -1239,7 +1245,6 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
12391245
});
12401246
}
12411247

1242-
// status is checked in `onFrame` and rejects if not OK, so, avoid parsing twice
12431248
await this.setProperty(writePropertyStreamRaw(payload, this.streamRawConfig));
12441249

12451250
logger.debug(() => `<=== MAC[seqNum=${seqNum} dest16=${dest16} dest64=${dest64}]`, NS);
@@ -2963,7 +2968,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
29632968

29642969
if (!processed) {
29652970
if (nwkHeader.source16 === undefined && nwkHeader.source64 === undefined) {
2966-
logger.debug("<=~= APS Ignoring frame with no sender info", NS);
2971+
logger.debug(() => "<=~= APS Ignoring frame with no sender info", NS);
29672972
return;
29682973
}
29692974

@@ -4442,6 +4447,8 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
44424447
try {
44434448
const state = await readFile(this.savePath);
44444449

4450+
logger.debug(() => `Loaded state from ${this.savePath} (${state.byteLength} bytes)`, NS);
4451+
44454452
if (state.byteLength < SaveConsts.NETWORK_DATA_SIZE) {
44464453
throw new Error("Invalid save state size");
44474454
}
@@ -4454,6 +4461,8 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
44544461
const deviceCount = state.readUInt16LE(offset);
44554462
offset += 2;
44564463

4464+
logger.debug(() => `Current save devices: ${deviceCount}`, NS);
4465+
44574466
for (let i = 0; i < deviceCount; i++) {
44584467
const address64 = state.readBigUInt64LE(offset);
44594468
offset += 8;
@@ -4569,6 +4578,8 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
45694578
const tcKeyFrameCounter = state.readUInt32LE(offset);
45704579
offset += 4;
45714580

4581+
logger.debug(() => `Current save network: eui64=${eui64} panId=${panId} channel=${channel}`, NS);
4582+
45724583
return {
45734584
eui64,
45744585
panId,
@@ -4605,11 +4616,18 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
46054616
* @param nwkDest16
46064617
* @param nwkDest64
46074618
* @param clusterId
4608-
* @returns The APS counter of the sent frame
4619+
* @returns
4620+
* - The APS counter of the sent frame.
4621+
* - The ZDO counter of the sent frame.
46094622
*/
4610-
public async sendZDO(payload: Buffer, nwkDest16: number, nwkDest64: bigint | undefined, clusterId: number): Promise<number> {
4623+
public async sendZDO(payload: Buffer, nwkDest16: number, nwkDest64: bigint | undefined, clusterId: number): Promise<[number, number]> {
4624+
if (nwkDest16 === ZigbeeConsts.COORDINATOR_ADDRESS || nwkDest64 === this.netParams.eui64) {
4625+
throw new Error("Cannot send ZDO to coordinator");
4626+
}
4627+
46114628
// increment and set the ZDO sequence number in outgoing payload
4612-
payload[0] = this.nextZDOSeqNum();
4629+
const zdoCounter = this.nextZDOSeqNum();
4630+
payload[0] = zdoCounter;
46134631

46144632
logger.debug(() => `===> ZDO[seqNum=${payload[0]} clusterId=${clusterId} nwkDest=${nwkDest16}:${nwkDest64}]`, NS);
46154633

@@ -4626,7 +4644,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
46264644
}, ZigbeeConsts.BCAST_TIME_WINDOW);
46274645
}
46284646

4629-
return await this.sendZigbeeAPSData(
4647+
const apsCounter = await this.sendZigbeeAPSData(
46304648
payload,
46314649
ZigbeeNWKRouteDiscovery.SUPPRESS, // nwkDiscoverRoute
46324650
nwkDest16, // nwkDest16
@@ -4638,6 +4656,8 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
46384656
ZigbeeConsts.ZDO_ENDPOINT, // sourceEndpoint
46394657
undefined, // group
46404658
);
4659+
4660+
return [apsCounter, zdoCounter];
46414661
}
46424662

46434663
/**
@@ -4649,7 +4669,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
46494669
* @param dest64
46504670
* @param destEp
46514671
* @param sourceEp
4652-
* @returns The APS counter of the sent frame
4672+
* @returns The APS counter of the sent frame.
46534673
*/
46544674
public async sendUnicast(
46554675
payload: Buffer,
@@ -4660,6 +4680,10 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
46604680
destEp: number,
46614681
sourceEp: number,
46624682
): Promise<number> {
4683+
if (dest16 === ZigbeeConsts.COORDINATOR_ADDRESS || dest64 === this.netParams.eui64) {
4684+
throw new Error("Cannot send unicast to coordinator");
4685+
}
4686+
46634687
return await this.sendZigbeeAPSData(
46644688
payload,
46654689
ZigbeeNWKRouteDiscovery.SUPPRESS, // nwkDiscoverRoute
@@ -4682,7 +4706,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
46824706
* @param dest16
46834707
* @param destEp
46844708
* @param sourceEp
4685-
* @returns The APS counter of the sent frame
4709+
* @returns The APS counter of the sent frame.
46864710
*/
46874711
public async sendMulticast(
46884712
payload: Buffer,
@@ -4714,7 +4738,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
47144738
* @param dest16
47154739
* @param destEp
47164740
* @param sourceEp
4717-
* @returns The APS counter of the sent frame
4741+
* @returns The APS counter of the sent frame.
47184742
*/
47194743
public async sendBroadcast(
47204744
payload: Buffer,

src/utils/logger.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
export interface Logger {
2-
debug: (messageOrLambda: string | (() => string), namespace: string) => void;
2+
debug: (messageOrLambda: () => string, namespace: string) => void;
33
info: (messageOrLambda: string | (() => string), namespace: string) => void;
44
warning: (messageOrLambda: string | (() => string), namespace: string) => void;
55
error: (messageOrLambda: string, namespace: string) => void;
66
}
77

88
export let logger: Logger = {
9-
debug: (messageOrLambda, namespace) =>
10-
console.debug(`[${new Date().toISOString()}] ${namespace}: ${typeof messageOrLambda === "function" ? messageOrLambda() : messageOrLambda}`),
9+
debug: (messageOrLambda, namespace) => console.debug(`[${new Date().toISOString()}] ${namespace}: ${messageOrLambda()}`),
1110
info: (messageOrLambda, namespace) =>
1211
console.info(`[${new Date().toISOString()}] ${namespace}: ${typeof messageOrLambda === "function" ? messageOrLambda() : messageOrLambda}`),
1312
warning: (messageOrLambda, namespace) =>

src/zigbee/zigbee-nwkgp.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,12 @@ export const enum ZigbeeNWKGPCommandId {
138138
SUCCESS = 0xe2,
139139
CHANNEL_REQUEST = 0xe3,
140140
APPLICATION_DESCRIPTION = 0xe4,
141+
//-- sent to GPD
141142
COMMISSIONING_REPLY = 0xf0,
142143
WRITE_ATTRIBUTES = 0xf1,
143144
READ_ATTRIBUTES = 0xf2,
144145
CHANNEL_CONFIGURATION = 0xf3,
146+
ZCL_TUNNELING_TO_GPD = 0x6,
145147
}
146148

147149
/**

test/ot-rcp-driver.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ describe("OT RCP Driver", () => {
371371

372372
it("throw when trying to reset network after state already loaded", async () => {
373373
// no-op
374-
vi.spyOn(driver, "setProperty").mockImplementation(() => Promise.resolve([0, Buffer.alloc(0)]));
374+
vi.spyOn(driver, "setProperty").mockImplementation(() => Promise.resolve());
375375
vi.spyOn(driver, "registerTimers").mockImplementation(() => Promise.resolve());
376376

377377
await driver.loadState(); // mock shallow start
@@ -380,7 +380,7 @@ describe("OT RCP Driver", () => {
380380

381381
it("forms network", async () => {
382382
// no-op
383-
vi.spyOn(driver, "setProperty").mockImplementation(() => Promise.resolve([0, Buffer.alloc(0)]));
383+
vi.spyOn(driver, "setProperty").mockImplementation(() => Promise.resolve());
384384
vi.spyOn(driver, "registerTimers").mockImplementation(() => Promise.resolve());
385385

386386
await driver.loadState(); // mock shallow start
@@ -635,7 +635,7 @@ describe("OT RCP Driver", () => {
635635
expect(onStreamRawFrameSpy).toHaveBeenCalledTimes(1);
636636
expect(onZigbeeAPSACKRequestSpy).toHaveBeenCalledTimes(0);
637637
expect(onZigbeeAPSFrameSpy).toHaveBeenCalledTimes(0);
638-
expect(processZigbeeNWKRouteReqSpy).toHaveBeenCalledTimes(1);
638+
expect(processZigbeeNWKRouteReqSpy).toHaveBeenCalledTimes(0);
639639
expect(sendZigbeeNWKRouteReplySpy).toHaveBeenCalledTimes(0);
640640
});
641641

0 commit comments

Comments
 (0)