Repository navigation
feat(api): fix touch velocity, name buttons, close stalled subscribers - #14
Open
hyperb1iss wants to merge 4 commits into
Open
hyperb1iss wants to merge 4 commits into
hyperb1iss wants to merge 4 commits into
Conversation
Touch velocity fields are two's complement bytes. The ROLI SDK converts them with IntegerWithBitSize::toBipolarFloat, which sign-extends the raw value and divides by 128. blocksd treated them as offset binary around 128, so a small positive velocity of 1 decoded as -1.0 and the most negative value decoded as 0.0. Golden vectors from the SDK formula now cover zero, both signs, and both extremes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Button events only said press or release, so clients could not tell the three LUMI Keys buttons apart. They now carry the protocol button index, its ROLI SDK function name, and the device timestamp. The SDK reports a button as an index into each model's button list, so the registry holds those lists in SDK order and the device group resolves the name from the block type. Touch events also carry the device timestamp, which lets consumers space out a burst of touches delivered together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a subscriber's bounded queue filled, the broadcaster removed the subscription but left the connection open. The client kept a healthy socket that would never deliver another event, with no way to notice. A long-lived consumer such as a lighting bridge would go quiet until it restarted. The broadcaster now shuts down the overflowed queue and remembers it. Each transport's event writer sees the shutdown, sends an error that explains the overflow, and closes the connection (WebSocket status 1008), so clients reconnect, resubscribe, and rebuild state. Other subscribers are unaffected. Broadcasts run inside device callbacks, so they must never raise. A subscribe on an overflowed connection is refused, and a shut-down queue that somehow remains subscribed is dropped instead of raising QueueShutDown into the device group. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The message tables described touch and control button payloads as starting with a 7-bit device index. The device index lives in the SysEx header; the payload starts with a 5-bit timestamp offset, as the SDK's HostPacketDecoder and blocksd's decoder both read it. The device reference also still said button events omit the protocol button ID, which is no longer true. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 of 15 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hypercolor is about to consume the blocksd event stream so Lightpad touches and LUMI key presses can drive effects. Three problems in that stream would have leaked straight into the integration. Touch velocity decoded with the wrong sign and scale, button events could not say which button was pressed, and a subscriber that fell behind silently stopped receiving events while its connection stayed open. This change fixes all three and corrects the protocol docs that described touch and button payloads wrongly.
🛠️ What changes
Touch velocity is signed. The firmware sends
vx,vy, andvzas two's complement bytes, which the ROLI SDK converts withIntegerWithBitSize::toBipolarFloat. blocksd treated them as offset binary around 128, so a small positive velocity of 1 decoded as -1.0 and the most negative value decoded as 0.0. Velocities now match the SDK exactly: raw values map to[-1, 127/128]and zero stays zero.Buttons say which button. Button events now carry the protocol
button_id, its SDK function name asbutton, and the devicetimestamp. The SDK reports a button as an index into each model's button list, so the registry holds those lists in SDK order and the device group resolves the name from the block type.button_id→buttonmode, 1down, 2upmodedown,upAn index a model does not define reports
"button": nullwith the raw ID intact. Touch events also gaintimestamp, the device clock in milliseconds, so a consumer can space out a burst of touches delivered together. Every change is additive; existing fields keep their meaning apart from the velocity fix.Overflowing subscribers are disconnected. Each subscriber still has a 1024-event queue. When it fills, the broadcaster now shuts the queue down and remembers it. The transport's event writer sees the shutdown, sends
{"type": "error", "message": "event queue overflowed; reconnect and subscribe again"}, and closes the connection (WebSocket close status 1008). Clients learn they missed events and can reconnect, resubscribe, and rediscover instead of holding a healthy-looking socket that never delivers another event. Other subscribers are unaffected.Docs. The message tables described touch and button payloads as starting with a 7-bit device index. The device index lives in the SysEx header; the payload starts with a 5-bit timestamp offset, which is how both the SDK decoder and blocksd read it. The device reference no longer claims button IDs are missing.
🎯 Invariant: broadcasts never raise into device callbacks
Event broadcasts run inside
DeviceGrouptouch and button callbacks, so an exception there would tear down the USB group for every client. The overflow path keeps that property in two ways. Asubscribeon a connection whose queue was shut down is refused. A shut-down queue that is still subscribed is dropped on the next broadcast instead of raisingQueueShutDown.🧪 Validation
just checkon1dd88d2passes: 670 Python tests, Ruff, ty, dashboard checks, docs build, and the fresh installed-wheel check.toBipolarFloatwith int32 and float32 semantics.DeviceGroupand the Unix socket produce the documented wire output for LUMI, Lightpad M, and Loop serials.Physical touch and button events were not captured for this change. The protocol mapping comes from the SDK and the decoder tests, not from live hardware.
🔍 Review focus
Start with
_broadcastinsrc/blocksd/api/events.pyand the two event writers insrc/blocksd/api/server.py. One boundary is deliberate: a client that stops reading entirely keeps its socket until its pending writes drain, because the writer is parked indrain()and never sees the shutdown. The subscription is already gone at that point, so the cost is one idle connection, and a client that resumes reading receives the error and the close. The docs describe it that way rather than promising an immediate close.📌 Follow-ups
The Hypercolor consumer is in progress on
nova/device-input(spec 82): a read-only event connection per blocksd instance, device-attributed touch and button input, and routing into Hypercolor's interaction pipeline. How LUMI touchxmaps onto the 24 key LEDs still needs a hardware capture.🤖 Generated with Claude Code