- Add TimeoutCancellationException handling to getNodeDetails and getMeshMetrics
AppFunctions (consistent with Phase 1 functions)
- Rethrow CancellationException in all provider catch blocks to preserve
structured concurrency semantics
- Fix voltage documentation: millivolts → volts (matches actual Float field)
- Fix stale test comment referencing non-existent test class
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add takeIf check to distinguish lastHeard=0 (never heard) from no nodes
- Previously: maxOfOrNull returns 0, Elvis operator doesn't trigger (0 is not null)
- Now: takeIf { it > 0 } filters out zero, falling back to current time
- Ensures API returns meaningful timestamp instead of epoch 1970
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix location filtering: Only treat (0,0) as invalid if position.time is 0
- Previously filtered all (0,0) coords as null, losing valid equatorial data
- Now checks position.time to distinguish 'no fix' from real coordinates
- Fix mostRecentPacketTime: Use max lastHeard from all nodes, not current time
- Previously returned current time, making mesh appear always active
- Now computes from actual node activity data
- Fix meshUptimeSeconds: Use local device's actual uptime, not epoch time
- Previously returned epoch seconds (~1.7B), not elapsed time
- Now uses device's DeviceMetrics.uptime_seconds
All checks passing: Android (Google/fdroid), detekt, spotless, unit tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Extend AiFunctionProvider with two new suspend methods for advanced queries
- getNodeDetails: Retrieve per-node telemetry (16 fields) by hex or user ID
- getMeshMetrics: Aggregate mesh statistics and compute health score
- Add result types (GetNodeDetailsResult, GetMeshMetricsResult) and data models
- Add response models (@AppFunctionSerializable) for KSP serialization
- Both methods support timeout protection and rate limiting
- Health score calculation: 50 base + 50 online ratio, clamped 0-100
- All JVM, Android, detekt, spotless checks passing
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously, AiFunctionProviderImpl returned a hardcoded messageId of 0
for all successful sends, preventing unique message identification. The
underlying SendMessageUseCase generates a packetId but had no return
value to expose it.
Changes:
- Modified SendMessageUseCase interface to return Int (the packetId)
- Updated SendMessageUseCaseImpl to return the generated packetId
- Updated AiFunctionProviderImpl to capture and use the returned messageId
This enables the AI system to track individual messages and correlate
responses to specific send requests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Expose Meshtastic mesh networking capabilities (sendMessage, getMeshStatus)
to Android system AI agents via the App Functions API.
Architecture:
- AiFunctionProvider interface in core/data commonMain (platform-agnostic)
- FuzzyNameResolver for node/channel name matching (LCS algorithm)
- RateLimiter with 5-call/60s sliding window to protect mesh radio
- AiFunctionProviderImpl wiring repositories and use cases
- @AppFunction declarations in androidApp Google flavor only
- GoogleMeshUtilApplication with AppFunctionConfiguration.Provider
- DI via AppFunctionsModule included in FlavorModule
Key design decisions:
- No confirmation dialog (AI invocation = user intent)
- Fuzzy name matching with 50% LCS threshold, error on ambiguity
- Admin channels excluded from resolution
- 5-second operation timeout
- 237-byte message length limit (Meshtastic standard)
Includes unit tests for RateLimiter and FuzzyNameResolver (LCS algorithm).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>