mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-05-24 14:50:26 -04:00
feat(ai): Add app_metadata.xml and fix KDoc for KSP compliance
Audit findings addressed: - Add res/xml/app_metadata.xml with LLM-facing operational patterns, workflow dependencies, and constraints for the AppFunctions suite - Register app_metadata in Google flavor AndroidManifest.xml - Convert all @AppFunctionSerializable class-level @property tags to inline KDoc per property (required by KSP for doc extraction) - Add app_description string resource for displayDescription Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -25,6 +25,9 @@
|
||||
<meta-data
|
||||
android:name="com.google.android.geo.API_KEY"
|
||||
android:value="${MAPS_API_KEY}" />
|
||||
<property
|
||||
android:name="android.app.appfunctions.app_metadata"
|
||||
android:resource="@xml/app_metadata" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -18,218 +18,191 @@ package org.meshtastic.app.ai.appfunctions
|
||||
|
||||
import androidx.appfunctions.AppFunctionSerializable
|
||||
|
||||
/**
|
||||
* Response returned when a message is successfully sent via the mesh network.
|
||||
*
|
||||
* @property messageId The identifier assigned to the outgoing message.
|
||||
* @property channel The channel or destination the message was sent to.
|
||||
* @property timestamp The time the message was sent (epoch milliseconds).
|
||||
*/
|
||||
/** Response returned when a message is successfully sent via the mesh network. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class SendMessageResponse(val messageId: Int, val channel: String, val timestamp: Long)
|
||||
data class SendMessageResponse(
|
||||
/** The identifier assigned to the outgoing message. */
|
||||
val messageId: Int,
|
||||
/** The channel or destination the message was sent to. */
|
||||
val channel: String,
|
||||
/** The time the message was sent (epoch milliseconds). */
|
||||
val timestamp: Long,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing the current status of the Meshtastic mesh network.
|
||||
*
|
||||
* @property connectionState The current radio connection state (e.g., CONNECTED, DISCONNECTED).
|
||||
* @property onlineNodeCount The number of nodes currently online (heard within the last 15 minutes).
|
||||
* @property totalNodeCount The total number of nodes known to the network.
|
||||
* @property localBatteryLevel The battery percentage of the connected Meshtastic device (1-100), or null if
|
||||
* unavailable.
|
||||
* @property localNodeName The display name of the local node, or null if not set.
|
||||
*/
|
||||
/** Response containing the current status of the Meshtastic mesh network. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class MeshStatusResponse(
|
||||
/** The current radio connection state (e.g., CONNECTED, DISCONNECTED). */
|
||||
val connectionState: String,
|
||||
/** The number of nodes currently online (heard within the last 2 hours). */
|
||||
val onlineNodeCount: Int,
|
||||
/** The total number of nodes known to the network. */
|
||||
val totalNodeCount: Int,
|
||||
/** The battery percentage of the connected Meshtastic device (1-100), or null if unavailable. */
|
||||
val localBatteryLevel: Int?,
|
||||
/** The display name of the local node, or null if not set. */
|
||||
val localNodeName: String?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing information about a single mesh node.
|
||||
*
|
||||
* @property id The unique node identifier in Meshtastic hex format (e.g., !abc12345).
|
||||
* @property name The human-readable name of the node.
|
||||
* @property batteryLevel The node's battery percentage (0-100), or null if unavailable.
|
||||
* @property lastHeard The time this node was last heard from (epoch milliseconds).
|
||||
* @property isOnline Whether this node is currently considered online.
|
||||
*/
|
||||
/** Information about a single mesh node. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class NodeInfo(
|
||||
/** The unique node identifier in Meshtastic hex format (e.g., !abc12345). */
|
||||
val id: String,
|
||||
/** The human-readable name of the node. */
|
||||
val name: String,
|
||||
/** The node's battery percentage (0-100), or null if unavailable. */
|
||||
val batteryLevel: Int?,
|
||||
/** The time this node was last heard from (epoch milliseconds). */
|
||||
val lastHeard: Long,
|
||||
/** Whether this node is currently considered online. */
|
||||
val isOnline: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing a list of nodes visible on the mesh network.
|
||||
*
|
||||
* @property nodes List of nodes sorted by most recently heard first.
|
||||
*/
|
||||
/** Response containing a list of nodes visible on the mesh network. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class GetNodeListResponse(val nodes: List<NodeInfo>)
|
||||
data class GetNodeListResponse(
|
||||
/** List of nodes sorted by most recently heard first. */
|
||||
val nodes: List<NodeInfo>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing information about a single mesh channel.
|
||||
*
|
||||
* @property index The channel index (0-7).
|
||||
* @property name The human-readable name of the channel.
|
||||
* @property isPrimary Whether this is the primary/default channel.
|
||||
* @property uplinkEnabled Whether uplink is enabled for this channel.
|
||||
* @property downlinkEnabled Whether downlink is enabled for this channel.
|
||||
*/
|
||||
/** Information about a single mesh channel. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class ChannelInfo(
|
||||
/** The channel index (0-7). */
|
||||
val index: Int,
|
||||
/** The human-readable name of the channel. */
|
||||
val name: String,
|
||||
/** Whether this is the primary/default channel. */
|
||||
val isPrimary: Boolean,
|
||||
/** Whether uplink is enabled for this channel. */
|
||||
val uplinkEnabled: Boolean,
|
||||
/** Whether downlink is enabled for this channel. */
|
||||
val downlinkEnabled: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing the list of available mesh channels.
|
||||
*
|
||||
* @property channels List of all configured channels.
|
||||
*/
|
||||
/** Response containing the list of available mesh channels. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class GetChannelInfoResponse(val channels: List<ChannelInfo>)
|
||||
data class GetChannelInfoResponse(
|
||||
/** List of all configured channels. */
|
||||
val channels: List<ChannelInfo>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing the status of the local Meshtastic device.
|
||||
*
|
||||
* @property model The hardware model of the device (e.g., "Meshtastic nRF52840").
|
||||
* @property firmwareVersion The firmware version string.
|
||||
* @property batteryLevel The device battery percentage (0-100), or null if not battery-powered.
|
||||
* @property chargingStatus The charging state (CHARGING, NOT_CHARGING, or UNKNOWN).
|
||||
* @property deviceName The display name of the device, or null if not set.
|
||||
* @property isActive Whether the radio is currently active and connected.
|
||||
*/
|
||||
/** Response containing the status of the local Meshtastic device. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class GetDeviceStatusResponse(
|
||||
/** The hardware model of the device (e.g., "Meshtastic nRF52840"). */
|
||||
val model: String,
|
||||
/** The firmware version string. */
|
||||
val firmwareVersion: String,
|
||||
/** The device battery percentage (0-100), or null if not battery-powered. */
|
||||
val batteryLevel: Int?,
|
||||
/** The charging state (CHARGING, NOT_CHARGING, or UNKNOWN). */
|
||||
val chargingStatus: String,
|
||||
/** The display name of the device, or null if not set. */
|
||||
val deviceName: String?,
|
||||
/** Whether the radio is currently active and connected. */
|
||||
val isActive: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing detailed telemetry for a specific mesh node.
|
||||
*
|
||||
* @property id Node ID in hex format (e.g., "!abc12345").
|
||||
* @property userId User ID string for this node.
|
||||
* @property name Display name of the node.
|
||||
* @property batteryLevel Battery percentage (0-100), or null if unavailable.
|
||||
* @property voltage Supply voltage in volts, or null if unavailable.
|
||||
* @property hardwareModel Hardware model string.
|
||||
* @property firmwareVersion Firmware version string.
|
||||
* @property snr Signal-to-noise ratio of strongest signal.
|
||||
* @property rssi Received signal strength indicator in dB.
|
||||
* @property hopsAway Number of hops away from local node (-1 if unknown).
|
||||
* @property channel Channel index this node is on.
|
||||
* @property lastHeard Last heard timestamp (milliseconds since epoch).
|
||||
* @property userRole User role or device type.
|
||||
* @property isLicensed Whether the user is licensed.
|
||||
* @property latitude Latitude in degrees, or null if unknown.
|
||||
* @property longitude Longitude in degrees, or null if unknown.
|
||||
*/
|
||||
/** Response containing detailed telemetry for a specific mesh node. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class GetNodeDetailsResponse(
|
||||
/** Node ID in hex format (e.g., "!abc12345"). */
|
||||
val id: String,
|
||||
/** User ID string for this node. */
|
||||
val userId: String,
|
||||
/** Display name of the node. */
|
||||
val name: String,
|
||||
/** Battery percentage (0-100), or null if unavailable. */
|
||||
val batteryLevel: Int?,
|
||||
/** Supply voltage in volts, or null if unavailable. */
|
||||
val voltage: Float?,
|
||||
/** Hardware model string. */
|
||||
val hardwareModel: String,
|
||||
/** Firmware version string. */
|
||||
val firmwareVersion: String,
|
||||
/** Signal-to-noise ratio of strongest signal. */
|
||||
val snr: Float,
|
||||
/** Received signal strength indicator in dB. */
|
||||
val rssi: Int,
|
||||
/** Number of hops away from local node (-1 if unknown). */
|
||||
val hopsAway: Int,
|
||||
/** Channel index this node is on. */
|
||||
val channel: Int,
|
||||
/** Last heard timestamp (milliseconds since epoch). */
|
||||
val lastHeard: Long,
|
||||
/** User role or device type. */
|
||||
val userRole: String,
|
||||
/** Whether the user is licensed. */
|
||||
val isLicensed: Boolean,
|
||||
/** Latitude in degrees, or null if unknown. */
|
||||
val latitude: Double?,
|
||||
/** Longitude in degrees, or null if unknown. */
|
||||
val longitude: Double?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing aggregate mesh network metrics.
|
||||
*
|
||||
* @property totalNodeCount Total number of known nodes.
|
||||
* @property onlineNodeCount Number of nodes currently online.
|
||||
* @property averageBatteryLevel Average battery level across mesh, or null.
|
||||
* @property meshHealthScore Estimated health score (0-100).
|
||||
* @property mostRecentPacketTime Timestamp of most recent packet (ms since epoch).
|
||||
* @property meshUptimeSeconds Mesh uptime in seconds.
|
||||
* @property channelUtilizationPercent Channel utilization percentage, or null if unavailable.
|
||||
*/
|
||||
/** Response containing aggregate mesh network metrics. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class GetMeshMetricsResponse(
|
||||
/** Total number of known nodes. */
|
||||
val totalNodeCount: Int,
|
||||
/** Number of nodes currently online. */
|
||||
val onlineNodeCount: Int,
|
||||
/** Average battery level across mesh, or null if no data. */
|
||||
val averageBatteryLevel: Int?,
|
||||
/** Estimated health score (0-100). */
|
||||
val meshHealthScore: Int,
|
||||
/** Timestamp of most recent packet (ms since epoch). */
|
||||
val mostRecentPacketTime: Long,
|
||||
/** Mesh uptime in seconds. */
|
||||
val meshUptimeSeconds: Long,
|
||||
/** Channel utilization percentage, or null if unavailable. */
|
||||
val channelUtilizationPercent: Int?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing recent messages from the mesh network.
|
||||
*
|
||||
* @property messages List of recent messages ordered by most recent first.
|
||||
*/
|
||||
/** Response containing recent messages from the mesh network. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class GetRecentMessagesResponse(val messages: List<MessageInfo>)
|
||||
data class GetRecentMessagesResponse(
|
||||
/** List of recent messages ordered by most recent first. */
|
||||
val messages: List<MessageInfo>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Information about a single mesh message.
|
||||
*
|
||||
* @property senderName Display name of the message sender.
|
||||
* @property text The message text content.
|
||||
* @property contactName Name of the channel or contact the message belongs to.
|
||||
* @property receivedTime Timestamp when the message was received (ms since epoch).
|
||||
* @property fromLocal True if this message was sent by the local user.
|
||||
* @property read True if this message has been read by the user.
|
||||
*/
|
||||
/** Information about a single mesh message. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class MessageInfo(
|
||||
/** Display name of the message sender. */
|
||||
val senderName: String,
|
||||
/** The message text content. */
|
||||
val text: String,
|
||||
/** Name of the channel or contact the message belongs to. */
|
||||
val contactName: String,
|
||||
/** Timestamp when the message was received (ms since epoch). */
|
||||
val receivedTime: Long,
|
||||
/** True if this message was sent by the local user. */
|
||||
val fromLocal: Boolean,
|
||||
/** True if this message has been read by the user. */
|
||||
val read: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* Response containing a summary of unread messages across all contacts.
|
||||
*
|
||||
* @property totalUnreadCount Total number of unread messages across all non-muted contacts.
|
||||
* @property contacts Per-contact breakdown of unread messages, sorted by most recent.
|
||||
*/
|
||||
/** Response containing a summary of unread messages across all contacts. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class GetUnreadSummaryResponse(val totalUnreadCount: Int, val contacts: List<ContactUnreadInfo>)
|
||||
data class GetUnreadSummaryResponse(
|
||||
/** Total number of unread messages across all non-muted contacts. */
|
||||
val totalUnreadCount: Int,
|
||||
/** Per-contact breakdown of unread messages, sorted by most recent. */
|
||||
val contacts: List<ContactUnreadInfo>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Unread message details for a single contact or channel.
|
||||
*
|
||||
* @property name Display name of the contact or channel.
|
||||
* @property unreadCount Number of unread messages from this contact.
|
||||
* @property lastMessagePreview Preview text of the most recent message (up to 100 chars), or null if unavailable.
|
||||
* @property lastMessageTime Timestamp of the most recent message (ms since epoch), or null if unavailable.
|
||||
*/
|
||||
/** Unread message details for a single contact or channel. */
|
||||
@AppFunctionSerializable(isDescribedByKDoc = true)
|
||||
data class ContactUnreadInfo(
|
||||
/** Display name of the contact or channel. */
|
||||
val name: String,
|
||||
/** Number of unread messages from this contact. */
|
||||
val unreadCount: Int,
|
||||
/** Preview text of the most recent message (up to 100 chars), or null if unavailable. */
|
||||
val lastMessagePreview: String?,
|
||||
/** Timestamp of the most recent message (ms since epoch), or null if unavailable. */
|
||||
val lastMessageTime: Long?,
|
||||
)
|
||||
|
||||
26
androidApp/src/google/res/xml/app_metadata.xml
Normal file
26
androidApp/src/google/res/xml/app_metadata.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2026 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
-->
|
||||
|
||||
<AppFunctionAppMetadata xmlns:appfn="http://schemas.android.com/apk/androidx.appfunctions"
|
||||
appfn:description="Meshtastic is a mesh radio networking app for off-grid communications using LoRa devices.
|
||||
|
||||
Operational Patterns:
|
||||
- Use 'getMeshStatus' to verify the radio is connected before calling functions that require a live connection.
|
||||
- Use 'getNodeList' or 'getNodeDetails' to resolve node names and IDs before calling 'sendMessage' with a recipientName.
|
||||
- Use 'getChannelInfo' to discover available channel names before sending broadcast messages to non-primary channels.
|
||||
- 'getRecentMessages' and 'getUnreadSummary' work offline from local message history and do not require an active connection.
|
||||
- Prefer 'getUnreadSummary' for a quick overview, then 'getRecentMessages' with a contactName filter for details.
|
||||
|
||||
Constraints:
|
||||
- Message text is limited to 237 bytes.
|
||||
- Node and channel name resolution is fuzzy (partial matches accepted).
|
||||
- Rate limit: 10 messages per 60 seconds via 'sendMessage'.
|
||||
- Message history limit: 1-50 messages per query (default 20)."
|
||||
appfn:displayDescription="@string/app_description" />
|
||||
@@ -17,4 +17,5 @@
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">Meshtastic</string>
|
||||
<string name="app_description">Off-grid mesh networking for secure, long-range communications via LoRa radio.</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user