From b2f2f033ad97c1905826162d0d243c45e4b8b3aa Mon Sep 17 00:00:00 2001 From: Jorge Schrauwen Date: Fri, 27 Sep 2024 14:47:01 +0200 Subject: [PATCH] fix: On NO_ENTRY error during unbind cleanup database * initial implementation * rework for reviewer feedback --- src/controller/model/endpoint.ts | 15 ++++++++++++--- test/controller.test.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/controller/model/endpoint.ts b/src/controller/model/endpoint.ts index 87a7f1356c..c90b803f9c 100644 --- a/src/controller/model/endpoint.ts +++ b/src/controller/model/endpoint.ts @@ -628,9 +628,18 @@ class Endpoint extends Entity { this.save(); } catch (error) { const err = error as Error; - err.message = `${log} failed (${err.message})`; - logger.debug(err.stack!, NS); - throw error; + if ((error as Zdo.StatusError).code == Zdo.Status.NO_ENTRY) { + /* Device returned NO_ENTRY error, meaning the bind is not present + * Do not threat this as an error and remove the entry from the database instead. + */ + logger.debug(`${log} failed (${err.message}), removing entry from database.`, NS); + this._binds.splice(index, 1); + this.save(); + } else { + err.message = `${log} failed (${err.message})`; + logger.debug(err.stack!, NS); + throw error; + } } } diff --git a/test/controller.test.ts b/test/controller.test.ts index 62a539fb0e..f16bf8c711 100755 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -7995,6 +7995,34 @@ describe('Controller', () => { expect(error).toStrictEqual(new Error(`Unbind 0x129/1 genOnOff invalid target '1' (no group with this ID exists).`)); }); + it('Unbind against unbound cluster', async () => { + await controller.start(); + await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'}); + await mockAdapterEvents['deviceJoined']({networkAddress: 170, ieeeAddr: '0x170'}); + const endpoint = controller.getDeviceByIeeeAddr('0x129')!.getEndpoint(1)!; + const target = controller.getDeviceByIeeeAddr('0x170')!.getEndpoint(1)!; + await endpoint.bind('genOnOff', target); + mockAdapterSendZdo.mockClear(); + + sendZdoResponseStatus = Zdo.Status.NO_ENTRY; + + await endpoint.unbind('genOnOff', target); + + const zdoPayload = Zdo.Buffalo.buildRequest( + false, + Zdo.ClusterId.UNBIND_REQUEST, + '0x129', + 1, + Zcl.Clusters.genOnOff.ID, + Zdo.UNICAST_BINDING, + '0x170', + 0, + 1, + ); + expect(mockAdapterSendZdo).toHaveBeenCalledWith('0x129', 129, Zdo.ClusterId.UNBIND_REQUEST, zdoPayload, false); + expect(endpoint.binds).toStrictEqual([]); + }); + it('Unbind error', async () => { await controller.start(); await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'});