Claude/cli bluetooth raspberry pi f8571a - #3
Merged
Conversation
The Pi (BlueZ) transport found and connected nothing because it did not do what the mobile transport does. Bring it to parity: - Power the radio on first: check the adapter's Powered property and call SetPoweredAsync(true); throw an actionable message (rfkill/bluetoothctl) when it cannot come up, instead of silently scanning nothing. - Scan on the LE transport (SetDiscoveryFilter Transport=le) so BLE-only hubs and their manufacturer data surface; BlueZ's default auto mode routinely misses them. - Also enumerate GetDevicesAsync() at scan start, since a fresh StartDiscovery never re-fires DeviceFound for already-cached devices. - Wait for ServicesResolved=true (not just Connected) before GATT lookups, which otherwise race and return null. - Cold connect: GetDeviceAsync does a brief LE scan when the hub is not in BlueZ's cache yet, so auto/connect work after a reboot with no discover. - Surface radio errors: DiscoverAsync awaits an EnsureReadyAsync preflight (Discover() is fire-and-forget and swallows exceptions) and DiscoverCommand prints the message cleanly. Add diagnostic log events. The preflight is why BlueZLegoService takes the concrete adapter, wired via a forwarding singleton so the SharpBrick host and the service share one radio instance. Co-Authored-By: Claude Opus 4.8 <[email protected]>
A long-running loop for unattended operation on the Pi (systemd/Docker). Every --interval seconds (default 60) it re-reads the saved trains from SQLite and applies each one's saved config — connect, set the hub LED, drive at its saved speed — reconnecting any hub that has dropped. On Ctrl+C / SIGINT it stops every motor and disconnects cleanly. - SetSpeed doubles as a health check: a dropped link throws, the train is disconnected, and the next sweep reconnects it fresh (self-healing). - The store is re-read each cycle, so new/edited trains are picked up live. - A failed sweep is caught and retried next interval so the daemon never dies; --all includes inactive trains. - Documented in ReadMe (EN + DE) incl. the systemd unit; added to the dashboard cheat-sheet and command help. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Ubuntu Server images ship without BlueZ, so trackify could not reach bluetoothd over D-Bus (bluetoothctl/bluetooth.service missing) even though the kernel BT stack was fine. Add first-class setup + diagnostics: - scripts/setup-bluez.sh: idempotent installer — apt install bluez+rfkill, enable/start bluetoothd, rfkill unblock, power on the adapter, add the user to the bluetooth group. - scripts/pi-bt-info.sh: read-only diagnostics — radio/rfkill/service/ permissions/adapter state + a live LE scan that flags LEGO (0x0397) manufacturer data. - ReadMe (EN + DE): new "install BlueZ" prerequisites section referencing both scripts and the manual apt steps. Both scripts are marked executable. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Guarantees the Pi setup/diagnostics scripts keep Unix line endings even when checked out or edited on Windows (CRLF breaks #!/usr/bin/env bash on Linux). Co-Authored-By: Claude Opus 4.8 <[email protected]>
Hosts an ASP.NET Core backend over the same Domain/Application/Infrastructure use-cases the CLI runs, so the Uno app can drive a Pi remotely: - REST (one-shot): GET /api/trains, POST /api/discover, connect/disconnect by hubId, GET /api/state — routes shared via Application/Remote/ApiRoutes. - SignalR hub /hubs/trains: real-time SetSpeed/SetLed/Stop + a SpeedChanged broadcast; method names shared via TrainHubMethods. Keyed by hubId so it maps 1:1 onto ILegoService (the app's existing control seam). - Composed with an AddTrackifyServer() DI extension; handlers resolve their use-cases from DI. `server` is a normal Spectre command (--urls override). - Config is externalised to appsettings.json (Serilog levels/sinks, Urls, DiscoverTimeoutSeconds) via Serilog.Settings.Configuration — nothing hardcoded. - Global exception handling: Spectre SetExceptionHandler (CLI) + UseExceptionHandler middleware (serve), both logging; new Info/Error log events. - ASP.NET arrives via a Microsoft.AspNetCore.App FrameworkReference (dropping the now-redundant M.E.DI/Logging package refs that tripped NU1510). Co-Authored-By: Claude Opus 4.8 <[email protected]>
The app gains an optional Server mode alongside its own Bluetooth (Direct mode); both are interchangeable ILegoService implementations, so the UI is unchanged: - RemoteLegoService : ILegoService — one-shot actions over REST (Refit ITrackifyApi) and real-time speed/LED over SignalR; raises SpeedChanged so the UI can show live speed. - RemoteTrainSync — pulls the backend's trains into the local SQLite store, de-duplicated by hub identity (HubId → BleAddress → Name), upserting in place. - AddTrackifyRemote(baseUrl) wires the Refit client + SignalR transport; when AppConfig.ServerUrl is set the app registers it (wins as ILegoService) and enables the sync — otherwise it stays in Direct mode (unchanged behaviour). - Global exception handling (ASP.NET-style): App hooks UnhandledException, AppDomain.UnhandledException and TaskScheduler.UnobservedTaskException, all logged via Serilog — no silent swallowing. The UI mode switch + IP entry is the remaining piece (wired via config for now). Co-Authored-By: Claude Opus 4.8 <[email protected]>
`trackify discover --save` stores each discovered hub in the train list, de-duplicated by hub identity (HubId then MAC). Saving goes through a new Application use-case (ITrainService.SaveDiscoveredAsync) so the CLI stays DTO-only (the entity boundary stays behind Application). Per-hub failures are reported, never swallowed. Co-Authored-By: Claude Opus 4.8 <[email protected]>
- TrainSaveDiscoveredTests: new-train creation, dedup by HubId and by MAC, and the unnamed-hub name fallback (Application, via FakeTrainRepository). - TrainStateStoreTests: latest-speed-per-hub tracking + empty snapshot (CLI). Co-Authored-By: Claude Opus 4.8 <[email protected]>
MainPage gains a Connection panel (header toggle → modal overlay): a Direct/Server switch + backend URL field + Apply, bound to a persisted ConnectionState. Switching is live (no restart): - SwitchingLegoService routes each ILegoService call to the local Bluetooth transport or a remote one, chosen per-call from ConnectionState; the remote transport is (re)created when the URL changes. Registered by wrapping the platform transport (ResolveLocal) so the two don't compete for resolution. - ConnectionState persists mode + URL in local app settings (seeded from AppConfig.ServerUrl); TrackifyApiFactory builds the Refit client per URL. - Apply persists the choice and, in Server mode, runs RemoteTrainSync (which now reads the URL from ConnectionState) to pull trains into local SQLite. - Quieted framework INFO noise (Uno appsettings probes, Microsoft host) so the log overview stays clean; new Info/Warning log events for mode/sync/settings. - Replaces the earlier config-only remote wiring (AddTrackifyRemote removed). Co-Authored-By: Claude Opus 4.8 <[email protected]>
Addresses the code-scanning findings on the server: - Log forging (CWE-117): sanitize user-controlled Request.Method/Path (strip CR/LF) before logging in the global exception handler. - Information disclosure (CWE-209): the 500 response now returns a generic "Internal server error" instead of echoing the exception message to clients; the detail is still logged server-side. - Over-permissive CORS (CWE-942): drop AllowCredentials so we no longer combine any-origin with credentials; the backend uses no cookies/auth. Co-Authored-By: Claude Opus 4.8 <[email protected]>
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.
No description provided.