Skip to content

[Bug] Deliberate machine disconnect emits false 'scaleDisconnected ... unexpectedly' for the machine's internal scale #766

Description

@tadelv

Summary

Disconnecting a machine through PUT /api/v1/devices/disconnect (app-initiated, clean) makes StatusPublisher emit an error-severity scaleDisconnected — "Scale disconnected unexpectedly." — for the machine's controller-owned internal scale (bengle-internal-<machineId>). The machine's own disconnect is correctly suppressed as expected; the internal scale's cascade is not.

Evidence (tablet logs, two independent occurrences)

Every deliberate API disconnect of a connected machine produced the error; a real USB detach produces both errors (correctly):

08:08:15.640 PUT /api/v1/devices/disconnect (machine usb-2e8a-a-<serial>, 200)
08:08:15.652 ScaleController - scale connection update: disconnected
08:08:15.655 WARNING StatusPublisher - emit error: kind=scaleDisconnected message=Scale disconnected unexpectedly. deviceId=bengle-internal-usb-2e8a-a-<serial>
   (no machineDisconnected error — machine expectation consumed correctly)

08:11:57.292 PUT /api/v1/devices/disconnect (machine usb-2e8a-a-<serial>, 200)
08:11:57.304 ScaleController - scale connection update: disconnected
08:11:57.306 WARNING StatusPublisher - emit error: kind=scaleDisconnected message=Scale disconnected unexpectedly. deviceId=bengle-internal-usb-2e8a-a-<serial>

By contrast, a physical USB_DETACHED at 07:58:51 emitted both machineDisconnected and scaleDisconnected — the expected behavior for a genuine unexpected drop.

Root cause

  1. BengleVirtualScale.connectionState is _machine.connectionState (lib/src/models/device/impl/bengle/bengle_virtual_scale.dart) — the internal scale mirrors the machine's connection-state stream, so a machine disconnect necessarily fires the scale's disconnect in the same emission.
  2. The API disconnect path (lib/src/services/webserver/devices_handler.dart, _handleDisconnect) marks only the target device as an expected disconnect: _connectionManager.markExpectingDisconnect(device.deviceId).
  3. DisconnectSupervisor._handleScaleDisconnect (lib/src/controllers/connection/disconnect_supervisor.dart) consumes expectations by scale id; none was registered for bengle-internal-<machineId> — so the "unexpectedly" error fires.

So a clean, app-initiated machine disconnect always reports its own internal scale as an unexpected drop. ConnectionManager.disconnectMachine() has the same gap (marks only de1.deviceId). The sleep flow at lib/src/controllers/de1_state_manager.dart:593 does mark the scale, which is why only some flows are affected.

Suggested fix directions

  • When a machine is deliberately disconnected, also mark its controller-owned scale(s) as expected — e.g. in disconnectMachine() and the devices-handler disconnect path, mark bengle-internal-<machineId> alongside the machine id.
  • Or, in DisconnectSupervisor, treat a scale disconnect as expected when it mirrors a machine whose disconnect was just accepted (scale id == bengle-internal-<lastKnownMachineId> and that machine's expectation was consumed).

Repro

  1. Connect a Bengle over USB (machine + internal bengle-internal-* scale become connected).
  2. PUT /api/v1/devices/disconnect with the machine id.
  3. Observe StatusPublisher warning error scaleDisconnected "unexpectedly" for the internal scale, even though the disconnect was deliberate.

Environment: Android tablet, Decaid with a Bengle composite USB device (machine on interface 0, serial scrubbed). Severity: low — no functional loss (the scale does disconnect with the machine), but it surfaces a false error banner/status to skins and pollutes telemetry with spurious scaleDisconnected events.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingready-for-agentFully specified, ready for an AFK agent

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions