Mercurial > ~darius > hgwebdir.cgi > OverviewJD
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OverviewJD.qml Mon Dec 13 23:05:38 2021 +1030 @@ -0,0 +1,698 @@ +// Modified version of OverviewMobile.qml +import QtQuick 1.1 +import com.victron.velib 1.0 +import "utils.js" as Utils + +OverviewPage { + id: root + + property variant sys: theSystem + property string settingsBindPreffix: "com.victronenergy.settings" + property string pumpBindPreffix: "com.victronenergy.pump.startstop0" + property variant activeNotifications: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + property string noAdjustableByDmc: qsTr("This setting is disabled when a Digital Multi Control " + + "is connected. If it was recently disconnected execute " + + "\"Redetect system\" that is avalible on the inverter menu page.") + property string noAdjustableByBms: qsTr("This setting is disabled when a VE.Bus BMS " + + "is connected. If it was recently disconnected execute " + + "\"Redetect system\" that is avalible on the inverter menu page.") + property string noAdjustableTextByConfig: qsTr("This setting is disabled. " + + "Possible reasons are \"Overruled by remote\" is not enabled or " + + "an assistant is preventing the adjustment. Please, check " + + "the inverter configuration with VEConfigure.") + property int numberOfMultis: 0 + property string vebusPrefix: "" + + // Keeps track of which button on the bottom row is active + property int buttonIndex: 0 + + title: qsTr("Java Drive") + + Component.onCompleted: discoverMulti() + + ListView { + id: pwColumn + + property int tilesCount: solarTile.visible || dcSystem.visible ? 3 : 2 + property int tileHeight: Math.ceil(height / tilesCount) + interactive: false // static tiles + + width: 136 + anchors { + left: parent.left + top: parent.top; + bottom: acModeButton.top; + } + + model: VisualItemModel { + Tile { + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("AC INPUT") + color: "#82acde" + visible: !dcSystem.visible || !solarTile.visible + values: [ + TileText { + text: sys.acInput.power.uiText + font.pixelSize: 25 + }, + TileText { + property VBusItem inV1: VBusItem { bind: Utils.path(sys.vebusPrefix, "/Ac/ActiveIn/L1/V"); unit: "V" } + text: inV1.format(1) + font.pixelSize: 15 + }, + TileText { + property VBusItem inI1: VBusItem { bind: Utils.path(sys.vebusPrefix, "/Ac/ActiveIn/L1/I"); unit: "A" } + text: inI1.format(1) + font.pixelSize: 15 + }, + TileText { + property VBusItem inF1: VBusItem { bind: Utils.path(sys.vebusPrefix, "/Ac/ActiveIn/L1/F"); unit: "Hz" } + text: inF1.format(0) + font.pixelSize: 15 + } + ] + } + + TileAcPower { + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("AC LOADS") + color: "#e68e8a" + values: [ + TileText { + text: sys.acLoad.power.uiText + font.pixelSize: 25 + }, + TileText { + property VBusItem outV1: VBusItem { bind: Utils.path(sys.vebusPrefix, "/Ac/Out/L1/V"); unit: "V" } + text: outV1.format(1) + font.pixelSize: 15 + }, + TileText { + property VBusItem outI1: VBusItem { bind: Utils.path(sys.vebusPrefix, "/Ac/Out/L1/I"); unit: "A" } + text: outI1.format(1) + font.pixelSize: 15 + }, + TileText { + property VBusItem outF1: VBusItem { bind: Utils.path(sys.vebusPrefix, "/Ac/Out/L1/F"); unit: "Hz" } + text: outF1.format(0) + font.pixelSize: 15 + } + ] + } + + Tile { + id: solarTile + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("PV CHARGER") + color: "#2cc36b" + visible : sys.pvCharger.power.valid + + values: [ + TileText { + font.pixelSize: 30 + text: sys.pvCharger.power.uiText + } + ] + } + Tile { + id: dcSystem + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("DC SYSTEM") + color: "#16a085" + visible : hasDcSys.value === 1 + + VBusItem { + id: hasDcSys + bind: Utils.path(settingsBindPreffix, "/Settings/SystemSetup/HasDcSystem") + } + + values: [ + TileText { + font.pixelSize: 30 + text: sys.dcSystem.power.format(0) + }, + TileText { + text: !sys.dcSystem.power.valid ? "---" : + sys.dcSystem.power.value < 0 ? qsTr("to battery") : qsTr("from battery") + } + ] + } + } + } + + Tile { + id: logoTile + + color: "#575748" + height: 120 + anchors { + left: pwColumn.right + right: tanksColum.left + top: parent.top + } + + MbIcon { + x: 1 + y: 1 + // see below, so the svg instead of a png if there is a 1x1 image + visible: customImage.sourceSize.width === 1 && customImage.sourceSize.height === 1 + iconId: "mobile-builder-logo-svg" + } + + // The uploaded png, the default is a 1x1 transparent pixel now. + Image { + id: customImage + source: "image://theme/mobile-builder-logo" + anchors.centerIn: parent + } + } + + Tile { + id: batteryTile + height: 112 + title: qsTr("BATTERY") + anchors { + left: pwColumn.right + right: stateTile.left + top: logoTile.bottom + bottom: acModeButton.top + } + + values: [ + TileText { + text: sys.battery.soc.absFormat(0) + font.pixelSize: 30 + height: 32 + }, + TileText { + text: { + if (!sys.battery.state.valid) + return "---" + switch(sys.battery.state.value) { + case sys.batteryStateIdle: return qsTr("idle") + case sys.batteryStateCharging : return qsTr("charging") + case sys.batteryStateDischarging : return qsTr("discharging") + } + } + }, + TileText { + text: sys.battery.power.absFormat(0) + }, + TileText { + text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1) + } + ] + } + + Tile { + id: stateTile + + width: 104 + title: qsTr("STATUS") + color: "#4789d0" + + anchors { + right: tanksColum.left + top: logoTile.bottom + bottom: acModeButton.top + } + + Timer { + id: wallClock + + running: true + repeat: true + interval: 1000 + triggeredOnStart: true + onTriggered: time = Qt.formatDateTime(new Date(), "hh:mm") + + property string time + } + + values: [ + TileText { + id: systemTile + text: wallClock.time + font.pixelSize: 30 + }, + TileText { + property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" } + property VeQuickItem speed: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Speed") } + property VeQuickItem speedUnit: VeQuickItem { uid: "dbus/com.victronenergy.settings/Settings/Gps/SpeedUnit" } + + text: speed.value === undefined ? "" : getValue() + visible: speed.value !== undefined && speedUnit.value !== undefined + + function getValue() + { + if (speedUnit.value === "km/h") + return (speed.value * 3.6).toFixed(1) + speedUnit.value + if (speedUnit.value === "mph") + return (speed.value * 2.236936).toFixed(1) + speedUnit.value + if (speedUnit.value === "kt") + return (speed.value * (3600/1852)).toFixed(1) + speedUnit.value + return speed.value.toFixed(2) + "m/s" + } + }, + Marquee { + text: notificationText() + width: stateTile.width + interval: 100 + fontSize: 13 + } + ] + } + + ListView { + id: tanksColum + + property int tileHeight: Math.ceil(height / Math.max(count, 2)) + width: 134 + interactive: false // static tiles + model: TankModel { id: tankModel } + delegate: TileTank { + // Without an intermediate assignment this will trigger a binding loop warning. + property variant theService: DBusServices.get(buddy.id) + service: theService + width: tanksColum.width + height: tanksColum.tileHeight + pumpBindPrefix: root.pumpBindPreffix + compact: tankModel.rowCount > (pumpButton.pumpEnabled ? 4 : 5) + Connections { + target: scrollTimer + onTriggered: doScroll() + } + } + + anchors { + top: root.top + bottom: pumpButton.pumpEnabled ? acModeButton.top : acModeButton.bottom + right: root.right + } + + // Synchronise tank name text scroll start + Timer { + id: scrollTimer + interval: 15000 + repeat: true + running: root.active && tankModel.rowCount > 4 + } + + Tile { + title: qsTr("TANKS") + anchors.fill: parent + values: TileText { + text: qsTr("No tanks found") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + + Keys.forwardTo: [keyHandler] + + Item { + id: keyHandler + Keys.onLeftPressed: { + if (buttonIndex > 0) + buttonIndex-- + + event.accepted = true + } + + Keys.onRightPressed: { + if (buttonIndex < (pumpButton.pumpEnabled ? 3 : 2)) + buttonIndex++ + + event.accepted = true + } + } + + MouseArea { + anchors.fill: parent + enabled: parent.active + onPressed: mouse.accepted = acCurrentButton.expanded + onClicked: acCurrentButton.cancel() + } + + TileSpinBox { + id: acCurrentButton + + anchors.bottom: parent.bottom + anchors.left: parent.left + isCurrentItem: (buttonIndex == 0) + focus: root.active && isCurrentItem + + bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimit") + title: qsTr("AC CURRENT LIMIT") + color: containsMouse && !editMode ? "#d3d3d3" : "#A8A8A8" + width: pumpButton.pumpEnabled ? 160 : 173 + fontPixelSize: 14 + unit: "A" + readOnly: currentLimitIsAdjustable.value !== 1 || numberOfMultis > 1 + buttonColor: "#979797" + + VBusItem { id: currentLimitIsAdjustable; bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimitIsAdjustable") } + + Keys.onSpacePressed: showErrorToast(event) + + function editIsAllowed() { + if (numberOfMultis > 1) { + toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000) + return false + } + + if (currentLimitIsAdjustable.value === 0) { + if (dmc.valid) { + toast.createToast(noAdjustableByDmc, 5000) + return false + } + if (bms.valid) { + toast.createToast(noAdjustableByBms, 5000) + return false + } + if (!dmc.valid && !bms.valid) { + toast.createToast(noAdjustableTextByConfig, 5000) + return false + } + } + + return true + } + + function showErrorToast(event) { + editIsAllowed() + event.accepted = true + } + } + + Tile { + id: acModeButton + anchors.left: acCurrentButton.right + anchors.bottom: parent.bottom + property variant texts: { 4: qsTr("OFF"), 3: qsTr("ON"), 1: qsTr("CHARGER ONLY") } + property int value: mode.valid ? mode.value : 3 + property int shownValue: applyAnimation2.running ? applyAnimation2.pendingValue : value + + isCurrentItem: (buttonIndex == 1) + focus: root.active && isCurrentItem + + editable: true + readOnly: !modeIsAdjustable.valid || modeIsAdjustable.value !== 1 || numberOfMultis > 1 + width: pumpButton.pumpEnabled ? 160 : 173 + height: 45 + color: acModeButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8" + title: qsTr("AC MODE") + + values: [ + TileText { + text: modeIsAdjustable.valid && numberOfMultis === 1 ? qsTr("%1").arg(acModeButton.texts[acModeButton.shownValue]) : qsTr("NOT AVAILABLE") + } + ] + + VBusItem { id: mode; bind: Utils.path(vebusPrefix, "/Mode") } + VBusItem { id: modeIsAdjustable; bind: Utils.path(vebusPrefix,"/ModeIsAdjustable") } + + Keys.onSpacePressed: edit() + + function edit() { + if (!mode.valid) + return + + if (numberOfMultis > 1) { + toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000) + return + } + + if (modeIsAdjustable.value === 0) { + if (dmc.valid) + toast.createToast(noAdjustableByDmc, 5000) + if (bms.valid) + toast.createToast(noAdjustableByBms, 5000) + if (!dmc.valid && !bms.valid) + toast.createToast(noAdjustableTextByConfig, 5000) + return + } + + switch (shownValue) { + case 4: + applyAnimation2.pendingValue = 3 + break; + case 3: + applyAnimation2.pendingValue = 1 + break; + case 1: + applyAnimation2.pendingValue = 4 + break; + } + + applyAnimation2.restart() + } + + MouseArea { + id: acModeButtonMouseArea + anchors.fill: parent + property bool containsPressed: containsMouse && pressed + onClicked: { + buttonIndex = 1 + parent.edit() + } + } + + Rectangle { + id: timerRect2 + height: 2 + width: acModeButton.width * 0.8 + visible: applyAnimation2.running + anchors { + bottom: parent.bottom; bottomMargin: 5 + horizontalCenter: parent.horizontalCenter + } + } + + SequentialAnimation { + id: applyAnimation2 + + property int pendingValue + + NumberAnimation { + target: timerRect2 + property: "width" + from: 0 + to: acModeButton.width * 0.8 + duration: 3000 + } + + ColorAnimation { + target: acModeButton + property: "color" + from: "#A8A8A8" + to: "#4789d0" + duration: 200 + } + + ColorAnimation { + target: acModeButton + property: "color" + from: "#4789d0" + to: "#A8A8A8" + duration: 200 + } + PropertyAction { + target: timerRect2 + property: "width" + value: 0 + } + + ScriptAction { script: mode.setValue(applyAnimation2.pendingValue) } + + PauseAnimation { duration: 1000 } + } + } + + TileSpinBox { + id: battCurrentButton + + anchors.bottom: parent.bottom + anchors.left: acModeButton.right + isCurrentItem: (buttonIndex == 2) + focus: root.active && isCurrentItem + + bind: Utils.path(vebusPrefix, "/Dc/0/MaxChargeCurrent") + title: qsTr("BAT CURR LIMIT") + color: containsMouse && !editMode ? "#d3d3d3" : "#A8A8A8" + width: 134 + fontPixelSize: 14 + unit: "A" + readOnly: false + editable: true + buttonColor: "#979797" + + Keys.onSpacePressed: showErrorToast(event) + + function showErrorToast(event) { + editIsAllowed() + event.accepted = true + } + } + + Tile { + id: pumpButton + + anchors.left: battCurrentButton.right + anchors.bottom: parent.bottom + + property variant texts: [ qsTr("AUTO"), qsTr("ON"), qsTr("OFF")] + property int value: 0 + property bool reset: false + property bool pumpEnabled: pumpRelay.value === 3 + + show: pumpEnabled + isCurrentItem: (buttonIndex == 3) + focus: root.active && isCurrentItem + + title: qsTr("PUMP") + width: show ? 160 : 0 + height: 45 + editable: true + readOnly: false + color: pumpButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8" + + VBusItem { id: pump; bind: Utils.path(settingsBindPreffix, "/Settings/Pump0/Mode") } + VBusItem { id: pumpRelay; bind: Utils.path(settingsBindPreffix, "/Settings/Relay/Function") } + + values: [ + TileText { + text: pumpButton.pumpEnabled ? qsTr("%1").arg(pumpButton.texts[pumpButton.value]) : qsTr("DISABLED") + } + ] + + Keys.onSpacePressed: edit() + + function edit() { + if (!pumpEnabled) { + toast.createToast(qsTr("Pump functionality is not enabled. To enable it go to the relay settings page and set function to \"Tank pump\""), 5000) + return + } + + reset = true + applyAnimation.restart() + reset = false + + if (value < 2) + value++ + else + value = 0 + } + + MouseArea { + id: pumpButtonMouseArea + property bool containsPressed: containsMouse && pressed + anchors.fill: parent + onClicked: { + buttonIndex = 2 + parent.edit() + } + } + + Rectangle { + id: timerRect + height: 2 + width: pumpButton.width * 0.8 + visible: applyAnimation.running + anchors { + bottom: parent.bottom; bottomMargin: 5 + horizontalCenter: parent.horizontalCenter + } + } + + SequentialAnimation { + id: applyAnimation + alwaysRunToEnd: false + NumberAnimation { + target: timerRect + property: "width" + from: 0 + to: pumpButton.width * 0.8 + duration: 3000 + } + + ColorAnimation { + target: pumpButton + property: "color" + from: "#A8A8A8" + to: "#4789d0" + duration: 200 + } + + ColorAnimation { + target: pumpButton + property: "color" + from: "#4789d0" + to: "#A8A8A8" + duration: 200 + } + PropertyAction { + target: timerRect + property: "width" + value: 0 + } + // Do not set value if the animation is restarted by user pressing the button + // to move between options + onCompleted: if (!pumpButton.reset) pump.setValue(pumpButton.value) + } + } + + // When new service is found check if is a tank sensor + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } + + function addService(service) + { + if (service.type === DBusService.DBUS_SERVICE_MULTI) { + numberOfMultis++ + if (vebusPrefix === "") + vebusPrefix = service.name; + } + } + + // Check available services to find tank sesnsors + function discoverMulti() + { + for (var i = 0; i < DBusServices.count; i++) { + if (DBusServices.at(i).type === DBusService.DBUS_SERVICE_MULTI) { + addService(DBusServices.at(i)) + } + } + } + + function notificationText() + { + if (activeNotifications.length === 0) + return qsTr("no alarms") + + var descr = [] + for (var n = 0; n < activeNotifications.length; n++) { + var notification = activeNotifications[n]; + + var text = notification.serviceName + " - " + notification.description; + if (notification.value !== "" ) + text += ": " + notification.value + + descr.push(text) + } + + return descr.join(" | ") + } + + VBusItem { id: dmc; bind: Utils.path(vebusPrefix, "/Devices/Dmc/Version") } + VBusItem { id: bms; bind: Utils.path(vebusPrefix, "/Devices/Bms/Version") } +}