Skip to content

Commit 4bfc114

Browse files
committed
fix: deCONZ: support optional firmware debug logs
Currently only for ConBee III debug firmware.
1 parent 7baf465 commit 4bfc114

4 files changed

Lines changed: 80 additions & 8 deletions

File tree

src/adapter/deconz/adapter/deconzAdapter.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
//import Device from "../../../controller/model/device";
44
import {existsSync, readFileSync} from "node:fs";
5+
import {dirname} from "node:path";
56
import type * as Models from "../../../models";
67
import type {Backup, UnifiedBackupStorage} from "../../../models";
78
import {BackupUtils, Waitress} from "../../../utils";
@@ -45,7 +46,27 @@ export class DeconzAdapter extends Adapter {
4546

4647
this.waitress = new Waitress<Events.ZclPayload, WaitressMatcher>(this.waitressValidator, this.waitressTimeoutFormatter);
4748

48-
this.driver = new Driver(serialPortOptions, networkOptions, this.getStoredBackup());
49+
const firmwareLog = [];
50+
if (backupPath) {
51+
// optional: get extra logs from the firmware (debug builds)
52+
const dirPath = dirname(backupPath);
53+
const configPath = `${dirPath}/deconz_options.json`;
54+
if (existsSync(configPath)) {
55+
try {
56+
const data = JSON.parse(readFileSync(configPath).toString());
57+
const log = data.firmware_log || [];
58+
if (Array.isArray(log)) {
59+
for (const level of log) {
60+
if (level === "APS" || level === "APS_L2") {
61+
firmwareLog.push(level);
62+
}
63+
}
64+
}
65+
} catch (_err) {}
66+
}
67+
}
68+
69+
this.driver = new Driver(serialPortOptions, networkOptions, this.getStoredBackup(), firmwareLog);
4970

5071
this.driver.on("rxFrame", (frame) => processFrame(frame));
5172
this.openRequestsQueue = [];

src/adapter/deconz/driver/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export enum FirmwareCommand {
6363
MacPollIndication = 0x1c,
6464
Reboot = 0x1e,
6565
Beacon = 0x1f,
66+
DebugLog = 0x22,
6667
}
6768

6869
export enum NetworkState {
@@ -107,6 +108,7 @@ export enum ParamId {
107108
STK_NWK_UPDATE_ID = 0x24,
108109
DEV_WATCHDOG_TTL = 0x26,
109110
STK_FRAME_COUNTER = 0x27,
111+
STK_DEBUG_LOG_LEVEL = 0x29,
110112
// internal
111113
NONE = 0xff,
112114
}
@@ -136,6 +138,7 @@ export const stackParameters = [
136138
{id: ParamId.NWK_EXTENDED_PANID, type: DataType.U64},
137139
{id: ParamId.APS_CHANNEL_MASK, type: DataType.U32},
138140
{id: ParamId.STK_FRAME_COUNTER, type: DataType.U32},
141+
{id: ParamId.STK_DEBUG_LOG_LEVEL, type: DataType.U32},
139142
{id: ParamId.APS_USE_EXTENDED_PANID, type: DataType.U64},
140143
{id: ParamId.APS_TRUST_CENTER_ADDRESS, type: DataType.U64},
141144
];

src/adapter/deconz/driver/driver.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class Driver extends events.EventEmitter {
9595
private tickTimer: NodeJS.Timeout;
9696
private driverStateStart = 0;
9797
private driverState: DriverState = DriverState.Init;
98-
98+
private firmwareLog: string[];
9999
private transactionID = 0; // for APS and ZDO
100100
// in flight lockstep sending commands
101101
private txState: TxState = TxState.Idle;
@@ -118,13 +118,14 @@ class Driver extends events.EventEmitter {
118118
public paramFrameCounter = 0;
119119
public paramApsUseExtPanid = 0n;
120120

121-
public constructor(serialPortOptions: SerialPortOptions, networkOptions: NetworkOptions, backup: Backup | undefined) {
121+
public constructor(serialPortOptions: SerialPortOptions, networkOptions: NetworkOptions, backup: Backup | undefined, firmwareLog: string[]) {
122122
super();
123123
this.seqNumber = 0;
124124
this.configChanged = 0;
125125
this.networkOptions = networkOptions;
126126
this.serialPortOptions = serialPortOptions;
127127
this.backup = backup;
128+
this.firmwareLog = firmwareLog;
128129

129130
this.writer = new Writer();
130131
this.parser = new Parser();
@@ -260,7 +261,7 @@ class Driver extends events.EventEmitter {
260261
}
261262
} else if (event === DriverEvent.Tick) {
262263
if (this.txState === TxState.WaitResponse) {
263-
if (Date.now() - this.txTime > 1000) {
264+
if (Date.now() - this.txTime > 2000) {
264265
this.emitStateEvent(DriverEvent.FirmwareCommandTimeout);
265266
}
266267
}
@@ -611,6 +612,16 @@ class Driver extends events.EventEmitter {
611612
logger.debug("Zigbee configuration valid", NS);
612613
this.driverStateStart = Date.now();
613614
this.driverState = DriverState.Connected;
615+
616+
// enable optional firmware debug messages
617+
let logLevel = 0;
618+
for (const level of this.firmwareLog) {
619+
if (level === "APS") logLevel |= 0x00000100;
620+
else if (level === "APS_L2") logLevel |= 0x00010000;
621+
}
622+
if (logLevel !== 0) {
623+
this.writeParameterRequest(ParamId.STK_DEBUG_LOG_LEVEL, logLevel);
624+
}
614625
} else {
615626
this.driverStateStart = Date.now();
616627
this.driverState = DriverState.Reconfigure;
@@ -1176,7 +1187,8 @@ class Driver extends events.EventEmitter {
11761187
}
11771188

11781189
busyQueue.push(req);
1179-
} catch (_) {
1190+
} catch (err) {
1191+
console.error(err);
11801192
req.reject(new Error(`Failed to process request ${FirmwareCommand[req.commandId]}, seq: ${req.seqNumber}`));
11811193
}
11821194
}

src/adapter/deconz/driver/frameParser.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -494,16 +494,50 @@ function parseGreenPowerDataIndication(view: DataView): GpDataInd | null {
494494
}
495495
function parseMacPollCommand(_view: DataView): number {
496496
//logger.debug("Received command MAC_POLL", NS);
497-
return 28;
497+
return FirmwareCommand.MacPollIndication;
498498
}
499499
function parseBeaconRequest(_view: DataView): number {
500500
logger.debug("Received Beacon Request", NS);
501-
return 31;
501+
return FirmwareCommand.Beacon;
502+
}
503+
504+
function parseDebugLog(view: DataView): null {
505+
let dbg = "";
506+
const buf = new Buffalo(Buffer.from(view.buffer));
507+
508+
/* const commandId = */ buf.readUInt8();
509+
/* const seqNr = */ buf.readUInt8();
510+
const status = buf.readUInt8();
511+
512+
if (status !== CommandStatus.Success) {
513+
// unlikely
514+
return null;
515+
}
516+
517+
/* const frameLength = */ buf.readUInt16();
518+
const payloadLength = buf.readUInt16();
519+
520+
for (let i = 0; i < payloadLength && buf.isMore(); i++) {
521+
const ch = buf.readUInt8();
522+
if (ch >= 32 && ch <= 127) {
523+
dbg += String.fromCharCode(ch);
524+
}
525+
}
526+
527+
if (dbg.length !== 0) {
528+
logger.debug(`firmware log: ${dbg}`, NS);
529+
}
530+
531+
return null;
502532
}
503533

504534
function parseUnknownCommand(view: DataView): number {
505535
const id = view.getUint8(0);
506-
logger.debug(`received unknown command - id ${id}`, NS);
536+
if (id in FirmwareCommand) {
537+
logger.debug(`received unsupported command: ${FirmwareCommand[id]} id: 0x${id.toString(16).padStart(2, "0")}`, NS);
538+
} else {
539+
logger.debug(`received unknown command: id: 0x${id.toString(16).padStart(2, "0")}`, NS);
540+
}
507541
return id;
508542
}
509543
function getParserForCommandId(id: number): (view: DataView) => Command | object | number | null {
@@ -532,6 +566,8 @@ function getParserForCommandId(id: number): (view: DataView) => Command | object
532566
return parseMacPollCommand;
533567
case FirmwareCommand.Beacon:
534568
return parseBeaconRequest;
569+
case FirmwareCommand.DebugLog:
570+
return parseDebugLog;
535571
default:
536572
return parseUnknownCommand;
537573
//throw new Error(`unknown command id ${id}`);

0 commit comments

Comments
 (0)