diff --git a/packages/zwave-js/src/lib/driver/Driver.ts b/packages/zwave-js/src/lib/driver/Driver.ts index 75b843aa94fa..0da3f4ee3c6a 100644 --- a/packages/zwave-js/src/lib/driver/Driver.ts +++ b/packages/zwave-js/src/lib/driver/Driver.ts @@ -5565,7 +5565,9 @@ ${handlers.length} left`, if (this.isMissingNodeACK(transaction, error)) { if (this.handleMissingNodeACK(transaction as any, error)) return; } else if ( - isMissingControllerACK(error) || isMissingControllerCallback(error) + isMissingControllerACK(error) + || (isSendData(transaction.message) + && isMissingControllerCallback(error)) ) { if (this.handleUnresponsiveController(transaction, error)) return; } diff --git a/packages/zwave-js/src/lib/test/driver/sendDataMissingCallbackAbort.test.ts b/packages/zwave-js/src/lib/test/driver/sendDataMissingCallbackAbort.test.ts index 7c12f8382a0b..f4e453f33eb7 100644 --- a/packages/zwave-js/src/lib/test/driver/sendDataMissingCallbackAbort.test.ts +++ b/packages/zwave-js/src/lib/test/driver/sendDataMissingCallbackAbort.test.ts @@ -9,6 +9,10 @@ import { import { ZWaveErrorCodes, assertZWaveError } from "@zwave-js/core"; import Sinon from "sinon"; import { SoftResetRequest } from "../../serialapi/misc/SoftResetRequest"; +import { + RequestNodeInfoRequest, + RequestNodeInfoResponse, +} from "../../serialapi/network-mgmt/RequestNodeInfoMessages"; import { SendDataAbort, SendDataRequest, @@ -318,3 +322,44 @@ integrationTest( }, }, ); + +integrationTest( + "Missing callback recovery only kicks in for SendData commands", + { + // debug: true, + + additionalDriverOptions: { + testingHooks: { + skipNodeInterview: true, + }, + }, + + customSetup: async (driver, mockController, mockNode) => { + // This is almost a 1:1 copy of the default behavior, except that the callback never gets sent + const handleBrokenRequestNodeInfo: MockControllerBehavior = { + async onHostMessage(host, controller, msg) { + if (msg instanceof RequestNodeInfoRequest) { + // Notify the host that the message was sent + const res = new RequestNodeInfoResponse(host, { + wasSent: true, + }); + await controller.sendToHost(res.serialize()); + + // And never send a callback + return true; + } + }, + }; + mockController.defineBehavior(handleBrokenRequestNodeInfo); + }, + testBody: async (t, driver, node, mockController, mockNode) => { + // Circumvent the options validation so the test doesn't take forever + driver.options.timeouts.sendDataCallback = 1500; + + await assertZWaveError(t, () => node.requestNodeInfo(), { + errorCode: ZWaveErrorCodes.Controller_Timeout, + context: "callback", + }); + }, + }, +);