Skip to content

Mid-task pause from empty clean-water tank: errorCode and FAULTS_STR stay empty (r2532a fw 4.3.9_2199) #11

Description

@malard

Summary

Issue #3 (closed) covered the start-refusal case where an empty clean-water tank rejects vacuum.start() and surfaces as MiotError.CleanWaterTankEmpty = 107. That path works.

This issue is about the sibling case the closing comment didn't cover: the tank running empty mid-task, where the device pauses an in-progress clean. In that scenario Vacuum.state doesn't expose anything actionable — neither errorCode nor faults[] populate, and the taskLifecycle event does not fire aborted.

Net effect on a consumer: a scheduled clean silently fails to complete, the robot sits paused for hours, and the lib provides no signal that a user-actionable condition exists.

Observed live (verified)

Device: dreame.vacuum.r2532a, fw 4.3.9_2199, node-dreame 0.4.1.

The Dreamehome app shows: paused, low water level in clean water tank.

Vacuum.state from the live MQTT subscription (state_age fresh ~8s after a refresh ACK):

{
  "miotState": 21,
  "miotStateRaw": 21,
  "errorCode": null,
  "faults": [],
  "stuck": false,
  "battery": 100,
  "charging": null,
  ... (most fields null) ...
}

miotState: 21 is MiotState.PausedCleaning. The pause is real, the cause is real, but neither errorCode nor faults[] carries 107 (or any code) for the duration of this paused-mid-task condition.

No taskLifecycle: aborted event fired either — likely because the task hasn't been aborted from the device's point of view, it's been paused pending user action. From the consumer's point of view it's identical (schedule will not complete until intervention) but the event taxonomy doesn't reflect that.

What the consumer needs

A signal — any of these would suffice — that distinguishes a normal user-initiated pause from a paused-because-needs-attention state:

  1. errorCode populated on the paused-mid-task transition (matching the start-refusal behaviour). Simplest and consistent.
  2. taskLifecycle event with a new phase: \"paused\" (or \"halted\") carrying a reason field, mirroring the aborted envelope.
  3. A separate pauseReason field on Vacuum.state populated when miotState === PausedCleaning and the underlying cause is known.

Investigation hints

  • The Dreamehome app clearly knows the cause, so the signal is reaching the cloud — question is which MIoT property carries it.
  • The tank-fill request in Expose clean-water tank fill state (and dirty-water tank, optionally) #3 was deferred for lack of a graded fill code; this case may be the same signal observed from a different angle. If a WATER_TANK_LEVEL (or similar) property turns up in the dock cluster (siid 27/28 was suggested), it may flip 0→1 at this pause boundary.
  • FAULTS_STR (siid 4 piid 18) co-fires with ERROR for the start-refusal case per the existing docs — worth confirming whether it stays empty for the paused-mid-task case (we observed it empty here).

Reproduction

  1. Start a clean while the clean-water tank has just enough to run partway.
  2. Wait for the device to pause itself mid-task.
  3. Observe Vacuum.statemiotState: 21, errorCode: null, faults: [].

Happy to capture MQTT/HTTP traces from this device if a candidate siid/piid is identified.

Workaround in the consumer

For now, the dunbar-os consumer treats miotState === PausedCleaning lasting longer than N minutes with no progress as a heuristic "needs attention" signal, but that's heuristic — a typed signal from the lib is preferable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions