7fcb8dd346488b1f4448afe5e725a0a90588c3b6
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7fcb8dd346 |
feat(devices): Android USB-serial support for Adalight/AmbiLED controllers
Adds end-to-end support for driving USB-connected Adalight / AmbiLED LED controllers from Android TV boxes. Android's security model blocks direct USB access from Python, so writes route through a Kotlin UsbSerialBridge singleton via Chaquopy. Python side: - New SerialTransport Protocol (serial_transport.py) with open / write / flush / close. Desktop uses PySerialTransport (wraps pyserial), Android uses AndroidSerialTransport (wraps the Kotlin bridge). - list_serial_ports() factory returns desktop COM ports on desktop, USB devices on Android — callers don't branch. - URL scheme extended: existing COM3[:baud] and /dev/ttyUSB0[:baud] unchanged; new usb:VID:PID[:serial][@baud] for Android (@ is the baud separator since : is already used between VID and PID). - AdalightClient and SerialDeviceProvider refactored to go through the transport — no more direct pyserial imports in hot paths. - 17 new unit tests cover URL parsing, PySerial transport, factory selection, platform-branching discovery. Full suite 750 passing. Kotlin side: - UsbSerialBridge.kt singleton uses com.hoho.android.usbserial (mik3y) which ships drivers for CH340, CP2102, FTDI, Prolific, and CDC-ACM (Arduino). Exposes listDevices, open, write, close via @JvmStatic for Chaquopy. First open() attempt without permission triggers the system USB permission dialog; next call succeeds once user grants. - usb-serial-for-android is distributed via JitPack — added that repo in settings.gradle.kts and the dependency in app/build.gradle.kts. - AndroidManifest declares uses-feature android.hardware.usb.host (required=false so non-USB-host phones still install). - LedGrabApp.onCreate calls UsbSerialBridge.init(this) so the bridge resolves the UsbManager without needing an Activity ref. Verified: ./gradlew compileDebugKotlin succeeds; off-Android import of android_serial_transport works. Real-hardware smoke test on a TV box with a CH340/CP2102/FTDI adapter still pending. ESP-NOW (espnow_client / espnow_provider) still imports pyserial directly because it needs bidirectional reads — separate refactor to extend the transport with read() if that path ever needs Android USB support. |
||
|
|
151cea3ecb |
ci: Android multi-ABI APK pipeline + pydantic-core wheel rebuild
Adds .gitea/workflows/build-android.yml — Linux runner installs JDK 17, Python 3.11, Android SDK/NDK, symlinks server/src/ledgrab into the Chaquopy python source dir, and runs assembleDebug on master pushes / assembleRelease on v* tags. APK is uploaded as an artifact and attached to the Gitea release on tag push. Conditional signing config in build.gradle.kts reads keystore from env vars (CI secrets) and falls back to debug signing locally. Gradle wrapper (gradlew/gradlew.bat/ gradle-wrapper.jar) committed so CI can drive the build. Rebuilds pydantic-core wheels for arm64-v8a and x86_64 — both were missing libpython3.11.so in NEEDED, which would have crashed at import on real devices. build-pydantic-core.sh rewritten as a multi-ABI builder: selects targets via args, sets RUSTFLAGS=-C link-arg=-Wl,--no-as-needed -C link-arg=-lpython3.11 to force the symbol-resolution dependency, uses the per-ABI sysconfigdata + libpython staged in android/.build-cache/, prefers `py -3.11` on Windows (Git Bash's python3.11 is an MSStore stub), uses the .cmd clang wrapper on Windows (fixes os error 193), and verifies NEEDED via llvm-readelf after each build. abiFilters restored to the full triple in build.gradle.kts; multi-ABI debug APK builds cleanly (~99 MB). |
||
|
|
8574424fb7 |
feat: Android TV app embedding Python server via Chaquopy
Lint & Test / test (push) Successful in 2m10s
Adds a native Android TV application that runs the full LedGrab Python server in-process via Chaquopy. Captures the TV box screen using the MediaProjection API and exposes the existing web UI on the device's local network — users configure via phone/tablet browser. Android (new /android/ module): - Kotlin shell: MainActivity, CaptureService (foreground service), ScreenCapture (MediaProjection + ImageReader), PythonBridge (Chaquopy). - Polished Leanback-themed UI with QR code for easy web UI access. - AGP 8.9 + Chaquopy 17 + Gradle 8.11 (avoids the AGP 8.7 thread-lock bug). - Pre-built pydantic-core wheels for arm64-v8a, x86_64, x86 cross-compiled with maturin + Android NDK, linked against Chaquopy's libpython3.11.so. Python server platform guards: - New utils/platform.py with is_android()/is_windows()/is_linux() helpers. - Guard every top-level import of desktop-only packages (mss, psutil, sounddevice, pyserial, PyAudioWPatch, etc.) with try/except ImportError. - Android-incompatible calls gated with None-checks so the server runs on reduced capabilities on Android (no CPU/RAM metrics, no mss displays). - utils/image_codec.py gains a Pillow fallback for resize + JPEG encode when cv2 is unavailable; all internal cv2.resize callers migrated. - New android_entry.py start_server/stop_server invoked from Kotlin. - get_displays API falls back to best available engine when mss fails. New capture engines: - MediaProjectionEngine: receives RGBA frames pushed from Kotlin through a thread-safe queue; caches last frame for static-screen previews. - ScrcpyClientEngine: optional H.264 streaming via scrcpy-client library (priority 10, overrides the ADB-screencap engine when installed). Frontend: - Tab loaders previously required an apiKey; now correctly treat "auth disabled" as authenticated (Android has no auth by default). - Re-trigger the active tab's loader after loadServerInfo resolves authRequired, since initTabs runs earlier. - Add i18n keys for the demo / mediaprojection / scrcpy_client engines. Docs: - TODO.md: follow-ups for multi-ABI wheel rebuilds, CI pipeline, USB serial LED controllers, root-only capture, perf metrics abstraction. - CLAUDE.md: Android dependency sync policy (pip --exclude doesn't exist). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |