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: false
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 === MAVLinkEnums.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: false
197 expandedComponentWaitForParameters: true
198 contentComponent: batteryContentComponent
199 expandedComponent: batteryExpandedComponent
207 Layout.fillHeight: true
208 spacing: ScreenTools.defaultFontPixelWidth / 4
210 function getBatteryColor() {
211 switch (battery.chargeState.rawValue) {
212 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_OK:
213 if (!isNaN(battery.percentRemaining.rawValue)) {
214 if (battery.percentRemaining.rawValue > threshold1) {
215 return qgcPal.colorGreen
216 } else if (battery.percentRemaining.rawValue > threshold2) {
217 return qgcPal.colorYellowGreen
219 return qgcPal.colorYellow
224 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_LOW:
225 return qgcPal.colorOrange
226 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_CRITICAL:
227 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_EMERGENCY:
228 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_FAILED:
229 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_UNHEALTHY:
230 return qgcPal.colorRed
236 function getBatterySvgSource() {
237 switch (battery.chargeState.rawValue) {
238 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_OK:
239 if (!isNaN(battery.percentRemaining.rawValue)) {
240 if (battery.percentRemaining.rawValue > threshold1) {
241 return "/qmlimages/BatteryGreen.svg"
242 } else if (battery.percentRemaining.rawValue > threshold2) {
243 return "/qmlimages/BatteryYellowGreen.svg"
245 return "/qmlimages/BatteryYellow.svg"
248 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_LOW:
249 return "/qmlimages/BatteryOrange.svg" // Low with orange svg
250 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_CRITICAL:
251 return "/qmlimages/BatteryCritical.svg" // Critical with red svg
252 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_EMERGENCY:
253 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_FAILED:
254 case MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_UNHEALTHY:
255 return "/qmlimages/BatteryEMERGENCY.svg" // Exclamation mark
257 return "/qmlimages/Battery.svg" // Fallback if percentage is unavailable
261 function getBatteryPercentageText() {
262 if (!isNaN(battery.percentRemaining.rawValue)) {
263 if (battery.percentRemaining.rawValue > 98.9) {
266 return battery.percentRemaining.valueString + battery.percentRemaining.units
268 } else if (!isNaN(battery.voltage.rawValue)) {
269 return battery.voltage.valueString + battery.voltage.units
270 } else if (battery.chargeState.rawValue !== MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_UNDEFINED) {
271 return battery.chargeState.enumStringValue
276 function getBatteryVoltageText() {
277 if (!isNaN(battery.voltage.rawValue)) {
278 return battery.voltage.valueString + battery.voltage.units
279 } else if (battery.chargeState.rawValue !== MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_UNDEFINED) {
280 return battery.chargeState.enumStringValue
286 id: debounceRecalcTimer
291 control._recalcLowestBatteryId()
295 target: battery.percentRemaining
296 function onRawValueChanged() {
297 debounceRecalcTimer.restart()
301 target: battery.voltage
302 function onRawValueChanged() {
303 debounceRecalcTimer.restart()
307 target: battery.chargeState
308 function onRawValueChanged() {
309 debounceRecalcTimer.restart()
314 anchors.top: parent.top
315 anchors.bottom: parent.bottom
317 sourceSize.width: width
318 source: getBatterySvgSource()
319 fillMode: Image.PreserveAspectFit
320 color: getBatteryColor()
324 id: batteryInfoColumn
325 anchors.top: parent.top
326 anchors.bottom: parent.bottom
330 Layout.alignment: Qt.AlignHCenter
331 verticalAlignment: Text.AlignVCenter
333 text: getBatteryPercentageText()
334 font.pointSize: _showBoth ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize
335 visible: _showBoth || _showPercentage
339 Layout.alignment: Qt.AlignHCenter
340 font.pointSize: _showBoth ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize
342 text: getBatteryVoltageText()
343 visible: _showBoth || _showVoltage
350 id: batteryContentComponent
353 spacing: ScreenTools.defaultFontPixelHeight / 2
356 id: batteryValuesAvailableComponent
359 property bool functionAvailable: battery.function.rawValue !== MAVLinkEnums.MAV_BATTERY_FUNCTION_UNKNOWN
360 property bool showFunction: functionAvailable && battery.function.rawValue != MAVLinkEnums.MAV_BATTERY_FUNCTION_ALL
361 property bool temperatureAvailable: !isNaN(battery.temperature.rawValue)
362 property bool currentAvailable: !isNaN(battery.current.rawValue)
363 property bool mahConsumedAvailable: !isNaN(battery.mahConsumed.rawValue)
364 property bool timeRemainingAvailable: !isNaN(battery.timeRemaining.rawValue)
365 property bool percentRemainingAvailable: !isNaN(battery.percentRemaining.rawValue)
366 property bool chargeStateAvailable: battery.chargeState.rawValue !== MAVLinkEnums.MAV_BATTERY_CHARGE_STATE_UNDEFINED
371 model: _activeVehicle ? _activeVehicle.batteries : 0
373 SettingsGroupLayout {
374 heading: qsTr("Battery %1").arg(_activeVehicle.batteries.length === 1 ? qsTr("Status") : object.id.rawValue)
378 property var batteryValuesAvailable: batteryValuesAvailableLoader.item
381 id: batteryValuesAvailableLoader
382 sourceComponent: batteryValuesAvailableComponent
384 property var battery: object
388 label: qsTr("Charge State")
389 labelText: object.chargeState.enumStringValue
390 visible: batteryValuesAvailable.chargeStateAvailable
394 label: qsTr("Remaining")
395 labelText: object.timeRemainingStr.value
396 visible: batteryValuesAvailable.timeRemainingAvailable
400 label: qsTr("Remaining")
401 labelText: object.percentRemaining.valueString + " " + object.percentRemaining.units
402 visible: batteryValuesAvailable.percentRemainingAvailable
406 label: qsTr("Voltage")
407 labelText: object.voltage.valueString + " " + object.voltage.units
411 label: qsTr("Consumed")
412 labelText: object.mahConsumed.valueString + " " + object.mahConsumed.units
413 visible: batteryValuesAvailable.mahConsumedAvailable
417 label: qsTr("Temperature")
418 labelText: object.temperature.valueString + " " + object.temperature.units
419 visible: batteryValuesAvailable.temperatureAvailable
423 label: qsTr("Function")
424 labelText: object.function.enumStringValue
425 visible: batteryValuesAvailable.showFunction
433 id: batteryExpandedComponent
436 spacing: ScreenTools.defaultFontPixelHeight / 2
438 property real batteryIconHeight: ScreenTools.defaultFontPixelWidth * 3
440 FactPanelController { id: controller }
442 SettingsGroupLayout {
443 heading: qsTr("Battery Display")
444 Layout.fillWidth: true
447 Layout.fillWidth: true
448 fact: _batterySettings.consolidateMultipleBatteries
449 text: qsTr("Only show battery with lowest charge")
450 visible: fact.userVisible
453 LabelledFactComboBox {
455 fact: _batterySettings.valueDisplay
456 visible: fact.userVisible
460 QGCLabel { text: qsTr("Coloring") }
463 spacing: ScreenTools.defaultFontPixelWidth
467 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label
469 source: "/qmlimages/BatteryGreen.svg"
471 height: batteryIconHeight
472 fillMode: Image.PreserveAspectFit
473 color: qgcPal.colorGreen
475 QGCLabel { text: qsTr("100%") }
480 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and field
482 source: "/qmlimages/BatteryYellowGreen.svg"
484 height: batteryIconHeight
485 fillMode: Image.PreserveAspectFit
486 color: qgcPal.colorYellowGreen
490 fact: _batterySettings.threshold1
491 implicitWidth: ScreenTools.defaultFontPixelWidth * 6
492 height: ScreenTools.defaultFontPixelHeight * 1.5
493 enabled: fact.userVisible
495 // Validate and set the new threshold value
496 _batterySettings.setThreshold1(parseInt(text));
503 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and field
505 source: "/qmlimages/BatteryYellow.svg"
507 height: batteryIconHeight
508 fillMode: Image.PreserveAspectFit
509 color: qgcPal.colorYellow
512 fact: _batterySettings.threshold2
513 implicitWidth: ScreenTools.defaultFontPixelWidth * 6
514 height: ScreenTools.defaultFontPixelHeight * 1.5
515 enabled: fact.userVisible
517 // Validate and set the new threshold value
518 _batterySettings.setThreshold2(parseInt(text));
525 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label
527 source: "/qmlimages/BatteryOrange.svg"
529 height: batteryIconHeight
530 fillMode: Image.PreserveAspectFit
531 color: qgcPal.colorOrange
533 QGCLabel { text: qsTr("Low") }
538 spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label
540 source: "/qmlimages/BatteryCritical.svg"
542 height: batteryIconHeight
543 fillMode: Image.PreserveAspectFit
544 color: qgcPal.colorRed
546 QGCLabel { text: qsTr("Critical") }
553 Layout.fillWidth: true
554 source: _activeVehicle.expandedToolbarIndicatorSource("Battery")
557 SettingsGroupLayout {
558 visible: _activeVehicle.autopilotPlugin.knownVehicleComponentAvailable(AutoPilotPlugin.KnownPowerVehicleComponent) &&
559 QGroundControl.corePlugin.showAdvancedUI
562 label: qsTr("Vehicle Power")
563 buttonText: qsTr("Configure")
566 mainWindow.showKnownVehicleComponentConfigPage(AutoPilotPlugin.KnownPowerVehicleComponent)
567 mainWindow.closeIndicatorDrawer()