You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Baristas weigh the dose on one scale and the shot on another. Today the gateway holds exactly one scale, so the second one cannot be connected at all.
I have this working and offered as #834 (issue #833), but the shape is the part worth discussing before anyone reviews the diff, because there are two quite different ways to get there and I deliberately took the smaller one.
Where the single scale is baked in
classScaleController {
Scale? _scale;
connectionGeneration, currentWeightSnapshot and weightSnapshot all hang off that slot, and through them ShotSequencer — tare at shot start, stop-at-weight, the shot record. The REST and WebSocket surface is singular to match: PUT /api/v1/scale/tare, ws/v1/scale/snapshot, with no way to name a scale.
Concurrent multi-scale streams are already logged as a P2 gap from the Bengle work (doc/plans/archive/bengle-integrated-scale/2026-05-05-bengle-integrated-scale.md, D1).
The two shapes
Role-keyed multi-scale. Turn _scale into something like Map<ScaleRole, Scale>, make the endpoints take a role, and let any number of scales exist with defined jobs. This is the better end state and I assume it is where the project lands eventually.
A second scale reserved by id, brewing path untouched. What I implemented:
DosingScaleController is a new class with its own slot, connection subscription and snapshot stream. ScaleController is not modified.
A dosingScaleId setting beside preferredScaleId. The brewing path's entire exposure is one exclusion in _runScalePhase — the reserved scale is filtered out of the list handed to _applyScalePolicy, and with dosingScaleId unset the function returns down the original path untouched.
New endpoints beside the existing ones rather than a role parameter on them: ws/v1/scale/dosing/snapshot and PUT /api/v1/scale/dosing/tare are new, ws/v1/scale/snapshot and PUT /api/v1/scale/tare unchanged.
Nothing in a shot reads it. A dose is weighed before extraction, so the dosing weight reaches the skin on its own socket and goes into the workflow as targetDoseWeight the way a typed dose does.
Why the smaller one
The role map is tidier and closer to the end state, but it is a change to the class every part of the brewing path reads from, which puts tare-before-pour, stop-at-weight and shot recording on the diff. What is actually being bought is a dosing readout, which no part of brewing consumes. Paying for that with regression risk in the shot path seemed like the wrong trade for a feature this size.
The cost is real and I would rather name it than not: two classes end up sharing shape without sharing code. If role-keyed multi-scale lands, this should be removed rather than kept alongside it — it is a stopgap, not a foundation.
Two scales connected at once is verified on hardware, so BLE adapter contention is not the open question here.
What I would like maintainers' view on
Is the additive shape acceptable as an interim, or would you rather this waited for a proper role-keyed design so there is only ever one way to address a scale?
If role-keyed multi-scale is on the roadmap in any form, I would rather put this effort into that instead. Is there an existing design to build against?
On the endpoints specifically — separate paths vs a role parameter on the existing ones. I chose separate paths because adding a parameter changes a request every existing skin already sends, for a feature none of them use. Happy to be told the opposite is preferred.
Full design doc with the alternatives rejected and why: doc/plans/archive/dosing-scale/2026-09-12-dosing-scale.md in the PR.
I am happy to rework or withdraw it — the reason for raising the design here rather than only in the PR is that "should this exist in this shape at all" is a maintainer call, not a review comment.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Baristas weigh the dose on one scale and the shot on another. Today the gateway holds exactly one scale, so the second one cannot be connected at all.
I have this working and offered as #834 (issue #833), but the shape is the part worth discussing before anyone reviews the diff, because there are two quite different ways to get there and I deliberately took the smaller one.
Where the single scale is baked in
connectionGeneration,currentWeightSnapshotandweightSnapshotall hang off that slot, and through themShotSequencer— tare at shot start, stop-at-weight, the shot record. The REST and WebSocket surface is singular to match:PUT /api/v1/scale/tare,ws/v1/scale/snapshot, with no way to name a scale.Concurrent multi-scale streams are already logged as a P2 gap from the Bengle work (
doc/plans/archive/bengle-integrated-scale/2026-05-05-bengle-integrated-scale.md, D1).The two shapes
Role-keyed multi-scale. Turn
_scaleinto something likeMap<ScaleRole, Scale>, make the endpoints take a role, and let any number of scales exist with defined jobs. This is the better end state and I assume it is where the project lands eventually.A second scale reserved by id, brewing path untouched. What I implemented:
DosingScaleControlleris a new class with its own slot, connection subscription and snapshot stream.ScaleControlleris not modified.dosingScaleIdsetting besidepreferredScaleId. The brewing path's entire exposure is one exclusion in_runScalePhase— the reserved scale is filtered out of the list handed to_applyScalePolicy, and withdosingScaleIdunset the function returns down the original path untouched.ws/v1/scale/dosing/snapshotandPUT /api/v1/scale/dosing/tareare new,ws/v1/scale/snapshotandPUT /api/v1/scale/tareunchanged.targetDoseWeightthe way a typed dose does.Why the smaller one
The role map is tidier and closer to the end state, but it is a change to the class every part of the brewing path reads from, which puts tare-before-pour, stop-at-weight and shot recording on the diff. What is actually being bought is a dosing readout, which no part of brewing consumes. Paying for that with regression risk in the shot path seemed like the wrong trade for a feature this size.
The cost is real and I would rather name it than not: two classes end up sharing shape without sharing code. If role-keyed multi-scale lands, this should be removed rather than kept alongside it — it is a stopgap, not a foundation.
Two scales connected at once is verified on hardware, so BLE adapter contention is not the open question here.
What I would like maintainers' view on
roleparameter on the existing ones. I chose separate paths because adding a parameter changes a request every existing skin already sends, for a feature none of them use. Happy to be told the opposite is preferred.Full design doc with the alternatives rejected and why:
doc/plans/archive/dosing-scale/2026-09-12-dosing-scale.mdin the PR.I am happy to rework or withdraw it — the reason for raising the design here rather than only in the PR is that "should this exist in this shape at all" is a maintainer call, not a review comment.
All reactions