5import QGroundControl.Controls
6import QGroundControl.FactControls
8//-------------------------------------------------------------------------
12 anchors.top: parent.top
13 anchors.bottom: parent.bottom
14 width: batteryIndicatorRow.width
16 property bool showIndicator: _activeVehicle && _activeVehicle.batteries.count > 0
17 property bool waitForParameters: true // UI won't show until parameters are ready
18 property Component expandedPageComponent
20 property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
21 property var _batterySettings: QGroundControl.settingsManager.batteryIndicatorSettings
22 property Fact _indicatorDisplay: _batterySettings.valueDisplay
23 property bool _showPercentage: _indicatorDisplay.rawValue === 0
24 property bool _showVoltage: _indicatorDisplay.rawValue === 1
25 property bool _showBoth: _indicatorDisplay.rawValue === 2
26 property int _lowestBatteryId: -1 // -1: show all batteries, otherwise show only battery with this id
28 // Properties to hold the thresholds
29 property int threshold1: _batterySettings.threshold1.rawValue
30 property int threshold2: _batterySettings.threshold2.rawValue
32 function _recalcLowestBatteryIdFromVoltage() {
34 // If there is only one battery then it is the lowest
35 if (_activeVehicle.batteries.count === 1) {
36 _lowestBatteryId = _activeVehicle.batteries.get(0).id.rawValue
40 // If we have valid voltage for all batteries we use that to determine lowest battery
41 let allHaveVoltage = true
42 for (var i = 0; i < _activeVehicle.batteries.count; i++) {
43 let battery = _activeVehicle.batteries.get(i)
44 if (isNaN(battery.voltage.rawValue)) {
45 allHaveVoltage = false
50 let lowestBattery = _activeVehicle.batteries.get(0)
51 let lowestBatteryId = lowestBattery.id.rawValue
52 for (var i = 1; i < _activeVehicle.batteries.count; i++) {
53 let battery = _activeVehicle.batteries.get(i)
54 if (battery.voltage.rawValue < lowestBattery.voltage.rawValue) {
55 lowestBattery = battery
56 lowestBatteryId = battery.id.rawValue
59 _lowestBatteryId = lowestBatteryId
64 // Couldn't determine lowest battery, show all
68 function _recalcLowestBatteryIdFromPercentage() {
70 // If there is only one battery then it is the lowest
71 if (_activeVehicle.batteries.count === 1) {
72 _lowestBatteryId = _activeVehicle.batteries.get(0).id.rawValue
76 // If we have valid percentage for all batteries we use that to determine lowest battery
77 let allHavePercentage = true
78 for (var i = 0; i < _activeVehicle.batteries.count; i++) {
79 let battery = _activeVehicle.batteries.get(i)
80 if (isNaN(battery.percentRemaining.rawValue)) {
81 allHavePercentage = false
85 if (allHavePercentage) {
86 let lowestBattery = _activeVehicle.batteries.get(0)
87 let lowestBatteryId = lowestBattery.id.rawValue
88 for (var i = 1; i < _activeVehicle.batteries.count; i++) {
89 let battery = _activeVehicle.batteries.get(i)
90 if (battery.percentRemaining.rawValue < lowestBattery.percentRemaining.rawValue) {
91 lowestBattery = battery
92 lowestBatteryId = battery.id.rawValue
95 _lowestBatteryId = lowestBatteryId
100 // Couldn't determine lowest battery, show all
101 _lowestBatteryId = -1
104 function _recalcLowestBatteryIdFromChargeState() {
105 if (_activeVehicle) {
106 // If there is only one battery then it is the lowest
107 if (_activeVehicle.batteries.count === 1) {
108 _lowestBatteryId = _activeVehicle.batteries.get(0).id.rawValue
112 // If we have valid chargeState for all batteries we use that to determine lowest battery
113 let allHaveChargeState = true
114 for (var i = 0; i < _activeVehicle.batteries.count; i++) {
115 let battery = _activeVehicle.batteries.get(i)
116 if (battery.chargeState.rawValue === MAVLink.MAV_BATTERY_CHARGE_STATE_UNDEFINED) {
117 allHaveChargeState = false
121 if (allHaveChargeState) {
122 let lowestBattery = _activeVehicle.batteries.get(0)
123 let lowestBatteryId = lowestBattery.id.rawValue
124 for (var i = 1; i < _activeVehicle.batteries.count; i++) {
125 let battery = _activeVehicle.batteries.get(i)
126 if (battery.chargeState.rawValue > lowestBattery.chargeState.rawValue) {
127 lowestBattery = battery
128 lowestBatteryId = battery.id.rawValue
131 _lowestBatteryId = lowestBatteryId
136 // Couldn't determine lowest battery, show all
137 _lowestBatteryId = -1
140 function _recalcLowestBatteryId() {
141 if (!_activeVehicle || _activeVehicle.batteries.count === 0) {
142 _lowestBatteryId = -1
145 if (_batterySettings.valueDisplay.rawValue === 0) {
146 // User wants percentage display so use that if available
147 _recalcLowestBatteryIdFromPercentage()
148 } else if (_batterySettings.valueDisplay.rawValue === 1) {
149 // User wants voltage display so use that if available
150 _recalcLowestBatteryIdFromVoltage()
152 // If we still dont have a lowest battery id then try charge state
153 if (_lowestBatteryId === -1) {
154 _recalcLowestBatteryIdFromChargeState()
158 Component.onCompleted: _recalcLowestBatteryId()
161 target: _activeVehicle ? _activeVehicle.batteries : null
162 function onCountChanged() {_recalcLowestBatteryId() }
165 QGCPalette { id: qgcPal }
168 id: batteryIndicatorRow
169 anchors.top: parent.top
170 anchors.bottom: parent.bottom
171 spacing: ScreenTools.defaultFontPixelWidth / 2
174 model: _activeVehicle ? _activeVehicle.batteries : 0
177 Layout.fillHeight: true
178 sourceComponent: batteryVisual
179 visible: control._lowestBatteryId === -1 || object.id.rawValue === control._lowestBatteryId || !control._batterySettings.consolidateMultipleBatteries.rawValue
181 property var battery: object
188 onClicked: mainWindow.showIndicatorDrawer(batteryPopup, control)
195 showExpand: expandedComponent ? true : false
196 waitForParameters: control.waitForParameters
197 contentComponent: batteryContentComponent
198 expandedComponent: batteryExpandedComponent
206 Layout.fillHeight: true
207 spacing: ScreenTools.defaultFontPixelWidth / 4
209 function getBatteryColor() {
210 switch (battery.chargeState.rawValue) {
211 case MAVLink.MAV_BATTERY_CHARGE_STATE_OK:
212 if (!isNaN(battery.percentRemaining.rawValue)) {
213 if (battery.percentRemaining.rawValue > threshold1) {
214 return qgcPal.colorGreen
215 } else if (battery.percentRemaining.rawValue > threshold2) {
216 return qgcPal.colorYellowGreen
218 return qgcPal.colorYellow
223 case MAVLink.MAV_BATTERY_CHARGE_STATE_LOW:
224 return qgcPal.colorOrange
225 case MAVLink.MAV_BATTERY_CHARGE_STATE_CRITICAL:
226 case MAVLink.MAV_BATTERY_CHARGE_STATE_EMERGENCY:
227 case MAVLink.MAV_BATTERY_CHARGE_STATE_FAILED:
228 case MAVLink.MAV_BATTERY_CHARGE_STATE_UNHEALTHY:
229 return qgcPal.colorRed
235 function getBatterySvgSource() {
236 switch (battery.chargeState.rawValue) {
237 case MAVLink.MAV_BATTERY_CHARGE_STATE_OK:
238 if (!isNaN(battery.percentRemaining.rawValue)) {
239 if (battery.percentRemaining.rawValue > threshold1) {
240 return "/qmlimages/BatteryGreen.svg"
241 } else if (battery.percentRemaining.rawValue > threshold2) {
242 return "/qmlimages/BatteryYellowGreen.svg"
244 return "/qmlimages/BatteryYellow.svg"
247 case MAVLink.MAV_BATTERY_CHARGE_STATE_LOW:
248 return "/qmlimages/BatteryOrange.svg" // Low with orange svg
249 case MAVLink.MAV_BATTERY_CHARGE_STATE_CRITICAL:
250 return "/qmlimages/BatteryCritical.svg" // Critical with red svg
251 case MAVLink.MAV_BATTERY_CHARGE_STATE_EMERGENCY:
252 case MAVLink.MAV_BATTERY_CHARGE_STATE_FAILED:
253 case MAVLink.MAV_BATTERY_CHARGE_STATE_UNHEALTHY:
254 return "/qmlimages/BatteryEMERGENCY.svg" // Exclamation mark
256 return "/qmlimages/Battery.svg" // Fallback if percentage is unavailable
260 function getBatteryPercentageText() {
261 if (!isNaN(battery.percentRemaining.rawValue)) {
262 if (battery.percentRemaining.rawValue > 98.9) {
265 return battery.percentRemaining.valueString + battery.percentRemaining.units
267 } else if (!isNaN(battery.voltage.rawValue)) {
268 return battery.voltage.valueString + battery.voltage.units
269 } else if (battery.chargeState.rawValue !== MAVLink.MAV_BATTERY_CHARGE_STATE_UNDEFINED) {
270 return battery.chargeState.enumStringValue
275 function getBatteryVoltageText() {
276 if (!isNaN(battery.voltage.rawValue)) {
277 return battery.voltage.valueString + battery.voltage.units
278 } else if (battery.chargeState.rawValue !== MAVLink.MAV_BATTERY_CHARGE_STATE_UNDEFINED) {
279 return battery.chargeState.enumStringValue
285 id: debounceRecalcTimer
290 control._recalcLowestBatteryId()
294 target: battery.percentRemaining
295 function onRawValueChanged() {
296 debounceRecalcTimer.restart()
300 target: battery.voltage
301 function onRawValueChanged() {
302 debounceRecalcTimer.restart()
306 target: battery.chargeState
307 function onRawValueChanged() {
308 debounceRecalcTimer.restart()
313 anchors.top: parent.top
314 anchors.bottom: parent.bottom
316 sourceSize.width: width
317 source: getBatterySvgSource()
318 fillMode: Image.PreserveAspectFit
319 color: getBatteryColor()
323 id: batteryInfoColumn
324 anchors.top: parent.top
325 anchors.bottom: parent.bottom
329 Layout.alignment: Qt.AlignHCenter
330 verticalAlignment: Text.AlignVCenter
331 color: qgcPal.windowTransparentText
332 text: getBatteryPercentageText()
333 font.pointSize: _showBoth ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize
334 visible: _showBoth || _showPercentage
338 Layout.alignment: Qt.AlignHCenter
339 font.pointSize: _showBoth ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize
340 color: qgcPal.windowTransparentText
341 text: getBatteryVoltageText()
342 visible: _showBoth || _showVoltage
349 id: batteryContentComponent
352 spacing: ScreenTools.defaultFontPixelHeight / 2
355 id: batteryValuesAvailableComponent
358 property bool functionAvailable: battery.function.rawValue !== MAVLink.MAV_BATTERY_FUNCTION_UNKNOWN
359 property bool showFunction: functionAvailable && battery.function.rawValue != MAVLink.MAV_BATTERY_FUNCTION_ALL
360 property bool temperatureAvailable: !isNaN(battery.temperature.rawValue)
361 property bool currentAvailable: !isNaN(battery.current.rawValue)
362 property bool mahConsumedAvailable: !isNaN(battery.mahConsumed.rawValue)
363 property bool timeRemainingAvailable: !isNaN(battery.timeRemaining.rawValue)
364 property bool percentRemainingAvailable: !isNaN(battery.percentRemaining.rawValue)
365 property bool chargeStateAvailable: battery.chargeState.rawValue !== MAVLink.MAV_BATTERY_CHARGE_STATE_UNDEFINED
370 model: _activeVehicle ? _activeVehicle.batteries : 0
372 SettingsGroupLayout {
373 heading: qsTr("Battery %1").arg(_activeVehicle.batteries.length === 1 ? qsTr("Status") : object.id.rawValue)
377 property var batteryValuesAvailable: batteryValuesAvailableLoader.item
380 id: batteryValuesAvailableLoader
381 sourceComponent: batteryValuesAvailableComponent
383 property var battery: object
387 label: qsTr("Charge State")
388 labelText: object.chargeState.enumStringValue
389 visible: batteryValuesAvailable.chargeStateAvailable
393 label: qsTr("Remaining")
394 labelText: object.timeRemainingStr.value
395 visible: batteryValuesAvailable.timeRemainingAvailable
399 label: qsTr("Remaining")
400 labelText: object.percentRemaining.valueString + " " + object.percentRemaining.units
401 visible: batteryValuesAvailable.percentRemainingAvailable
405 label: qsTr("Voltage")
406 labelText: object.voltage.valueString + " " + object.voltage.units
410 label: qsTr("Consumed")
411 labelText: object.mahConsumed.valueString + " " + object.mahConsumed.units
412 visible: batteryValuesAvailable.mahConsumedAvailable
416 label: qsTr("Temperature")
417 labelText: object.temperature.valueString + " " + object.temperature.units
418 visible: batteryValuesAvailable.temperatureAvailable
422 label: qsTr("Function")
423 labelText: object.function.enumStringValue
424 visible: batteryValuesAvailable.showFunction
432 id: batteryExpandedComponent
435 spacing: ScreenTools.defaultFontPixelHeight / 2
437 property real batteryIconHeight: ScreenTools.defaultFontPixelWidth * 3
439 FactPanelController { id: controller }
441 SettingsGroupLayout {
442 heading: qsTr("Battery Display")
443 Layout.fillWidth: true
446 Layout.fillWidth: true
447 fact: _batterySettings.consolidateMultipleBatteries
448 text: qsTr("Only show battery with lowest charge")
449 visible: fact.visible
452 LabelledFactComboBox {
454 fact: _batterySettings.valueDisplay
455 visible: fact.visible
459 QGCLabel { text: qsTr("Coloring") }
462 spacing: ScreenTools.defaultFontPixelWidth
466 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label
468 source: "/qmlimages/BatteryGreen.svg"
470 height: batteryIconHeight
471 fillMode: Image.PreserveAspectFit
472 color: qgcPal.colorGreen
474 QGCLabel { text: qsTr("100%") }
479 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and field
481 source: "/qmlimages/BatteryYellowGreen.svg"
483 height: batteryIconHeight
484 fillMode: Image.PreserveAspectFit
485 color: qgcPal.colorYellowGreen
489 fact: _batterySettings.threshold1
490 implicitWidth: ScreenTools.defaultFontPixelWidth * 6
491 height: ScreenTools.defaultFontPixelHeight * 1.5
492 enabled: fact.visible
494 // Validate and set the new threshold value
495 _batterySettings.setThreshold1(parseInt(text));
502 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and field
504 source: "/qmlimages/BatteryYellow.svg"
506 height: batteryIconHeight
507 fillMode: Image.PreserveAspectFit
508 color: qgcPal.colorYellow
511 fact: _batterySettings.threshold2
512 implicitWidth: ScreenTools.defaultFontPixelWidth * 6
513 height: ScreenTools.defaultFontPixelHeight * 1.5
514 enabled: fact.visible
516 // Validate and set the new threshold value
517 _batterySettings.setThreshold2(parseInt(text));
524 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label
526 source: "/qmlimages/BatteryOrange.svg"
528 height: batteryIconHeight
529 fillMode: Image.PreserveAspectFit
530 color: qgcPal.colorOrange
532 QGCLabel { text: qsTr("Low") }
537 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label
539 source: "/qmlimages/BatteryCritical.svg"
541 height: batteryIconHeight
542 fillMode: Image.PreserveAspectFit
543 color: qgcPal.colorRed
545 QGCLabel { text: qsTr("Critical") }
552 Layout.fillWidth: true
553 source: _activeVehicle.expandedToolbarIndicatorSource("Battery")
556 SettingsGroupLayout {
557 visible: _activeVehicle.autopilotPlugin.knownVehicleComponentAvailable(AutoPilotPlugin.KnownPowerVehicleComponent) &&
558 QGroundControl.corePlugin.showAdvancedUI
561 label: qsTr("Vehicle Power")
562 buttonText: qsTr("Configure")
565 mainWindow.showKnownVehicleComponentConfigPage(AutoPilotPlugin.KnownPowerVehicleComponent)
566 mainWindow.closeIndicatorDrawer()