Files
grafana-battery-visualisation/src/module.ts
T
Andy DixonandCopilot da80da71d1 Add field selectors for charge, load, and state metrics
Field Mapping options (new category at top of editor):
- Charge field: pick which numeric field drives the battery level
  (falls back to first number field if unset)
- Enable load metric: toggle + field picker for a load/power draw
  percentage, displayed as a themed semi-circular gauge arc beside
  the battery (green < 50%, amber 50-80%, red > 80%)
- Enable state field: toggle + field picker + user-defined values
  for charging/discharging so the panel uses an explicit data field
  rather than computing state from the rate of change

Visual integration:
- Load gauge renders in a card with the battery, scales responsively,
  and collapses to a compact text line in small panels
- State field drives the status badge, gradient direction, and
  charging animation (overrides computed rate when enabled)
- All new options use showIf for conditional visibility in the editor

Also fixed deprecation warnings by replacing .toArray() with
Array.from() for Grafana 12+ compatibility.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 13:15:21 +01:00

214 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { PanelPlugin } from '@grafana/data';
import { ChargeVizPanel } from './components/ChargeVizPanel';
import { ChargeVizOptions } from './types';
export const plugin = new PanelPlugin<ChargeVizOptions>(ChargeVizPanel).setPanelOptions(
(builder) => {
// -- Field Mapping category (top) --
builder.addFieldNamePicker({
path: 'chargeField',
name: 'Charge field',
category: ['Field Mapping'],
description: 'Numeric field that represents battery charge (0–100%). If blank, the first number field is used.',
settings: {
filter: (f) => f.type === 'number',
noFieldsMessage: 'No numeric fields found',
},
});
builder.addBooleanSwitch({
path: 'enableLoad',
name: 'Enable load metric',
defaultValue: false,
category: ['Field Mapping'],
description: 'Show a second metric representing current load / power draw.',
});
builder.addFieldNamePicker({
path: 'loadField',
name: 'Load field',
category: ['Field Mapping'],
description: 'Numeric field for load percentage (0–100%).',
settings: {
filter: (f) => f.type === 'number',
noFieldsMessage: 'No numeric fields found',
},
showIf: (opts) => opts.enableLoad === true,
});
builder.addBooleanSwitch({
path: 'enableStateField',
name: 'Enable state field',
defaultValue: false,
category: ['Field Mapping'],
description: 'Use a field from the data to determine charging / discharging state instead of computing it from the rate.',
});
builder.addFieldNamePicker({
path: 'stateField',
name: 'State field',
category: ['Field Mapping'],
description: 'String or numeric field whose value indicates the battery state.',
settings: {
noFieldsMessage: 'No fields found',
},
showIf: (opts) => opts.enableStateField === true,
});
builder.addTextInput({
path: 'stateChargingValue',
name: 'Charging value',
defaultValue: 'charging',
category: ['Field Mapping'],
description: 'Value in the state field that means "charging" (case-insensitive).',
showIf: (opts) => opts.enableStateField === true,
});
builder.addTextInput({
path: 'stateDischargingValue',
name: 'Discharging value',
defaultValue: 'discharging',
category: ['Field Mapping'],
description: 'Value in the state field that means "discharging" (case-insensitive).',
showIf: (opts) => opts.enableStateField === true,
});
// -- Colours category --
builder.addColorPicker({
path: 'lowColour',
name: 'Low battery colour',
defaultValue: '#F44336',
category: ['Colours'],
description: 'Colour when charge is below the low threshold.',
});
builder.addColorPicker({
path: 'midColour',
name: 'Medium battery colour',
defaultValue: '#FF9800',
category: ['Colours'],
description: 'Colour when charge is between the low and high thresholds.',
});
builder.addColorPicker({
path: 'highColour',
name: 'High battery colour',
defaultValue: '#4CAF50',
category: ['Colours'],
description: 'Colour when charge is above the high threshold.',
});
// -- Thresholds category --
builder.addNumberInput({
path: 'lowThreshold',
name: 'Low threshold (%)',
defaultValue: 30,
settings: { min: 0, max: 100, integer: true },
category: ['Thresholds'],
description: 'Charge level below which the low colour is used.',
});
builder.addNumberInput({
path: 'highThreshold',
name: 'High threshold (%)',
defaultValue: 70,
settings: { min: 0, max: 100, integer: true },
category: ['Thresholds'],
description: 'Charge level above which the high colour is used.',
});
// -- Appearance category --
builder.addBooleanSwitch({
path: 'gradientFill',
name: 'Gradient fill',
defaultValue: true,
category: ['Appearance'],
description: 'Show a gradient that hints at charging/discharging direction.',
});
builder.addSelect({
path: 'orientation',
name: 'Orientation',
defaultValue: 'vertical',
category: ['Appearance'],
settings: {
options: [
{ value: 'vertical', label: 'Vertical' },
{ value: 'horizontal', label: 'Horizontal' },
],
},
description: 'Battery orientation. Horizontal works well in wide, short panels.',
});
builder.addBooleanSwitch({
path: 'chargingAnimation',
name: 'Charging animation',
defaultValue: true,
category: ['Appearance'],
description: 'Show a pulse animation when the battery is charging.',
});
builder.addNumberInput({
path: 'borderWidth',
name: 'Border width',
defaultValue: 3,
settings: { min: 1, max: 8, integer: true },
category: ['Appearance'],
description: 'Thickness of the battery outline.',
});
// -- Display category --
builder.addBooleanSwitch({
path: 'showPercentage',
name: 'Show percentage',
defaultValue: true,
category: ['Display'],
description: 'Show the percentage text inside the battery.',
});
builder.addBooleanSwitch({
path: 'showRate',
name: 'Show charge rate',
defaultValue: true,
category: ['Display'],
description: 'Show the rate of change (%/min).',
});
builder.addBooleanSwitch({
path: 'showLowHigh',
name: 'Show low / high',
defaultValue: true,
category: ['Display'],
description: 'Show the minimum and maximum values from the series.',
});
builder.addBooleanSwitch({
path: 'showStatus',
name: 'Show charging status',
defaultValue: true,
category: ['Display'],
description: 'Show Charging / Discharging / Stable indicator.',
});
// -- Data category --
builder.addNumberInput({
path: 'decimalPlaces',
name: 'Decimal places',
defaultValue: 2,
settings: { min: 0, max: 6, integer: true },
category: ['Data'],
description: 'Decimal places for rate / low / high values.',
});
builder.addBooleanSwitch({
path: 'useRegressionForRate',
name: 'Use regression for rate',
defaultValue: false,
category: ['Data'],
description: 'Compute %/min using least-squares regression instead of simple first/last difference.',
});
return builder;
}
);