Skip to content

Commit

Permalink
fix: Fix crash on IEEE address request timeout (#1209)
Browse files Browse the repository at this point in the history
* fix: Fix crash on IEEE address request timeout

* fix tests

* add coverage
  • Loading branch information
Koenkk authored Sep 29, 2024
1 parent 01b76ff commit 985e11a
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 20 deletions.
30 changes: 18 additions & 12 deletions src/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,21 +540,27 @@ class Controller extends events.EventEmitter<ControllerEventMap> {
this.unknownDevices.add(nwkAddress);
const clusterId = Zdo.ClusterId.IEEE_ADDRESS_REQUEST;
const zdoPayload = Zdo.Buffalo.buildRequest(this.adapter.hasZdoMessageOverhead, clusterId, nwkAddress, false, 0);
const response = await this.adapter.sendZdo(ZSpec.BLANK_EUI64, nwkAddress, clusterId, zdoPayload, false);

if (Zdo.Buffalo.checkStatus(response)) {
const payload = response[1];
const device = Device.byIeeeAddr(payload.eui64);
try {
const response = await this.adapter.sendZdo(ZSpec.BLANK_EUI64, nwkAddress, clusterId, zdoPayload, false);

/* istanbul ignore else */
if (device) {
this.checkDeviceNetworkAddress(device, payload.eui64, payload.nwkAddress);
this.unknownDevices.delete(payload.nwkAddress);
}
if (Zdo.Buffalo.checkStatus(response)) {
const payload = response[1];
const device = Device.byIeeeAddr(payload.eui64);

return device;
} else {
logger.debug(`Failed to retrieve IEEE address for device '${nwkAddress}': ${Zdo.Status[response[0]]}`, NS);
/* istanbul ignore else */
if (device) {
this.checkDeviceNetworkAddress(device, payload.eui64, payload.nwkAddress);
this.unknownDevices.delete(payload.nwkAddress);
}

return device;
} else {
throw new Zdo.StatusError(response[0]);
}
} catch (error) {
// Catches 2 types of exception: Zdo.StatusError and no response from `adapter.sendZdo()`.
logger.debug(`Failed to retrieve IEEE address for device '${nwkAddress}': ${error}`, NS);
}

// NOTE: by keeping nwkAddress in `this.unknownDevices` on fail, it prevents a non-responding device from potentially spamming identify.
Expand Down
11 changes: 3 additions & 8 deletions test/controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10413,7 +10413,7 @@ describe('Controller', () => {
expect(device.networkAddress).toStrictEqual(oldNwkAddress);
expect(device.modelID).toBe('TRADFRI bulb E27 WS opal 980lm');
expect(mockLogger.debug).toHaveBeenCalledWith(
`Failed to retrieve IEEE address for device '${newNwkAddress}': INV_REQUESTTYPE`,
`Failed to retrieve IEEE address for device '${newNwkAddress}': Error: Status 'INV_REQUESTTYPE'`,
'zh:controller',
);
expect(events.lastSeenChanged.length).toBe(0);
Expand Down Expand Up @@ -10484,12 +10484,7 @@ describe('Controller', () => {
events.lastSeenChanged = [];
events.deviceNetworkAddressChanged = [];
mockAdapterSendZdo.mockClear();
mockAdapterSendZdo.mockImplementationOnce(async () => {
const zdoResponse = [Zdo.Status.NOT_SUPPORTED, undefined];

await mockAdapterEvents['zdoResponse'](Zdo.ClusterId.IEEE_ADDRESS_RESPONSE, zdoResponse);
return zdoResponse;
});
mockAdapterSendZdo.mockRejectedValueOnce(new Error('timeout'));
const identifyUnknownDeviceSpy = jest.spyOn(controller, 'identifyUnknownDevice');

const frame = Zcl.Frame.create(0, 1, true, undefined, 10, 'readRsp', 0, [{attrId: 5, status: 0, dataType: 66, attrData: 'new.model.id'}], {});
Expand All @@ -10506,7 +10501,7 @@ describe('Controller', () => {

expect(mockAdapterSendZdo).toHaveBeenCalledTimes(1);
expect(identifyUnknownDeviceSpy).toHaveBeenCalledTimes(1);
expect(mockLogger.debug).toHaveBeenCalledWith(`Failed to retrieve IEEE address for device '${nwkAddress}': NOT_SUPPORTED`, 'zh:controller');
expect(mockLogger.debug).toHaveBeenCalledWith(`Failed to retrieve IEEE address for device '${nwkAddress}': Error: timeout`, 'zh:controller');

await mockAdapterEvents['zclPayload']({
wasBroadcast: false,
Expand Down

0 comments on commit 985e11a

Please sign in to comment.