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