Commit graph

283 commits

Author SHA1 Message Date
torlando-tech
ff16eec6d4 fix: clamp accuracy to unsigned short range in pack_location_telemetry
struct.pack("!H") requires 0-65535 but coarsened locations with
approxRadius > 655m produce values exceeding this, crashing the send.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:01:16 -05:00
torlando-tech
6c8256481c fix: use appearance from telemetry message for map markers
Two bugs prevented icon rendering on Columba-to-Columba location sharing:

1. Python: location_event dict passed to kotlin_location_received_callback
   never included FIELD_ICON_APPEARANCE data — it was only parsed for the
   message callback path, not the location callback path.

2. Kotlin MapViewModel: marker icons were read exclusively from the RNS
   announce cache (announce?.iconName), ignoring the appearanceJson stored
   in ReceivedLocationEntity. Announce cache only updates on chat messages,
   so icon changes didn't propagate until a regular message was sent.

Fix: parse FIELD_ICON_APPEARANCE into location_event['appearance'] on
receive, and prefer telemetry appearance over announce data in MapViewModel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 20:54:20 -05:00
torlando-tech
ed805cd5d5 fix: attach FIELD_ICON_APPEARANCE to location telemetry messages
Columba was sending FIELD_TELEMETRY (0x02) without FIELD_ICON_APPEARANCE
(0x04) when sharing location. Sideband includes both, so Sideband icons
rendered correctly but Columba-to-Columba icons were lost.

Threads icon data through the full call chain:
- LocationSharingManager reads active identity's icon from IdentityRepository
- ReticulumProtocol.sendLocationTelemetry accepts IconAppearance param
- ServiceReticulumProtocol/Binder pass icon_name/fg/bg through AIDL
- Python send_location_telemetry attaches FIELD_ICON_APPEARANCE to LXMF fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 20:46:28 -05:00
torlando-tech
689979b93b fix: remove unused marker_symbol parameter from telemetry collector
Resolves Greptile review comment — the parameter was accepted and stored
but never passed by any caller, creating dead infrastructure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:30:31 -05:00
Claude
085734464a
fix: correct telemetry appearance byte order and add marker symbol registry
The Sideband FIELD_TELEMETRY_STREAM appearance format is
[icon_name, bg_rgb_bytes, fg_rgb_bytes] but the unpacker was treating
positions 1 and 2 as fg and bg respectively, swapping the colours.

- Fix appearance byte order in unpack_telemetry_stream to match Sideband
  format [icon_name, background, foreground]
- Fix FIELD_ICON_APPEARANCE constant comment to document correct order
- Allow hyphens in icon name validation for MDI names (e.g. "sail-boat")
- Add human-readable sensor logging in unpack_location_telemetry
- Add MARKER_SYMBOL_REGISTRY mapping 45 symbol keys to MDI icon names
- Add _color_from_symbol_key() for deterministic background colour
  derivation from marker symbol keys
- Add appearance_from_marker_symbol() helper for building Sideband-
  compatible appearance tuples from marker metadata
- Update _send_telemetry_stream_response to derive appearance from
  marker symbol when no explicit appearance is stored
- Update _store_telemetry_for_collector to accept marker_symbol param
- Persist appearance JSON in LocationSharingManager.handleReceivedLocation
- Update tests for corrected byte order and add marker symbol tests

Closes #405

https://claude.ai/code/session_01Y5NnRbAHQAVjfR5RezQyvm
2026-02-09 04:21:42 +00:00
torlando-tech
5efcf3f7eb fix: resolve PTT mute race condition and hangup deadlock
- Add Mutex to serialize mute IPC calls in CallViewModel, preventing
  race between PTT button release (setCallMuted(true)) and PTT mode
  toggle (setCallMuted(false)) on Dispatchers.IO thread pool

- Move link.teardown() outside _call_handler_lock in Python
  call_manager.hangup() to prevent deadlock: teardown() can trigger
  __link_closed() synchronously on the same thread, which tries to
  re-acquire the non-reentrant threading.Lock, blocking all subsequent
  call() invocations forever

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 19:54:18 -05:00
torlando-tech
615c23d4ed fix(test): stabilize flaky test_establish_link_timeout
Two root causes of intermittent failure:
- mock_time.time.side_effect list exhaustion raised StopIteration
  (str(StopIteration()) == '', causing AssertionError: '' != 'Timeout')
- RNS.Transport.active_links was an unmocked Mock object, raising
  TypeError when production code iterated it

Replace fixed side_effect list with counter function and set
active_links = [] to prevent both failure modes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 17:23:07 -05:00
torlando-tech
8d277aacad fix(python): cancel path discovery loops on hangup
Adds a threading.Event that hangup() sets and the identity/path
discovery loops check, so a cancelled outgoing call exits promptly
instead of blocking the thread for up to 75 seconds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:02:54 -05:00
torlando-tech
83f500a462 fix(python): add debug logging to call_manager.call() flow
Logs identity recall, destination hash, and has_path status so
call setup issues are visible without code changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 13:59:16 -05:00
torlando-tech
51a9c0db50 feat(telephony): improve call setup reliability and init speed
- Announce lxst.telephony destination alongside LXMF announces so
  remote peers discover both messaging and call paths together
- Add periodic re-announcing in CallManager (3-hour interval,
  matching reference LXST Telephony implementation)
- Auto-request identity path from network when unknown, with 5s
  retry loop (mirrors existing LXMF pattern in reticulum_wrapper)
- Move setupLxstCallManager() to first position in setupBridges()
  so Telephone is available immediately after Python init (was last,
  causing up to ~47s delay before calls could be placed)
- Remove stale "call.audio" announce handler that could never match
  the actual "lxst.telephony" aspect

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 12:43:23 -05:00
torlando-tech
13bedfe429 fix(python): guard call() with _busy flag to prevent race with incoming calls
The call() method checked and set self.active_call without holding
_call_handler_lock. During the up-to-70s path discovery window, an
incoming call could set active_call via __caller_identified, which
call() would then overwrite — orphaning the incoming link.

Fix: set _busy=True under lock before path discovery so incoming
calls see the reservation and get rejected. Clear _busy in finally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 22:44:21 -05:00
torlando-tech
9e51d70652 fix(python): reject outgoing call if active_call already set
Prevents orphaned Reticulum links when call() is invoked while an
existing call is active or connecting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 21:10:47 -05:00
torlando-tech
d139938531 fix(python): eliminate TOCTOU race in audio/signal hot path
Use local reference capture instead of locks for receive_audio_packet()
and receive_signal() to prevent race with hangup()/__link_closed()
nulling self.active_call. Lock-free approach avoids audio jitter from
contention on the 25-50Hz packet path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 19:52:48 -05:00
torlando-tech
c9054ece03 fix: address Sentry/Greptile review comments on PR #416
- Add _call_handler_lock to answer(), hangup(), and __link_closed()
  to prevent race conditions on self.active_call (Sentry HIGH)
- Remove invalid `=*` gitignore pattern (Greptile)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 19:18:25 -05:00
torlando-tech
990cddde1d test(python): improve call_manager.py coverage from 48% to 94%
Add 40 new tests across 12 test classes covering previously untested
code paths: incoming/outgoing link handling, packet reception, signal
routing, audio forwarding, and Kotlin callback notification. Add
pragma: no cover to RNS fallback stub (dead code in test context).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 19:05:02 -05:00
torlando-tech
77115a0a07 fix(test): rewrite test_call_manager.py for transport-layer API
call_manager.py was rewritten during LXST-kt extraction as a thin
Reticulum transport layer — profile constants, telephone attribute,
audio bridge, and callback handlers all moved to Kotlin. Rewrite all
50 tests to match the new API surface: active_call (RNS.Link),
packet forwarding, signal routing, and bridge wiring.

Key fix: use RNS.Link.ACTIVE symbolically (not hardcoded int) since
conftest.py globally replaces sys.modules['RNS'] with MagicMock.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 17:27:43 -05:00
torlando-tech
1c497946b3 chore: remove dead Python LXST dependencies and audio backend
Remove vestigial Python LXST artifacts that are no longer needed
now that audio, codecs, and filters are handled by Kotlin LXST-kt:

- Remove numpy pip dependency (was for Python audio float32 conversion)
- Remove pycodec2 pip install and pre-built wheels (Kotlin JNI handles codecs)
- Delete chaquopy_audio_backend.py (617 lines, replaced by AudioDevice.kt)
- Delete lxst and lxst-filterlib wheels (never installed)
- Remove dead set_audio_bridge() plumbing from wrapper and manager
- Update call_manager.py to use renamed onInboundPacket/onInboundSignal
- Update LXST-kt submodule with transport-agnostic core/ renames

Remaining Python: call_manager.py (Reticulum bridge), rns, lxmf, u-msgpack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:02:34 -05:00
torlando-tech
96a93038e2 fix(lxst): fix incoming call answer button not picking up calls
Python's _notify_kotlin callback (via Chaquopy function reference) silently
fails to reach Telephone.onIncomingCall(), leaving isIncomingCall=false so
answer() always rejected. Fix with three changes:

- Add Telephone.prepareForAnswer() for lightweight JIT state setup
- Binder answerCall() falls back to CallBridge identity when answer() fails
- VoiceCallScreen auto-answer no longer gates on callState, preventing
  notification answer button from starting a new outgoing call

Also: answer() now returns Boolean, onIncomingCall() no longer sends
duplicate STATUS_RINGING (Python already sent it to remote).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:02:33 -05:00
torlando-tech
80cfe18ffa feat(lxst): extract lxst module with audio pipeline, codecs, and telephony
Extract the LXST (voice call) stack into a standalone Gradle module
(lxst/) decoupled from the reticulum module. Includes the full audio
pipeline (LineSink, LineSource, Mixer, Packetizer, Pipeline), codec
wrappers (Opus with 16kHz decode-rate cap for wuqi-opus 1024-sample
limit, Codec2), bridge layer (KotlinAudioBridge with 500ms AudioTrack
buffer floor, NetworkPacketBridge, CallBridge), and telephony state
machine (Telephone, Profile, NetworkTransport interface).

LineSink improvements:
- Monotonic clock pacer eliminates cumulative drift from relative delays
- Post-underrun re-buffering (AUTOSTART_MIN frames) prevents rapid
  micro-underrun cycles that caused audible pops
- Underrun duration threshold: brief glitches (<2 frame periods) resume
  immediately, sustained gaps trigger full re-buffer
- MAX_FRAMES increased to 15 (900ms jitter absorption at 60ms/frame)

Test coverage:
- LineSinkPacingTest: 9 unit tests for monotonic clock, burst pacing,
  drift, underrun recovery, re-buffer behavior
- LineSinkRebufferInstrumentedTest: 5 hardware tests validating
  re-buffer strategy against real AudioTrack underrun counts
- OpusDecodeRateInstrumentedTest: 8 tests documenting wuqi-opus
  1024-sample buffer constraint and 16kHz decode cap
- LineSinkPacingInstrumentedTest, FullDuplexInstrumentedTest,
  LineSourceCaptureInstrumentedTest: hardware validation suite

Known limitations:
- RX delayed ~2-3s (16kHz decode → 60ms frames → large pre-buffer)
- Occasional TX pops (separate from RX re-buffer work)
- Fix requires replacing wuqi-opus with caller-allocated-buffer library

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:02:33 -05:00
torlando-tech
1d0cf79f0a feat(11.6): enable bidirectional voice calls with crash fixes
Wire Kotlin→Python audio transmit path by registering call_manager as
NetworkPacketBridge's pythonNetworkHandler. Fix wuqi-opus SIGSEGV by
capping decode sample rate to fit 1024-sample buffer. Fix LineSink
sample rate mismatch and dual-sink race condition. Re-enable full
transmit pipeline (Packetizer, Mixer, LineSource). Add jarray→bytes
conversion for Chaquopy type compatibility.

Key changes:
- PythonWrapperManager: register setPythonNetworkHandler after CallManager init
- Opus: compute safe decode sample rate for wuqi-opus buffer limit
- LineSink: add releasedFlag to prevent stale frame auto-restart
- Telephone: re-enable transmit pipeline, fix pipeline lifecycle
- NetworkPacketBridge: add diagnostic logging for consumer coroutine
- call_manager.py: add jarray→bytes conversion, diagnostic logging
- LinkSource: add packet decoding with codec header parsing
- Remove obsolete lxst_debug_instrumentation.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:02:33 -05:00
torlando-tech
44077010d4 feat(11.6-01): add Kotlin audio active check to LineSource instrumentation
- Check is_kotlin_audio_active() in instrumented_ingest_job()
- Skip frame processing when Kotlin audio active
- Log skip every 100 frames for verification
- Maintain timing with sleep(frame_time)
- Provides additional safety layer beyond recorder silence
2026-02-07 16:02:33 -05:00
torlando-tech
b405dfa462 feat(11.6-01): add set_kotlin_audio_active() to CallManager
- Add set_kotlin_audio_active() method to CallManager
- Delegates to chaquopy_audio_backend.set_kotlin_audio_active()
- Provides Kotlin-accessible method to control Python audio disable
- Logs state change for debugging
2026-02-07 16:02:33 -05:00
torlando-tech
a28e7db15f feat(11.6-01): add _kotlin_audio_active flag to disable Python LXST audio
- Add global _kotlin_audio_active flag (default False)
- Add set_kotlin_audio_active() and is_kotlin_audio_active() functions
- ChaquopyRecorder._record_chunk() returns silence when flag True
- ChaquopyPlayer.play() drops frames when flag True
- Prevents dual audio pipeline conflict during Kotlin calls
2026-02-07 16:02:33 -05:00
torlando-tech
f2fb803e5b feat(11.5-01): wire INTEGER signals from Python to Kotlin
- Add STATUS_* signalling constants (0x00-0x06) to CallManager
- Send INTEGER signals via send_signal() in all LXST callbacks:
  - _handle_ringing() -> STATUS_RINGING (0x04)
  - _handle_established() -> STATUS_CONNECTING (0x05) + STATUS_ESTABLISHED (0x06)
  - _handle_ended() -> STATUS_AVAILABLE (0x03)
  - _handle_busy() -> STATUS_BUSY (0x00)
  - _handle_rejected() -> STATUS_REJECTED (0x01)
- Preserve existing _notify_kotlin() string events for UI

This fixes the signal type mismatch (Gap 1 from 11-VERIFICATION.md)
where Kotlin Telephone expected INTEGER signals but Python only sent
STRING events. Kotlin Telephone can now transition to ESTABLISHED state
and open audio pipelines.
2026-02-07 16:02:33 -05:00
torlando-tech
d5615bae86 feat(11-04): add Kotlin Telephone integration to call_manager.py
- Add on_state_changed() for Kotlin state notifications
- Add on_profile_changed() for profile sync
- Add set_kotlin_telephone_callback() for callback registration
- Add _notify_kotlin() for Python->Kotlin event notifications
- Update receive_audio_packet() to forward to LXST Packetizer
- Update receive_signal() to forward to LXST signalling
- Wire existing handlers to notify Kotlin Telephone
2026-02-07 16:02:33 -05:00
torlando-tech
b53df88c45 feat(10-05): initialize NetworkPacketBridge in PythonWrapperManager
- Add NetworkPacketBridge import
- Add networkPacketBridge instance variable
- Initialize NetworkPacketBridge in setupCallManager()
- Pass bridge to Python via set_network_bridge()
- Add shutdown cleanup for networkPacketBridge
- Add set_network_bridge() method to reticulum_wrapper.py
- Pass kotlin_network_bridge to initialize_call_manager()
2026-02-07 16:02:33 -05:00
torlando-tech
cae71db622 feat(10-05): add network bridge methods to call_manager.py
- Add _kotlin_network_bridge instance variable
- Update initialize() to accept kotlin_network_bridge parameter
- Update initialize_call_manager() to pass network bridge
- Add send_audio_packet() for Python->Kotlin packets
- Add send_signal() for Python->Kotlin signalling
- Add receive_audio_packet() stub for Kotlin->Python (Phase 11)
- Add receive_signal() stub for Kotlin->Python (Phase 11)
2026-02-07 16:02:33 -05:00
torlando-tech
a745afd43a chore(07-02): add opus-jni dependency to build.gradle.kts
- Added cn.entertech.android:wuqi-opus:1.0.3 from Maven Central
- Library wraps theeasiestway/android-opus-codec (libopus 1.3.1)
- Provides OpusEncoder/OpusDecoder with JNI bindings
- Native .so libraries for arm64-v8a and x86_64 ABIs
2026-02-07 16:02:33 -05:00
torlando-tech
86252a908e fix(python): make display_name optional in create_identity()
The second create_identity() method at line 7376 shadows the first one at
line 2338. Making display_name optional allows callers that don't pass a
name to work correctly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 10:31:56 -05:00
torlando-tech
153112dbc1 docs(deps): document RPC exception handling fix in requirements.txt
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:49:23 -05:00
torlando-tech
f4a8fe8658 fix(deps): update RNS to fix socket leak in TCPInterface
Update Reticulum fork from rebase-1.1.3 to fix/socket-leak-1.1.3 branch.

The fix closes sockets on connection failure in TCPInterface.connect() and
BackboneInterface.connect() to prevent resource leak during reconnection
attempts (~780 leaked sockets/hour, ~4.3 MB/hour native memory growth).

Addresses memory growth identified during Phase 9 profiling investigation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:29:03 -05:00
torlando-tech
dd416490a2 docs(05-02): audit PyObject lifecycle for memory leaks
- Comprehensive review of all PyObject usage in Kotlin code
- Confirmed no PyObject storage beyond method scope
- All PyObjects immediately converted to Kotlin types
- Chaquopy automatic reference counting handles cleanup
- Documented findings in memory_profiler.py comments

Files audited:
- PythonWrapperManager.kt ✓
- EventHandler.kt ✓
- IdentityManager.kt ✓
- RoutingManager.kt ✓
- PythonResultConverter.kt ✓

Conclusion: No PyObject reference leaks in Kotlin code.
2026-01-29 18:41:10 -05:00
torlando-tech
42667c8308 test(05-02): add unit tests for memory profiler
Cover start/stop, snapshots, periodic scheduling, and thread safety.
13 test cases for ~90% coverage of memory_profiler.py.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:33:04 -05:00
torlando-tech
9899f10ad0 fix(05-02): prevent race condition in memory profiler timer
Add threading.Lock to synchronize access to _profiling_active and
_snapshot_timer between stop_profiling() and timer callback.

Prevents orphaned timer continuing after stop_profiling() returns.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:29:26 -05:00
torlando-tech
9355889a31 feat(05-01): add Python memory profiler module
- Create memory_profiler.py with tracemalloc snapshot comparison
- Add enable_memory_profiling() method to ReticulumWrapper
- Schedule periodic snapshots using threading.Timer (Chaquopy-compatible)
- Filter frozen importlib and unknown traces to reduce noise
- Log top 10 growing allocations to Android logcat via logging_utils
- Provide get_memory_profile() for current stats query
- Zero overhead when disabled (lazy import pattern)
2026-01-29 17:08:16 -05:00
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