-
Notifications
You must be signed in to change notification settings - Fork 2
Bluetooth CLI actions, pair/trust connect #234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #234 +/- ##
==========================================
- Coverage 15.41% 14.71% -0.70%
==========================================
Files 104 106 +2
Lines 17120 18493 +1373
==========================================
+ Hits 2639 2722 +83
- Misses 14481 15771 +1290
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1 file with indirect coverage changes 🚀 New features to boost your workflow:
|
|
|
||
| // List available Bluetooth devices (uses BlueZ) | ||
| rpc ListBluetoothDevices(ListBluetoothDevicesRequest) returns (ListBluetoothDevicesResponse); | ||
|
|
||
| // Start Bluetooth discovery scan | ||
| rpc StartBluetoothScan(StartBluetoothScanRequest) returns (StartBluetoothScanResponse); | ||
|
|
||
| // Stop Bluetooth discovery scan | ||
| rpc StopBluetoothScan(StopBluetoothScanRequest) returns (StopBluetoothScanResponse); | ||
|
|
||
| // Connect to a Bluetooth device | ||
| rpc ConnectBluetoothDevice(ConnectBluetoothDeviceRequest) returns (ConnectBluetoothDeviceResponse); | ||
|
|
||
| // Disconnect from a Bluetooth device | ||
| rpc DisconnectBluetoothDevice(DisconnectBluetoothDeviceRequest) returns (DisconnectBluetoothDeviceResponse); | ||
|
|
||
| // Forget (remove) a Bluetooth device | ||
| rpc ForgetBluetoothDevice(ForgetBluetoothDeviceRequest) returns (ForgetBluetoothDeviceResponse); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to bundle this in a new service proto
| bool connected = 5; | ||
|
|
||
| // Device type/class (e.g., "audio-headset", "keyboard", "mouse") | ||
| string device_type = 6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a known enum we can reflect here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I will make a task for this in Linear to track it, will address in a future PR.
| let effectiveTimeoutSeconds = | ||
| timeoutSeconds == 0 ? Self.defaultScanTimeoutSeconds : timeoutSeconds | ||
|
|
||
| _ = Task.detached { [logger, self] in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't need to be a (detached) background task. Can we just call this inline?
| if timeoutSeconds > 0 { | ||
| Task { [logger] in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be worth having a func run() async throws on BluetoothManager, so we can signal the run-loop with start/stop commands in a structured way. If two calls happen simultaneously, both asking to scan with timeout, they'll conflict.
| signalSource.cancel() | ||
| signal(SIGINT, SIG_DFL) | ||
| Task { | ||
| _ = try? await agent.stopBluetoothScan(.init()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't work, the signal will happen immediately and not wait for the Task. You wan tto queue the signal after running this stopBluetoothScan.
| } | ||
|
|
||
| guard let targetDevice else { | ||
| return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a warning?
|
@Joannis I added |
- Fix race condition in BluetoothCommand.swift where defer block used fire-and-forget Task for stopBluetoothScan, causing the function to return before scan was actually stopped - Convert BluetoothManager from struct to actor for proper concurrency - Add run() method for structured lifecycle management - Track scanTask to prevent concurrent scan conflicts - Handle CancellationError gracefully when scans are stopped early
732d630 to
21c4d5e
Compare
| INFO = 2; | ||
| WARNING = 3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do INFO and WARNING mean? Are they successful status codes?
Sources/Wendy/CLI+Support.swift
Outdated
| func responseStatusLevelDescription( | ||
| _ level: Wendy_Agent_Services_V1_ResponseStatus.Level | ||
| ) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| func responseStatusLevelDescription( | |
| _ level: Wendy_Agent_Services_V1_ResponseStatus.Level | |
| ) -> String { | |
| extension Wendy_Agent_Services_V1_ResponseStatus.Level: CustomStringConvertible { | |
| public var description: String { |
|
Addressed Joannis notes:\n- Documented ResponseStatus levels (INFO/WARNING are successful with extra context).\n- Added CustomStringConvertible for ResponseStatus.Level and updated CLI JSON/text output to use it.\n- Bluetooth CLI SIGINT: stop scan inline and re-raise SIGINT after cleanup; removed detached stop-scan tasks.\n- Added a warning when no device is selected (disconnect/forget).\n\nBluetoothManager already has a run() loop + scanGeneration guard to prevent overlapping scan requests; can wire it into ServiceGroup if you want. |
| // The run loop keeps the actor alive and allows for structured concurrency. | ||
| // When cancelled, any active scan task will be cancelled as well. | ||
| defer { | ||
| scanTask?.cancel() | ||
| scanTask = nil | ||
| } | ||
|
|
||
| // Wait indefinitely until cancelled | ||
| try await Task.sleep(for: .seconds(Int64.max)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still uses unstructured tasks with background logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now, if I don't use it, the wendy-agent hangs. Let me investigate further.
| read -p "Enter hostname [wendyos-merry-aurora.local]: " HOSTNAME | ||
| HOSTNAME=${HOSTNAME:-wendyos-merry-aurora.local} | ||
| USER=edgeos | ||
| HOSTNAME=wendyos-spirited-rose.local |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
host and user/password are hardcoded. I'd remove at least the user/password and let them be passed in interactively
Summary
IMPORTANT It requires: wendylabsinc/meta-wendyos-jetson#20 SPECIFICALLY NEEDS HOST TO HAVE BLUEZ running!
Testing
Whenever we release a
wendy os install --nightlywherebluezis available you can test this agent and cli