Commit graph

248 commits

Author SHA1 Message Date
Torlando
c0529dc307
Merge pull request #284 from torlando-tech/feature/issue-216-rns-discovery
feat: add RNS 1.1.x bootstrap and interface discovery support
2026-01-22 13:48:06 -05:00
torlando-tech
bb601d071b test: add comprehensive tests for get_discovered_interfaces()
Add 27 tests covering:
- get_discovered_interfaces(): TCP interfaces, radio interfaces with LoRa
  params, interfaces with location data, multiple interfaces sorted by
  status and stamp value, bytes/string transport_id handling, None value
  filtering, exception handling
- _get_discovery_status_name(): status code to name conversion
- _create_config_file() discovery options: discover_interfaces,
  autoconnect_discovered_interfaces, interface_discovery_sources,
  required_discovery_value, bootstrap_only for TCP clients

This improves patch coverage from ~56% to target higher coverage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 23:19:20 -05:00
torlando-tech
02b53b64a8 test: add comprehensive unit tests for RNS discovery feature
Add unit tests for the RNS 1.1.x discovery functionality:

Python tests:
- test_autoconnected_endpoints.py: 14 tests for get_autoconnected_interface_endpoints()
- test_discovery_enabled.py: 12 tests for is_discovery_enabled()

Kotlin tests:
- DiscoveredInterfacesViewModelTest.kt: 22 tests covering autoconnected endpoints
  state, isAutoconnected() helper, and discovery settings loading
- DiscoveredInterfacesScreenTest.kt: 29 tests for UI helpers including
  isYggdrasilAddress(), formatInterfaceType(), and icon/badge visibility

Also fixes:
- Fix detekt ComplexCondition issues in RNodeWizardScreen, MapScreen, MainActivity
- Fix detekt LongMethod in ColumbaApplication by extracting restorePeerIdentities()
- Fix detekt SwallowedException in InterfaceRepository with verbose logging
- Fix detekt MatchingDeclarationName by moving FocusInterfaceDetails to own file
- Fix detekt ReturnCount in isYggdrasilAddress and ViewModel helper functions
- Update StartupConfigLoaderTest for new discovery settings
- Update TcpClientWizardViewModelTest for allInterfaceEntities changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 22:58:08 -05:00
torlando-tech
53fa9f24e0 feat: add connected indicator and improved icons for discovered interfaces
- Add "Connected" badge showing which discovered interfaces are currently
  auto-connected by RNS discovery
- Add getAutoconnectedEndpoints() API to fetch active auto-connected interfaces
- Use type-specific icons: globe for TCP, antenna for radio, incognito for I2P,
  tree-pine for Yggdrasil (detected via 0200::/7 IPv6 range)
- Show I2P b32 addresses with .b32.i2p suffix
- Display human-readable interface type labels (e.g., "Backbone (TCP)")

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 21:12:05 -05:00
torlando-tech
8bc1d054d4 feat: add discovery toggle and fix discovered interfaces display
- Add toggle in Discovered Interfaces screen to enable/disable discovery
- Service auto-restarts when toggle is changed (no app restart needed)
- Show bootstrap-enabled interface names in settings card
- Add discovery settings to SettingsRepository and StartupConfigLoader
- Include discover_interfaces and autoconnect_discovered_interfaces in config
- Include bootstrap_only for TCPClient interfaces in buildConfigJson
- Fix Python wrapper bug: discovered_interfaces() returns list, not dict
- Add bottom padding to list to avoid nav bar overlap

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 19:49:16 -05:00
torlando-tech
c40f7425ca test: Add comprehensive tests for interface name extraction
Add unit tests for ReceivingInterfaceInfo:
- 25 Kotlin tests covering TCP, Auto, BLE, RNode, Serial, and unknown interfaces
- Tests for friendly name extraction, interface type parsing, and edge cases

Refactor ReceivingInterfaceInfo to reduce cyclomatic complexity:
- Extract InterfaceCategory enum for display properties
- Extract categorizeInterface() and looksLikeAddress() helper functions
- Main function now delegates to smaller, focused functions

Add 2 additional Python tests for interface name formatting:
- Test interface.name same as class name returns class only
- Test interface.name being None returns class only

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 19:07:22 -05:00
torlando-tech
66982b18de feat: Show user-configured interface name in Node Details
Update the "Received Via" card in Node Details to show the user-configured
interface name (e.g., "Sideband Server") instead of generic type names
(e.g., "TCP/IP").

Python changes:
- Build formatted interface string "ClassName[UserConfiguredName]" from
  the interface object's type and .name attribute
- This allows Kotlin to extract both the friendly name and interface type

Kotlin changes:
- Add extractFriendlyName() to parse user-configured name from brackets
- Add extractInterfaceType() to parse class name before brackets
- Display friendly name as main text, interface type as subtitle

The card now shows:
- Title: "Received Via"
- Content: "Sideband Server" (user-configured name)
- Subtitle: "TCPClientInterface" (interface type)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 19:02:43 -05:00
torlando-tech
93d794c1fd feat: extend DiscoveredInterface data model with full RNS 1.1.x fields
Add comprehensive discovery data to DiscoveredInterface including:
- Transport/network IDs and status info (statusCode, lastHeard, hops)
- TCP-specific fields (reachableOn, port)
- Radio-specific fields (frequency, bandwidth, SF, CR, modulation)
- Location fields (latitude, longitude, height)
- Helper properties for interface type detection

Update Python bridge to return full discovery data from RNS 1.1.x API.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:11:18 -05:00
torlando-tech
e0dae212dc feat: add RNS 1.1.x bootstrap and interface discovery support (#216)
Add support for RNS 1.1.0+ interface discovery and bootstrap features,
enabling new users to connect without relying on deprecated public
infrastructure.

Changes:
- Add discovery settings to ReticulumConfig (discoverInterfaces,
  autoconnectDiscoveredInterfaces, interfaceDiscoverySources,
  requiredDiscoveryValue)
- Add bootstrapOnly option to TCPClient interface config
- Update Python config generation for discovery and bootstrap options
- Add get_discovered_interfaces() and is_discovery_enabled() API methods
- Extend AIDL interface with discovery methods
- Mark 3 reliable community servers as bootstrap candidates (Beleth,
  Quad4, FireZen)
- Add bootstrap toggle to TCP Client wizard UI

Note: The RNS fork needs to be rebased on 1.1.2 to enable the actual
discovery features. The code is ready but awaiting the dependency update.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:45:38 -05:00
Torlando
3ca089d02c
Merge pull request #97 from torlando-tech/dependabot/gradle/cameraX-1.5.2
deps(deps): bump cameraX from 1.4.1 to 1.5.2
2026-01-21 14:11:47 -05:00
torlando-tech
975c8c003a Fix flaky tests and improve CI failure visibility
- Harden 4 Python tests that were flaky due to mock object identity
  comparisons by using explicit sentinel values for link status constants
- Add failure annotations to CI workflow so root cause job is clearly
  marked when fail-fast cancels sibling jobs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 13:52:47 -05:00
Torlando
4af51e7c8a
Merge pull request #311 from torlando-tech/feature/issue-215-telemetry-collector-api
Add telemetry collector API with group tracking features
2026-01-21 13:49:52 -05:00
torlando-tech
7e95f278b3 Add tests for set_telemetry_allowed_requesters method
Cover all branches: success with items, empty list, lowercase
normalization, empty string filtering, set storage, deduplication,
and list replacement.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 13:32:29 -05:00
torlando-tech
9bf59cbec6 Fix telemetry request tests and add blocked requester tests
- Add requester to allowed list in tests that expect requests to succeed
- Add test_blocks_request_from_non_allowed_requester to verify blocking
- Add test_blocks_request_when_allowed_list_empty to verify empty = block all

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 12:44:42 -05:00
torlando-tech
2a79ed2613 Fix empty telemetry stream responses appearing in chat
Empty telemetry stream responses (0 entries from collector) were
appearing as blank messages in chat because is_location_only was
only set when the stream had entries. Now telemetry stream messages
are always marked as location-only regardless of whether they
contain data.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 11:28:37 -05:00
torlando-tech
b060512aee Add allowed telemetry requesters feature for host mode
Implements the ability to control which contacts can request telemetry
when acting as a collector (host mode). Users can select specific
contacts who are allowed to request their group's location data.
Requests from non-allowed contacts are silently blocked.

Changes:
- Add telemetry_allowed_requesters state and filtering in Python layer
- Add AIDL interface and bridge for setTelemetryAllowedRequesters
- Add SettingsRepository storage with DataStore persistence
- Add TelemetryCollectorManager state flow and Python sync
- Add SettingsViewModel state management with ContactRepository
- Add UI components: AllowedRequestersSection and AllowedRequestersDialog
- Fix ArrayList to Python list conversion in ReticulumServiceBinder
- Fix state preservation in SettingsViewModel.loadSettings()
- Add CLAUDE.md with Chaquopy ArrayList pitfall documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 10:39:49 -05:00
Torlando
3fe17c2ff9
Merge pull request #243 from torlando-tech/feature/issue-215-telemetry-collector-api
Add Telemetry Collector API support
2026-01-21 00:28:51 -05:00
Torlando
3657c288fe
Merge pull request #280 from torlando-tech/feature/usb-rnode-support
feat: USB RNode support (Issue #173)
2026-01-21 00:24:41 -05:00
torlando-tech
a05e26194c Add comprehensive tests for unpack_telemetry_stream
Add 16 new tests for unpack_telemetry_stream covering:
- Empty stream handling
- Bytes and string source hash conversion
- Entries with too few elements (validation)
- Invalid telemetry data handling
- Valid timestamp application (converted to milliseconds)
- Future timestamp rejection (>1 hour ahead)
- Zero/None timestamp handling
- Valid appearance parsing (icon, fg/bg colors)
- Invalid icon name formats (special chars, too long)
- Invalid color bytes (too short, non-bytes)
- Multiple entry processing
- Exception handling during entry processing

Total Python tests: 61

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 00:15:16 -05:00
torlando-tech
4282e99f7c Add comprehensive tests for send_telemetry_request and FIELD_COMMANDS handling
Expand Python test coverage to include:

TestSendTelemetryRequestSuccess (6 tests):
- Successful send with immediate identity recall
- Successful send with cached identity
- Error when local_lxmf_destination is missing
- Error when identity cannot be resolved after path request
- jarray conversion handling
- Correct FIELD_COMMANDS structure building

TestOnLxmfDeliveryFieldCommands (4 tests):
- FIELD_COMMANDS handling when collector enabled
- Skipping FIELD_COMMANDS when collector disabled
- Identity retry mechanism in delivery handler
- Ignoring non-collector requests

Total Python tests: 45 (up from 35)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 00:12:56 -05:00
torlando-tech
9b6452660c Expand Python test coverage for telemetry host mode
Add 14 new tests covering:
- _send_telemetry_stream_response: filtering by timebase, LXMF message
  creation, handling empty telemetry, cleanup of expired entries
- send_telemetry_request: error handling for uninitialized states
- FIELD_COMMANDS constants: verify all protocol constants exist
- Timebase filtering: verify received_at vs timestamp filtering

These tests improve coverage for the Python telemetry collector
implementation, specifically the FIELD_TELEMETRY_STREAM response
generation and FIELD_COMMANDS request handling paths.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 23:06:28 -05:00
torlando-tech
5b724e34fd Add comprehensive unit tests for telemetry collector host mode
- Add 4 tests to SettingsRepositoryTest for host mode persistence
- Add 7 tests to TelemetryCollectorManagerTest for host mode management
- Add 6 tests to SettingsViewModelTest for host mode UI state
- Create test_telemetry_host_mode.py with 21 Python tests for:
  - pack_telemetry_stream function
  - set_telemetry_collector_enabled method
  - _store_telemetry_for_collector method
  - _cleanup_expired_telemetry method
  - Integration tests for full workflow
- Fix detekt issues with appropriate suppressions
- Fix SwallowedException by including exception in log

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:30:04 -05:00
torlando-tech
ba2cf44dea Add telemetry collector (host mode) for Group Tracker
Enable Columba to act as a telemetry collector compatible with Sideband's
protocol. When Host Group mode is enabled, Columba will:

- Store incoming FIELD_TELEMETRY (0x02) location data from peers
- Handle FIELD_COMMANDS (0x09) telemetry requests
- Respond with FIELD_TELEMETRY_STREAM (0x03) containing all stored entries

Implementation details:
- In-memory storage with 24-hour TTL, keeping latest per source
- Uses received_at timestamp for timebase filtering (handles clock skew)
- Follows Sideband's identity recall + path request pattern
- UI toggle in Location Sharing settings card

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:04:13 -05:00
torlando-tech
2a19d58f1d Add telemetry request functionality to Group Tracker
Enable requesting location data from group host (telemetry collector):

- Add send_telemetry_request() to Python wrapper using FIELD_COMMANDS
- Add path request retry logic when identity not immediately known
- Add request toggle and interval settings to UI
- TelemetryCollectorManager handles periodic location requests
- New AIDL/protocol methods for sendTelemetryRequest

UI renamed from "Telemetry Collector" to "Group Tracker" with
user-friendly labels (Group Host, Share with group, etc.)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 20:22:54 -05:00
torlando-tech
049424f75f Add FIELD_COMMANDS constant for LXMF telemetry requests
Previously Columba was using FIELD_IMAGE (0x06) for telemetry
commands, causing Sideband to interpret requests as image
attachments. Add the correct FIELD_COMMANDS constant (0x09)
per the LXMF specification.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 20:22:13 -05:00
torlando-tech
130bd1a723 fix: abort USB start if old read thread won't stop
Prevents race condition where two threads compete for USB reads
if the previous thread doesn't terminate within timeout.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 19:37:34 -05:00
torlando-tech
93bdec9ebd fix: address code review feedback for USB bridge
- Set readTimeout=100ms to reduce CPU usage from continuous polling
- Escalate thread leak warning to ERROR level for visibility
- Capture deviceId early in detach handler for stability across Android versions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 19:30:53 -05:00
torlando-tech
a4959d4b4a Address PR review comments for telemetry collector
- Add hex character validation for collector address (prevents crash)
- Fix periodic send race condition by awaiting in-progress sends
- Fix auto-confirm timing using LaunchedEffect for state sync
- Optimize SQL queries from O(n²) to O(n) using JOIN
- Add future timestamp rejection (>1hr) to prevent UI issues
- Add icon name validation (alphanumeric/underscore, max 50 chars)
- Add comprehensive tests for Python validation logic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 18:40:47 -05:00
torlando-tech
f0bd380a4e Add comprehensive unit tests for Greptile review fixes
- Add Python tests for unpack_telemetry_stream function (464 lines)
  - Tests basic unpacking, timestamp handling, source hash conversion
  - Tests appearance data parsing and error handling
  - Tests Sideband format compatibility
  - Verifies None/0/negative timestamp validation

- Add Kotlin tests for location.time timestamp semantics
  - LocationTelemetryTest: Verifies timestamp represents capture time
  - LocationTelemetryTest: Verifies millisecond precision preservation
  - LocationTelemetryTest: Verifies collector API serialization compatibility
  - TelemetryCollectorManagerTest: Documents expected timestamp behavior

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 18:40:47 -05:00
torlando-tech
c1090423d6 Address remaining Greptile review comments
- Use location.time instead of System.currentTimeMillis() for accurate
  location capture timestamp in TelemetryCollectorManager
- Add timestamp validation in Python to prevent issues with None/0 values

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 18:40:47 -05:00
torlando-tech
a4db8fc4fb Add Telemetry Collector API support (#215)
Implements support for sending location telemetry to a collector and
receiving bulk telemetry streams from multiple sources.

Features:
- TelemetryCollectorManager for scheduled/manual telemetry sends
- TelemetryCollectorCard UI for configuration in Settings
- FIELD_TELEMETRY_STREAM (0x03) handling in Python layer
- Database migration for appearanceJson field on received locations
- SettingsRepository preferences for collector address, intervals, etc.

Testing:
- TelemetryCollectorManagerTest with mockk and Turbine
- TelemetryCollectorCardTest with Compose testing
- SettingsViewModelTest telemetry method tests
- SettingsRepositoryTest telemetry preference tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 18:40:47 -05:00
torlando-tech
49d6321ae0 refactor: extract testable utilities to improve test coverage
Extracted business logic from ViewModels and UI components into testable utility classes (InterfaceFormattingUtils, DeviceNameValidator, RssiThrottler, DeviceClassifier) to enable comprehensive unit testing without platform dependencies. Added 116 new tests covering formatting, validation, throttling, and device classification logic, increasing patch coverage from 23.5% to approximately 35-40%.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-20 17:46:09 -05:00
torlando-tech
df4e7761f0 feat: add USB-assisted Bluetooth pairing with device selection
When pairing a USB-connected RNode via Bluetooth, the wizard now:
- Adds "Pair via USB" option to the Bluetooth tab
- Receives PIN from RNode over USB connection
- Scans for unbonded RNodes and shows device selection list
- Auto-selects if only one unbonded RNode is found
- Uses BroadcastReceiver to properly detect bond state changes

This solves the issue where multiple RNodes in range would cause
pairing failures because the PIN belongs to the USB-connected device,
not necessarily the first discovered one.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 12:21:04 -05:00
torlando-tech
07893238cd test: improve rnode_interface.py coverage and fix PIN parsing
Test coverage improvements (26% -> 47%):
- Add tests for _get_usb_bridge() success/failure cases
- Add tests for _start_usb() VID/PID lookup, connection, callbacks
- Add tests for _on_usb_connection_state_changed() callback
- Add tests for USB VID/PID config attributes
- Add tests for start() routing to correct implementation

PIN parsing fix:
- Fix CMD_BT_PIN parsing to use 4-byte big-endian integer format
- Matches Kotlin KotlinUSBBridge implementation
- Update test to verify correct frame structure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 00:52:45 -05:00
torlando-tech
2b87931f01 fix: resolve detekt issues and update tests for USB RNode support
- Fix SwallowedException in KotlinUSBBridge by adding verbose logging
- Fix ImplicitDefaultLocale by using Locale.US for PIN formatting
- Add @Suppress for NestedBlockDepth in KISS protocol parser
- Remove unused generateDefaultInterfaceName and waitForBondState functions
- Add @Suppress for loadExistingConfig complexity (inherent to config mapping)
- Update DebugViewModelEventDrivenTest with interfaceRepository parameter
- Update ReviewConfigStepTest with isUsbMode and getConnectionTypeString mocks
- Fix test_usb_bridge.py mock to use Chaquopy getter methods

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 00:24:53 -05:00
torlando-tech
3acebc9c93 chore: remove unused CMD_DEV_HASH constant
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 23:56:14 -05:00
torlando-tech
f7307377d1 fix: auto-generate Bluetooth RNode interface names from device name
- Fix CompanionDeviceManager association path (Android 12+) not updating
  interface name when a device is selected
- Extract identifier from Bluetooth device name (e.g., "RNode 958F" -> "958F")
- Add BLE/BT suffix based on device type (e.g., "RNode 958F BLE")
- Remove unused device hash query code from KotlinUSBBridge, usb_bridge.py,
  and reticulum_wrapper.py

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 23:48:06 -05:00
torlando-tech
21f64cc0ea feat: simplify USB RNode interface naming to "RNode USB"
Remove device hash query complexity for USB RNodes since CMD_DEV_HASH
returns a different hash than what's used for Bluetooth naming. USB
interfaces now simply use "RNode USB" as the default name.

The device hash query code is retained in KotlinUSBBridge for potential
future use (debugging, device identification).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 23:43:57 -05:00
torlando-tech
c6862914b8 fix: stop USB RNode read loop on disconnect to prevent thread leak
When switching between multiple USB RNodes, the read loop thread was not
being stopped on disconnect. This caused multiple threads to compete for
data from the shared USB buffer, leading to:
- KISS frames being split across threads
- Detection responses consumed by wrong/stale threads
- Detection timeout despite successful response

The fix:
- Clear _running flag in disconnect callback to stop read loop
- Add safety check in _start_usb() to stop any stale read thread
- Reset detection state on reconnection for clean configuration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 21:00:35 -05:00
torlando-tech
09f6c04c4a fix: USB RNode reconnection with VID/PID matching
- Use SQLite json_extract() for reliable VID/PID queries instead of
  fragile LIKE pattern matching in InterfaceDao
- Fix Chaquopy interop: use .size() for Java ArrayList instead of len()
- Fix Kotlin data class property access: use getter methods (getDeviceId())
  instead of direct property access
- Add debug logging for USB device lookup flow

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 20:39:19 -05:00
torlando-tech
54c6dca087 feat: add connecting spinner to interface stats and fix service binding race
Interface Stats Screen:
- Add "CONNECTING" status badge with spinner when interface is connecting
- Show spinner for up to 15 seconds on screen load if interface is offline
- Use tertiary color for connecting state to distinguish from online/offline
- Reset connecting state when interface goes offline (for replug detection)

Service Binding Fix:
- Wait up to 10 seconds for service to bind before attempting RNode reconnect
- Previously, reconnectRNodeInterface() silently did nothing if service wasn't
  bound yet, causing cold start reconnects to fail
- Add proper logging for service binding wait and timeout cases

Also adds connecting field to Python interface stats for future use.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 18:13:31 -05:00
torlando-tech
20a6c1b615 perf: remove 0.5s USB stabilization delay on RNode connect
USB connections are reliable enough to skip the stabilization delay
that was previously added for safety. This reduces reconnect time
by 0.5 seconds.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:17:27 -05:00
torlando-tech
1f548d1781 fix: USB RNode auto-reconnect with permission-aware retry
When USB RNode is replugged:
- BroadcastReceiver catches USB attach event early
- Checks if USB permission is already granted
- If no permission: skips reconnect, allows retry via Activity intent
- If permission granted: reconnects immediately

Also:
- Clear stale USB connection in Python before reconnecting
- Track if reconnect was attempted to allow permission-based retry
- Listen for USB detach events to reset debounce state

This fixes the race condition where BroadcastReceiver fired before
Android granted USB permission, causing reconnect to fail.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:13:15 -05:00
torlando-tech
7907d07fd1 feat: add USB intent handling and interface stats screen
When a USB device is attached:
- Check if it's already configured as an RNode interface
- If configured: navigate directly to the new Interface Stats screen
- If not configured: navigate to RNode wizard with USB pre-selected

New Interface Stats screen displays:
- Online/offline status with enable/disable toggle
- Connection type and details (TCP/USB/BLE/Bluetooth)
- RNode radio settings (frequency, bandwidth, SF, TX power)
- Live traffic statistics (RX/TX bytes, RSSI, SNR)
- Edit button to modify interface configuration

Also:
- Make interface cards clickable in management screen
- Add singleTask launchMode to prevent duplicate Activity instances
- Add get_interface_stats() to Python wrapper for live stats

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 12:53:29 -05:00
torlando-tech
758754ddee fix: pass USB bridge to Python for RNode interface startup
The USB RNode interface was saved to database correctly but failed to
start because:

1. The usb_device_id was missing from _pending_rnode_config in
   reticulum_wrapper.py
2. The KotlinUSBBridge was never passed to the Python side (unlike
   KotlinRNodeBridge for Bluetooth)
3. The connect() call didn't pass baud rate (Chaquopy doesn't support
   Kotlin default parameters)

Changes:
- Add usb_device_id to _pending_rnode_config dictionary
- Add set_usb_bridge() method to reticulum_wrapper.py
- Set USB bridge in ReticulumServiceBinder before Python init
- Pass baud rate (115200) explicitly to usb_bridge.connect()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 12:15:26 -05:00
torlando-tech
8db0e78d4b test: add unit tests for BLE interface RSSI methods
Add comprehensive tests for the get_rssi() method in AndroidBLEInterface
and signal metrics capture in reticulum_wrapper.

New test files:
- test_android_ble_interface.py: 9 tests for AndroidBLEInterface.get_rssi()
- test_ble_modules_android_ble_interface.py: 7 tests for ble_modules version

Updated test_wrapper_messaging.py:
- Add TestOnLxmfDeliverySignalMetrics class with 4 tests verifying
  RSSI and SNR capture during LXMF message delivery

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 21:52:09 -05:00
torlando-tech
b045ca3aed feat: add BLE peer RSSI signal tracking
Extends signal metrics support to BLE interfaces by implementing RSSI
tracking for BLE peer connections. Uses existing KotlinBLEBridge to
query the last RSSI value from connected peers via the Android GATT
connection.

Changes:
- Add getPeerRssi() method to KotlinBLEBridge for querying BLE RSSI
- Add get_peer_rssi() to AndroidBLEDriver with last-receive tracking
- Add get_rssi() to AndroidBLEInterface wrapper classes
- Handle BLEPeerInterface in signal_quality.py for proper RSSI extraction
- Add 16 comprehensive unit tests for new functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 21:25:47 -05:00
torlando-tech
cd345baba9 feat: add RSSI and SNR signal metrics to messages
Add signal quality metrics (RSSI and SNR) to received messages, displayed
on the message detail screen. RNode-delivered messages show both metrics;
BLE shows RSSI only (Android limitation).

Changes:
- Python layer: New signal_quality.py module for extracting metrics from
  Reticulum interfaces at message delivery time
- Database: Add receivedRssi (INTEGER) and receivedSnr (REAL) columns with
  migration 30->31
- Protocol: Parse rssi/snr from JSON in ServiceReticulumProtocol and
  EventHandler
- UI: New SignalQualityInfo utility with color-coded signal strength/quality
  display in MessageDetailScreen

Signal thresholds:
- RSSI: Excellent (>-50), Good (-50 to -70), Fair (-70 to -85), Weak (-85 to -100), Very Weak (<-100) dBm
- SNR: Excellent (>10), Good (5-10), Fair (0-5), Poor (-5-0), Very Poor (<-5) dB

Closes #209

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 21:23:46 -05:00
torlando-tech
686120a737 Add unit tests for USB RNode support
Add comprehensive unit tests for the USB bridge and RNode interface:

Python tests (test_usb_bridge.py - 43 tests):
- USB bridge setup and initialization
- Device enumeration
- Permission handling
- Connection/disconnection
- Read/write operations
- Callback setters

Python tests (test_rnode_interface.py - added USB tests):
- USB mode constants (MODE_USB, CMD_BT_PIN, BT_CTRL_PAIRING_MODE)
- USB mode interface configuration
- Bluetooth pairing mode over USB
- KISS Bluetooth command framing
- Fixed existing tests to work with new connection_mode field

Kotlin tests (KotlinUSBBridgeTest.kt - 21 tests):
- Bridge initialization
- Connection listener registration/removal
- Duplicate listener prevention
- Listener exception isolation
- Bluetooth PIN callback handling
- Write/read operations when not connected
- Permission checking
- Thread safety for concurrent listener registration
- Coroutine scope shutdown
- Disconnect state clearing
- UsbDeviceInfo data class
- Supported VID verification

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 20:31:29 -05:00
torlando-tech
3e645dd7d7 feat: add USB RNode support (Issue #173)
Add USB serial support for RNode devices with two modes:
1. USB Serial Mode: Operate RNode entirely over USB cable
2. Bluetooth Pairing Mode: Pair RNode via USB (for T114 and devices without user button)

Key changes:
- Add KotlinUSBBridge for USB serial communication using usb-serial-for-android library
- Support FTDI, CP210x, CH340, PL2303, and CDC-ACM USB serial drivers
- Create Python usb_bridge.py module for Python-side USB access
- Extend rnode_interface.py with MODE_USB and Bluetooth pairing commands
- Add USB tab to RNode wizard UI with device enumeration and permission handling
- Implement Bluetooth pairing mode flow with PIN display
- Add DiscoveredUsbDevice data class for USB device information
- Update InterfaceConfig.RNode with usbDeviceId field
- Add USB host feature declaration to AndroidManifest

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 20:31:29 -05:00