Refactor BLEInterface to driver-based architecture
Major architectural refactoring to separate high-level Reticulum protocol
logic from platform-specific Bluetooth operations. This enables code sharing
between pure Python and Android (Columba) implementations, improves
testability, and creates a clean boundary for future platform support.
ARCHITECTURE CHANGES:
1. **Driver Abstraction Layer**
- Created BLEDriverInterface (bluetooth_driver.py) defining the contract
for all platform-specific BLE drivers
- Abstraction includes 18 methods + 6 callbacks for complete BLE lifecycle
- Enhanced BLEDevice dataclass with service_uuids and manufacturer_data
- Added on_mtu_negotiated callback for delayed MTU reporting
- Added on_error callback for consistent platform error reporting
2. **Linux Driver Implementation**
- Created LinuxBluetoothDriver (linux_bluetooth_driver.py, 1534 lines)
- Moved ALL bleak/bluezero/D-Bus code from BLEInterface
- Preserves 5 critical platform workarounds:
* BlueZ ServicesResolved race condition patch
* D-Bus LE-only connection (ConnectDevice)
* BLE Agent registration for Just Works pairing
* MTU negotiation with 3-method fallback
* Service discovery delay for bluezero timing
- Role-aware send() automatically chooses GATT write vs notification
- Dedicated asyncio event loop management in separate thread
- Configuration via constructor (no Reticulum dependencies)
3. **Refactored BLEInterface**
- Removed 801 lines (32.3% reduction: 2479 → 1678 lines)
- Removed all platform-specific imports (bleak, bluezero, dbus_fast)
- Removed 9 async methods (moved to driver)
- Driver dependency injection via constructor
- Implemented 6 driver callbacks for event handling
- PRESERVED high-level logic:
* Peer scoring algorithm (RSSI + history + recency)
* Connection blacklist with exponential backoff
* MAC-based connection direction (prevents dual connections)
* Fragmentation/reassembly orchestration (identity-based keying)
* Interface spawning per peer
4. **Simplified BLEPeerInterface**
- Removed connection_type, client, mtu parameters
- Deleted _send_via_central() and _send_via_peripheral() methods
- Single send path via driver.send() (driver handles role routing)
- 77 lines removed from peer interface class
5. **Mock Driver for Testing**
- Created MockBLEDriver (tests/mock_ble_driver.py)
- Complete BLEDriverInterface implementation without hardware
- Bidirectional communication via link_drivers()
- Enables unit testing of BLEInterface logic (fragmentation, reassembly,
peer lifecycle, blacklist management)
CRITICAL FIXES:
1. **Restored Periodic Cleanup Task** (CRITICAL: prevents memory leaks)
- Converted from async (driver-owned loop) to threading.Timer
- Runs every 30 seconds to clean stale reassembly buffers
- Essential for long-running instances (Pi Zero with 512MB RAM)
- Properly cancelled in detach() for clean shutdown
2. **Fixed Naming Consistency**
- Renamed processOutgoing → process_outgoing (snake_case)
FILES MODIFIED:
- src/RNS/Interfaces/BLEInterface.py (refactored, -801 lines)
FILES ADDED:
- bluetooth_driver.py (driver abstraction interface)
- linux_bluetooth_driver.py (Linux/BlueZ implementation, 1534 lines)
- tests/mock_ble_driver.py (mock driver for unit tests)
- REFACTORING_GUIDE.md (comprehensive refactoring documentation)
- BLE_PROTOCOL_v2.2.md (protocol specification)
- tests/test_refactor_suite.py (initial test suite)
BENEFITS:
1. **Testability** - Mock driver enables hardware-free unit testing
2. **Portability** - Easy to create Android/Windows/macOS drivers
3. **Maintainability** - Platform quirks isolated in single driver file
4. **Code Sharing** - High-level logic shared across all platforms
5. **Clean Architecture** - Clear separation of concerns
TESTING REQUIRED:
- Tier 1 (Unit): Test with MockBLEDriver (fragmentation, reassembly, lifecycle)
- Tier 2 (Integration): Test on Raspberry Pi hardware (scanning, connecting,
dual mode, MTU negotiation, identity exchange)
- Tier 3 (Regression): Full Reticulum stack (announces, LXMF, multi-hop)
- Tier 4 (Edge Cases): MAC rotation, identity handshake, reconnection,
reassembly timeout, discovery cache pruning
BACKWARD COMPATIBILITY:
- Configuration: Fully backward compatible (same config parameters)
- Protocol: No changes to BLE wire protocol (v2.2)
- Interface API: Unchanged for Reticulum Transport integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:03:54 -05:00
|
|
|
"""
|
|
|
|
|
Mock BLE Driver for Unit Testing
|
|
|
|
|
|
|
|
|
|
This module provides a mock implementation of BLEDriverInterface that simulates
|
|
|
|
|
BLE behavior without requiring actual Bluetooth hardware. It's designed for
|
|
|
|
|
unit testing BLEInterface logic including:
|
|
|
|
|
|
|
|
|
|
- Fragmentation and reassembly
|
|
|
|
|
- Peer lifecycle management
|
|
|
|
|
- Connection blacklist logic
|
|
|
|
|
- MAC-based connection direction
|
|
|
|
|
- Error handling
|
|
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
# Create two mock drivers to simulate a pair of peers
|
|
|
|
|
driver1 = MockBLEDriver()
|
|
|
|
|
driver2 = MockBLEDriver()
|
|
|
|
|
|
|
|
|
|
# Link them to enable bidirectional communication
|
|
|
|
|
MockBLEDriver.link_drivers(driver1, driver2)
|
|
|
|
|
|
|
|
|
|
# Simulate discovery
|
|
|
|
|
driver1.simulate_device_discovered("AA:BB:CC:DD:EE:FF", "RNS-Test", -60)
|
|
|
|
|
|
|
|
|
|
# Simulate connection
|
|
|
|
|
driver1.connect("AA:BB:CC:DD:EE:FF")
|
|
|
|
|
|
|
|
|
|
# Simulate data transfer
|
|
|
|
|
driver1.send("AA:BB:CC:DD:EE:FF", b"test data")
|
|
|
|
|
# -> Triggers driver2.on_data_received("11:22:33:44:55:66", b"test data")
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import sys
|
|
|
|
|
import os
|
|
|
|
|
|
2025-11-26 12:06:54 -05:00
|
|
|
# Add src directory to path for imports
|
|
|
|
|
src_path = os.path.join(os.path.dirname(__file__), '..', 'src')
|
|
|
|
|
if src_path not in sys.path:
|
|
|
|
|
sys.path.insert(0, src_path)
|
|
|
|
|
|
|
|
|
|
# Import directly using importlib to bypass RNS namespace conflicts
|
|
|
|
|
# This avoids issues when a real RNS package is installed globally
|
|
|
|
|
import importlib.util
|
2026-01-01 14:03:59 -05:00
|
|
|
bluetooth_driver_path = os.path.join(src_path, 'ble_reticulum', 'bluetooth_driver.py')
|
2025-11-26 12:06:54 -05:00
|
|
|
spec = importlib.util.spec_from_file_location("bluetooth_driver", bluetooth_driver_path)
|
|
|
|
|
bluetooth_driver = importlib.util.module_from_spec(spec)
|
|
|
|
|
spec.loader.exec_module(bluetooth_driver)
|
|
|
|
|
|
|
|
|
|
BLEDriverInterface = bluetooth_driver.BLEDriverInterface
|
|
|
|
|
BLEDevice = bluetooth_driver.BLEDevice
|
|
|
|
|
DriverState = bluetooth_driver.DriverState
|
Refactor BLEInterface to driver-based architecture
Major architectural refactoring to separate high-level Reticulum protocol
logic from platform-specific Bluetooth operations. This enables code sharing
between pure Python and Android (Columba) implementations, improves
testability, and creates a clean boundary for future platform support.
ARCHITECTURE CHANGES:
1. **Driver Abstraction Layer**
- Created BLEDriverInterface (bluetooth_driver.py) defining the contract
for all platform-specific BLE drivers
- Abstraction includes 18 methods + 6 callbacks for complete BLE lifecycle
- Enhanced BLEDevice dataclass with service_uuids and manufacturer_data
- Added on_mtu_negotiated callback for delayed MTU reporting
- Added on_error callback for consistent platform error reporting
2. **Linux Driver Implementation**
- Created LinuxBluetoothDriver (linux_bluetooth_driver.py, 1534 lines)
- Moved ALL bleak/bluezero/D-Bus code from BLEInterface
- Preserves 5 critical platform workarounds:
* BlueZ ServicesResolved race condition patch
* D-Bus LE-only connection (ConnectDevice)
* BLE Agent registration for Just Works pairing
* MTU negotiation with 3-method fallback
* Service discovery delay for bluezero timing
- Role-aware send() automatically chooses GATT write vs notification
- Dedicated asyncio event loop management in separate thread
- Configuration via constructor (no Reticulum dependencies)
3. **Refactored BLEInterface**
- Removed 801 lines (32.3% reduction: 2479 → 1678 lines)
- Removed all platform-specific imports (bleak, bluezero, dbus_fast)
- Removed 9 async methods (moved to driver)
- Driver dependency injection via constructor
- Implemented 6 driver callbacks for event handling
- PRESERVED high-level logic:
* Peer scoring algorithm (RSSI + history + recency)
* Connection blacklist with exponential backoff
* MAC-based connection direction (prevents dual connections)
* Fragmentation/reassembly orchestration (identity-based keying)
* Interface spawning per peer
4. **Simplified BLEPeerInterface**
- Removed connection_type, client, mtu parameters
- Deleted _send_via_central() and _send_via_peripheral() methods
- Single send path via driver.send() (driver handles role routing)
- 77 lines removed from peer interface class
5. **Mock Driver for Testing**
- Created MockBLEDriver (tests/mock_ble_driver.py)
- Complete BLEDriverInterface implementation without hardware
- Bidirectional communication via link_drivers()
- Enables unit testing of BLEInterface logic (fragmentation, reassembly,
peer lifecycle, blacklist management)
CRITICAL FIXES:
1. **Restored Periodic Cleanup Task** (CRITICAL: prevents memory leaks)
- Converted from async (driver-owned loop) to threading.Timer
- Runs every 30 seconds to clean stale reassembly buffers
- Essential for long-running instances (Pi Zero with 512MB RAM)
- Properly cancelled in detach() for clean shutdown
2. **Fixed Naming Consistency**
- Renamed processOutgoing → process_outgoing (snake_case)
FILES MODIFIED:
- src/RNS/Interfaces/BLEInterface.py (refactored, -801 lines)
FILES ADDED:
- bluetooth_driver.py (driver abstraction interface)
- linux_bluetooth_driver.py (Linux/BlueZ implementation, 1534 lines)
- tests/mock_ble_driver.py (mock driver for unit tests)
- REFACTORING_GUIDE.md (comprehensive refactoring documentation)
- BLE_PROTOCOL_v2.2.md (protocol specification)
- tests/test_refactor_suite.py (initial test suite)
BENEFITS:
1. **Testability** - Mock driver enables hardware-free unit testing
2. **Portability** - Easy to create Android/Windows/macOS drivers
3. **Maintainability** - Platform quirks isolated in single driver file
4. **Code Sharing** - High-level logic shared across all platforms
5. **Clean Architecture** - Clear separation of concerns
TESTING REQUIRED:
- Tier 1 (Unit): Test with MockBLEDriver (fragmentation, reassembly, lifecycle)
- Tier 2 (Integration): Test on Raspberry Pi hardware (scanning, connecting,
dual mode, MTU negotiation, identity exchange)
- Tier 3 (Regression): Full Reticulum stack (announces, LXMF, multi-hop)
- Tier 4 (Edge Cases): MAC rotation, identity handshake, reconnection,
reassembly timeout, discovery cache pruning
BACKWARD COMPATIBILITY:
- Configuration: Fully backward compatible (same config parameters)
- Protocol: No changes to BLE wire protocol (v2.2)
- Interface API: Unchanged for Reticulum Transport integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:03:54 -05:00
|
|
|
from typing import List, Optional, Callable, Dict
|
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MockBLEDriver(BLEDriverInterface):
|
|
|
|
|
"""
|
|
|
|
|
Mock BLE driver that simulates Bluetooth behavior for testing.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, local_address: str = "11:22:33:44:55:66"):
|
|
|
|
|
"""
|
|
|
|
|
Initialize the mock driver.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
local_address: Simulated MAC address for this driver
|
|
|
|
|
"""
|
|
|
|
|
self.local_address = local_address
|
|
|
|
|
self._state = DriverState.IDLE
|
|
|
|
|
self._connected_peers: Dict[str, dict] = {} # address -> {role, mtu, identity}
|
|
|
|
|
self._identity: Optional[bytes] = None
|
|
|
|
|
self._service_discovery_delay: float = 0.0 # No delay in mock
|
|
|
|
|
self._power_mode: str = "balanced"
|
|
|
|
|
|
|
|
|
|
# UUIDs (set via start())
|
|
|
|
|
self._service_uuid: Optional[str] = None
|
|
|
|
|
self._rx_char_uuid: Optional[str] = None
|
|
|
|
|
self._tx_char_uuid: Optional[str] = None
|
|
|
|
|
self._identity_char_uuid: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
# Callbacks (assigned by consumer)
|
|
|
|
|
self.on_device_discovered: Optional[Callable[[BLEDevice], None]] = None
|
2026-01-18 01:26:57 -05:00
|
|
|
self.on_device_connected: Optional[Callable[[str, Optional[bytes]], None]] = None # address, peer_identity
|
Refactor BLEInterface to driver-based architecture
Major architectural refactoring to separate high-level Reticulum protocol
logic from platform-specific Bluetooth operations. This enables code sharing
between pure Python and Android (Columba) implementations, improves
testability, and creates a clean boundary for future platform support.
ARCHITECTURE CHANGES:
1. **Driver Abstraction Layer**
- Created BLEDriverInterface (bluetooth_driver.py) defining the contract
for all platform-specific BLE drivers
- Abstraction includes 18 methods + 6 callbacks for complete BLE lifecycle
- Enhanced BLEDevice dataclass with service_uuids and manufacturer_data
- Added on_mtu_negotiated callback for delayed MTU reporting
- Added on_error callback for consistent platform error reporting
2. **Linux Driver Implementation**
- Created LinuxBluetoothDriver (linux_bluetooth_driver.py, 1534 lines)
- Moved ALL bleak/bluezero/D-Bus code from BLEInterface
- Preserves 5 critical platform workarounds:
* BlueZ ServicesResolved race condition patch
* D-Bus LE-only connection (ConnectDevice)
* BLE Agent registration for Just Works pairing
* MTU negotiation with 3-method fallback
* Service discovery delay for bluezero timing
- Role-aware send() automatically chooses GATT write vs notification
- Dedicated asyncio event loop management in separate thread
- Configuration via constructor (no Reticulum dependencies)
3. **Refactored BLEInterface**
- Removed 801 lines (32.3% reduction: 2479 → 1678 lines)
- Removed all platform-specific imports (bleak, bluezero, dbus_fast)
- Removed 9 async methods (moved to driver)
- Driver dependency injection via constructor
- Implemented 6 driver callbacks for event handling
- PRESERVED high-level logic:
* Peer scoring algorithm (RSSI + history + recency)
* Connection blacklist with exponential backoff
* MAC-based connection direction (prevents dual connections)
* Fragmentation/reassembly orchestration (identity-based keying)
* Interface spawning per peer
4. **Simplified BLEPeerInterface**
- Removed connection_type, client, mtu parameters
- Deleted _send_via_central() and _send_via_peripheral() methods
- Single send path via driver.send() (driver handles role routing)
- 77 lines removed from peer interface class
5. **Mock Driver for Testing**
- Created MockBLEDriver (tests/mock_ble_driver.py)
- Complete BLEDriverInterface implementation without hardware
- Bidirectional communication via link_drivers()
- Enables unit testing of BLEInterface logic (fragmentation, reassembly,
peer lifecycle, blacklist management)
CRITICAL FIXES:
1. **Restored Periodic Cleanup Task** (CRITICAL: prevents memory leaks)
- Converted from async (driver-owned loop) to threading.Timer
- Runs every 30 seconds to clean stale reassembly buffers
- Essential for long-running instances (Pi Zero with 512MB RAM)
- Properly cancelled in detach() for clean shutdown
2. **Fixed Naming Consistency**
- Renamed processOutgoing → process_outgoing (snake_case)
FILES MODIFIED:
- src/RNS/Interfaces/BLEInterface.py (refactored, -801 lines)
FILES ADDED:
- bluetooth_driver.py (driver abstraction interface)
- linux_bluetooth_driver.py (Linux/BlueZ implementation, 1534 lines)
- tests/mock_ble_driver.py (mock driver for unit tests)
- REFACTORING_GUIDE.md (comprehensive refactoring documentation)
- BLE_PROTOCOL_v2.2.md (protocol specification)
- tests/test_refactor_suite.py (initial test suite)
BENEFITS:
1. **Testability** - Mock driver enables hardware-free unit testing
2. **Portability** - Easy to create Android/Windows/macOS drivers
3. **Maintainability** - Platform quirks isolated in single driver file
4. **Code Sharing** - High-level logic shared across all platforms
5. **Clean Architecture** - Clear separation of concerns
TESTING REQUIRED:
- Tier 1 (Unit): Test with MockBLEDriver (fragmentation, reassembly, lifecycle)
- Tier 2 (Integration): Test on Raspberry Pi hardware (scanning, connecting,
dual mode, MTU negotiation, identity exchange)
- Tier 3 (Regression): Full Reticulum stack (announces, LXMF, multi-hop)
- Tier 4 (Edge Cases): MAC rotation, identity handshake, reconnection,
reassembly timeout, discovery cache pruning
BACKWARD COMPATIBILITY:
- Configuration: Fully backward compatible (same config parameters)
- Protocol: No changes to BLE wire protocol (v2.2)
- Interface API: Unchanged for Reticulum Transport integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:03:54 -05:00
|
|
|
self.on_device_disconnected: Optional[Callable[[str], None]] = None
|
|
|
|
|
self.on_data_received: Optional[Callable[[str, bytes], None]] = None
|
|
|
|
|
self.on_mtu_negotiated: Optional[Callable[[str, int], None]] = None
|
|
|
|
|
self.on_error: Optional[Callable[[str, str, Optional[Exception]], None]] = None
|
|
|
|
|
|
|
|
|
|
# Linked driver for bidirectional communication testing
|
|
|
|
|
self._linked_driver: Optional['MockBLEDriver'] = None
|
|
|
|
|
|
|
|
|
|
# Simulated characteristics storage
|
|
|
|
|
self._characteristics: Dict[str, bytes] = {} # char_uuid -> value
|
|
|
|
|
|
|
|
|
|
# Track sent data for assertions
|
|
|
|
|
self.sent_data: List[tuple] = [] # [(address, data), ...]
|
|
|
|
|
|
|
|
|
|
# --- Lifecycle & Configuration ---
|
|
|
|
|
|
|
|
|
|
def start(self, service_uuid: str, rx_char_uuid: str, tx_char_uuid: str, identity_char_uuid: str):
|
|
|
|
|
"""Initialize the mock driver with UUIDs."""
|
|
|
|
|
self._service_uuid = service_uuid
|
|
|
|
|
self._rx_char_uuid = rx_char_uuid
|
|
|
|
|
self._tx_char_uuid = tx_char_uuid
|
|
|
|
|
self._identity_char_uuid = identity_char_uuid
|
|
|
|
|
self._state = DriverState.IDLE
|
|
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
|
"""Stop all activity and disconnect all peers."""
|
|
|
|
|
for address in list(self._connected_peers.keys()):
|
|
|
|
|
self.disconnect(address)
|
|
|
|
|
self._state = DriverState.IDLE
|
|
|
|
|
|
|
|
|
|
def set_identity(self, identity_bytes: bytes):
|
|
|
|
|
"""Set the local identity value."""
|
|
|
|
|
self._identity = identity_bytes
|
|
|
|
|
self._characteristics[self._identity_char_uuid] = identity_bytes
|
|
|
|
|
|
|
|
|
|
# --- State & Properties ---
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def state(self) -> DriverState:
|
|
|
|
|
"""Return current state."""
|
|
|
|
|
return self._state
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def connected_peers(self) -> List[str]:
|
|
|
|
|
"""Return list of connected peer addresses."""
|
|
|
|
|
return list(self._connected_peers.keys())
|
|
|
|
|
|
|
|
|
|
# --- Core Actions ---
|
|
|
|
|
|
|
|
|
|
def start_scanning(self):
|
|
|
|
|
"""Start scanning (simulated)."""
|
|
|
|
|
self._state = DriverState.SCANNING
|
|
|
|
|
|
|
|
|
|
def stop_scanning(self):
|
|
|
|
|
"""Stop scanning."""
|
|
|
|
|
if self._state == DriverState.SCANNING:
|
|
|
|
|
self._state = DriverState.IDLE
|
|
|
|
|
|
|
|
|
|
def start_advertising(self, device_name: str, identity: bytes):
|
|
|
|
|
"""Start advertising (simulated)."""
|
|
|
|
|
self._identity = identity
|
|
|
|
|
self._characteristics[self._identity_char_uuid] = identity
|
|
|
|
|
self._state = DriverState.ADVERTISING
|
|
|
|
|
|
|
|
|
|
def stop_advertising(self):
|
|
|
|
|
"""Stop advertising."""
|
|
|
|
|
if self._state == DriverState.ADVERTISING:
|
|
|
|
|
self._state = DriverState.IDLE
|
|
|
|
|
|
|
|
|
|
def connect(self, address: str):
|
|
|
|
|
"""
|
|
|
|
|
Simulate connecting to a peer (central role).
|
|
|
|
|
|
|
|
|
|
If a linked driver is set and its address matches, establishes
|
|
|
|
|
a bidirectional connection.
|
|
|
|
|
"""
|
|
|
|
|
if address in self._connected_peers:
|
|
|
|
|
return # Already connected
|
|
|
|
|
|
2026-01-18 01:26:57 -05:00
|
|
|
# Get peer identity if linked driver is set
|
|
|
|
|
peer_identity = None
|
|
|
|
|
if self._linked_driver and self._linked_driver.local_address == address:
|
|
|
|
|
peer_identity = self._linked_driver._identity
|
|
|
|
|
|
Refactor BLEInterface to driver-based architecture
Major architectural refactoring to separate high-level Reticulum protocol
logic from platform-specific Bluetooth operations. This enables code sharing
between pure Python and Android (Columba) implementations, improves
testability, and creates a clean boundary for future platform support.
ARCHITECTURE CHANGES:
1. **Driver Abstraction Layer**
- Created BLEDriverInterface (bluetooth_driver.py) defining the contract
for all platform-specific BLE drivers
- Abstraction includes 18 methods + 6 callbacks for complete BLE lifecycle
- Enhanced BLEDevice dataclass with service_uuids and manufacturer_data
- Added on_mtu_negotiated callback for delayed MTU reporting
- Added on_error callback for consistent platform error reporting
2. **Linux Driver Implementation**
- Created LinuxBluetoothDriver (linux_bluetooth_driver.py, 1534 lines)
- Moved ALL bleak/bluezero/D-Bus code from BLEInterface
- Preserves 5 critical platform workarounds:
* BlueZ ServicesResolved race condition patch
* D-Bus LE-only connection (ConnectDevice)
* BLE Agent registration for Just Works pairing
* MTU negotiation with 3-method fallback
* Service discovery delay for bluezero timing
- Role-aware send() automatically chooses GATT write vs notification
- Dedicated asyncio event loop management in separate thread
- Configuration via constructor (no Reticulum dependencies)
3. **Refactored BLEInterface**
- Removed 801 lines (32.3% reduction: 2479 → 1678 lines)
- Removed all platform-specific imports (bleak, bluezero, dbus_fast)
- Removed 9 async methods (moved to driver)
- Driver dependency injection via constructor
- Implemented 6 driver callbacks for event handling
- PRESERVED high-level logic:
* Peer scoring algorithm (RSSI + history + recency)
* Connection blacklist with exponential backoff
* MAC-based connection direction (prevents dual connections)
* Fragmentation/reassembly orchestration (identity-based keying)
* Interface spawning per peer
4. **Simplified BLEPeerInterface**
- Removed connection_type, client, mtu parameters
- Deleted _send_via_central() and _send_via_peripheral() methods
- Single send path via driver.send() (driver handles role routing)
- 77 lines removed from peer interface class
5. **Mock Driver for Testing**
- Created MockBLEDriver (tests/mock_ble_driver.py)
- Complete BLEDriverInterface implementation without hardware
- Bidirectional communication via link_drivers()
- Enables unit testing of BLEInterface logic (fragmentation, reassembly,
peer lifecycle, blacklist management)
CRITICAL FIXES:
1. **Restored Periodic Cleanup Task** (CRITICAL: prevents memory leaks)
- Converted from async (driver-owned loop) to threading.Timer
- Runs every 30 seconds to clean stale reassembly buffers
- Essential for long-running instances (Pi Zero with 512MB RAM)
- Properly cancelled in detach() for clean shutdown
2. **Fixed Naming Consistency**
- Renamed processOutgoing → process_outgoing (snake_case)
FILES MODIFIED:
- src/RNS/Interfaces/BLEInterface.py (refactored, -801 lines)
FILES ADDED:
- bluetooth_driver.py (driver abstraction interface)
- linux_bluetooth_driver.py (Linux/BlueZ implementation, 1534 lines)
- tests/mock_ble_driver.py (mock driver for unit tests)
- REFACTORING_GUIDE.md (comprehensive refactoring documentation)
- BLE_PROTOCOL_v2.2.md (protocol specification)
- tests/test_refactor_suite.py (initial test suite)
BENEFITS:
1. **Testability** - Mock driver enables hardware-free unit testing
2. **Portability** - Easy to create Android/Windows/macOS drivers
3. **Maintainability** - Platform quirks isolated in single driver file
4. **Code Sharing** - High-level logic shared across all platforms
5. **Clean Architecture** - Clear separation of concerns
TESTING REQUIRED:
- Tier 1 (Unit): Test with MockBLEDriver (fragmentation, reassembly, lifecycle)
- Tier 2 (Integration): Test on Raspberry Pi hardware (scanning, connecting,
dual mode, MTU negotiation, identity exchange)
- Tier 3 (Regression): Full Reticulum stack (announces, LXMF, multi-hop)
- Tier 4 (Edge Cases): MAC rotation, identity handshake, reconnection,
reassembly timeout, discovery cache pruning
BACKWARD COMPATIBILITY:
- Configuration: Fully backward compatible (same config parameters)
- Protocol: No changes to BLE wire protocol (v2.2)
- Interface API: Unchanged for Reticulum Transport integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:03:54 -05:00
|
|
|
# Simulate connection with default MTU
|
|
|
|
|
self._connected_peers[address] = {
|
|
|
|
|
"role": "central",
|
|
|
|
|
"mtu": 185, # Default MTU
|
2026-01-18 01:26:57 -05:00
|
|
|
"identity": peer_identity
|
Refactor BLEInterface to driver-based architecture
Major architectural refactoring to separate high-level Reticulum protocol
logic from platform-specific Bluetooth operations. This enables code sharing
between pure Python and Android (Columba) implementations, improves
testability, and creates a clean boundary for future platform support.
ARCHITECTURE CHANGES:
1. **Driver Abstraction Layer**
- Created BLEDriverInterface (bluetooth_driver.py) defining the contract
for all platform-specific BLE drivers
- Abstraction includes 18 methods + 6 callbacks for complete BLE lifecycle
- Enhanced BLEDevice dataclass with service_uuids and manufacturer_data
- Added on_mtu_negotiated callback for delayed MTU reporting
- Added on_error callback for consistent platform error reporting
2. **Linux Driver Implementation**
- Created LinuxBluetoothDriver (linux_bluetooth_driver.py, 1534 lines)
- Moved ALL bleak/bluezero/D-Bus code from BLEInterface
- Preserves 5 critical platform workarounds:
* BlueZ ServicesResolved race condition patch
* D-Bus LE-only connection (ConnectDevice)
* BLE Agent registration for Just Works pairing
* MTU negotiation with 3-method fallback
* Service discovery delay for bluezero timing
- Role-aware send() automatically chooses GATT write vs notification
- Dedicated asyncio event loop management in separate thread
- Configuration via constructor (no Reticulum dependencies)
3. **Refactored BLEInterface**
- Removed 801 lines (32.3% reduction: 2479 → 1678 lines)
- Removed all platform-specific imports (bleak, bluezero, dbus_fast)
- Removed 9 async methods (moved to driver)
- Driver dependency injection via constructor
- Implemented 6 driver callbacks for event handling
- PRESERVED high-level logic:
* Peer scoring algorithm (RSSI + history + recency)
* Connection blacklist with exponential backoff
* MAC-based connection direction (prevents dual connections)
* Fragmentation/reassembly orchestration (identity-based keying)
* Interface spawning per peer
4. **Simplified BLEPeerInterface**
- Removed connection_type, client, mtu parameters
- Deleted _send_via_central() and _send_via_peripheral() methods
- Single send path via driver.send() (driver handles role routing)
- 77 lines removed from peer interface class
5. **Mock Driver for Testing**
- Created MockBLEDriver (tests/mock_ble_driver.py)
- Complete BLEDriverInterface implementation without hardware
- Bidirectional communication via link_drivers()
- Enables unit testing of BLEInterface logic (fragmentation, reassembly,
peer lifecycle, blacklist management)
CRITICAL FIXES:
1. **Restored Periodic Cleanup Task** (CRITICAL: prevents memory leaks)
- Converted from async (driver-owned loop) to threading.Timer
- Runs every 30 seconds to clean stale reassembly buffers
- Essential for long-running instances (Pi Zero with 512MB RAM)
- Properly cancelled in detach() for clean shutdown
2. **Fixed Naming Consistency**
- Renamed processOutgoing → process_outgoing (snake_case)
FILES MODIFIED:
- src/RNS/Interfaces/BLEInterface.py (refactored, -801 lines)
FILES ADDED:
- bluetooth_driver.py (driver abstraction interface)
- linux_bluetooth_driver.py (Linux/BlueZ implementation, 1534 lines)
- tests/mock_ble_driver.py (mock driver for unit tests)
- REFACTORING_GUIDE.md (comprehensive refactoring documentation)
- BLE_PROTOCOL_v2.2.md (protocol specification)
- tests/test_refactor_suite.py (initial test suite)
BENEFITS:
1. **Testability** - Mock driver enables hardware-free unit testing
2. **Portability** - Easy to create Android/Windows/macOS drivers
3. **Maintainability** - Platform quirks isolated in single driver file
4. **Code Sharing** - High-level logic shared across all platforms
5. **Clean Architecture** - Clear separation of concerns
TESTING REQUIRED:
- Tier 1 (Unit): Test with MockBLEDriver (fragmentation, reassembly, lifecycle)
- Tier 2 (Integration): Test on Raspberry Pi hardware (scanning, connecting,
dual mode, MTU negotiation, identity exchange)
- Tier 3 (Regression): Full Reticulum stack (announces, LXMF, multi-hop)
- Tier 4 (Edge Cases): MAC rotation, identity handshake, reconnection,
reassembly timeout, discovery cache pruning
BACKWARD COMPATIBILITY:
- Configuration: Fully backward compatible (same config parameters)
- Protocol: No changes to BLE wire protocol (v2.2)
- Interface API: Unchanged for Reticulum Transport integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:03:54 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-18 01:26:57 -05:00
|
|
|
# Trigger callback with peer identity (central mode receives identity during connection)
|
Refactor BLEInterface to driver-based architecture
Major architectural refactoring to separate high-level Reticulum protocol
logic from platform-specific Bluetooth operations. This enables code sharing
between pure Python and Android (Columba) implementations, improves
testability, and creates a clean boundary for future platform support.
ARCHITECTURE CHANGES:
1. **Driver Abstraction Layer**
- Created BLEDriverInterface (bluetooth_driver.py) defining the contract
for all platform-specific BLE drivers
- Abstraction includes 18 methods + 6 callbacks for complete BLE lifecycle
- Enhanced BLEDevice dataclass with service_uuids and manufacturer_data
- Added on_mtu_negotiated callback for delayed MTU reporting
- Added on_error callback for consistent platform error reporting
2. **Linux Driver Implementation**
- Created LinuxBluetoothDriver (linux_bluetooth_driver.py, 1534 lines)
- Moved ALL bleak/bluezero/D-Bus code from BLEInterface
- Preserves 5 critical platform workarounds:
* BlueZ ServicesResolved race condition patch
* D-Bus LE-only connection (ConnectDevice)
* BLE Agent registration for Just Works pairing
* MTU negotiation with 3-method fallback
* Service discovery delay for bluezero timing
- Role-aware send() automatically chooses GATT write vs notification
- Dedicated asyncio event loop management in separate thread
- Configuration via constructor (no Reticulum dependencies)
3. **Refactored BLEInterface**
- Removed 801 lines (32.3% reduction: 2479 → 1678 lines)
- Removed all platform-specific imports (bleak, bluezero, dbus_fast)
- Removed 9 async methods (moved to driver)
- Driver dependency injection via constructor
- Implemented 6 driver callbacks for event handling
- PRESERVED high-level logic:
* Peer scoring algorithm (RSSI + history + recency)
* Connection blacklist with exponential backoff
* MAC-based connection direction (prevents dual connections)
* Fragmentation/reassembly orchestration (identity-based keying)
* Interface spawning per peer
4. **Simplified BLEPeerInterface**
- Removed connection_type, client, mtu parameters
- Deleted _send_via_central() and _send_via_peripheral() methods
- Single send path via driver.send() (driver handles role routing)
- 77 lines removed from peer interface class
5. **Mock Driver for Testing**
- Created MockBLEDriver (tests/mock_ble_driver.py)
- Complete BLEDriverInterface implementation without hardware
- Bidirectional communication via link_drivers()
- Enables unit testing of BLEInterface logic (fragmentation, reassembly,
peer lifecycle, blacklist management)
CRITICAL FIXES:
1. **Restored Periodic Cleanup Task** (CRITICAL: prevents memory leaks)
- Converted from async (driver-owned loop) to threading.Timer
- Runs every 30 seconds to clean stale reassembly buffers
- Essential for long-running instances (Pi Zero with 512MB RAM)
- Properly cancelled in detach() for clean shutdown
2. **Fixed Naming Consistency**
- Renamed processOutgoing → process_outgoing (snake_case)
FILES MODIFIED:
- src/RNS/Interfaces/BLEInterface.py (refactored, -801 lines)
FILES ADDED:
- bluetooth_driver.py (driver abstraction interface)
- linux_bluetooth_driver.py (Linux/BlueZ implementation, 1534 lines)
- tests/mock_ble_driver.py (mock driver for unit tests)
- REFACTORING_GUIDE.md (comprehensive refactoring documentation)
- BLE_PROTOCOL_v2.2.md (protocol specification)
- tests/test_refactor_suite.py (initial test suite)
BENEFITS:
1. **Testability** - Mock driver enables hardware-free unit testing
2. **Portability** - Easy to create Android/Windows/macOS drivers
3. **Maintainability** - Platform quirks isolated in single driver file
4. **Code Sharing** - High-level logic shared across all platforms
5. **Clean Architecture** - Clear separation of concerns
TESTING REQUIRED:
- Tier 1 (Unit): Test with MockBLEDriver (fragmentation, reassembly, lifecycle)
- Tier 2 (Integration): Test on Raspberry Pi hardware (scanning, connecting,
dual mode, MTU negotiation, identity exchange)
- Tier 3 (Regression): Full Reticulum stack (announces, LXMF, multi-hop)
- Tier 4 (Edge Cases): MAC rotation, identity handshake, reconnection,
reassembly timeout, discovery cache pruning
BACKWARD COMPATIBILITY:
- Configuration: Fully backward compatible (same config parameters)
- Protocol: No changes to BLE wire protocol (v2.2)
- Interface API: Unchanged for Reticulum Transport integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:03:54 -05:00
|
|
|
if self.on_device_connected:
|
2026-01-18 01:26:57 -05:00
|
|
|
self.on_device_connected(address, peer_identity)
|
Refactor BLEInterface to driver-based architecture
Major architectural refactoring to separate high-level Reticulum protocol
logic from platform-specific Bluetooth operations. This enables code sharing
between pure Python and Android (Columba) implementations, improves
testability, and creates a clean boundary for future platform support.
ARCHITECTURE CHANGES:
1. **Driver Abstraction Layer**
- Created BLEDriverInterface (bluetooth_driver.py) defining the contract
for all platform-specific BLE drivers
- Abstraction includes 18 methods + 6 callbacks for complete BLE lifecycle
- Enhanced BLEDevice dataclass with service_uuids and manufacturer_data
- Added on_mtu_negotiated callback for delayed MTU reporting
- Added on_error callback for consistent platform error reporting
2. **Linux Driver Implementation**
- Created LinuxBluetoothDriver (linux_bluetooth_driver.py, 1534 lines)
- Moved ALL bleak/bluezero/D-Bus code from BLEInterface
- Preserves 5 critical platform workarounds:
* BlueZ ServicesResolved race condition patch
* D-Bus LE-only connection (ConnectDevice)
* BLE Agent registration for Just Works pairing
* MTU negotiation with 3-method fallback
* Service discovery delay for bluezero timing
- Role-aware send() automatically chooses GATT write vs notification
- Dedicated asyncio event loop management in separate thread
- Configuration via constructor (no Reticulum dependencies)
3. **Refactored BLEInterface**
- Removed 801 lines (32.3% reduction: 2479 → 1678 lines)
- Removed all platform-specific imports (bleak, bluezero, dbus_fast)
- Removed 9 async methods (moved to driver)
- Driver dependency injection via constructor
- Implemented 6 driver callbacks for event handling
- PRESERVED high-level logic:
* Peer scoring algorithm (RSSI + history + recency)
* Connection blacklist with exponential backoff
* MAC-based connection direction (prevents dual connections)
* Fragmentation/reassembly orchestration (identity-based keying)
* Interface spawning per peer
4. **Simplified BLEPeerInterface**
- Removed connection_type, client, mtu parameters
- Deleted _send_via_central() and _send_via_peripheral() methods
- Single send path via driver.send() (driver handles role routing)
- 77 lines removed from peer interface class
5. **Mock Driver for Testing**
- Created MockBLEDriver (tests/mock_ble_driver.py)
- Complete BLEDriverInterface implementation without hardware
- Bidirectional communication via link_drivers()
- Enables unit testing of BLEInterface logic (fragmentation, reassembly,
peer lifecycle, blacklist management)
CRITICAL FIXES:
1. **Restored Periodic Cleanup Task** (CRITICAL: prevents memory leaks)
- Converted from async (driver-owned loop) to threading.Timer
- Runs every 30 seconds to clean stale reassembly buffers
- Essential for long-running instances (Pi Zero with 512MB RAM)
- Properly cancelled in detach() for clean shutdown
2. **Fixed Naming Consistency**
- Renamed processOutgoing → process_outgoing (snake_case)
FILES MODIFIED:
- src/RNS/Interfaces/BLEInterface.py (refactored, -801 lines)
FILES ADDED:
- bluetooth_driver.py (driver abstraction interface)
- linux_bluetooth_driver.py (Linux/BlueZ implementation, 1534 lines)
- tests/mock_ble_driver.py (mock driver for unit tests)
- REFACTORING_GUIDE.md (comprehensive refactoring documentation)
- BLE_PROTOCOL_v2.2.md (protocol specification)
- tests/test_refactor_suite.py (initial test suite)
BENEFITS:
1. **Testability** - Mock driver enables hardware-free unit testing
2. **Portability** - Easy to create Android/Windows/macOS drivers
3. **Maintainability** - Platform quirks isolated in single driver file
4. **Code Sharing** - High-level logic shared across all platforms
5. **Clean Architecture** - Clear separation of concerns
TESTING REQUIRED:
- Tier 1 (Unit): Test with MockBLEDriver (fragmentation, reassembly, lifecycle)
- Tier 2 (Integration): Test on Raspberry Pi hardware (scanning, connecting,
dual mode, MTU negotiation, identity exchange)
- Tier 3 (Regression): Full Reticulum stack (announces, LXMF, multi-hop)
- Tier 4 (Edge Cases): MAC rotation, identity handshake, reconnection,
reassembly timeout, discovery cache pruning
BACKWARD COMPATIBILITY:
- Configuration: Fully backward compatible (same config parameters)
- Protocol: No changes to BLE wire protocol (v2.2)
- Interface API: Unchanged for Reticulum Transport integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:03:54 -05:00
|
|
|
|
|
|
|
|
# Trigger MTU negotiation callback
|
|
|
|
|
if self.on_mtu_negotiated:
|
|
|
|
|
self.on_mtu_negotiated(address, 185)
|
|
|
|
|
|
|
|
|
|
# If linked driver exists and address matches, establish reverse connection
|
|
|
|
|
if self._linked_driver and self._linked_driver.local_address == address:
|
|
|
|
|
self._linked_driver._accept_connection(self.local_address)
|
|
|
|
|
|
|
|
|
|
def _accept_connection(self, address: str):
|
|
|
|
|
"""
|
|
|
|
|
Internal: Accept incoming connection (peripheral role).
|
|
|
|
|
Called by linked driver when it connects to us.
|
|
|
|
|
"""
|
|
|
|
|
if address in self._connected_peers:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self._connected_peers[address] = {
|
|
|
|
|
"role": "peripheral",
|
|
|
|
|
"mtu": 185,
|
|
|
|
|
"identity": None
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 01:26:57 -05:00
|
|
|
# Peripheral role: identity is None because we receive it via handshake later
|
Refactor BLEInterface to driver-based architecture
Major architectural refactoring to separate high-level Reticulum protocol
logic from platform-specific Bluetooth operations. This enables code sharing
between pure Python and Android (Columba) implementations, improves
testability, and creates a clean boundary for future platform support.
ARCHITECTURE CHANGES:
1. **Driver Abstraction Layer**
- Created BLEDriverInterface (bluetooth_driver.py) defining the contract
for all platform-specific BLE drivers
- Abstraction includes 18 methods + 6 callbacks for complete BLE lifecycle
- Enhanced BLEDevice dataclass with service_uuids and manufacturer_data
- Added on_mtu_negotiated callback for delayed MTU reporting
- Added on_error callback for consistent platform error reporting
2. **Linux Driver Implementation**
- Created LinuxBluetoothDriver (linux_bluetooth_driver.py, 1534 lines)
- Moved ALL bleak/bluezero/D-Bus code from BLEInterface
- Preserves 5 critical platform workarounds:
* BlueZ ServicesResolved race condition patch
* D-Bus LE-only connection (ConnectDevice)
* BLE Agent registration for Just Works pairing
* MTU negotiation with 3-method fallback
* Service discovery delay for bluezero timing
- Role-aware send() automatically chooses GATT write vs notification
- Dedicated asyncio event loop management in separate thread
- Configuration via constructor (no Reticulum dependencies)
3. **Refactored BLEInterface**
- Removed 801 lines (32.3% reduction: 2479 → 1678 lines)
- Removed all platform-specific imports (bleak, bluezero, dbus_fast)
- Removed 9 async methods (moved to driver)
- Driver dependency injection via constructor
- Implemented 6 driver callbacks for event handling
- PRESERVED high-level logic:
* Peer scoring algorithm (RSSI + history + recency)
* Connection blacklist with exponential backoff
* MAC-based connection direction (prevents dual connections)
* Fragmentation/reassembly orchestration (identity-based keying)
* Interface spawning per peer
4. **Simplified BLEPeerInterface**
- Removed connection_type, client, mtu parameters
- Deleted _send_via_central() and _send_via_peripheral() methods
- Single send path via driver.send() (driver handles role routing)
- 77 lines removed from peer interface class
5. **Mock Driver for Testing**
- Created MockBLEDriver (tests/mock_ble_driver.py)
- Complete BLEDriverInterface implementation without hardware
- Bidirectional communication via link_drivers()
- Enables unit testing of BLEInterface logic (fragmentation, reassembly,
peer lifecycle, blacklist management)
CRITICAL FIXES:
1. **Restored Periodic Cleanup Task** (CRITICAL: prevents memory leaks)
- Converted from async (driver-owned loop) to threading.Timer
- Runs every 30 seconds to clean stale reassembly buffers
- Essential for long-running instances (Pi Zero with 512MB RAM)
- Properly cancelled in detach() for clean shutdown
2. **Fixed Naming Consistency**
- Renamed processOutgoing → process_outgoing (snake_case)
FILES MODIFIED:
- src/RNS/Interfaces/BLEInterface.py (refactored, -801 lines)
FILES ADDED:
- bluetooth_driver.py (driver abstraction interface)
- linux_bluetooth_driver.py (Linux/BlueZ implementation, 1534 lines)
- tests/mock_ble_driver.py (mock driver for unit tests)
- REFACTORING_GUIDE.md (comprehensive refactoring documentation)
- BLE_PROTOCOL_v2.2.md (protocol specification)
- tests/test_refactor_suite.py (initial test suite)
BENEFITS:
1. **Testability** - Mock driver enables hardware-free unit testing
2. **Portability** - Easy to create Android/Windows/macOS drivers
3. **Maintainability** - Platform quirks isolated in single driver file
4. **Code Sharing** - High-level logic shared across all platforms
5. **Clean Architecture** - Clear separation of concerns
TESTING REQUIRED:
- Tier 1 (Unit): Test with MockBLEDriver (fragmentation, reassembly, lifecycle)
- Tier 2 (Integration): Test on Raspberry Pi hardware (scanning, connecting,
dual mode, MTU negotiation, identity exchange)
- Tier 3 (Regression): Full Reticulum stack (announces, LXMF, multi-hop)
- Tier 4 (Edge Cases): MAC rotation, identity handshake, reconnection,
reassembly timeout, discovery cache pruning
BACKWARD COMPATIBILITY:
- Configuration: Fully backward compatible (same config parameters)
- Protocol: No changes to BLE wire protocol (v2.2)
- Interface API: Unchanged for Reticulum Transport integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:03:54 -05:00
|
|
|
if self.on_device_connected:
|
2026-01-18 01:26:57 -05:00
|
|
|
self.on_device_connected(address, None)
|
Refactor BLEInterface to driver-based architecture
Major architectural refactoring to separate high-level Reticulum protocol
logic from platform-specific Bluetooth operations. This enables code sharing
between pure Python and Android (Columba) implementations, improves
testability, and creates a clean boundary for future platform support.
ARCHITECTURE CHANGES:
1. **Driver Abstraction Layer**
- Created BLEDriverInterface (bluetooth_driver.py) defining the contract
for all platform-specific BLE drivers
- Abstraction includes 18 methods + 6 callbacks for complete BLE lifecycle
- Enhanced BLEDevice dataclass with service_uuids and manufacturer_data
- Added on_mtu_negotiated callback for delayed MTU reporting
- Added on_error callback for consistent platform error reporting
2. **Linux Driver Implementation**
- Created LinuxBluetoothDriver (linux_bluetooth_driver.py, 1534 lines)
- Moved ALL bleak/bluezero/D-Bus code from BLEInterface
- Preserves 5 critical platform workarounds:
* BlueZ ServicesResolved race condition patch
* D-Bus LE-only connection (ConnectDevice)
* BLE Agent registration for Just Works pairing
* MTU negotiation with 3-method fallback
* Service discovery delay for bluezero timing
- Role-aware send() automatically chooses GATT write vs notification
- Dedicated asyncio event loop management in separate thread
- Configuration via constructor (no Reticulum dependencies)
3. **Refactored BLEInterface**
- Removed 801 lines (32.3% reduction: 2479 → 1678 lines)
- Removed all platform-specific imports (bleak, bluezero, dbus_fast)
- Removed 9 async methods (moved to driver)
- Driver dependency injection via constructor
- Implemented 6 driver callbacks for event handling
- PRESERVED high-level logic:
* Peer scoring algorithm (RSSI + history + recency)
* Connection blacklist with exponential backoff
* MAC-based connection direction (prevents dual connections)
* Fragmentation/reassembly orchestration (identity-based keying)
* Interface spawning per peer
4. **Simplified BLEPeerInterface**
- Removed connection_type, client, mtu parameters
- Deleted _send_via_central() and _send_via_peripheral() methods
- Single send path via driver.send() (driver handles role routing)
- 77 lines removed from peer interface class
5. **Mock Driver for Testing**
- Created MockBLEDriver (tests/mock_ble_driver.py)
- Complete BLEDriverInterface implementation without hardware
- Bidirectional communication via link_drivers()
- Enables unit testing of BLEInterface logic (fragmentation, reassembly,
peer lifecycle, blacklist management)
CRITICAL FIXES:
1. **Restored Periodic Cleanup Task** (CRITICAL: prevents memory leaks)
- Converted from async (driver-owned loop) to threading.Timer
- Runs every 30 seconds to clean stale reassembly buffers
- Essential for long-running instances (Pi Zero with 512MB RAM)
- Properly cancelled in detach() for clean shutdown
2. **Fixed Naming Consistency**
- Renamed processOutgoing → process_outgoing (snake_case)
FILES MODIFIED:
- src/RNS/Interfaces/BLEInterface.py (refactored, -801 lines)
FILES ADDED:
- bluetooth_driver.py (driver abstraction interface)
- linux_bluetooth_driver.py (Linux/BlueZ implementation, 1534 lines)
- tests/mock_ble_driver.py (mock driver for unit tests)
- REFACTORING_GUIDE.md (comprehensive refactoring documentation)
- BLE_PROTOCOL_v2.2.md (protocol specification)
- tests/test_refactor_suite.py (initial test suite)
BENEFITS:
1. **Testability** - Mock driver enables hardware-free unit testing
2. **Portability** - Easy to create Android/Windows/macOS drivers
3. **Maintainability** - Platform quirks isolated in single driver file
4. **Code Sharing** - High-level logic shared across all platforms
5. **Clean Architecture** - Clear separation of concerns
TESTING REQUIRED:
- Tier 1 (Unit): Test with MockBLEDriver (fragmentation, reassembly, lifecycle)
- Tier 2 (Integration): Test on Raspberry Pi hardware (scanning, connecting,
dual mode, MTU negotiation, identity exchange)
- Tier 3 (Regression): Full Reticulum stack (announces, LXMF, multi-hop)
- Tier 4 (Edge Cases): MAC rotation, identity handshake, reconnection,
reassembly timeout, discovery cache pruning
BACKWARD COMPATIBILITY:
- Configuration: Fully backward compatible (same config parameters)
- Protocol: No changes to BLE wire protocol (v2.2)
- Interface API: Unchanged for Reticulum Transport integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:03:54 -05:00
|
|
|
|
|
|
|
|
if self.on_mtu_negotiated:
|
|
|
|
|
self.on_mtu_negotiated(address, 185)
|
|
|
|
|
|
|
|
|
|
def disconnect(self, address: str):
|
|
|
|
|
"""Disconnect from a peer."""
|
|
|
|
|
if address not in self._connected_peers:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Remove peer
|
|
|
|
|
role = self._connected_peers[address]["role"]
|
|
|
|
|
del self._connected_peers[address]
|
|
|
|
|
|
|
|
|
|
# Trigger callback
|
|
|
|
|
if self.on_device_disconnected:
|
|
|
|
|
self.on_device_disconnected(address)
|
|
|
|
|
|
|
|
|
|
# If linked, trigger disconnect on other side
|
|
|
|
|
if self._linked_driver and self._linked_driver.local_address == address:
|
|
|
|
|
if role == "central":
|
|
|
|
|
self._linked_driver._handle_disconnect(self.local_address)
|
|
|
|
|
else:
|
|
|
|
|
self._linked_driver._handle_disconnect(self.local_address)
|
|
|
|
|
|
|
|
|
|
def _handle_disconnect(self, address: str):
|
|
|
|
|
"""Internal: Handle disconnection initiated by peer."""
|
|
|
|
|
if address not in self._connected_peers:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
del self._connected_peers[address]
|
|
|
|
|
|
|
|
|
|
if self.on_device_disconnected:
|
|
|
|
|
self.on_device_disconnected(address)
|
|
|
|
|
|
|
|
|
|
def send(self, address: str, data: bytes):
|
|
|
|
|
"""
|
|
|
|
|
Send data to a connected peer.
|
|
|
|
|
|
|
|
|
|
Role-aware: automatically routes to linked driver's on_data_received.
|
|
|
|
|
"""
|
|
|
|
|
if address not in self._connected_peers:
|
|
|
|
|
raise ConnectionError(f"Not connected to {address}")
|
|
|
|
|
|
|
|
|
|
# Track for assertions
|
|
|
|
|
self.sent_data.append((address, data))
|
|
|
|
|
|
|
|
|
|
# If linked driver exists, deliver data
|
|
|
|
|
if self._linked_driver and self._linked_driver.local_address == address:
|
|
|
|
|
if self._linked_driver.on_data_received:
|
|
|
|
|
self._linked_driver.on_data_received(self.local_address, data)
|
|
|
|
|
|
|
|
|
|
# --- GATT Characteristic Operations ---
|
|
|
|
|
|
|
|
|
|
def read_characteristic(self, address: str, char_uuid: str) -> bytes:
|
|
|
|
|
"""
|
|
|
|
|
Read a characteristic value from a peer.
|
|
|
|
|
|
|
|
|
|
If linked driver exists, reads from its characteristics.
|
|
|
|
|
"""
|
|
|
|
|
if address not in self._connected_peers:
|
|
|
|
|
raise ConnectionError(f"Not connected to {address}")
|
|
|
|
|
|
|
|
|
|
# If linked driver, read from its characteristics
|
|
|
|
|
if self._linked_driver and self._linked_driver.local_address == address:
|
|
|
|
|
if char_uuid in self._linked_driver._characteristics:
|
|
|
|
|
return self._linked_driver._characteristics[char_uuid]
|
|
|
|
|
else:
|
|
|
|
|
raise KeyError(f"Characteristic {char_uuid} not found")
|
|
|
|
|
else:
|
|
|
|
|
# For testing without linked driver
|
|
|
|
|
if char_uuid in self._characteristics:
|
|
|
|
|
return self._characteristics[char_uuid]
|
|
|
|
|
else:
|
|
|
|
|
raise KeyError(f"Characteristic {char_uuid} not found")
|
|
|
|
|
|
|
|
|
|
def write_characteristic(self, address: str, char_uuid: str, data: bytes):
|
|
|
|
|
"""
|
|
|
|
|
Write a characteristic value to a peer.
|
|
|
|
|
|
|
|
|
|
If linked driver exists, writes to its characteristics.
|
|
|
|
|
"""
|
|
|
|
|
if address not in self._connected_peers:
|
|
|
|
|
raise ConnectionError(f"Not connected to {address}")
|
|
|
|
|
|
|
|
|
|
# If linked driver, write to its characteristics
|
|
|
|
|
if self._linked_driver and self._linked_driver.local_address == address:
|
|
|
|
|
self._linked_driver._characteristics[char_uuid] = data
|
|
|
|
|
else:
|
|
|
|
|
# For testing without linked driver
|
|
|
|
|
self._characteristics[char_uuid] = data
|
|
|
|
|
|
|
|
|
|
def start_notify(self, address: str, char_uuid: str, callback: Callable[[bytes], None]):
|
|
|
|
|
"""
|
|
|
|
|
Subscribe to notifications from a characteristic.
|
|
|
|
|
|
|
|
|
|
In the mock, this is a no-op since data delivery is automatic via send().
|
|
|
|
|
"""
|
|
|
|
|
if address not in self._connected_peers:
|
|
|
|
|
raise ConnectionError(f"Not connected to {address}")
|
|
|
|
|
# In mock, notifications are handled automatically via send()
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# --- Configuration & Queries ---
|
|
|
|
|
|
|
|
|
|
def get_local_address(self) -> str:
|
|
|
|
|
"""Return the simulated local MAC address."""
|
|
|
|
|
return self.local_address
|
|
|
|
|
|
|
|
|
|
def set_service_discovery_delay(self, seconds: float):
|
|
|
|
|
"""Set service discovery delay (no-op in mock)."""
|
|
|
|
|
self._service_discovery_delay = seconds
|
|
|
|
|
|
|
|
|
|
def set_power_mode(self, mode: str):
|
|
|
|
|
"""Set power mode (tracked but not enforced in mock)."""
|
|
|
|
|
self._power_mode = mode
|
|
|
|
|
|
|
|
|
|
# --- Test Helper Methods ---
|
|
|
|
|
|
|
|
|
|
def simulate_device_discovered(self, address: str, name: str, rssi: int,
|
|
|
|
|
service_uuids: Optional[List[str]] = None,
|
|
|
|
|
manufacturer_data: Optional[Dict[int, bytes]] = None):
|
|
|
|
|
"""
|
|
|
|
|
Simulate discovering a BLE device.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
address: Device MAC address
|
|
|
|
|
name: Device name
|
|
|
|
|
rssi: Signal strength
|
|
|
|
|
service_uuids: Optional list of advertised service UUIDs
|
|
|
|
|
manufacturer_data: Optional manufacturer data
|
|
|
|
|
"""
|
|
|
|
|
if self._state != DriverState.SCANNING:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
device = BLEDevice(
|
|
|
|
|
address=address,
|
|
|
|
|
name=name,
|
|
|
|
|
rssi=rssi,
|
|
|
|
|
service_uuids=service_uuids or [],
|
|
|
|
|
manufacturer_data=manufacturer_data or {}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if self.on_device_discovered:
|
|
|
|
|
self.on_device_discovered(device)
|
|
|
|
|
|
|
|
|
|
def simulate_mtu_change(self, address: str, new_mtu: int):
|
|
|
|
|
"""
|
|
|
|
|
Simulate MTU renegotiation on an existing connection.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
address: Peer address
|
|
|
|
|
new_mtu: New MTU value
|
|
|
|
|
"""
|
|
|
|
|
if address not in self._connected_peers:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self._connected_peers[address]["mtu"] = new_mtu
|
|
|
|
|
|
|
|
|
|
if self.on_mtu_negotiated:
|
|
|
|
|
self.on_mtu_negotiated(address, new_mtu)
|
|
|
|
|
|
|
|
|
|
def simulate_error(self, severity: str, message: str, exception: Optional[Exception] = None):
|
|
|
|
|
"""
|
|
|
|
|
Simulate a platform error.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
severity: "warning" or "error"
|
|
|
|
|
message: Error message
|
|
|
|
|
exception: Optional exception object
|
|
|
|
|
"""
|
|
|
|
|
if self.on_error:
|
|
|
|
|
self.on_error(severity, message, exception)
|
|
|
|
|
|
|
|
|
|
def get_peer_role(self, address: str) -> Optional[str]:
|
|
|
|
|
"""
|
|
|
|
|
Get the connection role for a peer.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
address: Peer address
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
"central" or "peripheral", or None if not connected
|
|
|
|
|
"""
|
|
|
|
|
if address in self._connected_peers:
|
|
|
|
|
return self._connected_peers[address]["role"]
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def link_drivers(driver1: 'MockBLEDriver', driver2: 'MockBLEDriver'):
|
|
|
|
|
"""
|
|
|
|
|
Link two mock drivers for bidirectional communication.
|
|
|
|
|
|
|
|
|
|
This simulates a pair of BLE devices that can discover, connect,
|
|
|
|
|
and exchange data with each other.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
driver1: First driver
|
|
|
|
|
driver2: Second driver
|
|
|
|
|
"""
|
|
|
|
|
driver1._linked_driver = driver2
|
|
|
|
|
driver2._linked_driver = driver1
|
|
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
"""Reset the mock driver to initial state (useful between tests)."""
|
|
|
|
|
self.stop()
|
|
|
|
|
self.sent_data.clear()
|
|
|
|
|
self._characteristics.clear()
|
|
|
|
|
self._identity = None
|