NetFetch Plugin¶
NetFetch is a minimal but complete plugin that fetches internet data and displays it in AMC. It demonstrates every hook type: data singleton, settings, upper fly view, menu item button, and menu item view.
What it does¶
- Fetches data from three sources: current time (JS Date), mock weather, and ISS location (HTTP API).
- Displays the result in the upper fly view and in a custom full-screen menu view.
- Provides a settings panel with enable/disable toggle and data source selector.
- Adds a button in the side menu drawer.
File structure¶
NetFetch/
├── plugin.json
├── build.sh
├── src/
│ ├── NetFetchData.qml ← data singleton
│ ├── NetFetchSettings.qml ← settings panel
│ ├── StatusBox.qml ← shared status box component
│ ├── NetFetchUpperFlyView.qml ← flight display widget
│ ├── NetFetchMenuItemButton.qml ← side menu button
│ └── NetFetchMenuItemView.qml ← full-screen view
└── img/
└── metadata.svg ← menu button icon
plugin.json¶
{
"name": "NetFetch",
"description": "Fetch and display internet data",
"author": "Auterion",
"copyright": "(c) 2026 Auterion",
"license": "MIT",
"type": "qml"
}
NetFetchData.qml -- Data Singleton¶
This is the heart of the plugin. It holds all shared state and logic.
Key patterns demonstrated:
pragma Singletondeclarationenum DataSourcefor type-safe data source selectionTimerfor periodic data fetchingXMLHttpRequestfor HTTP calls (ISS location)QtCorefor persistingenabledandselectedDataSourcePluginManager.log()for logging
pragma Singleton
import QGroundControl
import QtQuick
import QtCore
import QGroundControl.Plugin
Item {
id: root
enum DataSource {
CurrentTime,
WeatherDemo,
ISSLocation
}
property bool enabled: true
property int selectedDataSource: NetFetchData.DataSource.CurrentTime
property string currentData: "No data"
property bool loading: false
property string lastError: ""
property int updateCount: 0
readonly property var dataSourceLabels: [
"Current Time", "Weather Demo", "ISS Location"
]
Timer {
id: updateTimer
interval: 10000
running: root.enabled
repeat: true
triggeredOnStart: true
onTriggered: fetchData()
}
function fetchData() {
if (!enabled) return
loading = true
lastError = ""
switch (selectedDataSource) {
case NetFetchData.DataSource.CurrentTime:
fetchTime(); break
case NetFetchData.DataSource.WeatherDemo:
fetchWeather(); break
case NetFetchData.DataSource.ISSLocation:
fetchISS(); break
}
updateCount++
PluginManager.log("NetFetch: source=" + selectedDataSource +
", count=" + updateCount)
}
function fetchISS() {
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
loading = false
if (xhr.status === 200) {
try {
var r = JSON.parse(xhr.responseText)
var lat = parseFloat(r.iss_position.latitude).toFixed(2)
var lon = parseFloat(r.iss_position.longitude).toFixed(2)
currentData = "ISS: " + lat + "°, " + lon + "°"
lastError = ""
} catch (e) {
lastError = "Parse error: " + e
currentData = "Error: " + lastError
}
} else {
lastError = "HTTP " + xhr.status
currentData = "Error: " + lastError
}
}
}
xhr.open("GET", "http://api.open-notify.org/iss-now.json")
xhr.send()
}
// ... fetchTime() and fetchWeather() omitted for brevity
function refresh() { fetchData() }
Settings {
id: settings
category: "NetFetch"
property alias enabled: root.enabled
property alias selectedDataSource: root.selectedDataSource
}
Component.onCompleted: {
PluginManager.log("NetFetch: Data component initialized")
if (enabled) fetchData()
}
onEnabledChanged: {
if (enabled) fetchData()
else currentData = "Disabled"
}
onSelectedDataSourceChanged: {
if (enabled) fetchData()
}
}
NetFetchSettings.qml -- Settings Panel¶
The settings panel uses a collapsible pattern: a top bar with a CustomSwitch toggle and an expand/collapse chevron, followed by a settings grid that animates in/out.
Key patterns demonstrated:
CustomSwitchfor enable/disableQGCComboBoxbound to the data singleton's labels and selected indexQGCButtonfor manual refreshQGCColoredImagefor a themed expand/collapse chevron- Animated height transition on the settings container
import QtQuick
import QtQuick.Layouts
import QGroundControl.Controls
import QGroundControl.Palette
import QGroundControl.ScreenTools
import QGroundControl.Plugin
ColumnLayout {
property real _margins: ScreenTools.defaultFontPixelWidth
// Top bar with switch + title + chevron
Rectangle {
Layout.preferredHeight: ScreenTools.defaultFontPixelHeight * 4
Layout.fillWidth: true
color: qgcPal.windowShade
radius: ScreenTools.defaultFontPixelWidth * 2
RowLayout {
// ...
CustomSwitch {
checked: NetFetchData.enabled
onClicked: NetFetchData.enabled = checked
}
QGCLabel {
text: qsTr("NetFetch")
font.pointSize: ScreenTools.largeFontPointSize
font.family: ScreenTools.demiboldFontFamily
font.bold: true
}
// chevron icon...
}
}
// Collapsible settings area
Rectangle {
id: settingsContainer
property bool hidden: !NetFetchData.enabled
Layout.preferredHeight: hidden ? 0 : settingsGrid.height + _margins * 2
clip: true
color: qgcPal.windowShade
Behavior on Layout.preferredHeight {
NumberAnimation { duration: 250 }
}
GridLayout {
id: settingsGrid
columns: 2
QGCLabel { text: qsTr("Data Source") }
QGCComboBox {
model: NetFetchData.dataSourceLabels
currentIndex: NetFetchData.selectedDataSource
onActivated: (index) => NetFetchData.selectedDataSource = index
}
QGCLabel { text: qsTr("Current Data") }
QGCLabel {
text: NetFetchData.currentData
color: NetFetchData.lastError ?
qgcPal.colorOrange : qgcPal.text
}
QGCButton {
text: qsTr("Refresh")
Layout.columnSpan: 2
enabled: NetFetchData.enabled && !NetFetchData.loading
onClicked: NetFetchData.refresh()
}
}
}
}
StatusBox.qml -- Shared Status Box¶
The status box is the main data display component, reused by both the UpperFlyView and MenuItemView. Extracting it into its own file avoids duplicating the UI across hooks.
Key patterns demonstrated:
- Conditional border color based on state (loading / error / OK)
- Pulse animation when loading
- Spinning status icon with rotation animation
- Data-change fade animation
- Click-to-refresh via
MouseArea - Update count badge
Naming
The filename deliberately avoids the plugin prefix and hook keywords (like data) to make it clear this is a private helper, not a hook target. Non-hook QML files within a plugin are available as local types to other files in the same plugin.
import QtQuick
import QtQuick.Layouts
import QGroundControl.Controls
import QGroundControl.Palette
import QGroundControl.ScreenTools
import QGroundControl.Plugin
Rectangle {
id: root
width: contentRow.width + (padding * 2)
height: contentRow.height + (padding * 2)
property real padding: ScreenTools.defaultFontPixelWidth
color: qgcPal.windowShade
border.width: 2
border.color: NetFetchData.loading ? qgcPal.colorBlue :
NetFetchData.lastError ? qgcPal.colorOrange :
qgcPal.colorGreen
radius: ScreenTools.defaultFontPixelWidth / 2
visible: NetFetchData.enabled
// ... pulse animation, status indicator, labels,
// update count badge, click-to-refresh MouseArea
}
NetFetchUpperFlyView.qml -- Flight Display Widget¶
Displays the shared status box in the upper fly view area.
import QtQuick
import QGroundControl.Plugin
StatusBox {
Component.onCompleted: {
PluginManager.log("NetFetch: Upper fly view component loaded")
}
}
Menu Integration¶
NetFetch demonstrates the full MenuItemButton + MenuItemView pattern.
NetFetchMenuItemButton.qml¶
Uses PluginMenuItemButton as the root element.
import QGroundControl.Widgets
PluginMenuItemButton {
text: "Widgets"
icon.source: Qt.resolvedUrl("../img/metadata.svg")
}
NetFetchMenuItemView.qml¶
A plain Item that centres the shared StatusBox.
Building¶
cd examples/v2/NetFetch
./build.sh
cp NetFetch.plugin ~/Documents/"Auterion Mission Control"/Plugins/
The build.sh script: