From 5d02efe44826401521b85259dde5e3ca4421e312 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:12:58 +0200 Subject: [PATCH] fix(ignore): zigate: Workaround missing ZDO `LEAVE_RESPONSE` (#1204) --- src/adapter/zigate/adapter/zigateAdapter.ts | 10 ++-- src/adapter/zigate/driver/zigate.ts | 52 +++++++++++++-------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/adapter/zigate/adapter/zigateAdapter.ts b/src/adapter/zigate/adapter/zigateAdapter.ts index 27634359f0..5c2670076c 100644 --- a/src/adapter/zigate/adapter/zigateAdapter.ts +++ b/src/adapter/zigate/adapter/zigateAdapter.ts @@ -229,8 +229,6 @@ class ZiGateAdapter extends Adapter { prefixedPayload.set(payload, 0); payload = prefixedPayload; - // XXX: zigate is missing ZDO LEAVE_RESPONSE, force disable to avoid waitress timeout (LeaveIndication will do the rest) - disableResponse = true; break; } @@ -271,7 +269,10 @@ class ZiGateAdapter extends Adapter { if (responseClusterId) { waiter = this.driver.zdoWaitFor({ clusterId: responseClusterId, - target: responseClusterId === Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE ? ieeeAddress : networkAddress, + target: + responseClusterId === Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE || responseClusterId === Zdo.ClusterId.LEAVE_RESPONSE + ? ieeeAddress + : networkAddress, }); } } @@ -282,9 +283,6 @@ class ZiGateAdapter extends Adapter { const result = await waiter.start().promise; return result.zdo as ZdoTypes.RequestToResponseMap[K]; - } else if (clusterId === Zdo.ClusterId.LEAVE_REQUEST) { - // mock missing response (see above) - return [Zdo.Status.SUCCESS, undefined]; } }, networkAddress); } diff --git a/src/adapter/zigate/driver/zigate.ts b/src/adapter/zigate/driver/zigate.ts index 1d44555bd1..f2bb8a7190 100644 --- a/src/adapter/zigate/driver/zigate.ts +++ b/src/adapter/zigate/driver/zigate.ts @@ -11,7 +11,7 @@ import {logger} from '../../../utils/logger'; import Waitress from '../../../utils/waitress'; import * as ZSpec from '../../../zspec'; import * as Zdo from '../../../zspec/zdo'; -import {EndDeviceAnnounce, GenericZdoResponse} from '../../../zspec/zdo/definition/tstypes'; +import {EndDeviceAnnounce, GenericZdoResponse, ResponseMap as ZdoResponseMap} from '../../../zspec/zdo/definition/tstypes'; import {SerialPort} from '../../serialPort'; import SerialPortUtils from '../../serialPortUtils'; import SocketPortUtils from '../../socketPortUtils'; @@ -345,33 +345,47 @@ export default class ZiGate extends EventEmitter { this.zdoWaitress.resolve({ziGatePayload, zdo}); this.emit('zdoResponse', ziGatePayload.clusterID, zdo); + } else if (code === ZiGateMessageCode.LeaveIndication && ziGateObject.payload.rejoin === 0) { + // mock a ZDO response (if waiter present) as zigate does not follow spec on this (missing ZDO LEAVE_RESPONSE) + const ziGatePayload: ZdoWaitressPayload['ziGatePayload'] = { + status: 0, + profileID: Zdo.ZDO_PROFILE_ID, + clusterID: Zdo.ClusterId.LEAVE_RESPONSE, // only piece actually required for waitress validation + sourceEndpoint: Zdo.ZDO_ENDPOINT, + destinationEndpoint: Zdo.ZDO_ENDPOINT, + sourceAddressMode: 0x03, + sourceAddress: ziGateObject.payload.extendedAddress, + destinationAddressMode: 0x03, + destinationAddress: ZSpec.BLANK_EUI64, + // @ts-expect-error not used + payload: undefined, + }; + + // Workaround: `zdo` is not valid for LEAVE_RESPONSE, but required to pass altered waitress validation (in sendZdo) + if (this.zdoWaitress.resolve({ziGatePayload, zdo: [Zdo.Status.SUCCESS, {eui64: ziGateObject.payload.extendedAddress}]})) { + this.emit('zdoResponse', Zdo.ClusterId.LEAVE_RESPONSE, [ + Zdo.Status.SUCCESS, + undefined, + ] as ZdoResponseMap[Zdo.ClusterId.LEAVE_RESPONSE]); + } + + this.emit('LeaveIndication', ziGateObject); } else { this.waitress.resolve(ziGateObject); - } - switch (code) { - case ZiGateMessageCode.DataIndication: - switch (ziGateObject.payload.profileID) { - case Zdo.ZDO_PROFILE_ID: - // handled above - break; - case ZSpec.HA_PROFILE_ID: - this.emit('received', ziGateObject); - break; - default: - logger.debug('not implemented profile: ' + ziGateObject.payload.profileID, NS); + if (code === ZiGateMessageCode.DataIndication) { + if (ziGateObject.payload.profileID === ZSpec.HA_PROFILE_ID) { + this.emit('received', ziGateObject); + } else { + logger.debug('not implemented profile: ' + ziGateObject.payload.profileID, NS); } - break; - case ZiGateMessageCode.LeaveIndication: - this.emit('LeaveIndication', ziGateObject); - break; - case ZiGateMessageCode.DeviceAnnounce: + } else if (code === ZiGateMessageCode.DeviceAnnounce) { this.emit('DeviceAnnounce', { nwkAddress: ziGateObject.payload.shortAddress, eui64: ziGateObject.payload.ieee, capabilities: ziGateObject.payload.MACcapability, }); - break; + } } } catch (error) { logger.error(`Parsing error: ${error}`, NS);