Skip to content

Tasks Fire and Forget

Rin Nguyen edited this page Jun 18, 2026 · 1 revision

Fire-and-Forget: Task Breakdown

Reference: docs/plan-fire-and-forget-v2.md


Phase 1 — Trivial send() conversions

Task 1.1 — Convert findMyRobot from get() to send()

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts line 103

Change: await this.client.get(duid, request)await this.client.send(duid, request)

Definition of Done:

  • findMyRobot() calls client.send() not client.get()
  • npm run type-check exits 0

Task 1.2 — Update getDeviceStatus interface return type to void

File: src/roborockCommunication/protocol/dispatcher/abstractMessageDispatcher.ts line 10

Change: Promise<DeviceStatus | undefined>Promise<void>

Definition of Done:

  • Interface signature is getDeviceStatus(duid: string): Promise<void>
  • npm run type-check exits 0 (all implementations must also be updated in same step if TS complains)

Task 1.3 — Convert V10.getDeviceStatus() from get() to send()

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts lines 30-40

Change: Remove response handling, call client.send(), return void

Definition of Done:

  • Method signature is Promise<void>
  • Calls client.send() not client.get()
  • CloudMessageResult import removed if unused
  • debugStringify import removed if unused
  • npm run type-check exits 0

Task 1.4 — Convert Q10.getNetworkInfo() from get() to send()

File: src/roborockCommunication/protocol/dispatcher/Q10MessageDispatcher.ts lines 36-40

Change: Replace client.get() + response log with client.send()

Definition of Done:

  • Calls client.send() not client.get()
  • Response log line removed
  • debugStringify import removed if unused
  • npm run type-check exits 0

Task 1.5 — Convert Q10.getDeviceStatus() from get() to send()

File: src/roborockCommunication/protocol/dispatcher/Q10MessageDispatcher.ts lines 47-50

Change: client.get()client.send(), return type Promise<void>

Definition of Done:

  • Method signature is Promise<void>
  • Calls client.send() not client.get()
  • npm run type-check exits 0

Task 1.6 — Convert Q10.getMapInfo() from get() to send()

File: src/roborockCommunication/protocol/dispatcher/Q10MessageDispatcher.ts lines 58-67

Change: client.get()client.send(), remove response/log, return stub

Definition of Done:

  • Calls client.send() not client.get()
  • Response variable and log line removed
  • Returns new MapInfo({ max_multi_map: 0, max_bak_map: 0, multi_map_count: 0, map_info: [] })
  • npm run type-check exits 0

Task 1.7 — Convert Q10.getRoomMap() from get() to send()

File: src/roborockCommunication/protocol/dispatcher/Q10MessageDispatcher.ts lines 70-73

Change: client.get()client.send(), return []

Definition of Done:

  • Calls client.send() not client.get()
  • Returns []
  • npm run type-check exits 0

Task 1.8 — Convert Q7.getMapInfo() from get() to send()

File: src/roborockCommunication/protocol/dispatcher/Q7MessageDispatcher.ts lines 82-93

Change: client.get()client.send(), remove response/log, return stub

Definition of Done:

  • Calls client.send() not client.get()
  • Response variable and log line removed
  • Returns new MapInfo({ max_multi_map: 0, max_bak_map: 0, multi_map_count: 0, map_info: [] })
  • npm run type-check exits 0

Task 1.9 — Convert Q7.getRoomMap() from get() to send()

File: src/roborockCommunication/protocol/dispatcher/Q7MessageDispatcher.ts lines 95-106

Change: client.get()client.send(), remove response/log, return []

Definition of Done:

  • Calls client.send() not client.get()
  • Response variable and log line removed
  • Returns []
  • npm run type-check exits 0

Phase 1 Gate

  • All tasks 1.1–1.9 complete
  • npm run type-check exits 0
  • npm test passes

Phase 2 — Introduce OneShotResponseListener + query()

Task 2.1 — Create OneShotResponseListener

New file: src/roborockCommunication/routing/listeners/oneShotResponseListener.ts

Definition of Done:

  • File exists at the path above
  • Implements AbstractMessageListener (name, duid, onMessage())
  • Has waitFor(): Promise<T> that resolves on first parseFn match, rejects on timeout
  • Has settled flag — onMessage() is no-op after resolving or rejecting
  • Timeout default is MESSAGE_TIMEOUT_MS from src/constants/index.js
  • npm run type-check exits 0

Task 2.2 — Add query<T>() to Client interface

File: src/roborockCommunication/routing/client.ts

Definition of Done:

  • query<T>(duid, request, parseFn, timeoutMs?) method signature present in interface
  • ResponseMessage imported from '../models/index.js'
  • npm run type-check exits 0

Task 2.3 — Add query<T>() to AbstractClient

File: src/roborockCommunication/routing/abstractClient.ts

Definition of Done:

  • public async query<T>() method implemented
  • Creates OneShotResponseListener, registers on responseBroadcaster, calls sendInternal(), awaits waitFor()
  • Errors caught and logged, returns undefined on failure
  • OneShotResponseListener imported
  • npm run type-check exits 0

Task 2.4 — Add query<T>() to ClientRouter

File: src/roborockCommunication/routing/clientRouter.ts

Definition of Done:

  • public async query<T>() method implemented
  • Creates OneShotResponseListener, registers on broadcasterFactory, calls send(), awaits waitFor()
  • Errors caught and logged, returns undefined on failure
  • OneShotResponseListener imported
  • npm run type-check exits 0

Task 2.5 — Add parseV1Result<T>() module-level helper to V10MessageDispatcher

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts

Definition of Done:

  • parseV1Result<T>(msg, messageId) function exists at module level (before the class)
  • Tries Protocol.rpc_response, then Protocol.general_response, then Protocol.general_request
  • Returns undefined if no matching DPS or dps.id !== messageId
  • Returns result[0] for arrays, result for non-arrays
  • DpsPayload, Protocol, ResponseMessage imported as needed
  • npm run type-check exits 0

Task 2.6 — Migrate V10.getNetworkInfo() to query()

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts

Definition of Done:

  • Calls client.query<NetworkInfo>() with parseV1Result parseFn
  • No client.get() call remains
  • npm run type-check exits 0

Task 2.7 — Migrate V10.getSerialNumber() to query()

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts

Definition of Done:

  • Calls client.query<{ serial_number: string }[]>() with inline parseFn returning dps.result (not result[0])
  • No client.get() call remains
  • Return logic (response[0].serial_number ?? duid) preserved
  • npm run type-check exits 0

Task 2.8 — Migrate V10.getHomeMap() to query()

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts

Definition of Done:

  • Calls client.query<MapRoomResponse>() with parseV1Result parseFn
  • Returns response ?? {}
  • No client.get() call remains
  • npm run type-check exits 0

Task 2.9 — Migrate V10.getMapInfo() to query()

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts

Definition of Done:

  • Calls client.query<MultipleMapDto>() (not MultipleMapDto[]) with parseV1Result parseFn
  • Passes response directly to new MapInfo() (not response[0]parseV1Result already unwraps it)
  • Fallback to empty MapInfo on undefined
  • No client.get() call remains
  • npm run type-check exits 0

Task 2.10 — Migrate V10.getRoomMap() to query()

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts

Definition of Done:

  • Calls client.query<RawRoomMappingData>() with inline parseFn (dps.result directly)
  • Returns response ?? []
  • No client.get() call remains
  • npm run type-check exits 0

Task 2.11 — Migrate V10.getCustomMessage() to query()

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts

Definition of Done:

  • Calls client.query<T>() with parseV1Result parseFn using def.messageId
  • Returns result as T
  • Method is now async
  • No client.get() call remains
  • npm run type-check exits 0

Task 2.12 — Fix V10.changeCleanMode() read-before-write guard

File: src/roborockCommunication/protocol/dispatcher/V10MessageDispatcher.ts

Definition of Done:

  • getCustomMessage('get_custom_mode') read removed
  • currentMopMode, smartMopMode, customMopMode, customMopMode variables removed
  • Guard replaced with: if (mopRoute && mopRoute !== smartMopRoute) { send set_mop_mode(customMopRoute) }
  • Remaining sends (set_custom_mode, set_water_box_custom_mode, set_mop_mode) unchanged
  • VacuumSuctionPower import removed if no longer used
  • npm run type-check exits 0

Task 2.13 — Migrate Q10.getCustomMessage() to query()

File: src/roborockCommunication/protocol/dispatcher/Q10MessageDispatcher.ts

Definition of Done:

  • Calls client.query<T>() with B01 parseFn (extract Object.values(msg.body.data)[0])
  • Returns result as T
  • No client.get() call remains
  • npm run type-check exits 0

Task 2.14 — Migrate Q7.getCustomMessage() to query()

File: src/roborockCommunication/protocol/dispatcher/Q7MessageDispatcher.ts

Definition of Done:

  • Calls client.query<T>() with B01 parseFn (same pattern as Q10)
  • Returns result as T
  • No client.get() call remains
  • npm run type-check exits 0

Phase 2 Gate

  • All tasks 2.1–2.14 complete
  • Zero client.get() calls remain in any dispatcher (grep -r "client\.get(" src/)
  • npm run type-check exits 0
  • npm test passes

Phase 3 — Delete tracker infrastructure

Task 3.1 — Remove get<T>() from Client interface

File: src/roborockCommunication/routing/client.ts

Definition of Done:

  • get<T>() line deleted from interface
  • npm run type-check exits 0

Task 3.2 — Remove get<T>() and responseTracker from AbstractClient

File: src/roborockCommunication/routing/abstractClient.ts

Definition of Done:

  • responseTracker: PendingResponseTracker constructor param removed (line 30)
  • PendingResponseTracker import removed (line 13)
  • public async get<T>() method deleted (lines 81-92)
  • npm run type-check exits 0

Task 3.3 — Remove get<T>() from ClientRouter

File: src/roborockCommunication/routing/clientRouter.ts

Definition of Done:

  • public async get<T>() method deleted (lines 103-109)
  • npm run type-check exits 0

Task 3.4 — Remove tryResolve() from ResponseBroadcaster interface

File: src/roborockCommunication/routing/listeners/responseBroadcaster.ts

Definition of Done:

  • tryResolve(response: ResponseMessage): void; line deleted
  • npm run type-check exits 0

Task 3.5 — Remove tracker from V1ResponseBroadcaster

File: src/roborockCommunication/routing/listeners/v1ResponseBroadcaster.ts

Definition of Done:

  • tracker: V1PendingResponseTracker constructor param removed
  • V1PendingResponseTracker import removed
  • tryResolve() method deleted
  • unregister() simplified to this.listeners = []
  • npm run type-check exits 0

Task 3.6 — Remove tracker from B01ResponseBroadcaster

File: src/roborockCommunication/routing/listeners/b01ResponseBroadcaster.ts

Definition of Done:

  • tracker: B01PendingResponseTracker constructor param removed
  • B01PendingResponseTracker import removed
  • waitFor() method deleted
  • tryResolve() method deleted
  • unregister() simplified to this.listeners = []
  • npm run type-check exits 0

Task 3.7 — Simplify ResponseBroadcasterFactory

File: src/roborockCommunication/routing/listeners/responseBroadcasterFactory.ts

Definition of Done:

  • implements PendingResponseTracker removed from class declaration
  • v1Tracker and b01Tracker fields deleted
  • Tracker imports (B01PendingResponseTracker, V1PendingResponseTracker, PendingResponseTracker) deleted
  • RequestMessage import removed if unused
  • tryResolve(), waitFor(), cancelAll(), getTrackerForDevice() methods deleted
  • Constructor updated: tracker instantiation removed, broadcaster construction updated (no tracker args)
  • getBroadcasterForResponse() kept (still used by onMessage())
  • npm run type-check exits 0

Task 3.8 — Remove tryResolve() from LocalNetworkClient

File: src/roborockCommunication/local/localClient.ts

Definition of Done:

  • this.responseBroadcaster.tryResolve(response) line deleted (line ~221)
  • PendingResponseTracker import removed (line 13)
  • responseTracker: PendingResponseTracker constructor param removed (line 34)
  • super() call updated to remove tracker arg (line 36)
  • npm run type-check exits 0

Task 3.9 — Remove tryResolve() from MQTTClient

File: src/roborockCommunication/mqtt/mqttClient.ts

Definition of Done:

  • this.responseBroadcaster.tryResolve(response) line deleted (line ~262)
  • PendingResponseTracker import removed (line 9)
  • responseTracker: PendingResponseTracker constructor param removed (line 29)
  • super() call updated to remove tracker arg (line 31)
  • npm run type-check exits 0

Task 3.10 — Update ClientRouter client construction

File: src/roborockCommunication/routing/clientRouter.ts

Definition of Done:

  • MQTTClient construction (line 28) removes this.broadcasterFactory as 5th arg (responseTracker)
  • LocalNetworkClient construction (lines 43-49) removes this.broadcasterFactory as 6th arg (responseTracker)
  • npm run type-check exits 0

Task 3.11 — Delete tracker source files

Files to delete:

  • src/roborockCommunication/routing/services/pendingResponseTracker.ts
  • src/roborockCommunication/routing/services/v1PendingResponseTracker.ts
  • src/roborockCommunication/routing/services/b01PendingResponseTracker.ts

Definition of Done:

  • All three files deleted from disk
  • No remaining imports of these files anywhere (grep -r "pendingResponseTracker\|v1PendingResponseTracker\|b01PendingResponseTracker" src/)
  • npm run type-check exits 0

Task 3.12 — Delete tracker test files

Files to delete:

  • src/tests/roborockCommunication/routing/services/v1PendingResponseTracker.test.ts
  • src/tests/roborockCommunication/routing/services/b01PendingResponseTracker.test.ts

Definition of Done:

  • Both test files deleted from disk
  • npm test passes

Task 3.13 — Update responseBroadcasterFactory.test.ts

File: src/tests/roborockCommunication/routing/listeners/responseBroadcasterFactory.test.ts

Remove test cases:

  • 'should route tryResolve to V1 broadcaster for V1 responses'
  • 'should route tryResolve to B01 broadcaster for B01 responses'
  • 'should route waitFor to V1 tracker for V1 devices'
  • 'should route waitFor to B01 tracker for B01 devices'
  • 'should cancelAll pending requests from B01 tracker'
  • 'should fall back to V1 tracker when protocol version is not B01'

Definition of Done:

  • All 6 test cases above removed
  • RequestMessage import removed if unused
  • Remaining tests pass: npm test

Task 3.14 — Update v1ResponseBroadcaster.test.ts

File: src/tests/roborockCommunication/routing/listeners/v1ResponseBroadcaster.test.ts

Definition of Done:

  • V1PendingResponseTracker removed from imports and beforeEach
  • Any tryResolve test cases removed
  • Broadcaster construction updated: new V1ResponseBroadcaster(logger) (no tracker param)
  • npm test passes

Task 3.15 — Update b01ResponseBroadcaster.test.ts

File: src/tests/roborockCommunication/routing/listeners/b01ResponseBroadcaster.test.ts

Definition of Done:

  • B01PendingResponseTracker removed from imports and beforeEach
  • tryResolve and waitFor test cases removed
  • Broadcaster construction updated: new B01ResponseBroadcaster(logger) (no tracker param)
  • npm test passes

Task 3.16 — Create oneShotResponseListener.test.ts

New file: src/tests/roborockCommunication/routing/listeners/oneShotResponseListener.test.ts

Required test cases:

  1. should resolve when parseFn returns a value
  2. should ignore messages where parseFn returns undefined
  3. should reject on timeout
  4. should be a no-op after settling

Definition of Done:

  • All 4 test cases implemented using vitest
  • Fake timers used for timeout test (vi.useFakeTimers() / vi.useRealTimers())
  • No client.get() or tracker references
  • npm test passes

Phase 3 Gate

  • All tasks 3.1–3.16 complete
  • Zero references to PendingResponseTracker, tryResolve, waitFor, cancelAll in non-test src (grep -r "tryResolve\|PendingResponseTracker" src/ --include="*.ts" --exclude="*.test.ts")
  • npm run type-check exits 0
  • npm test passes

Clone this wiki locally