comparison OverviewMobile.qml @ 0:57ffb39f29d4

First commit of new carousel page to allow battery charging current to be adjusted.
author Daniel O'Connor <darius@dons.net.au>
date Mon, 13 Dec 2021 23:05:38 +1030
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:57ffb39f29d4
1 import QtQuick 1.1
2 import com.victron.velib 1.0
3 import "utils.js" as Utils
4
5 OverviewPage {
6 id: root
7
8 property variant sys: theSystem
9 property string settingsBindPreffix: "com.victronenergy.settings"
10 property string pumpBindPreffix: "com.victronenergy.pump.startstop0"
11 property variant activeNotifications: NotificationCenter.notifications.filter(
12 function isActive(obj) { return obj.active} )
13 property string noAdjustableByDmc: qsTr("This setting is disabled when a Digital Multi Control " +
14 "is connected. If it was recently disconnected execute " +
15 "\"Redetect system\" that is avalible on the inverter menu page.")
16 property string noAdjustableByBms: qsTr("This setting is disabled when a VE.Bus BMS " +
17 "is connected. If it was recently disconnected execute " +
18 "\"Redetect system\" that is avalible on the inverter menu page.")
19 property string noAdjustableTextByConfig: qsTr("This setting is disabled. " +
20 "Possible reasons are \"Overruled by remote\" is not enabled or " +
21 "an assistant is preventing the adjustment. Please, check " +
22 "the inverter configuration with VEConfigure.")
23 property int numberOfMultis: 0
24 property string vebusPrefix: ""
25
26 // Keeps track of which button on the bottom row is active
27 property int buttonIndex: 0
28
29 title: qsTr("Mobile")
30
31 Component.onCompleted: discoverMulti()
32
33 ListView {
34 id: pwColumn
35
36 property int tilesCount: solarTile.visible || dcSystem.visible ? 3 : 2
37 property int tileHeight: Math.ceil(height / tilesCount)
38 interactive: false // static tiles
39
40 width: 136
41 anchors {
42 left: parent.left
43 top: parent.top;
44 bottom: acModeButton.top;
45 }
46
47 model: VisualItemModel {
48 Tile {
49 width: pwColumn.width
50 height: visible ? pwColumn.tileHeight : 0
51 title: qsTr("AC INPUT")
52 color: "#82acde"
53 visible: !dcSystem.visible || !solarTile.visible
54 values: [
55 TileText {
56 text: sys.acInput.power.uiText
57 font.pixelSize: 30
58 }
59
60 ]
61 }
62
63 TileAcPower {
64 width: pwColumn.width
65 height: visible ? pwColumn.tileHeight : 0
66 title: qsTr("AC LOADS")
67 color: "#e68e8a"
68 values: [
69 TileText {
70 text: sys.acLoad.power.uiText
71 font.pixelSize: 30
72 }
73 ]
74 }
75
76 Tile {
77 id: solarTile
78 width: pwColumn.width
79 height: visible ? pwColumn.tileHeight : 0
80 title: qsTr("PV CHARGER")
81 color: "#2cc36b"
82 visible : sys.pvCharger.power.valid
83
84 values: [
85 TileText {
86 font.pixelSize: 30
87 text: sys.pvCharger.power.uiText
88 }
89 ]
90 }
91 Tile {
92 id: dcSystem
93 width: pwColumn.width
94 height: visible ? pwColumn.tileHeight : 0
95 title: qsTr("DC SYSTEM")
96 color: "#16a085"
97 visible : hasDcSys.value === 1
98
99 VBusItem {
100 id: hasDcSys
101 bind: Utils.path(settingsBindPreffix, "/Settings/SystemSetup/HasDcSystem")
102 }
103
104 values: [
105 TileText {
106 font.pixelSize: 30
107 text: sys.dcSystem.power.format(0)
108 },
109 TileText {
110 text: !sys.dcSystem.power.valid ? "---" :
111 sys.dcSystem.power.value < 0 ? qsTr("to battery") : qsTr("from battery")
112 }
113 ]
114 }
115 }
116 }
117
118 Tile {
119 id: logoTile
120
121 color: "#575748"
122 height: 120
123 anchors {
124 left: pwColumn.right
125 right: tanksColum.left
126 top: parent.top
127 }
128
129 MbIcon {
130 x: 1
131 y: 1
132 // see below, so the svg instead of a png if there is a 1x1 image
133 visible: customImage.sourceSize.width === 1 && customImage.sourceSize.height === 1
134 iconId: "mobile-builder-logo-svg"
135 }
136
137 // The uploaded png, the default is a 1x1 transparent pixel now.
138 Image {
139 id: customImage
140 source: "image://theme/mobile-builder-logo"
141 anchors.centerIn: parent
142 }
143 }
144
145 Tile {
146 id: batteryTile
147 height: 112
148 title: qsTr("BATTERY")
149 anchors {
150 left: pwColumn.right
151 right: stateTile.left
152 top: logoTile.bottom
153 bottom: acModeButton.top
154 }
155
156 values: [
157 TileText {
158 text: sys.battery.soc.absFormat(0)
159 font.pixelSize: 30
160 height: 32
161 },
162 TileText {
163 text: {
164 if (!sys.battery.state.valid)
165 return "---"
166 switch(sys.battery.state.value) {
167 case sys.batteryStateIdle: return qsTr("idle")
168 case sys.batteryStateCharging : return qsTr("charging")
169 case sys.batteryStateDischarging : return qsTr("discharging")
170 }
171 }
172 },
173 TileText {
174 text: sys.battery.power.absFormat(0)
175 },
176 TileText {
177 text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1)
178 }
179 ]
180 }
181
182 Tile {
183 id: stateTile
184
185 width: 104
186 title: qsTr("STATUS")
187 color: "#4789d0"
188
189 anchors {
190 right: tanksColum.left
191 top: logoTile.bottom
192 bottom: acModeButton.top
193 }
194
195 Timer {
196 id: wallClock
197
198 running: true
199 repeat: true
200 interval: 1000
201 triggeredOnStart: true
202 onTriggered: time = Qt.formatDateTime(new Date(), "hh:mm")
203
204 property string time
205 }
206
207 values: [
208 TileText {
209 id: systemTile
210 text: wallClock.time
211 font.pixelSize: 30
212 },
213 TileText {
214 property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" }
215 property VeQuickItem speed: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Speed") }
216 property VeQuickItem speedUnit: VeQuickItem { uid: "dbus/com.victronenergy.settings/Settings/Gps/SpeedUnit" }
217
218 text: speed.value === undefined ? "" : getValue()
219 visible: speed.value !== undefined && speedUnit.value !== undefined
220
221 function getValue()
222 {
223 if (speedUnit.value === "km/h")
224 return (speed.value * 3.6).toFixed(1) + speedUnit.value
225 if (speedUnit.value === "mph")
226 return (speed.value * 2.236936).toFixed(1) + speedUnit.value
227 if (speedUnit.value === "kt")
228 return (speed.value * (3600/1852)).toFixed(1) + speedUnit.value
229 return speed.value.toFixed(2) + "m/s"
230 }
231 },
232 Marquee {
233 text: notificationText()
234 width: stateTile.width
235 interval: 100
236 fontSize: 13
237 }
238 ]
239 }
240
241 ListView {
242 id: tanksColum
243
244 property int tileHeight: Math.ceil(height / Math.max(count, 2))
245 width: 134
246 interactive: false // static tiles
247 model: TankModel { id: tankModel }
248 delegate: TileTank {
249 // Without an intermediate assignment this will trigger a binding loop warning.
250 property variant theService: DBusServices.get(buddy.id)
251 service: theService
252 width: tanksColum.width
253 height: tanksColum.tileHeight
254 pumpBindPrefix: root.pumpBindPreffix
255 compact: tankModel.rowCount > (pumpButton.pumpEnabled ? 4 : 5)
256 Connections {
257 target: scrollTimer
258 onTriggered: doScroll()
259 }
260 }
261
262 anchors {
263 top: root.top
264 bottom: pumpButton.pumpEnabled ? acModeButton.top : acModeButton.bottom
265 right: root.right
266 }
267
268 // Synchronise tank name text scroll start
269 Timer {
270 id: scrollTimer
271 interval: 15000
272 repeat: true
273 running: root.active && tankModel.rowCount > 4
274 }
275
276 Tile {
277 title: qsTr("TANKS")
278 anchors.fill: parent
279 values: TileText {
280 text: qsTr("No tanks found")
281 width: parent.width
282 wrapMode: Text.WordWrap
283 }
284 z: -1
285 }
286 }
287
288 Keys.forwardTo: [keyHandler]
289
290 Item {
291 id: keyHandler
292 Keys.onLeftPressed: {
293 if (buttonIndex > 0)
294 buttonIndex--
295
296 event.accepted = true
297 }
298
299 Keys.onRightPressed: {
300 if (buttonIndex < (pumpButton.pumpEnabled ? 2 : 1))
301 buttonIndex++
302
303 event.accepted = true
304 }
305 }
306
307 MouseArea {
308 anchors.fill: parent
309 enabled: parent.active
310 onPressed: mouse.accepted = acCurrentButton.expanded
311 onClicked: acCurrentButton.cancel()
312 }
313
314 TileSpinBox {
315 id: acCurrentButton
316
317 anchors.bottom: parent.bottom
318 anchors.left: parent.left
319 isCurrentItem: (buttonIndex == 0)
320 focus: root.active && isCurrentItem
321
322 bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimit")
323 title: qsTr("AC CURRENT LIMIT")
324 color: containsMouse && !editMode ? "#d3d3d3" : "#A8A8A8"
325 width: pumpButton.pumpEnabled ? 160 : 173
326 fontPixelSize: 14
327 unit: "A"
328 readOnly: currentLimitIsAdjustable.value !== 1 || numberOfMultis > 1
329 buttonColor: "#979797"
330
331 VBusItem { id: currentLimitIsAdjustable; bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimitIsAdjustable") }
332
333 Keys.onSpacePressed: showErrorToast(event)
334
335 function editIsAllowed() {
336 if (numberOfMultis > 1) {
337 toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000)
338 return false
339 }
340
341 if (currentLimitIsAdjustable.value === 0) {
342 if (dmc.valid) {
343 toast.createToast(noAdjustableByDmc, 5000)
344 return false
345 }
346 if (bms.valid) {
347 toast.createToast(noAdjustableByBms, 5000)
348 return false
349 }
350 if (!dmc.valid && !bms.valid) {
351 toast.createToast(noAdjustableTextByConfig, 5000)
352 return false
353 }
354 }
355
356 return true
357 }
358
359 function showErrorToast(event) {
360 editIsAllowed()
361 event.accepted = true
362 }
363 }
364
365 Tile {
366 id: acModeButton
367 anchors.left: acCurrentButton.right
368 anchors.bottom: parent.bottom
369 property variant texts: { 4: qsTr("OFF"), 3: qsTr("ON"), 1: qsTr("CHARGER ONLY") }
370 property int value: mode.valid ? mode.value : 3
371 property int shownValue: applyAnimation2.running ? applyAnimation2.pendingValue : value
372
373 isCurrentItem: (buttonIndex == 1)
374 focus: root.active && isCurrentItem
375
376 editable: true
377 readOnly: !modeIsAdjustable.valid || modeIsAdjustable.value !== 1 || numberOfMultis > 1
378 width: pumpButton.pumpEnabled ? 160 : 173
379 height: 45
380 color: acModeButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8"
381 title: qsTr("AC MODE")
382
383 values: [
384 TileText {
385 text: modeIsAdjustable.valid && numberOfMultis === 1 ? qsTr("%1").arg(acModeButton.texts[acModeButton.shownValue]) : qsTr("NOT AVAILABLE")
386 }
387 ]
388
389 VBusItem { id: mode; bind: Utils.path(vebusPrefix, "/Mode") }
390 VBusItem { id: modeIsAdjustable; bind: Utils.path(vebusPrefix,"/ModeIsAdjustable") }
391
392 Keys.onSpacePressed: edit()
393
394 function edit() {
395 if (!mode.valid)
396 return
397
398 if (numberOfMultis > 1) {
399 toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000)
400 return
401 }
402
403 if (modeIsAdjustable.value === 0) {
404 if (dmc.valid)
405 toast.createToast(noAdjustableByDmc, 5000)
406 if (bms.valid)
407 toast.createToast(noAdjustableByBms, 5000)
408 if (!dmc.valid && !bms.valid)
409 toast.createToast(noAdjustableTextByConfig, 5000)
410 return
411 }
412
413 switch (shownValue) {
414 case 4:
415 applyAnimation2.pendingValue = 3
416 break;
417 case 3:
418 applyAnimation2.pendingValue = 1
419 break;
420 case 1:
421 applyAnimation2.pendingValue = 4
422 break;
423 }
424
425 applyAnimation2.restart()
426 }
427
428 MouseArea {
429 id: acModeButtonMouseArea
430 anchors.fill: parent
431 property bool containsPressed: containsMouse && pressed
432 onClicked: {
433 buttonIndex = 1
434 parent.edit()
435 }
436 }
437
438 Rectangle {
439 id: timerRect2
440 height: 2
441 width: acModeButton.width * 0.8
442 visible: applyAnimation2.running
443 anchors {
444 bottom: parent.bottom; bottomMargin: 5
445 horizontalCenter: parent.horizontalCenter
446 }
447 }
448
449 SequentialAnimation {
450 id: applyAnimation2
451
452 property int pendingValue
453
454 NumberAnimation {
455 target: timerRect2
456 property: "width"
457 from: 0
458 to: acModeButton.width * 0.8
459 duration: 3000
460 }
461
462 ColorAnimation {
463 target: acModeButton
464 property: "color"
465 from: "#A8A8A8"
466 to: "#4789d0"
467 duration: 200
468 }
469
470 ColorAnimation {
471 target: acModeButton
472 property: "color"
473 from: "#4789d0"
474 to: "#A8A8A8"
475 duration: 200
476 }
477 PropertyAction {
478 target: timerRect2
479 property: "width"
480 value: 0
481 }
482
483 ScriptAction { script: mode.setValue(applyAnimation2.pendingValue) }
484
485 PauseAnimation { duration: 1000 }
486 }
487 }
488
489 Tile {
490 id: pumpButton
491
492 anchors.left: acModeButton.right
493 anchors.bottom: parent.bottom
494
495 property variant texts: [ qsTr("AUTO"), qsTr("ON"), qsTr("OFF")]
496 property int value: 0
497 property bool reset: false
498 property bool pumpEnabled: pumpRelay.value === 3
499
500 show: pumpEnabled
501 isCurrentItem: (buttonIndex == 2)
502 focus: root.active && isCurrentItem
503
504 title: qsTr("PUMP")
505 width: show ? 160 : 0
506 height: 45
507 editable: true
508 readOnly: false
509 color: pumpButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8"
510
511 VBusItem { id: pump; bind: Utils.path(settingsBindPreffix, "/Settings/Pump0/Mode") }
512 VBusItem { id: pumpRelay; bind: Utils.path(settingsBindPreffix, "/Settings/Relay/Function") }
513
514 values: [
515 TileText {
516 text: pumpButton.pumpEnabled ? qsTr("%1").arg(pumpButton.texts[pumpButton.value]) : qsTr("DISABLED")
517 }
518 ]
519
520 Keys.onSpacePressed: edit()
521
522 function edit() {
523 if (!pumpEnabled) {
524 toast.createToast(qsTr("Pump functionality is not enabled. To enable it go to the relay settings page and set function to \"Tank pump\""), 5000)
525 return
526 }
527
528 reset = true
529 applyAnimation.restart()
530 reset = false
531
532 if (value < 2)
533 value++
534 else
535 value = 0
536 }
537
538 MouseArea {
539 id: pumpButtonMouseArea
540 property bool containsPressed: containsMouse && pressed
541 anchors.fill: parent
542 onClicked: {
543 buttonIndex = 2
544 parent.edit()
545 }
546 }
547
548 Rectangle {
549 id: timerRect
550 height: 2
551 width: pumpButton.width * 0.8
552 visible: applyAnimation.running
553 anchors {
554 bottom: parent.bottom; bottomMargin: 5
555 horizontalCenter: parent.horizontalCenter
556 }
557 }
558
559 SequentialAnimation {
560 id: applyAnimation
561 alwaysRunToEnd: false
562 NumberAnimation {
563 target: timerRect
564 property: "width"
565 from: 0
566 to: pumpButton.width * 0.8
567 duration: 3000
568 }
569
570 ColorAnimation {
571 target: pumpButton
572 property: "color"
573 from: "#A8A8A8"
574 to: "#4789d0"
575 duration: 200
576 }
577
578 ColorAnimation {
579 target: pumpButton
580 property: "color"
581 from: "#4789d0"
582 to: "#A8A8A8"
583 duration: 200
584 }
585 PropertyAction {
586 target: timerRect
587 property: "width"
588 value: 0
589 }
590 // Do not set value if the animation is restarted by user pressing the button
591 // to move between options
592 onCompleted: if (!pumpButton.reset) pump.setValue(pumpButton.value)
593 }
594 }
595
596 // When new service is found check if is a tank sensor
597 Connections {
598 target: DBusServices
599 onDbusServiceFound: addService(service)
600 }
601
602 function addService(service)
603 {
604 if (service.type === DBusService.DBUS_SERVICE_MULTI) {
605 numberOfMultis++
606 if (vebusPrefix === "")
607 vebusPrefix = service.name;
608 }
609 }
610
611 // Check available services to find tank sesnsors
612 function discoverMulti()
613 {
614 for (var i = 0; i < DBusServices.count; i++) {
615 if (DBusServices.at(i).type === DBusService.DBUS_SERVICE_MULTI) {
616 addService(DBusServices.at(i))
617 }
618 }
619 }
620
621 function notificationText()
622 {
623 if (activeNotifications.length === 0)
624 return qsTr("no alarms")
625
626 var descr = []
627 for (var n = 0; n < activeNotifications.length; n++) {
628 var notification = activeNotifications[n];
629
630 var text = notification.serviceName + " - " + notification.description;
631 if (notification.value !== "" )
632 text += ": " + notification.value
633
634 descr.push(text)
635 }
636
637 return descr.join(" | ")
638 }
639
640 VBusItem { id: dmc; bind: Utils.path(vebusPrefix, "/Devices/Dmc/Version") }
641 VBusItem { id: bms; bind: Utils.path(vebusPrefix, "/Devices/Bms/Version") }
642 }