Skip to content

SHIELD remote support: discover ATVV handles per device (UNTESTED) - #3

Open
xbmc4lyfe wants to merge 2 commits into
mainfrom
feature/shield-remote-handle-discovery
Open

SHIELD remote support: discover ATVV handles per device (UNTESTED)#3
xbmc4lyfe wants to merge 2 commits into
mainfrom
feature/shield-remote-handle-discovery

Conversation

@xbmc4lyfe

Copy link
Copy Markdown
Contributor

Summary

Generalizes voice input so ATVV remotes other than the UR02 — notably the NVIDIA SHIELD remote — can work, without hardcoding any one remote's ATT handles.

Not yet tested on a SHIELD — opening for later on-device testing. No behavior change for the UR02 (handle discovery returns its existing values, verified offline against a real capture).

Background

All these remotes expose the same Google Android TV Voice (ATVV) GATT service (ab5e0001-...). But btmon reports ATT handles, not UUIDs, and the previous code matched the UR02's hardcoded handles (0x0042 status, 0x003f audio, 0x003d control). The SHIELD lays its attributes out at different handles, so its notifications were invisible.

Changes

  • Per-device discovery (discover_voice_endpoints in ble.py): finds the control/audio/status characteristics of a connected ATVV remote and derives each one's ATT value handle from its D-Bus path. BlueZ names the object by the declaration handle (.../charNNNN); the value handle used in ATT PDUs is declaration+1 — verified against the UR02 (char0041→0x0042, char003e→0x003f, char003c→0x003d).
  • btmon reader (service.py) now matches the discovered self._status_handle/self._audio_handle instead of literals; the backend takes the audio handle too.
  • New setting voice_device_address ("Voice remote MAC (blank = auto-detect)") to bind a specific remote when more than one is paired (UR02 vs SHIELD). Blank auto-picks the first remote exposing the service. Added to all five language files.
  • Bump to 3.0.1-beta.4.

Testing notes (for the SHIELD, later)

  1. Pair the SHIELD; set the MAC in the new setting if both remotes are connected.
  2. Watch kodi.log for Voice keyboard BLE: bound <dev> (status=0x…, audio=0x…).
  3. Confirm mic press logs mic button detected via btmon. If not, the declaration+1 value-handle assumption is the first thing to check — capture with btmon and compare the actual notification handle to the discovered one.
  4. If audio is garbled, the SHIELD may use a different notification chunk size than the UR02's 20-byte frames (see BLEPacketFramer).

Offline verification

py_compile clean; handle-mapping logic unit-tested against the real UR02 D-Bus paths (all three map to the correct value handles).

🤖 Generated with Claude Code

The btmon reader matched hardcoded ATT handles (0x0042 status,
0x003f audio) that are specific to the UR02, so no other ATVV remote
could be seen even though they all expose the same ab5e... service.

Discover the control/audio/status characteristics per device at service
start and derive their ATT value handles (BlueZ names the object by the
declaration handle .../charNNNN; the value handle is declaration+1 —
verified against the UR02: char0041->0x0042, char003e->0x003f,
char003c->0x003d). The btmon reader now matches the discovered handles,
so any device exposing the ATVV voice service works without hardcoding.

Add a 'Voice remote MAC (blank = auto-detect)' setting to bind a
specific remote when more than one is paired (e.g. UR02 vs SHIELD);
blank auto-picks the first remote exposing the voice service.

No behavior change for the UR02 (discovery returns its known handles).

UNTESTED on the SHIELD — the declaration+1 handle convention is the
main thing to confirm there; opening for later on-device testing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@xbmc4lyfe, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f278a21f-ee50-4568-b85e-18a7669cacc4

📥 Commits

Reviewing files that changed from the base of the PR and between 5740b92 and ee92a9a.

📒 Files selected for processing (2)
  • lib/audio_capture/ble.py
  • service.py
📝 Walkthrough

Walkthrough

The BLE voice flow now discovers ATVV characteristic paths and handles per remote, supports optional remote MAC selection, uses discovered handles during monitoring, and documents the 3.0.1-beta.4 release with localized settings text.

Changes

ATVV remote support

Layer / File(s) Summary
BLE characteristic discovery
lib/audio_capture/ble.py
Adds ATVV characteristic scanning, UUID-based path lookup, derived value handles, and control-handle defaults.
Dynamic BLE monitoring
service.py, lib/audio_capture/ble.py
Binds discovered endpoints, configures notifications and audio capture, performs the capability handshake, and matches status/audio events using remote-specific handles.
Remote selection and release metadata
resources/settings.xml, resources/language/.../strings.po, changelog.txt, addon.xml
Adds the BLE-only Voice remote MAC setting, localized labels, the beta.4 changelog entry, and the version increment.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: swackhamer

Poem

A rabbit hears the remotes hum,
Finds each handle, one by one.
MACs may guide the signal’s flight,
Status, audio—paths just right.
Beta blooms beneath moonlight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: per-device ATVV handle discovery for SHIELD and similar remotes.
Description check ✅ Passed The description directly matches the changeset, covering handle discovery, new settings, version bump, and SHIELD testing notes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/shield-remote-handle-discovery
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feature/shield-remote-handle-discovery

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/audio_capture/ble.py`:
- Around line 93-96: Update the device filtering logic around device_address so
it matches the complete /dev_<MAC>/ path component rather than checking whether
the normalized address appears anywhere in path. Preserve the existing
case-insensitive behavior and continue when the exact device component is
absent.

In `@service.py`:
- Around line 359-365: Update _start_ble_monitor() to inspect the return values
from both _start_notify() calls and return failure when either status or audio
notification setup fails, preserving retry behavior. Update _start_notify() to
treat any nonzero dbus-send exit status as failure. Keep _send_get_caps()
optional, reporting its failure as a warning without preventing successful
monitoring when notifications are active.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e79bb64-adfd-4d9e-9741-40463ceed5af

📥 Commits

Reviewing files that changed from the base of the PR and between cbe7808 and 5740b92.

📒 Files selected for processing (10)
  • addon.xml
  • changelog.txt
  • lib/audio_capture/ble.py
  • resources/language/resource.language.en_gb/strings.po
  • resources/language/resource.language.es_es/strings.po
  • resources/language/resource.language.fr_fr/strings.po
  • resources/language/resource.language.he_il/strings.po
  • resources/language/resource.language.it_it/strings.po
  • resources/settings.xml
  • service.py
📜 Review details
🧰 Additional context used
🪛 Ruff (0.15.21)
lib/audio_capture/ble.py

[warning] 107-107: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (10)
resources/settings.xml (1)

149-161: LGTM!

resources/language/resource.language.en_gb/strings.po (1)

105-108: LGTM!

resources/language/resource.language.es_es/strings.po (1)

105-108: LGTM!

resources/language/resource.language.fr_fr/strings.po (1)

105-108: LGTM!

resources/language/resource.language.he_il/strings.po (1)

108-111: LGTM!

resources/language/resource.language.it_it/strings.po (1)

104-108: LGTM!

changelog.txt (1)

1-7: LGTM!

addon.xml (1)

2-2: LGTM!

lib/audio_capture/ble.py (1)

2-55: LGTM!

Also applies to: 72-92, 97-178, 336-341, 426-427

service.py (1)

49-55: LGTM!

Also applies to: 323-358, 366-368, 436-463

Comment thread lib/audio_capture/ble.py
Comment thread service.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5740b9289f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread service.py
continue
line = raw.decode("utf-8", errors="replace").rstrip()

status_match = "Handle: " + self._status_handle in prev_line

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter btmon events by connection as well as ATT handle

When a MAC is configured, discover_voice_endpoints(addr) selects that remote's characteristic paths, but the btmon reader later treats any packet with the same ATT handle as selected. ATT handles are only unique within a peripheral's GATT database, so with two connected ATVV remotes that use the same status/audio handle (for example, two of the same model), pressing or streaming from the non-selected remote will still trip this match and can start or corrupt capture despite the configured MAC. The watcher needs to also associate btmon packets with the selected connection/device for the new binding setting to be reliable.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid — acknowledged and documented in _btmon_reader (ee92a9a). This only bites with two paired remotes of the same model (identical ATT handles); different models (e.g. UR02 vs SHIELD) use different handles and are unaffected, so the voice_device_address binding is reliable in the mixed-model case this PR targets. Full ACL-connection correlation is deferred because it needs two remotes to validate on hardware, and this PR is explicitly untested pending SHIELD testing. Leaving this thread open to track the follow-up.

CodeRabbit + Codex review on PR #3:

- _scan_chars: match the exact /dev_<MAC>/ path component instead of a
  substring, so a partial/ambiguous voice_device_address can't bind an
  unintended remote. Uppercase the whole component (incl. the 'dev_'
  literal) before comparing to path.upper() — a unit test caught that the
  naive form (lowercase 'dev_' vs path.upper()) never matches and would
  have broken discovery whenever a MAC was configured.
- _start_notify: treat a nonzero dbus-send exit as failure, tolerating
  only the benign already-notifying / InProgress case.
- _start_ble_monitor: return False if either status/audio notification
  fails to enable, so the retry loop stays alive instead of silently
  going deaf; GET_CAPS failure remains a non-fatal warning.
- Document the known handle-only matching limitation in _btmon_reader:
  two identical-model remotes sharing ATT handles could cross-trip; full
  ACL-connection correlation is deferred until testable with two remotes.

Verified offline: py_compile clean; unit tests cover exact-match
(case-insensitive, partial rejection, other-device rejection) and the
notify tolerance logic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee92a9adcb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread service.py
Comment on lines +347 to +349
self._ble_backend = BLEAudioCapture(
audio_handle=endpoints["audio_handle"]
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Defer publishing the BLE backend until monitoring is active

When either StartNotify call fails below, _start_ble_monitor() returns False and run() does not start the shared btmon watcher, but this earlier assignment leaves _worker() treating BLE as an externally fed backend. Any screen/RunScript activation during that retry window then calls start_recording(external_feed=True) with no external feed running, so it waits until timeout instead of failing fast or starting its own capture; assign self._ble_backend only after notifications succeed, or clear it on the failure path.

Useful? React with 👍 / 👎.

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