Documentation Index
Fetch the complete documentation index at: https://docs.altnautica.com/llms.txt
Use this file to discover all available pages before exploring further.
The Battery Health Panel is the first first-party extension on the
new plugin host. It is a GCS-only plugin that reads the host’s
normalized battery telemetry, runs an anomaly engine, and emits
notifications when a rule fires. Source lives at
altnautica/ADOSExtensions/extensions/battery-health-panel.
This page walks through the parts that matter for plugin authors.
Manifest
plugin:
id: com.altnautica.battery-health-panel
name: "ADOS Battery Health Panel"
version: "1.0.0"
license: "GPL-3.0-or-later"
compatibility:
ados_version: ">=0.10.0"
gcs_version: ">=0.5.0"
profiles: ["drone"]
gcs:
bundle: "gcs/plugin.bundle.js"
isolation: "iframe-sandbox"
permissions:
- ui.slot.fc-tab
- ui.slot.notification
- telemetry.subscribe.battery
- telemetry.subscribe.mavlink
- recording.write
contributes:
panels:
- id: battery-health-tab
slot: fc.tab
title: "Battery Health"
order: 30
Six permissions, three contributes blocks, no agent half. Risk band:
low (no vehicle.command, no host file system, no network).
Entry point
import { definePlugin } from "@altnautica/plugin-sdk";
import { createBatteryStore } from "./batteryStore";
import type { BatterySample } from "./types";
const store = createBatteryStore();
definePlugin({
id: "com.altnautica.battery-health-panel",
version: "1.0.0",
async mount(ctx) {
await ctx.telemetry.subscribe<BatterySample>(
"battery",
(sample) => store.ingest(sample),
);
store.onAnomaly(async (fired) => {
for (const event of fired) {
await ctx.notifications.publish({
channelId: "battery-anomaly",
severity: event.severity,
title: event.title,
body: event.body,
});
await ctx.recording.mark({ label: event.title });
}
});
},
});
definePlugin is the only SDK call. Everything else is plain
TypeScript: a store, a rule engine, a render loop. The SDK gets out
of the way.
Anomaly rule
const ruleCellLow: Rule = ({ curr, config }) => {
const min = minDefined(curr.cellVoltagesV);
if (min === null || min >= config.lowCellVoltageV) return null;
if (min < config.criticalCellVoltageV) return null;
return mk("cell_low", curr, "warning", "Cell low", `${min.toFixed(2)} V`);
};
A rule is a pure function from (prev, curr, config) to
AnomalyEvent | null. The store calls every rule on every new
sample. Hysteresis is the store’s job: an anomaly stays in the live
list until the underlying condition has been clear for at least 5
seconds.
Testing without a host
The SDK ships a synthetic-host harness:
import { createPluginHarness } from "@altnautica/plugin-sdk/harness";
const harness = createPluginHarness({
grantedCapabilities: [
"telemetry.subscribe.battery",
"ui.slot.notification",
"recording.write",
],
mount: async (ctx) => {
await ctx.telemetry.subscribe("battery", (s) => store.ingest(s));
store.onAnomaly(async (fired) => {
for (const e of fired) {
await ctx.notifications.publish({ ... });
}
});
},
});
await harness.start();
harness.pushTelemetry("battery", { cellVoltagesV: [3.9, 3.4, 3.9, 3.9], ... });
expect(harness.notifications).toHaveLength(1);
expect(harness.notifications[0]).toMatchObject({ severity: "warning" });
await harness.teardown();
The harness lets a single Vitest run validate the whole plugin
without spinning up Mission Control or a real drone.
Packing and installing
pnpm test
pnpm build
./scripts/pack.sh battery-health-panel
Drag dist/com.altnautica.battery-health-panel-1.0.0.adosplug into
Mission Control -> Settings -> Plugins -> Install plugin. Approve
the six declared permissions. The panel mounts under the FC tab.
What is next