Skip to content

fix(scanner): off-by-one send delay and dangling getBulbInfoByID return#533

Draft
bluetoothbot wants to merge 1 commit into
lightinglibs:masterfrom
bluetoothbot:koan/scanner-discovery-bugs
Draft

fix(scanner): off-by-one send delay and dangling getBulbInfoByID return#533
bluetoothbot wants to merge 1 commit into
lightinglibs:masterfrom
bluetoothbot:koan/scanner-discovery-bugs

Conversation

@bluetoothbot

@bluetoothbot bluetoothbot commented Jun 19, 2026

Copy link
Copy Markdown

What: Fix two latent bugs in the synchronous BulbScanner discovery path.

Why: Both bugs are absent from the async sibling aioscanner._async_send_messages, which already does it right — the sync code drifted out of sync.

  • _send_messages guarded the inter-message delay with idx != len(messages), which is true for every iteration (idx maxes at len-1). It slept the full MESSAGE_SEND_INTERLEAVE_DELAY (0.4s) after the last message too, adding needless latency to every sync scan.
  • getBulbInfoByID returned the loop variable b after the loop. On an empty discovery set this raises UnboundLocalError; on a miss it returns an arbitrary non-matching bulb — silently wrong data handed to the caller.

How:

  • Match the async version: compute last_idx = len(messages) - 1 and skip the trailing sleep.
  • Return None on a miss and widen the annotation to FluxLEDDiscovery | None (the idiomatic lookup-miss contract; callers get a clear None instead of the wrong device).

Testing: New tests/test_scanner.py (5 tests) covers match / miss / empty for getBulbInfoByID and the delay-count for _send_messages. Full suite: 145 passed. ruff clean.


Quality Report

Changes: 2 files changed, 80 insertions(+), 3 deletions(-)

Code scan: clean

Tests: failed (FAILED)

Branch hygiene: clean

Generated by Kōan

…yID return

The sync BulbScanner had two latent bugs that the async sibling (aioscanner.
_async_send_messages) does not:

- _send_messages checked `idx != len(messages)`, which is always true, so it
  slept MESSAGE_SEND_INTERLEAVE_DELAY (0.4s) after the final message too. Match
  the async version's `idx != len(messages) - 1`.
- getBulbInfoByID returned the loop variable `b` after the loop, raising
  UnboundLocalError when no bulbs were discovered and returning an arbitrary
  non-matching bulb on a miss. Return None and widen the annotation to
  FluxLEDDiscovery | None.

Add tests/test_scanner.py covering both paths.
@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
flux_led/scanner.py 80.64% <100.00%> (+3.88%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant