- Remove getPacketId() from MessageSender interface (SDK owns packet IDs)
- Remove requestId parameter from requestTraceroute/requestNeighborInfo
- Make getPacketId() private in SdkRadioController (still used internally
for delivery tracking correlation)
- Replace radioController.getPacketId() in map with local Random.nextInt()
for waypoint ID generation (only needs uniqueness, not SDK correlation)
- Remove messageSender dependency from CommonNodeRequestActions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- P0: setDeviceAddress now persists address to RadioPrefs before reconnect
- P0: Document nodedbReset firmware limitation (preserveFavorites is local-only)
- P1: Add writeAction helper for AdminException → sendError in RadioConfigViewModel
- P1: Wrap CommonNodeRequestActions scope.launch with runCatching for crash safety
- P1: Create CongestionLevel typealias in core/model to decouple feature modules from SDK
- P1: Cancel prior loadJob in setResponseStateLoading to prevent stale results
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the manual packet-ID tracking and meshPacketFlow subscription in
RadioConfigViewModel with direct typed returns from the SDK via
RadioConfigUseCase. The ViewModel now awaits typed results (User, Config,
ModuleConfig, channels, etc.) from suspend calls and maps AdminException
to UI error states.
Key changes:
- Delete ProcessRadioResponseUseCase (130 lines of manual proto decode)
- Remove requestIds state, registerRequestId, processPacketResponse,
sendAdminRequest, and meshPacketFlow subscription from ViewModel
- Rewrite setResponseStateLoading to use direct coroutine calls
- Admin actions (reboot/shutdown/etc.) fire directly without session key
preflight (SDK handles retryOnSessionExpiry transparently)
- All setters (setConfig, setModuleConfig, setOwner, updateChannels)
no longer return/track packetIds
- Remove messageSender dependency from NodeManagementActions
- Update InstallProfileUseCase to use editSettings {} receiver pattern
- Update all callers: CleanNodeDatabaseUseCase, Esp32OtaUpdateHandler,
NodeManagementActions
- Rewrite RadioConfigViewModelTest for new direct-await semantics
- Update RadioConfigUseCaseTest and InstallProfileUseCaseTest
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace all isLocalNode/sendRemoteAdmin patterns with
client.admin.forNode(NodeId(destNum)).method()
- Use client.sendReaction() instead of manual MeshPacket construction
- Use client.admin.forNode(dest).getDeviceMetadata() for remote metadata
- Delete sendRemoteAdmin() and isLocalNode() helpers
- Remove unused imports (AdminMessage, Data, MeshPacket from SdkStateBridge)
Net: -131 lines, all admin ops now go through SDK's typed API with
proper ACK tracking and session-key retry.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove handler interfaces, implementations, and tests that have zero production
callers after the SDK became authoritative for protocol handling:
- TelemetryPacketHandler/Impl — SDK owns telemetry via NodeChange.Updated
- StoreForwardPacketHandler/Impl — SDK owns S&F lifecycle + SFPP
- NeighborInfoHandler/Impl — SDK owns NeighborInfo model
- TracerouteHandler/Impl — SDK owns traceroute via AdminResult flow
- MeshDataHandler/MessagePersistenceHandler — handleReceivedData was no-op
- HistoryManager/Impl — only caller was deleted StoreForwardPacketHandlerImpl
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SdkStateBridge:
- Wrap handleServiceAction in try/catch to prevent bridge death
- Favorite/Ignore/Mute: only apply local state update on admin
success (eliminates optimistic state inconsistency)
- ImportContact: guard with runCatching, log failures
- Extract dispatchAction for clean separation of concerns
SdkRadioController:
- Wrap sendMessage with try/catch + logging before re-throw
- Wrap sendRemoteAdmin with try/catch + logging before re-throw
- Ensures BLE disconnect errors are visible in logs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eliminate the vestigial NodeManager/NodeRepository interface split.
All runtime node state management methods (handleReceivedUser,
handleReceivedPosition, handleReceivedTelemetry, updateNode, etc.)
now live directly on NodeRepository alongside the query surface.
- Delete NodeManager.kt (82 LOC)
- Extend NodeRepository with NodeIdLookup and add all manager methods
- Update 8 consumers to inject NodeRepository instead of NodeManager
- Remove dead nodeManager param from MeshServiceOrchestrator
- Add NodeManager methods to FakeNodeRepository test double
- Update all tests (mocks, constructor params, verifications)
- SdkNodeRepositoryImpl now binds [NodeRepository, NodeIdLookup]
Build: assembleDebug ✅, desktop:compileKotlin ✅, all jvmTests ✅
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Delete dead methods from NodeManager/NodeRepository interfaces:
- allowNodeDbWrites, setAllowNodeDbWrites (never read)
- loadCachedNodeDB (no-op, zero-value call in orchestrator)
- getNodes(): List<NodeInfo> (deprecated, unused)
- handleReceivedPaxcounter (zero callers)
- handleReceivedNodeStatus, updateNodeStatus (zero callers)
- insertMetadata (zero production callers)
Delete NodeInfo data class (85 LOC):
- All consumers now use Node domain model directly
- Retained MeshUser, Position, DeviceMetrics, EnvironmentMetrics
which have active consumers across feature modules
Remove corresponding implementations from SdkNodeRepositoryImpl,
FakeNodeRepository, and test verifications.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Merge NodeManagerImpl logic into SdkNodeRepositoryImpl (single source
of truth for all node state — eliminates duplicate in-memory maps)
- SdkNodeRepositoryImpl now binds NodeRepository, NodeManager, NodeIdLookup
- Delete NodeManagerImpl.kt (377 LOC)
- Add meshActivityFlow to ServiceRepository for nav-bar icon animation
- Emit MeshActivity.Send from SdkPacketHandler and SdkRadioController
- Emit MeshActivity.Receive from ServiceRepositoryImpl.emitMeshPacket()
- Wire UIViewModel.meshActivity to serviceRepository.meshActivityFlow
- Align insertMetadata signature (remove unnecessary suspend)
- Adapt NodeManagerImplTest to test SdkNodeRepositoryImpl directly
- Update FakeServiceRepository with meshActivityFlow stub
All targets compile clean (Android + Desktop), all tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove my_node, nodes, metadata tables via AutoMigration with @DeleteTable
- Parameterize all 34 PacketDao queries to accept myNodeNum directly
- Delete NodeInfoDao, NodeEntity, MyNodeEntity, MetadataEntity
- Delete CommonNodeInfoDaoTest (dead)
- Rewrite PacketRepositoryImpl to inject NodeRepository for myNodeNum
- Rewrite CommonPacketDaoTest and CommonPacketRepositoryTest
- Update MigrationTest to remove nodeInfoDao usage
- Update core/database README to reflect current schema
SDK is now sole source of truth for all node data.
Room only stores messages, logs, and user annotations.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MeshLogRepositoryImpl was the sole external consumer of
NodeInfoReadDataSource — it only needed myNodeNum to identify the
local node in logs. Replace with NodeRepository.myNodeInfo (already
SDK-backed via SdkNodeRepositoryImpl).
PacketRepositoryImpl referenced NodeInfoDao.MAX_BIND_PARAMS — inlined
as a private constant since it's just the SQLite bind-param limit.
With zero external consumers remaining, delete:
- NodeInfoReadDataSource interface
- SwitchingNodeInfoReadDataSource implementation
The Room NodeInfoDao/NodeEntity/MyNodeEntity remain in the database
module for now (internal tests + migration history) but have no
external dependents — ready for a future Room migration 40 to DROP.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Constants.kt re-exported MeshtasticIntent constants for the deleted
ServiceBroadcasts system. No consumers remain after broadcast removal.
Also remove MeshService.actionReceived() companion method (no callers).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wires core flows to the meshtastic-sdk (0.1.0-SNAPSHOT) while keeping the
legacy path alive. Goal: prove the SDK works with a real Android app and
surface API deficiencies.
Build:
- settings.gradle.kts: composite build inclusion for meshtastic-sdk
(../meshtastic-sdk) with dependency substitution for all SDK artifacts
- libs.versions.toml: sdk = "0.1.0-SNAPSHOT", mavenCentral snapshots repo
- app/build.gradle.kts: sdk-core, sdk-proto, sdk-transport-ble,
sdk-storage-sqldelight dependencies
Bootstrap:
- MeshUtilApplication: AndroidContextHolder.context set in onCreate()
before startKoin so SqlDelightStorageProvider can locate app files
- RadioClientProvider (@Single, binds SdkClientLifecycle): mutex-serialized
rebuildAndConnect(), strips 'x' prefix from BLE devAddr, holds
RadioClient StateFlow
- RadioClientViewModel: exposes RadioClientProvider to UI layer
SDK ViewModels (POC quality, compile-verified):
- SdkNodeListViewModel: NodeChange.Snapshot/Added/Updated/Removed → UiNode
- SdkMessagingViewModel: sendText() via client.sendText(), incomingText
via client.textMessages (Gap B — now fixed in SDK)
- SdkConfigViewModel: configBundle reads, setConfig/setOwner writes,
loadChannels() via admin, Gap G workaround (local override map)
- SdkTelemetryViewModel: TelemetryApi.observe(NodeId), requestDeviceMetrics
Service lifecycle:
- SdkClientLifecycle interface in core:service (avoids reverse dep from
service → app); RadioClientProvider implements it
- MeshService.onDestroy: calls sdkClientLifecycle.disconnect() before
serviceJob.cancel()
- BlePeripheralFactory.kt in core:ble: public buildPeripheralForAddress()
wrapper (Gap F workaround; proper fix needed in SDK transport-ble)
SDK gaps discovered and logged:
Gap B - textMessages flow (FIXED in SDK feat/meshtastic-android-integration-gaps)
Gap C - channels StateFlow (no reactive cache, only admin.listChannels())
Gap F - BleTransport MAC string factory (requires live Peripheral today)
Gap G - configBundle not refreshed after editSettings writes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>