diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e43b0f98 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/FileSets/OverviewMobileEnhanced.qml b/FileSets/OverviewMobileEnhanced.qml new file mode 100644 index 00000000..4e88e86a --- /dev/null +++ b/FileSets/OverviewMobileEnhanced.qml @@ -0,0 +1,744 @@ +// Enhancements to OverviewMobile screen + +// This version supports Venus versions 2.4, 2.5 and 2.60 +// Removed logo and added AC INPUT and SYSTEM tiles originally displayed on other overviews +// Added voltage, current and frequency to AC INPUT and AC LOADS tiles +// Rearranged tiles to match a left to right signal flow : sources on left, loads on right +// Standardized "info" tile sizes to 1 or 1.5 wide x 1 or 2 high +// infoArea defines usable space for info tiles and all tiles are a child of infoArea +// (makes repositioning easier than when they were in separate column objects) +// Large text for main paremeter in each tile has been reduced in size to allow more parameters without +// expanding tile height (30 to 22) +// merged SYSTEM and STATUS tiles +// removed speed from STATUS to reduce tile height +// hide "reason" text if it's blank to save space +// changed clock to 12-hour format +// Capitialized battery state: "Idle", "Charging", "Discharging" +// errors and notificaitons in SYSTEM/STATUS tile may push clock off bottom of tile +// Tile content for items that are not present are made invisible - tile remains in place +// that is no height adjustments when a tile provides no information +// Adjust button widths so that pump button fits within tank column +// Hide pump button when not enabled giving more room for tanks + +// Includes changes to handle SeeLevel NMEA2000 tank sensor: +// Ignore the real incoming tank dBus service because it's information changes +// Changes in TileText.qml are also part of the TankRepeater package + +// Search for //////// TANK REPEATER to find changes + +import QtQuick 1.1 +import com.victron.velib 1.0 +import "utils.js" as Utils + +OverviewPage { + title: qsTr("Mobile") + 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 + +//////// add for system state + property bool hasSystemState: _systemState.valid + +//////// add for SYSTEM tile and voltage, power and frequency values + property string systemPrefix: "com.victronenergy.system" + property VBusItem _systemState: VBusItem { bind: Utils.path(systemPrefix, "/SystemState/State") } +//////// add for PV CHARGER voltage and current + property string pvChargerPrefix: "" + property int numberOfPvChargers: 0 + + + //////// standard tile sizes + //////// positions are left, center, right and top, center, bottom of infoArea + property int infoTilesCount: 3 + property int buttonRowHeight: 45 // must match TileSpinBox height used on AC CURRENT LIMIT + property int tankWidth: 130 + + property int infoTileHeight: Math.ceil((height - buttonRowHeight) / infoTilesCount) + property int infoTile2High: (infoTileHeight * 2) - 1 + + property int infoWidth: width - tankWidth + property int infoWidth3Column: infoWidth / 3 + property int infoWidth2Column: infoWidth / 2 + + Component.onCompleted: discoverTanks() + + ListView { + id: infoArea + interactive: false // static tiles + + anchors { + left: parent.left + right: tanksColum.left + top: parent.top; + bottom: acModeButton.top; + } + +//////// copied SYSTEM from OverviewTiles.qml & combined SYSTEM and STATUS tiles + Tile { + title: qsTr("STATUS") + id: statusTile + anchors { left: parent.left; top: parent.top } + width: root.infoWidth3Column + height: root.infoTile2High + color: "#4789d0" + + VBusItem{ + id: systemName + bind: Utils.path(settingsBindPreffix, "/Settings/SystemSetup/SystemName") + } + + Timer { + id: wallClock + + running: true + repeat: true + interval: 1000 + triggeredOnStart: true +//////// change time to 12 hour format + onTriggered: time = Qt.formatDateTime(new Date(), "h:mm ap") + + property string time + } +//////// relorder to give priority to errors + values: [ + TileText { + text: systemName.valid && systemName.value !== "" ? systemName.value : sys.systemType.valid ? sys.systemType.value.toUpperCase() : "" + font.pixelSize: 20 + wrapMode: Text.WordWrap + width: statusTile.width + }, + TileText { + text: wallClock.time + font.pixelSize: 20 + }, +//////// spacer to separate Multi mode from system name + TileText { + text: " " + font.pixelSize: 4 + }, + TileText { + text: qsTr(systemState.text) + + SystemState { + id: systemState + bind: hasSystemState?Utils.path(systemPrefix, "/SystemState/State"):Utils.path(sys.vebusPrefix, "/State") + } + }, + +//////// combine SystemReason with notifications + Marquee { + text: + { + if (activeNotifications.length === 0) + return systemReasonMessage.text + else + return notificationText() + " || " + systemReasonMessage.text + } + width: statusTile.width + interval: 100 + SystemReasonMessage { + id: systemReasonMessage + } + } +//////// remove speed to make more room + ] + } // end Tile STATUS + + Tile { + title: qsTr("BATTERY") + id: batteryTile + anchors { horizontalCenter: parent.horizontalCenter; top: parent.top } + width: root.infoWidth3Column + height: root.infoTile2High + + values: [ + TileText { + text: sys.battery.soc.absFormat(0) + font.pixelSize: 22 +//////// remove height (for consistency with other tiles) + }, + TileText { + text: { + if (!sys.battery.state.valid) + return "---" + switch(sys.battery.state.value) { +//////// change - capitalized words look better + case sys.batteryStateIdle: return qsTr("Idle") + case sys.batteryStateCharging : return qsTr("Charging") + case sys.batteryStateDischarging : return qsTr("Discharging") + } + } + }, + TileText { +//////// change to show negative for battery drain + text: sys.battery.power.text + }, + TileText { + text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1) + }, + TileText { + text: qsTr("Remaining:") + }, + TileText { + text: { + if (timeToGo.valid) + return Utils.secondsToString(timeToGo.value) + else + return "> 10d" + } + + VBusItem { + id: timeToGo + bind: Utils.path("com.victronenergy.system","/Dc/Battery/TimeToGo") + } + } + ] + } // end Tile BATTERY + + Tile { + title: qsTr("DC SYSTEM") + id: dcSystem + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + width: root.infoWidth3Column + height: root.infoTileHeight + color: "#16a085" + + VBusItem { + id: hasDcSys + bind: Utils.path(settingsBindPreffix, "/Settings/SystemSetup/HasDcSystem") + } + + values: [ + TileText { + font.pixelSize: 22 + text: hasDcSys.value === 1 ? sys.dcSystem.power.format(0) : "none" + }, + TileText { + text: !sys.dcSystem.power.valid ? "---" : + sys.dcSystem.power.value < 0 ? qsTr("to battery") : qsTr("from battery") + visible: hasDcSys.value === 1 + } + ] + } // end Tile DC SYSTEM + + Tile { + id: solarTile + title: qsTr("PV CHARGER") + anchors { right: parent.right; top: parent.top } + width: root.infoWidth3Column + height: root.infoTileHeight + color: "#2cc36b" +//////// add voltage and current + VBusItem { id: pvCurrent; bind: Utils.path(pvChargerPrefix, "/Pv/I") } + VBusItem { id: pvVoltage; bind: Utils.path(pvChargerPrefix, "/Pv/V") } + values: [ + TileText { + font.pixelSize: 22 + text: sys.pvCharger.power.valid ? sys.pvCharger.power.uiText : "none" + }, +//////// add voltage and current + TileText { + text: numberOfPvChargers === 1 ? pvVoltage.text + " " + pvCurrent.text : "" + visible: sys.pvCharger.power.valid + } + ] + } // end Tile PV CHARGER + +//////// add AC INPUT tile + Tile { + title: qsTr("AC INPUT") + id: acInputTile + anchors { left: parent.left; bottom: parent.bottom } + width: root.infoWidth2Column + height: root.infoTileHeight + color: "#82acde" +//////// add voltage and current + VBusItem { id: inVoltage; bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/L1/V") } + VBusItem { id: inCurrent; bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/L1/I") } + VBusItem { id: inFrequency; bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/L1/F") } + values: [ + TileText { + text: sys.acInput.power.uiText + font.pixelSize: 22 + }, +//////// add voltage and current + TileText { + text: inVoltage.text + " " + inCurrent.text + " " + inFrequency.text + } + ] + } + + Tile { + title: qsTr("AC LOADS") + id: acLoadsTile + anchors { right: parent.right; bottom: parent.bottom} + width: root.infoWidth2Column + height: root.infoTileHeight + color: "#e68e8a" +//////// add voltage and current + VBusItem { id: outVoltage; bind: Utils.path(vebusPrefix, "/Ac/Out/L1/V") } + VBusItem { id: outCurrent; bind: Utils.path(vebusPrefix, "/Ac/Out/L1/I") } + VBusItem { id: outFrequency; bind: Utils.path(vebusPrefix, "/Ac/Out/L1/F") } + + values: [ + TileText { + text: sys.acLoad.power.uiText + font.pixelSize: 22 + }, +//////// add voltage and current + TileText { + text: outVoltage.text + " " + outCurrent.text + " " + outFrequency.text + } + ] + } + + } // end ListView infoArea + + ListView { + id: tanksColum + + property int tankTileHeight: Math.ceil(height / Math.max(count, 2)) +//////// change - group layout parameters in one place + width: root.tankWidth + interactive: false // static tiles + + model: tanksModel + delegate: TileTank { + width: tanksColum.width + height: tanksColum.tankTileHeight + pumpBindPrefix: root.pumpBindPreffix + } + + anchors { + top: root.top + bottom: pumpButton.pumpEnabled ? acModeButton.top : acModeButton.bottom + right: root.right + } + + Tile { + title: qsTr("TANKS") + anchors.fill: parent + values: TileText { + text: qsTr("No tanks found") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + + ListModel { + id: tanksModel + } + + Keys.forwardTo: [keyHandler] + + Item { + id: keyHandler + Keys.onLeftPressed: { + if (buttonIndex > 0) + buttonIndex-- + + event.accepted = true + } + + Keys.onRightPressed: { + var lastButton = pumpButton.pumpEnabled ? 2 : 1 + ++buttonIndex + if (buttonIndex > lastButton) + buttonIndex = lastButton + + event.accepted = true + } + } + + MouseArea { + anchors.fill: parent + enabled: parent.active + onPressed: mouse.accepted = acCurrentButton.expanded + onClicked: acCurrentButton.cancel() + } + + TileSpinBox { + title: qsTr("AC CURRENT LIMIT") + id: acCurrentButton + + anchors.bottom: parent.bottom + anchors.left: parent.left + isCurrentItem: (buttonIndex == 0) + focus: root.active && isCurrentItem + + bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimit") + color: containsMouse && !editMode ? "#d3d3d3" : "#A8A8A8" + width: show ? root.infoWidth2Column : 0 + 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"), 2: qsTr("INVERTER 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: root.infoWidth2Column + height: buttonRowHeight + 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: + //////// modify to add inverter only (was = 4) + applyAnimation2.pendingValue = 2 + break; +//////// add case 2 (inverter only) + case 2: + 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 + } + onCompleted: if (!acModeButton.reset) mode.setValue(acModeButton.value) + + ScriptAction { script: mode.setValue(applyAnimation2.pendingValue) } + + PauseAnimation { duration: 1000 } + } + } + + Tile { + id: pumpButton + + anchors.left: acModeButton.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 + isCurrentItem: (buttonIndex == 2) + focus: root.active && isCurrentItem + + title: qsTr("PUMP") + width: show && pumpEnabled ? root.tankWidth : 0 + height: buttonRowHeight + 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) + { + var name = service.name + if (service.type === DBusService.DBUS_SERVICE_TANK) { +//////// TANK REPEATER - add to hide the service for the physical sensor + if (name !== incomingTankServiceName) // hide incoming N2K tank dBus object +//////// TANK REPEATER - end add + tanksModel.append({serviceName: service.name}) + } + if (service.type === DBusService.DBUS_SERVICE_MULTI) { + numberOfMultis++ + if (vebusPrefix === "") + vebusPrefix = name; + } +//////// add for PV CHARGER voltage and current display + if (service.type === DBusService.DBUS_SERVICE_SOLAR_CHARGER) { + numberOfPvChargers++ + if (pvChargerPrefix === "") + pvChargerPrefix = name; + } + } + + // Check available services to find tank sesnsors +//////// also adds info for Muitls and PV Chargers + function discoverTanks() + { +//////// TANK REPEATER - add this + incomingTankServiceName = incomingTankName.valid ? incomingTankName.value : "" +//////// TANK REPEATER - end add this + tanksModel.clear() + for (var i = 0; i < DBusServices.count; i++) { + if (DBusServices.at(i).type === DBusService.DBUS_SERVICE_TANK) { + addService(DBusServices.at(i)) + } + if (DBusServices.at(i).type === DBusService.DBUS_SERVICE_MULTI) { + addService(DBusServices.at(i)) + } +//////// add for PV CHARGER voltage and current display + if (DBusServices.at(i).type === DBusService.DBUS_SERVICE_SOLAR_CHARGER) { + addService(DBusServices.at(i)) + } + } + } + + function notificationText() + { + if (activeNotifications.length === 0) + return qsTr("") + + 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") } + +//////// TANK REPEATER - add to hide the service for the physical sensor + property string incomingTankServiceName: "" + VBusItem { id: incomingTankName; + bind: Utils.path(settingsBindPreffix, "/Settings/Devices/TankRepeater/IncomingTankService") } +//////// TANK REPEATER - end add +} diff --git a/FileSets/SystemReasonMessage.qml b/FileSets/SystemReasonMessage.qml new file mode 100644 index 00000000..8750e4d7 --- /dev/null +++ b/FileSets/SystemReasonMessage.qml @@ -0,0 +1,49 @@ +import QtQuick 1.1 +import com.victron.velib 1.0 +import "utils.js" as Utils + +Item { + id: root + + property string text: getReasonText() + property variant flags: getFlags() + property string systemPrefix: "com.victronenergy.system" + + // Flags to monitor + property list flagItems: [ + VBusItem { bind: Utils.path(systemPrefix, "/SystemState/LowSoc") }, +// VBusItem { bind: Utils.path(systemPrefix, "/SystemState/BatteryLife") }, + VBusItem { bind: Utils.path(systemPrefix, "/SystemState/ChargeDisabled") }, + VBusItem { bind: Utils.path(systemPrefix, "/SystemState/DischargeDisabled") }, + VBusItem { bind: Utils.path(systemPrefix, "/SystemState/SlowCharge") }, + VBusItem { bind: Utils.path(systemPrefix, "/SystemState/UserChargeLimited") }, + VBusItem { bind: Utils.path(systemPrefix, "/SystemState/UserDischargeLimited") } + ] + + VBusItem { + id: multiStatusReason + } + + function getFlags(){ + var r = []; + var reasonMessage = + [ + "Low SOC", +// "Battery Life", + "Charge Off", + "Disch Off", + "Slow Charge", + "Charge Limited", + "Disch Limited" + ] + for (var i=0; i 0 ? active.length : "" + } + subpage: Component { PageNotifications {} } + } + } + + Component { + id: submenuLoader + MbDevice { + iconId: "icon-toolbar-enter" + } + } + + Component { + id: vebusPage + PageVebus {} + } + + Component { + id: batteryPage + PageBattery {} + } + + Component { + id: solarChargerPage + PageSolarCharger {} + } + + Component { + id: acInPage + PageAcIn {} + } + + Component { + id: acChargerPage + PageAcCharger {} + } + + Component { + id: tankPage + PageTankSensor {} + } + + Component { + id: motorDrivePage + PageMotorDrive {} + } + + Component { + id: inverterPage + PageInverter {} + } + + Component { + id: pulseCounterPage + PagePulseCounter {} + } + + Component { + id: digitalInputPage + PageDigitalInput {} + } + + Component { + id: temperatureSensorPage + PageTemperatureSensor {} + } + + Component { + id: unsupportedDevicePage + PageUnsupportedDevice {} + } + + Component { + id: meteoDevicePage + PageMeteo {} + } + + function addService(service) + { + var name = service.name + + var page + switch(service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + page = vebusPage + break; + case DBusService.DBUS_SERVICE_BATTERY: + page = batteryPage + break; + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + page = solarChargerPage + break; + case DBusService.DBUS_SERVICE_PV_INVERTER: + page = acInPage + break; + case DBusService.DBUS_SERVICE_AC_CHARGER: + page = acChargerPage + break; + case DBusService.DBUS_SERVICE_TANK: + page = tankPage + break; + case DBusService.DBUS_SERVICE_GRIDMETER: + page = acInPage + break + case DBusService.DBUS_SERVICE_GENSET: + page = acInPage + break + case DBusService.DBUS_SERVICE_MOTOR_DRIVE: + page = motorDrivePage + break + case DBusService.DBUS_SERVICE_INVERTER: + page = inverterPage + break; + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + page = temperatureSensorPage + break; + case DBusService.DBUS_SERVICE_SYSTEM_CALC: + return; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + page = digitalInputPage + break; + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + page = pulseCounterPage + break; + case DBusService.DBUS_SERVICE_UNSUPPORTED: + page = unsupportedDevicePage + break; + case DBusService.DBUS_SERVICE_METEO: + page = meteoDevicePage + break; + case DBusService.DBUS_SERVICE_VECAN: + return; + default: + console.log("unknown service " + name) + return; + } + + var submenu = submenuLoader.createObject(root) + submenu.service = service + + // option 1, load when being opened + // submenu.subpage = page + // submenu.subpageProperties = {service: service} + + // option 2, create it now + submenu.subpage = page.createObject(submenu, {service: service, bindPrefix: service.name}) + + // sort on (initial) description + var i = 0 +// MODIFIED leave Settings and Notifications at top of list (don't sort first 2 entries) + for (i = 2; i < model.count - 2; i++ ) { + if (model.children[i].description.localeCompare(service.description) > 0) + break; + } + + model.insert(i, submenu) + } + + Component.onCompleted: { + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + listview.currentIndex = 0 + } + + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } +} diff --git a/FileSets/v2.40/PageMain.qml.orig b/FileSets/v2.40/PageMain.qml.orig new file mode 100644 index 00000000..860e228f --- /dev/null +++ b/FileSets/v2.40/PageMain.qml.orig @@ -0,0 +1,189 @@ +import QtQuick 1.1 +import com.victron.velib 1.0 + +MbPage { + id: root + title: qsTr("Device List") + + model: VisualItemModel { + MbSubMenu { + id: menuNotifications + description: qsTr("Notifications") + item: VBusItem { + property variant active: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + value: active.length > 0 ? active.length : "" + } + subpage: Component { PageNotifications {} } + } + + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } + } + } + + Component { + id: submenuLoader + MbDevice { + iconId: "icon-toolbar-enter" + } + } + + Component { + id: vebusPage + PageVebus {} + } + + Component { + id: batteryPage + PageBattery {} + } + + Component { + id: solarChargerPage + PageSolarCharger {} + } + + Component { + id: acInPage + PageAcIn {} + } + + Component { + id: acChargerPage + PageAcCharger {} + } + + Component { + id: tankPage + PageTankSensor {} + } + + Component { + id: motorDrivePage + PageMotorDrive {} + } + + Component { + id: inverterPage + PageInverter {} + } + + Component { + id: pulseCounterPage + PagePulseCounter {} + } + + Component { + id: digitalInputPage + PageDigitalInput {} + } + + Component { + id: temperatureSensorPage + PageTemperatureSensor {} + } + + Component { + id: unsupportedDevicePage + PageUnsupportedDevice {} + } + + Component { + id: meteoDevicePage + PageMeteo {} + } + + function addService(service) + { + var name = service.name + + var page + switch(service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + page = vebusPage + break; + case DBusService.DBUS_SERVICE_BATTERY: + page = batteryPage + break; + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + page = solarChargerPage + break; + case DBusService.DBUS_SERVICE_PV_INVERTER: + page = acInPage + break; + case DBusService.DBUS_SERVICE_AC_CHARGER: + page = acChargerPage + break; + case DBusService.DBUS_SERVICE_TANK: + page = tankPage + break; + case DBusService.DBUS_SERVICE_GRIDMETER: + page = acInPage + break + case DBusService.DBUS_SERVICE_GENSET: + page = acInPage + break + case DBusService.DBUS_SERVICE_MOTOR_DRIVE: + page = motorDrivePage + break + case DBusService.DBUS_SERVICE_INVERTER: + page = inverterPage + break; + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + page = temperatureSensorPage + break; + case DBusService.DBUS_SERVICE_SYSTEM_CALC: + return; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + page = digitalInputPage + break; + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + page = pulseCounterPage + break; + case DBusService.DBUS_SERVICE_UNSUPPORTED: + page = unsupportedDevicePage + break; + case DBusService.DBUS_SERVICE_METEO: + page = meteoDevicePage + break; + case DBusService.DBUS_SERVICE_VECAN: + return; + default: + console.log("unknown service " + name) + return; + } + + var submenu = submenuLoader.createObject(root) + submenu.service = service + + // option 1, load when being opened + // submenu.subpage = page + // submenu.subpageProperties = {service: service} + + // option 2, create it now + submenu.subpage = page.createObject(submenu, {service: service, bindPrefix: service.name}) + + // sort on (initial) description + var i = 0 + for (i = 0; i < model.count - 2; i++ ) { + if (model.children[i].description.localeCompare(service.description) > 0) + break; + } + + model.insert(i, submenu) + } + + Component.onCompleted: { + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + listview.currentIndex = 0 + } + + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } +} diff --git a/FileSets/v2.40/main.qml b/FileSets/v2.40/main.qml new file mode 100644 index 00000000..b237612e --- /dev/null +++ b/FileSets/v2.40/main.qml @@ -0,0 +1,365 @@ +//// Modified to hide the OverviewTiles page +//// Search for MODIFIED to find changes + +import QtQuick 1.1 + +import Qt.labs.components.native 1.0 +import com.victron.velib 1.0 + +PageStackWindow { + id: rootWindow + + gpsConnected: gpsConnected.value === 1 + onCompletedChanged: checkAlarm() + initialPage: PageMain {} + + property bool completed: false + property bool showAlert: NotificationCenter.alert + property bool alarm: NotificationCenter.alarm + property bool overviewsLoaded: defaultOverview.valid && generatorOverview.valid && mobileOverview.valid && startWithMenu.valid + property string bindPrefix: "com.victronenergy.settings" + + property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") + property bool isOverviewPage: pageStack.currentPage && pageStack.currentPage.model === overviewModel; + property bool isOfflineFwUpdatePage: pageStack.currentPage && pageStack.currentPage.objectName === "offlineFwUpdatePage"; + + + property string hubOverviewType: theSystem.systemType.valid ? + withoutGridMeter.value === 1 ? "Hub" : theSystem.systemType.value : "" + + // Keep track of the current view (menu/overview) to show as default next time the + // CCGX is restarted + onIsOverviewPageChanged: startWithMenu.setValue(isOverviewPage ? 0 : 1) + + // Add the correct OverviewHub page + onHubOverviewTypeChanged: { + switch(hubOverviewType){ + case "Hub": + case "Hub-1": + case "Hub-2": + case "Hub-3": + replaceOverview("OverviewGridParallel.qml", "OverviewHub.qml"); + break; + case "Hub-4": + case "ESS": + replaceOverview("OverviewHub.qml", "OverviewGridParallel.qml"); + break; + default: + break; + } + // Workaround the QTBUG-17012 (only the first sentence in each case of Switch Statement can be executed) + // by adding a return statement + return + } + + VBusItem { + id: generatorOverview + bind: "com.victronenergy.settings/Settings/Relay/Function" + onValueChanged: extraOverview("OverviewGeneratorRelay.qml", value === 1) + } + + VBusItem { + id: fischerPandaGenOverview + bind: "com.victronenergy.settings/Settings/Services/FischerPandaAutoStartStop" + onValueChanged: extraOverview("OverviewGeneratorFp.qml", value === 1) + } + + VBusItem { + id: mobileOverview + bind: "com.victronenergy.settings/Settings/Gui/MobileOverview" + onValueChanged:{ + extraOverview("OverviewMobile.qml", value === 1) + } + } + + VBusItem { + id: startWithMenu + bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView" + } + + VBusItem { + id: withoutGridMeter + bind: "com.victronenergy.settings/Settings/CGwacs/RunWithoutGridMeter" + } + + + VBusItem { + id: defaultOverview + bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" + } + + // Note: finding a firmware image on the storage device is error 4 for vrm storage + // since it should not be used for logging. That fact is used here to determine if + // there is a firmware image. + Connections { + target: storageEvents + onVrmStorageError: { + if (error === 4) { + setTopPage(offlineFwUpdates) + } + } + } + + onAlarmChanged: { + if (completed) + checkAlarm() + } + + // always keep track of system information + HubData { + id: theSystem + } + + VBusItem { + id: gpsConnected + bind: "com.victronenergy.gps/Fix" + } + + // note: used for leaving the overviews as well + function backToMainMenu() + { + pageStack.pop(initialPage); + } + + Toast { + id: toast + transform: Scale { + xScale: screen.scaleX + yScale: screen.scaleY + origin.x: toast.width / 2 + origin.y: toast.height / 2 + } + } + + SignalToaster {} + + ToolBarLayout { + id: mbTools + height: parent.height + + Item { + anchors.verticalCenter: parent.verticalCenter + anchors.left: mbTools.left + height: mbTools.height + width: 200 + + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage && pageStack.currentPage.toolbarHandler) + pageStack.currentPage.toolbarHandler.leftAction() + else + showOverview() + } + } + + Row { + anchors.centerIn: parent + + MbIcon { + anchors.verticalCenter: parent.verticalCenter + iconId: pageStack.currentPage ? pageStack.currentPage.leftIcon : "" + } + + Text { + anchors.verticalCenter: parent.verticalCenter + text: pageStack.currentPage ? pageStack.currentPage.leftText : "" + color: "white" + font.bold: true + font.pixelSize: 16 + } + } + } + + MbIcon { + id: centerScrollIndicator + + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: mbTools.verticalCenter + } + iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" + } + + Item { + anchors.verticalCenter: parent.verticalCenter + height: mbTools.height + anchors.right: mbTools.right + width: 200 + + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage && pageStack.currentPage.toolbarHandler) + pageStack.currentPage.toolbarHandler.rightAction() + else + backToMainMenu() + } + } + + Row { + anchors.centerIn: parent + + MbIcon { + iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: pageStack.currentPage ? pageStack.currentPage.rightText : "" + anchors.verticalCenter: parent.verticalCenter + color: "white" + font.bold: true + font.pixelSize: 16 + } + } + } + } + + Component.onCompleted: { + completed = true + } + + ListModel { + id: overviewModel + ListElement { + pageSource: "OverviewHub.qml" + } +//// MODIFIED (commented out) +// ListElement { +// pageSource: "OverviewTiles.qml" +// } + } + + Component { + id: overviewComponent + PageFlow { + // Display default overview when loaded + defaultIndex: getDefaultOverviewIndex() + // Store the current overview page as default + onCurrentIndexChanged: if (active) defaultOverview.setValue(overviewModel.get(currentIndex).pageSource.replace(".qml", "")) + model: overviewModel + } + } + + // When all the related settings items are valid, show the overview page if was the last oppened page + // before restarting + Timer { + interval: 2000 + running: completed && overviewsLoaded && startWithMenu.valid + onTriggered: if (startWithMenu.value === 0) showOverview() + } + + function getDefaultOverviewIndex() + { + if(!defaultOverview.valid) + return 0 + for (var i = 0; i < overviewModel.count; i++){ + if (overviewModel.get(i).pageSource.replace(".qml", "") === defaultOverview.value) { + return i + } + } + return 0 + } + + Component { + id: noticationsComponent + PageNotifications {} + } + + Component { + id: offlineFwUpdates + PageSettingsFirmwareOffline { checkOnCompleted: true} + + } + + // Add or remove extra overviews. for example, generator overview + // shouldn't be shown if the start/stop functionality is not enabled. + // Index parameter is optional, usefull to keep an order. + function extraOverview(name, show, index) + { + var i = 0 + if (show) { + if (index !== undefined) { + if (overviewModel.get(index).pageSource === name) + return + // First append the page + overviewModel.append({"pageSource": name}) + // Then move all the pages behind index + overviewModel.move(index, overviewModel.count - 2, overviewModel.count - 2) + } else { + for (i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === name) + // Don't append if already exists + return + overviewModel.append({"pageSource": name}) + } + } else { + for (i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === name) + overviewModel.remove(i) + } + } + + function replaceOverview(oldPage, newPage) + { + for (var i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === oldPage) + overviewModel.get(i).pageSource = newPage + } + + Keys.onEscapePressed: showOverview() + Keys.onReturnPressed: backToMainMenu() + + // Central mover for the ball animation on the overviews + // Instead of using a timer per line, using a central one + // reduces the CPU usage a little bit and makes the animations + // smoother. + Timer { + id: mover + property double pos: _counter / _loops + property int _counter + property int _loops: 13 + + interval: 100 + running: true + repeat: true + onTriggered: if (_counter >= (_loops - 1)) _counter = 0; else _counter++ + } + + // If an overview or notifications is active, the new page will replace it + // instead to be pushed. This way we prevent an unwanted stackpage depth + // increment everytime another page wants to be on top. + function setTopPage(page) + { + if (isNotificationPage || isOverviewPage || isOfflineFwUpdatePage) + rootWindow.pageStack.replace(page); + else + rootWindow.pageStack.push(page); + } + + function spuriousKeyPress() + { + return !pageStack.currentPage || !pageStack.currentPage.active + } + + function showOverview() + { + if (spuriousKeyPress() || isOverviewPage) + return + setTopPage(overviewComponent) + } + + function showPageNotifications() + { + if (spuriousKeyPress() || isNotificationPage) + return + setTopPage(noticationsComponent) + } + + function checkAlarm() + { + if (alarm) + showPageNotifications() + } +} diff --git a/FileSets/v2.40/main.qml.orig b/FileSets/v2.40/main.qml.orig new file mode 100644 index 00000000..f531d364 --- /dev/null +++ b/FileSets/v2.40/main.qml.orig @@ -0,0 +1,361 @@ +import QtQuick 1.1 + +import Qt.labs.components.native 1.0 +import com.victron.velib 1.0 + +PageStackWindow { + id: rootWindow + + gpsConnected: gpsConnected.value === 1 + onCompletedChanged: checkAlarm() + initialPage: PageMain {} + + property bool completed: false + property bool showAlert: NotificationCenter.alert + property bool alarm: NotificationCenter.alarm + property bool overviewsLoaded: defaultOverview.valid && generatorOverview.valid && mobileOverview.valid && startWithMenu.valid + property string bindPrefix: "com.victronenergy.settings" + + property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") + property bool isOverviewPage: pageStack.currentPage && pageStack.currentPage.model === overviewModel; + property bool isOfflineFwUpdatePage: pageStack.currentPage && pageStack.currentPage.objectName === "offlineFwUpdatePage"; + + + property string hubOverviewType: theSystem.systemType.valid ? + withoutGridMeter.value === 1 ? "Hub" : theSystem.systemType.value : "" + + // Keep track of the current view (menu/overview) to show as default next time the + // CCGX is restarted + onIsOverviewPageChanged: startWithMenu.setValue(isOverviewPage ? 0 : 1) + + // Add the correct OverviewHub page + onHubOverviewTypeChanged: { + switch(hubOverviewType){ + case "Hub": + case "Hub-1": + case "Hub-2": + case "Hub-3": + replaceOverview("OverviewGridParallel.qml", "OverviewHub.qml"); + break; + case "Hub-4": + case "ESS": + replaceOverview("OverviewHub.qml", "OverviewGridParallel.qml"); + break; + default: + break; + } + // Workaround the QTBUG-17012 (only the first sentence in each case of Switch Statement can be executed) + // by adding a return statement + return + } + + VBusItem { + id: generatorOverview + bind: "com.victronenergy.settings/Settings/Relay/Function" + onValueChanged: extraOverview("OverviewGeneratorRelay.qml", value === 1) + } + + VBusItem { + id: fischerPandaGenOverview + bind: "com.victronenergy.settings/Settings/Services/FischerPandaAutoStartStop" + onValueChanged: extraOverview("OverviewGeneratorFp.qml", value === 1) + } + + VBusItem { + id: mobileOverview + bind: "com.victronenergy.settings/Settings/Gui/MobileOverview" + onValueChanged:{ + extraOverview("OverviewMobile.qml", value === 1) + } + } + + VBusItem { + id: startWithMenu + bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView" + } + + VBusItem { + id: withoutGridMeter + bind: "com.victronenergy.settings/Settings/CGwacs/RunWithoutGridMeter" + } + + + VBusItem { + id: defaultOverview + bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" + } + + // Note: finding a firmware image on the storage device is error 4 for vrm storage + // since it should not be used for logging. That fact is used here to determine if + // there is a firmware image. + Connections { + target: storageEvents + onVrmStorageError: { + if (error === 4) { + setTopPage(offlineFwUpdates) + } + } + } + + onAlarmChanged: { + if (completed) + checkAlarm() + } + + // always keep track of system information + HubData { + id: theSystem + } + + VBusItem { + id: gpsConnected + bind: "com.victronenergy.gps/Fix" + } + + // note: used for leaving the overviews as well + function backToMainMenu() + { + pageStack.pop(initialPage); + } + + Toast { + id: toast + transform: Scale { + xScale: screen.scaleX + yScale: screen.scaleY + origin.x: toast.width / 2 + origin.y: toast.height / 2 + } + } + + SignalToaster {} + + ToolBarLayout { + id: mbTools + height: parent.height + + Item { + anchors.verticalCenter: parent.verticalCenter + anchors.left: mbTools.left + height: mbTools.height + width: 200 + + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage && pageStack.currentPage.toolbarHandler) + pageStack.currentPage.toolbarHandler.leftAction() + else + showOverview() + } + } + + Row { + anchors.centerIn: parent + + MbIcon { + anchors.verticalCenter: parent.verticalCenter + iconId: pageStack.currentPage ? pageStack.currentPage.leftIcon : "" + } + + Text { + anchors.verticalCenter: parent.verticalCenter + text: pageStack.currentPage ? pageStack.currentPage.leftText : "" + color: "white" + font.bold: true + font.pixelSize: 16 + } + } + } + + MbIcon { + id: centerScrollIndicator + + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: mbTools.verticalCenter + } + iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" + } + + Item { + anchors.verticalCenter: parent.verticalCenter + height: mbTools.height + anchors.right: mbTools.right + width: 200 + + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage && pageStack.currentPage.toolbarHandler) + pageStack.currentPage.toolbarHandler.rightAction() + else + backToMainMenu() + } + } + + Row { + anchors.centerIn: parent + + MbIcon { + iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: pageStack.currentPage ? pageStack.currentPage.rightText : "" + anchors.verticalCenter: parent.verticalCenter + color: "white" + font.bold: true + font.pixelSize: 16 + } + } + } + } + + Component.onCompleted: { + completed = true + } + + ListModel { + id: overviewModel + ListElement { + pageSource: "OverviewHub.qml" + } + ListElement { + pageSource: "OverviewTiles.qml" + } + } + + Component { + id: overviewComponent + PageFlow { + // Display default overview when loaded + defaultIndex: getDefaultOverviewIndex() + // Store the current overview page as default + onCurrentIndexChanged: if (active) defaultOverview.setValue(overviewModel.get(currentIndex).pageSource.replace(".qml", "")) + model: overviewModel + } + } + + // When all the related settings items are valid, show the overview page if was the last oppened page + // before restarting + Timer { + interval: 2000 + running: completed && overviewsLoaded && startWithMenu.valid + onTriggered: if (startWithMenu.value === 0) showOverview() + } + + function getDefaultOverviewIndex() + { + if(!defaultOverview.valid) + return 0 + for (var i = 0; i < overviewModel.count; i++){ + if (overviewModel.get(i).pageSource.replace(".qml", "") === defaultOverview.value) { + return i + } + } + return 0 + } + + Component { + id: noticationsComponent + PageNotifications {} + } + + Component { + id: offlineFwUpdates + PageSettingsFirmwareOffline { checkOnCompleted: true} + + } + + // Add or remove extra overviews. for example, generator overview + // shouldn't be shown if the start/stop functionality is not enabled. + // Index parameter is optional, usefull to keep an order. + function extraOverview(name, show, index) + { + var i = 0 + if (show) { + if (index !== undefined) { + if (overviewModel.get(index).pageSource === name) + return + // First append the page + overviewModel.append({"pageSource": name}) + // Then move all the pages behind index + overviewModel.move(index, overviewModel.count - 2, overviewModel.count - 2) + } else { + for (i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === name) + // Don't append if already exists + return + overviewModel.append({"pageSource": name}) + } + } else { + for (i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === name) + overviewModel.remove(i) + } + } + + function replaceOverview(oldPage, newPage) + { + for (var i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === oldPage) + overviewModel.get(i).pageSource = newPage + } + + Keys.onEscapePressed: showOverview() + Keys.onReturnPressed: backToMainMenu() + + // Central mover for the ball animation on the overviews + // Instead of using a timer per line, using a central one + // reduces the CPU usage a little bit and makes the animations + // smoother. + Timer { + id: mover + property double pos: _counter / _loops + property int _counter + property int _loops: 13 + + interval: 100 + running: true + repeat: true + onTriggered: if (_counter >= (_loops - 1)) _counter = 0; else _counter++ + } + + // If an overview or notifications is active, the new page will replace it + // instead to be pushed. This way we prevent an unwanted stackpage depth + // increment everytime another page wants to be on top. + function setTopPage(page) + { + if (isNotificationPage || isOverviewPage || isOfflineFwUpdatePage) + rootWindow.pageStack.replace(page); + else + rootWindow.pageStack.push(page); + } + + function spuriousKeyPress() + { + return !pageStack.currentPage || !pageStack.currentPage.active + } + + function showOverview() + { + if (spuriousKeyPress() || isOverviewPage) + return + setTopPage(overviewComponent) + } + + function showPageNotifications() + { + if (spuriousKeyPress() || isNotificationPage) + return + setTopPage(noticationsComponent) + } + + function checkAlarm() + { + if (alarm) + showPageNotifications() + } +} diff --git a/FileSets/v2.41/PageMain.qml b/FileSets/v2.41/PageMain.qml new file mode 120000 index 00000000..cdaeb0c0 --- /dev/null +++ b/FileSets/v2.41/PageMain.qml @@ -0,0 +1 @@ +../v2.40/PageMain.qml \ No newline at end of file diff --git a/FileSets/v2.41/PageMain.qml.orig b/FileSets/v2.41/PageMain.qml.orig new file mode 120000 index 00000000..f3c164d8 --- /dev/null +++ b/FileSets/v2.41/PageMain.qml.orig @@ -0,0 +1 @@ +../v2.40/PageMain.qml.orig \ No newline at end of file diff --git a/FileSets/v2.41/main.qml b/FileSets/v2.41/main.qml new file mode 120000 index 00000000..0973153c --- /dev/null +++ b/FileSets/v2.41/main.qml @@ -0,0 +1 @@ +../v2.40/main.qml \ No newline at end of file diff --git a/FileSets/v2.41/main.qml.orig b/FileSets/v2.41/main.qml.orig new file mode 120000 index 00000000..7e14725d --- /dev/null +++ b/FileSets/v2.41/main.qml.orig @@ -0,0 +1 @@ +../v2.40/main.qml.orig \ No newline at end of file diff --git a/FileSets/v2.42/PageMain.qml b/FileSets/v2.42/PageMain.qml new file mode 120000 index 00000000..cdaeb0c0 --- /dev/null +++ b/FileSets/v2.42/PageMain.qml @@ -0,0 +1 @@ +../v2.40/PageMain.qml \ No newline at end of file diff --git a/FileSets/v2.42/PageMain.qml.orig b/FileSets/v2.42/PageMain.qml.orig new file mode 120000 index 00000000..f3c164d8 --- /dev/null +++ b/FileSets/v2.42/PageMain.qml.orig @@ -0,0 +1 @@ +../v2.40/PageMain.qml.orig \ No newline at end of file diff --git a/FileSets/v2.42/main.qml b/FileSets/v2.42/main.qml new file mode 120000 index 00000000..0973153c --- /dev/null +++ b/FileSets/v2.42/main.qml @@ -0,0 +1 @@ +../v2.40/main.qml \ No newline at end of file diff --git a/FileSets/v2.42/main.qml.orig b/FileSets/v2.42/main.qml.orig new file mode 120000 index 00000000..7e14725d --- /dev/null +++ b/FileSets/v2.42/main.qml.orig @@ -0,0 +1 @@ +../v2.40/main.qml.orig \ No newline at end of file diff --git a/FileSets/v2.51/PageMain.qml b/FileSets/v2.51/PageMain.qml new file mode 120000 index 00000000..cdaeb0c0 --- /dev/null +++ b/FileSets/v2.51/PageMain.qml @@ -0,0 +1 @@ +../v2.40/PageMain.qml \ No newline at end of file diff --git a/FileSets/v2.51/PageMain.qml.orig b/FileSets/v2.51/PageMain.qml.orig new file mode 120000 index 00000000..f3c164d8 --- /dev/null +++ b/FileSets/v2.51/PageMain.qml.orig @@ -0,0 +1 @@ +../v2.40/PageMain.qml.orig \ No newline at end of file diff --git a/FileSets/v2.51/main.qml b/FileSets/v2.51/main.qml new file mode 120000 index 00000000..0973153c --- /dev/null +++ b/FileSets/v2.51/main.qml @@ -0,0 +1 @@ +../v2.40/main.qml \ No newline at end of file diff --git a/FileSets/v2.51/main.qml.orig b/FileSets/v2.51/main.qml.orig new file mode 120000 index 00000000..7e14725d --- /dev/null +++ b/FileSets/v2.51/main.qml.orig @@ -0,0 +1 @@ +../v2.40/main.qml.orig \ No newline at end of file diff --git a/FileSets/v2.52/PageMain.qml b/FileSets/v2.52/PageMain.qml new file mode 120000 index 00000000..cdaeb0c0 --- /dev/null +++ b/FileSets/v2.52/PageMain.qml @@ -0,0 +1 @@ +../v2.40/PageMain.qml \ No newline at end of file diff --git a/FileSets/v2.52/PageMain.qml.orig b/FileSets/v2.52/PageMain.qml.orig new file mode 120000 index 00000000..f3c164d8 --- /dev/null +++ b/FileSets/v2.52/PageMain.qml.orig @@ -0,0 +1 @@ +../v2.40/PageMain.qml.orig \ No newline at end of file diff --git a/FileSets/v2.52/main.qml b/FileSets/v2.52/main.qml new file mode 120000 index 00000000..0973153c --- /dev/null +++ b/FileSets/v2.52/main.qml @@ -0,0 +1 @@ +../v2.40/main.qml \ No newline at end of file diff --git a/FileSets/v2.52/main.qml.orig b/FileSets/v2.52/main.qml.orig new file mode 120000 index 00000000..7e14725d --- /dev/null +++ b/FileSets/v2.52/main.qml.orig @@ -0,0 +1 @@ +../v2.40/main.qml.orig \ No newline at end of file diff --git a/FileSets/v2.53/PageMain.qml b/FileSets/v2.53/PageMain.qml new file mode 120000 index 00000000..cdaeb0c0 --- /dev/null +++ b/FileSets/v2.53/PageMain.qml @@ -0,0 +1 @@ +../v2.40/PageMain.qml \ No newline at end of file diff --git a/FileSets/v2.53/PageMain.qml.orig b/FileSets/v2.53/PageMain.qml.orig new file mode 120000 index 00000000..f3c164d8 --- /dev/null +++ b/FileSets/v2.53/PageMain.qml.orig @@ -0,0 +1 @@ +../v2.40/PageMain.qml.orig \ No newline at end of file diff --git a/FileSets/v2.53/main.qml b/FileSets/v2.53/main.qml new file mode 120000 index 00000000..0973153c --- /dev/null +++ b/FileSets/v2.53/main.qml @@ -0,0 +1 @@ +../v2.40/main.qml \ No newline at end of file diff --git a/FileSets/v2.53/main.qml.orig b/FileSets/v2.53/main.qml.orig new file mode 120000 index 00000000..7e14725d --- /dev/null +++ b/FileSets/v2.53/main.qml.orig @@ -0,0 +1 @@ +../v2.40/main.qml.orig \ No newline at end of file diff --git a/FileSets/v2.54/PageMain.qml b/FileSets/v2.54/PageMain.qml new file mode 120000 index 00000000..cdaeb0c0 --- /dev/null +++ b/FileSets/v2.54/PageMain.qml @@ -0,0 +1 @@ +../v2.40/PageMain.qml \ No newline at end of file diff --git a/FileSets/v2.54/PageMain.qml.orig b/FileSets/v2.54/PageMain.qml.orig new file mode 120000 index 00000000..f3c164d8 --- /dev/null +++ b/FileSets/v2.54/PageMain.qml.orig @@ -0,0 +1 @@ +../v2.40/PageMain.qml.orig \ No newline at end of file diff --git a/FileSets/v2.54/main.qml b/FileSets/v2.54/main.qml new file mode 120000 index 00000000..0973153c --- /dev/null +++ b/FileSets/v2.54/main.qml @@ -0,0 +1 @@ +../v2.40/main.qml \ No newline at end of file diff --git a/FileSets/v2.54/main.qml.orig b/FileSets/v2.54/main.qml.orig new file mode 120000 index 00000000..7e14725d --- /dev/null +++ b/FileSets/v2.54/main.qml.orig @@ -0,0 +1 @@ +../v2.40/main.qml.orig \ No newline at end of file diff --git a/FileSets/v2.57/PageMain.qml b/FileSets/v2.57/PageMain.qml new file mode 120000 index 00000000..cdaeb0c0 --- /dev/null +++ b/FileSets/v2.57/PageMain.qml @@ -0,0 +1 @@ +../v2.40/PageMain.qml \ No newline at end of file diff --git a/FileSets/v2.57/PageMain.qml.orig b/FileSets/v2.57/PageMain.qml.orig new file mode 120000 index 00000000..f3c164d8 --- /dev/null +++ b/FileSets/v2.57/PageMain.qml.orig @@ -0,0 +1 @@ +../v2.40/PageMain.qml.orig \ No newline at end of file diff --git a/FileSets/v2.57/main.qml b/FileSets/v2.57/main.qml new file mode 120000 index 00000000..0973153c --- /dev/null +++ b/FileSets/v2.57/main.qml @@ -0,0 +1 @@ +../v2.40/main.qml \ No newline at end of file diff --git a/FileSets/v2.57/main.qml.orig b/FileSets/v2.57/main.qml.orig new file mode 120000 index 00000000..7e14725d --- /dev/null +++ b/FileSets/v2.57/main.qml.orig @@ -0,0 +1 @@ +../v2.40/main.qml.orig \ No newline at end of file diff --git a/FileSets/v2.58/PageMain.qml b/FileSets/v2.58/PageMain.qml new file mode 120000 index 00000000..cdaeb0c0 --- /dev/null +++ b/FileSets/v2.58/PageMain.qml @@ -0,0 +1 @@ +../v2.40/PageMain.qml \ No newline at end of file diff --git a/FileSets/v2.58/PageMain.qml.orig b/FileSets/v2.58/PageMain.qml.orig new file mode 120000 index 00000000..f3c164d8 --- /dev/null +++ b/FileSets/v2.58/PageMain.qml.orig @@ -0,0 +1 @@ +../v2.40/PageMain.qml.orig \ No newline at end of file diff --git a/FileSets/v2.58/main.qml b/FileSets/v2.58/main.qml new file mode 120000 index 00000000..0973153c --- /dev/null +++ b/FileSets/v2.58/main.qml @@ -0,0 +1 @@ +../v2.40/main.qml \ No newline at end of file diff --git a/FileSets/v2.58/main.qml.orig b/FileSets/v2.58/main.qml.orig new file mode 120000 index 00000000..7e14725d --- /dev/null +++ b/FileSets/v2.58/main.qml.orig @@ -0,0 +1 @@ +../v2.40/main.qml.orig \ No newline at end of file diff --git a/FileSets/v2.60/PageMain.qml b/FileSets/v2.60/PageMain.qml new file mode 100644 index 00000000..7c07f3fe --- /dev/null +++ b/FileSets/v2.60/PageMain.qml @@ -0,0 +1,198 @@ +// modified order to put Settings, then Notifications at top of list +//// search for MODIFIED + +import QtQuick 1.1 +import com.victron.velib 1.0 + +MbPage { + id: root + title: qsTr("Device List") + + model: VisualItemModel { +// MODIFIED put Settings at top of list + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } + } + +// MODIFIED put Notifications second + MbSubMenu { + id: menuNotifications + description: qsTr("Notifications") + item: VBusItem { + property variant active: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + value: active.length > 0 ? active.length : "" + } + subpage: Component { PageNotifications {} } + } + + } + + Component { + id: submenuLoader + MbDevice { + iconId: "icon-toolbar-enter" + } + } + + Component { + id: vebusPage + PageVebus {} + } + + Component { + id: batteryPage + PageBattery {} + } + + Component { + id: solarChargerPage + PageSolarCharger {} + } + + Component { + id: acInPage + PageAcIn {} + } + + Component { + id: acChargerPage + PageAcCharger {} + } + + Component { + id: tankPage + PageTankSensor {} + } + + Component { + id: motorDrivePage + PageMotorDrive {} + } + + Component { + id: inverterPage + PageInverter {} + } + + Component { + id: pulseCounterPage + PagePulseCounter {} + } + + Component { + id: digitalInputPage + PageDigitalInput {} + } + + Component { + id: temperatureSensorPage + PageTemperatureSensor {} + } + + Component { + id: unsupportedDevicePage + PageUnsupportedDevice {} + } + + Component { + id: meteoDevicePage + PageMeteo {} + } + + function addService(service) + { + var name = service.name + + var page + switch(service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + page = vebusPage + break; + case DBusService.DBUS_SERVICE_BATTERY: + page = batteryPage + break; + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + page = solarChargerPage + break; + case DBusService.DBUS_SERVICE_PV_INVERTER: + page = acInPage + break; + case DBusService.DBUS_SERVICE_AC_CHARGER: + page = acChargerPage + break; + case DBusService.DBUS_SERVICE_TANK: + page = tankPage + break; + case DBusService.DBUS_SERVICE_GRIDMETER: + page = acInPage + break + case DBusService.DBUS_SERVICE_GENSET: + page = acInPage + break + case DBusService.DBUS_SERVICE_MOTOR_DRIVE: + page = motorDrivePage + break + case DBusService.DBUS_SERVICE_INVERTER: + page = inverterPage + break; + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + page = temperatureSensorPage + break; + case DBusService.DBUS_SERVICE_SYSTEM_CALC: + return; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + page = digitalInputPage + break; + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + page = pulseCounterPage + break; + case DBusService.DBUS_SERVICE_UNSUPPORTED: + page = unsupportedDevicePage + break; + case DBusService.DBUS_SERVICE_METEO: + page = meteoDevicePage + break; + case DBusService.DBUS_SERVICE_VECAN: + return; + default: + console.log("unknown service " + name) + return; + } + + var submenu = submenuLoader.createObject(root) + submenu.service = service + + // option 1, load when being opened + // submenu.subpage = page + // submenu.subpageProperties = {service: service} + + // option 2, create it now + submenu.subpage = page.createObject(submenu, {service: service, bindPrefix: service.name}) + + // sort on (initial) description + var i = 0 +// MODIFIED leave Settings and Notifications at top of list (don't sort first 2 entries) + for (i = 2; i < model.count - 2; i++ ) { + if (model.children[i].description.localeCompare(service.description) > 0) + break; + } + + model.insert(i, submenu) + + initListView() + } + + Component.onCompleted: { + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + listview.currentIndex = 0 + } + + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } +} diff --git a/FileSets/v2.60/PageMain.qml.orig b/FileSets/v2.60/PageMain.qml.orig new file mode 100644 index 00000000..9466857f --- /dev/null +++ b/FileSets/v2.60/PageMain.qml.orig @@ -0,0 +1,191 @@ +import QtQuick 1.1 +import com.victron.velib 1.0 + +MbPage { + id: root + title: qsTr("Device List") + + model: VisualItemModel { + MbSubMenu { + id: menuNotifications + description: qsTr("Notifications") + item: VBusItem { + property variant active: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + value: active.length > 0 ? active.length : "" + } + subpage: Component { PageNotifications {} } + } + + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } + } + } + + Component { + id: submenuLoader + MbDevice { + iconId: "icon-toolbar-enter" + } + } + + Component { + id: vebusPage + PageVebus {} + } + + Component { + id: batteryPage + PageBattery {} + } + + Component { + id: solarChargerPage + PageSolarCharger {} + } + + Component { + id: acInPage + PageAcIn {} + } + + Component { + id: acChargerPage + PageAcCharger {} + } + + Component { + id: tankPage + PageTankSensor {} + } + + Component { + id: motorDrivePage + PageMotorDrive {} + } + + Component { + id: inverterPage + PageInverter {} + } + + Component { + id: pulseCounterPage + PagePulseCounter {} + } + + Component { + id: digitalInputPage + PageDigitalInput {} + } + + Component { + id: temperatureSensorPage + PageTemperatureSensor {} + } + + Component { + id: unsupportedDevicePage + PageUnsupportedDevice {} + } + + Component { + id: meteoDevicePage + PageMeteo {} + } + + function addService(service) + { + var name = service.name + + var page + switch(service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + page = vebusPage + break; + case DBusService.DBUS_SERVICE_BATTERY: + page = batteryPage + break; + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + page = solarChargerPage + break; + case DBusService.DBUS_SERVICE_PV_INVERTER: + page = acInPage + break; + case DBusService.DBUS_SERVICE_AC_CHARGER: + page = acChargerPage + break; + case DBusService.DBUS_SERVICE_TANK: + page = tankPage + break; + case DBusService.DBUS_SERVICE_GRIDMETER: + page = acInPage + break + case DBusService.DBUS_SERVICE_GENSET: + page = acInPage + break + case DBusService.DBUS_SERVICE_MOTOR_DRIVE: + page = motorDrivePage + break + case DBusService.DBUS_SERVICE_INVERTER: + page = inverterPage + break; + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + page = temperatureSensorPage + break; + case DBusService.DBUS_SERVICE_SYSTEM_CALC: + return; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + page = digitalInputPage + break; + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + page = pulseCounterPage + break; + case DBusService.DBUS_SERVICE_UNSUPPORTED: + page = unsupportedDevicePage + break; + case DBusService.DBUS_SERVICE_METEO: + page = meteoDevicePage + break; + case DBusService.DBUS_SERVICE_VECAN: + return; + default: + console.log("unknown service " + name) + return; + } + + var submenu = submenuLoader.createObject(root) + submenu.service = service + + // option 1, load when being opened + // submenu.subpage = page + // submenu.subpageProperties = {service: service} + + // option 2, create it now + submenu.subpage = page.createObject(submenu, {service: service, bindPrefix: service.name}) + + // sort on (initial) description + var i = 0 + for (i = 0; i < model.count - 2; i++ ) { + if (model.children[i].description.localeCompare(service.description) > 0) + break; + } + + model.insert(i, submenu) + + initListView() + } + + Component.onCompleted: { + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + listview.currentIndex = 0 + } + + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } +} diff --git a/FileSets/v2.60/main.qml b/FileSets/v2.60/main.qml new file mode 100644 index 00000000..61e699e5 --- /dev/null +++ b/FileSets/v2.60/main.qml @@ -0,0 +1,361 @@ +//// Modified to hide the OverviewTiles page +//// Search for MODIFIED to find changes + +import QtQuick 1.1 + +import Qt.labs.components.native 1.0 +import com.victron.velib 1.0 +import "utils.js" as Utils + +PageStackWindow { + id: rootWindow + + gpsConnected: gpsFix.value === 1 + onCompletedChanged: checkAlarm() + initialPage: PageMain {} + + property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" } + property VeQuickItem gpsFix: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Fix") } + property bool completed: false + property bool showAlert: NotificationCenter.alert + property bool alarm: NotificationCenter.alarm + property bool overviewsLoaded: defaultOverview.valid && generatorOverview.valid && mobileOverview.valid && startWithMenu.valid + property string bindPrefix: "com.victronenergy.settings" + + property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") + property bool isOverviewPage: pageStack.currentPage && pageStack.currentPage.model === overviewModel; + property bool isOfflineFwUpdatePage: pageStack.currentPage && pageStack.currentPage.objectName === "offlineFwUpdatePage"; + + + property string hubOverviewType: theSystem.systemType.valid ? + withoutGridMeter.value === 1 ? "Hub" : theSystem.systemType.value : "" + + // Keep track of the current view (menu/overview) to show as default next time the + // CCGX is restarted + onIsOverviewPageChanged: startWithMenu.setValue(isOverviewPage ? 0 : 1) + + // Add the correct OverviewHub page + onHubOverviewTypeChanged: { + switch(hubOverviewType){ + case "Hub": + case "Hub-1": + case "Hub-2": + case "Hub-3": + replaceOverview("OverviewGridParallel.qml", "OverviewHub.qml"); + break; + case "Hub-4": + case "ESS": + replaceOverview("OverviewHub.qml", "OverviewGridParallel.qml"); + break; + default: + break; + } + // Workaround the QTBUG-17012 (only the first sentence in each case of Switch Statement can be executed) + // by adding a return statement + return + } + + VBusItem { + id: generatorOverview + bind: "com.victronenergy.settings/Settings/Relay/Function" + onValueChanged: extraOverview("OverviewGeneratorRelay.qml", value === 1) + } + + VBusItem { + id: fischerPandaGenOverview + bind: "com.victronenergy.settings/Settings/Services/FischerPandaAutoStartStop" + onValueChanged: extraOverview("OverviewGeneratorFp.qml", value === 1) + } + + VBusItem { + id: mobileOverview + bind: "com.victronenergy.settings/Settings/Gui/MobileOverview" + onValueChanged:{ + extraOverview("OverviewMobile.qml", value === 1) + } + } + + VBusItem { + id: startWithMenu + bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView" + } + + VBusItem { + id: withoutGridMeter + bind: "com.victronenergy.settings/Settings/CGwacs/RunWithoutGridMeter" + } + + + VBusItem { + id: defaultOverview + bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" + } + + // Note: finding a firmware image on the storage device is error 4 for vrm storage + // since it should not be used for logging. That fact is used here to determine if + // there is a firmware image. + Connections { + target: storageEvents + onVrmStorageError: { + if (error === 4) { + setTopPage(offlineFwUpdates) + } + } + } + + onAlarmChanged: { + if (completed) + checkAlarm() + } + + // always keep track of system information + HubData { + id: theSystem + } + + // note: used for leaving the overviews as well + function backToMainMenu() + { + pageStack.pop(initialPage); + } + + Toast { + id: toast + transform: Scale { + xScale: screen.scaleX + yScale: screen.scaleY + origin.x: toast.width / 2 + origin.y: toast.height / 2 + } + } + + SignalToaster {} + + ToolbarHandlerPages { + id: mainToolbarHandler + isDefault: true + } + + ToolBarLayout { + id: mbTools + height: parent.height + + Item { + anchors.verticalCenter: parent.verticalCenter + anchors.left: mbTools.left + height: mbTools.height + width: 200 + + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage) + pageStack.currentPage.toolbarHandler.leftAction(true) + } + } + + Row { + anchors.centerIn: parent + + MbIcon { + anchors.verticalCenter: parent.verticalCenter + iconId: pageStack.currentPage ? pageStack.currentPage.leftIcon : "" + } + + Text { + anchors.verticalCenter: parent.verticalCenter + text: pageStack.currentPage ? pageStack.currentPage.leftText : "" + color: "white" + font.bold: true + font.pixelSize: 16 + } + } + } + + MbIcon { + id: centerScrollIndicator + + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: mbTools.verticalCenter + } + iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" + } + + Item { + anchors.verticalCenter: parent.verticalCenter + height: mbTools.height + anchors.right: mbTools.right + width: 200 + + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage) + pageStack.currentPage.toolbarHandler.rightAction(true) + } + } + + Row { + anchors.centerIn: parent + + MbIcon { + iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: pageStack.currentPage ? pageStack.currentPage.rightText : "" + anchors.verticalCenter: parent.verticalCenter + color: "white" + font.bold: true + font.pixelSize: 16 + } + } + } + } + + Component.onCompleted: { + completed = true + } + + ListModel { + id: overviewModel + ListElement { + pageSource: "OverviewHub.qml" + } +//// MODIFIED (commented out) +// ListElement { +// pageSource: "OverviewTiles.qml" +// } + } + + Component { + id: overviewComponent + PageFlow { + // Display default overview when loaded + defaultIndex: getDefaultOverviewIndex() + // Store the current overview page as default + onCurrentIndexChanged: if (active) defaultOverview.setValue(overviewModel.get(currentIndex).pageSource.replace(".qml", "")) + model: overviewModel + } + } + + // When all the related settings items are valid, show the overview page if was the last oppened page + // before restarting + Timer { + interval: 2000 + running: completed && overviewsLoaded && startWithMenu.valid + onTriggered: if (startWithMenu.value === 0) showOverview() + } + + function getDefaultOverviewIndex() + { + if(!defaultOverview.valid) + return 0 + for (var i = 0; i < overviewModel.count; i++){ + if (overviewModel.get(i).pageSource.replace(".qml", "") === defaultOverview.value) { + return i + } + } + return 0 + } + + Component { + id: noticationsComponent + PageNotifications {} + } + + Component { + id: offlineFwUpdates + PageSettingsFirmwareOffline { checkOnCompleted: true} + + } + + // Add or remove extra overviews. for example, generator overview + // shouldn't be shown if the start/stop functionality is not enabled. + // Index parameter is optional, usefull to keep an order. + function extraOverview(name, show, index) + { + var i = 0 + if (show) { + if (index !== undefined) { + if (overviewModel.get(index).pageSource === name) + return + // First append the page + overviewModel.append({"pageSource": name}) + // Then move all the pages behind index + overviewModel.move(index, overviewModel.count - 2, overviewModel.count - 2) + } else { + for (i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === name) + // Don't append if already exists + return + overviewModel.append({"pageSource": name}) + } + } else { + for (i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === name) + overviewModel.remove(i) + } + } + + function replaceOverview(oldPage, newPage) + { + for (var i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === oldPage) + overviewModel.get(i).pageSource = newPage + } + + // Central mover for the ball animation on the overviews + // Instead of using a timer per line, using a central one + // reduces the CPU usage a little bit and makes the animations + // smoother. + Timer { + id: mover + property double pos: _counter / _loops + property int _counter + property int _loops: 13 + + interval: 100 + running: true + repeat: true + onTriggered: if (_counter >= (_loops - 1)) _counter = 0; else _counter++ + } + + // If an overview or notifications is active, the new page will replace it + // instead to be pushed. This way we prevent an unwanted stackpage depth + // increment everytime another page wants to be on top. + function setTopPage(page) + { + if (isNotificationPage || isOverviewPage || isOfflineFwUpdatePage) + rootWindow.pageStack.replace(page); + else + rootWindow.pageStack.push(page); + } + + function spuriousKeyPress() + { + return !pageStack.currentPage || !pageStack.currentPage.active + } + + function showOverview() + { + if (spuriousKeyPress() || isOverviewPage) + return + setTopPage(overviewComponent) + } + + function showPageNotifications() + { + if (spuriousKeyPress() || isNotificationPage) + return + setTopPage(noticationsComponent) + } + + function checkAlarm() + { + if (alarm) + showPageNotifications() + } +} diff --git a/FileSets/v2.60/main.qml.orig b/FileSets/v2.60/main.qml.orig new file mode 100644 index 00000000..f6adcef6 --- /dev/null +++ b/FileSets/v2.60/main.qml.orig @@ -0,0 +1,357 @@ +import QtQuick 1.1 + +import Qt.labs.components.native 1.0 +import com.victron.velib 1.0 +import "utils.js" as Utils + +PageStackWindow { + id: rootWindow + + gpsConnected: gpsFix.value === 1 + onCompletedChanged: checkAlarm() + initialPage: PageMain {} + + property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" } + property VeQuickItem gpsFix: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Fix") } + property bool completed: false + property bool showAlert: NotificationCenter.alert + property bool alarm: NotificationCenter.alarm + property bool overviewsLoaded: defaultOverview.valid && generatorOverview.valid && mobileOverview.valid && startWithMenu.valid + property string bindPrefix: "com.victronenergy.settings" + + property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") + property bool isOverviewPage: pageStack.currentPage && pageStack.currentPage.model === overviewModel; + property bool isOfflineFwUpdatePage: pageStack.currentPage && pageStack.currentPage.objectName === "offlineFwUpdatePage"; + + + property string hubOverviewType: theSystem.systemType.valid ? + withoutGridMeter.value === 1 ? "Hub" : theSystem.systemType.value : "" + + // Keep track of the current view (menu/overview) to show as default next time the + // CCGX is restarted + onIsOverviewPageChanged: startWithMenu.setValue(isOverviewPage ? 0 : 1) + + // Add the correct OverviewHub page + onHubOverviewTypeChanged: { + switch(hubOverviewType){ + case "Hub": + case "Hub-1": + case "Hub-2": + case "Hub-3": + replaceOverview("OverviewGridParallel.qml", "OverviewHub.qml"); + break; + case "Hub-4": + case "ESS": + replaceOverview("OverviewHub.qml", "OverviewGridParallel.qml"); + break; + default: + break; + } + // Workaround the QTBUG-17012 (only the first sentence in each case of Switch Statement can be executed) + // by adding a return statement + return + } + + VBusItem { + id: generatorOverview + bind: "com.victronenergy.settings/Settings/Relay/Function" + onValueChanged: extraOverview("OverviewGeneratorRelay.qml", value === 1) + } + + VBusItem { + id: fischerPandaGenOverview + bind: "com.victronenergy.settings/Settings/Services/FischerPandaAutoStartStop" + onValueChanged: extraOverview("OverviewGeneratorFp.qml", value === 1) + } + + VBusItem { + id: mobileOverview + bind: "com.victronenergy.settings/Settings/Gui/MobileOverview" + onValueChanged:{ + extraOverview("OverviewMobile.qml", value === 1) + } + } + + VBusItem { + id: startWithMenu + bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView" + } + + VBusItem { + id: withoutGridMeter + bind: "com.victronenergy.settings/Settings/CGwacs/RunWithoutGridMeter" + } + + + VBusItem { + id: defaultOverview + bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" + } + + // Note: finding a firmware image on the storage device is error 4 for vrm storage + // since it should not be used for logging. That fact is used here to determine if + // there is a firmware image. + Connections { + target: storageEvents + onVrmStorageError: { + if (error === 4) { + setTopPage(offlineFwUpdates) + } + } + } + + onAlarmChanged: { + if (completed) + checkAlarm() + } + + // always keep track of system information + HubData { + id: theSystem + } + + // note: used for leaving the overviews as well + function backToMainMenu() + { + pageStack.pop(initialPage); + } + + Toast { + id: toast + transform: Scale { + xScale: screen.scaleX + yScale: screen.scaleY + origin.x: toast.width / 2 + origin.y: toast.height / 2 + } + } + + SignalToaster {} + + ToolbarHandlerPages { + id: mainToolbarHandler + isDefault: true + } + + ToolBarLayout { + id: mbTools + height: parent.height + + Item { + anchors.verticalCenter: parent.verticalCenter + anchors.left: mbTools.left + height: mbTools.height + width: 200 + + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage) + pageStack.currentPage.toolbarHandler.leftAction(true) + } + } + + Row { + anchors.centerIn: parent + + MbIcon { + anchors.verticalCenter: parent.verticalCenter + iconId: pageStack.currentPage ? pageStack.currentPage.leftIcon : "" + } + + Text { + anchors.verticalCenter: parent.verticalCenter + text: pageStack.currentPage ? pageStack.currentPage.leftText : "" + color: "white" + font.bold: true + font.pixelSize: 16 + } + } + } + + MbIcon { + id: centerScrollIndicator + + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: mbTools.verticalCenter + } + iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" + } + + Item { + anchors.verticalCenter: parent.verticalCenter + height: mbTools.height + anchors.right: mbTools.right + width: 200 + + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage) + pageStack.currentPage.toolbarHandler.rightAction(true) + } + } + + Row { + anchors.centerIn: parent + + MbIcon { + iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: pageStack.currentPage ? pageStack.currentPage.rightText : "" + anchors.verticalCenter: parent.verticalCenter + color: "white" + font.bold: true + font.pixelSize: 16 + } + } + } + } + + Component.onCompleted: { + completed = true + } + + ListModel { + id: overviewModel + ListElement { + pageSource: "OverviewHub.qml" + } + ListElement { + pageSource: "OverviewTiles.qml" + } + } + + Component { + id: overviewComponent + PageFlow { + // Display default overview when loaded + defaultIndex: getDefaultOverviewIndex() + // Store the current overview page as default + onCurrentIndexChanged: if (active) defaultOverview.setValue(overviewModel.get(currentIndex).pageSource.replace(".qml", "")) + model: overviewModel + } + } + + // When all the related settings items are valid, show the overview page if was the last oppened page + // before restarting + Timer { + interval: 2000 + running: completed && overviewsLoaded && startWithMenu.valid + onTriggered: if (startWithMenu.value === 0) showOverview() + } + + function getDefaultOverviewIndex() + { + if(!defaultOverview.valid) + return 0 + for (var i = 0; i < overviewModel.count; i++){ + if (overviewModel.get(i).pageSource.replace(".qml", "") === defaultOverview.value) { + return i + } + } + return 0 + } + + Component { + id: noticationsComponent + PageNotifications {} + } + + Component { + id: offlineFwUpdates + PageSettingsFirmwareOffline { checkOnCompleted: true} + + } + + // Add or remove extra overviews. for example, generator overview + // shouldn't be shown if the start/stop functionality is not enabled. + // Index parameter is optional, usefull to keep an order. + function extraOverview(name, show, index) + { + var i = 0 + if (show) { + if (index !== undefined) { + if (overviewModel.get(index).pageSource === name) + return + // First append the page + overviewModel.append({"pageSource": name}) + // Then move all the pages behind index + overviewModel.move(index, overviewModel.count - 2, overviewModel.count - 2) + } else { + for (i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === name) + // Don't append if already exists + return + overviewModel.append({"pageSource": name}) + } + } else { + for (i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === name) + overviewModel.remove(i) + } + } + + function replaceOverview(oldPage, newPage) + { + for (var i = 0; i < overviewModel.count; i++) + if (overviewModel.get(i).pageSource === oldPage) + overviewModel.get(i).pageSource = newPage + } + + // Central mover for the ball animation on the overviews + // Instead of using a timer per line, using a central one + // reduces the CPU usage a little bit and makes the animations + // smoother. + Timer { + id: mover + property double pos: _counter / _loops + property int _counter + property int _loops: 13 + + interval: 100 + running: true + repeat: true + onTriggered: if (_counter >= (_loops - 1)) _counter = 0; else _counter++ + } + + // If an overview or notifications is active, the new page will replace it + // instead to be pushed. This way we prevent an unwanted stackpage depth + // increment everytime another page wants to be on top. + function setTopPage(page) + { + if (isNotificationPage || isOverviewPage || isOfflineFwUpdatePage) + rootWindow.pageStack.replace(page); + else + rootWindow.pageStack.push(page); + } + + function spuriousKeyPress() + { + return !pageStack.currentPage || !pageStack.currentPage.active + } + + function showOverview() + { + if (spuriousKeyPress() || isOverviewPage) + return + setTopPage(overviewComponent) + } + + function showPageNotifications() + { + if (spuriousKeyPress() || isNotificationPage) + return + setTopPage(noticationsComponent) + } + + function checkAlarm() + { + if (alarm) + showPageNotifications() + } +} diff --git a/FileSets/v2.70~3-large-12/PageMain.qml b/FileSets/v2.70~3-large-12/PageMain.qml new file mode 100644 index 00000000..faf0a296 --- /dev/null +++ b/FileSets/v2.70~3-large-12/PageMain.qml @@ -0,0 +1,215 @@ +// modified order to put Settings, then Notifications at top of list +//// search for MODIFIED + +import QtQuick 1.1 +import com.victron.velib 1.0 + +MbPage { + id: root + title: qsTr("Device List") + + model: VisualModels { +// MODIFIED put Settings at top of list + VisualItemModel { + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } + } + +// MODIFIED put Notifications second + MbSubMenu { + id: menuNotifications + description: qsTr("Notifications") + item: VBusItem { + property variant active: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + value: active.length > 0 ? active.length : "" + } + subpage: Component { PageNotifications {} } + } + + MbOK { + description: qsTr("Remove disconnected devices") + value: qsTr("Press to remove") + show: deviceList.disconnectedDevices != 0 + editable: true + + function clicked() { + listview.decrementCurrentIndex() + deviceList.removeDisconnected() + } + } + } + VisualDataModel { + model: VeSortFilterProxyModel { + model: DeviceList { + id: deviceList + onRowsAboutToBeRemoved: { + for (var i = first; i <= last; i++) + deviceList.page(i).destroy() + } + } + sortRole: DeviceList.DescriptionRole + dynamicSortFilter: true + naturalSort: true + sortCaseSensitivity: Qt.CaseInsensitive + } + + delegate: MbDevice { + iconId: "icon-toolbar-enter" + service: model.page.service + subpage: model.page + } + } + } + + Component { + id: vebusPage + PageVebus {} + } + + Component { + id: batteryPage + PageBattery {} + } + + Component { + id: solarChargerPage + PageSolarCharger {} + } + + Component { + id: acInPage + PageAcIn {} + } + + Component { + id: acChargerPage + PageAcCharger {} + } + + Component { + id: tankPage + PageTankSensor {} + } + + Component { + id: motorDrivePage + PageMotorDrive {} + } + + Component { + id: inverterPage + PageInverter {} + } + + Component { + id: pulseCounterPage + PagePulseCounter {} + } + + Component { + id: digitalInputPage + PageDigitalInput {} + } + + Component { + id: temperatureSensorPage + PageTemperatureSensor {} + } + + Component { + id: unsupportedDevicePage + PageUnsupportedDevice {} + } + + Component { + id: meteoDevicePage + PageMeteo {} + } + + Component { + id: evChargerPage + PageEvCharger {} + } + + function addService(service) + { + var name = service.name + + var page + switch(service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + page = vebusPage + break; + case DBusService.DBUS_SERVICE_BATTERY: + page = batteryPage + break; + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + page = solarChargerPage + break; + case DBusService.DBUS_SERVICE_PV_INVERTER: + page = acInPage + break; + case DBusService.DBUS_SERVICE_AC_CHARGER: + page = acChargerPage + break; + case DBusService.DBUS_SERVICE_TANK: + page = tankPage + break; + case DBusService.DBUS_SERVICE_GRIDMETER: + page = acInPage + break + case DBusService.DBUS_SERVICE_GENSET: + page = acInPage + break + case DBusService.DBUS_SERVICE_MOTOR_DRIVE: + page = motorDrivePage + break + case DBusService.DBUS_SERVICE_INVERTER: + page = inverterPage + break; + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + page = temperatureSensorPage + break; + case DBusService.DBUS_SERVICE_SYSTEM_CALC: + return; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + page = digitalInputPage + break; + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + page = pulseCounterPage + break; + case DBusService.DBUS_SERVICE_UNSUPPORTED: + page = unsupportedDevicePage + break; + case DBusService.DBUS_SERVICE_METEO: + page = meteoDevicePage + break; + case DBusService.DBUS_SERVICE_VECAN: + return; + case DBusService.DBUS_SERVICE_EVCHARGER: + page = evChargerPage + break + case DBusService.DBUS_SERVICE_HUB4: + return; + default: + console.log("unknown service " + name) + return; + } + + deviceList.append(service, page.createObject(root, {service: service, bindPrefix: service.name})) + initListView() + } + + Component.onCompleted: { + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + } + + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } +} diff --git a/FileSets/v2.70~3-large-12/PageMain.qml.orig b/FileSets/v2.70~3-large-12/PageMain.qml.orig new file mode 100644 index 00000000..138237d9 --- /dev/null +++ b/FileSets/v2.70~3-large-12/PageMain.qml.orig @@ -0,0 +1,210 @@ +import QtQuick 1.1 +import com.victron.velib 1.0 + +MbPage { + id: root + title: qsTr("Device List") + + model: VisualModels { + VisualDataModel { + model: VeSortFilterProxyModel { + model: DeviceList { + id: deviceList + onRowsAboutToBeRemoved: { + for (var i = first; i <= last; i++) + deviceList.page(i).destroy() + } + } + sortRole: DeviceList.DescriptionRole + dynamicSortFilter: true + naturalSort: true + sortCaseSensitivity: Qt.CaseInsensitive + } + + delegate: MbDevice { + iconId: "icon-toolbar-enter" + service: model.page.service + subpage: model.page + } + } + VisualItemModel { + MbSubMenu { + id: menuNotifications + description: qsTr("Notifications") + item: VBusItem { + property variant active: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + value: active.length > 0 ? active.length : "" + } + subpage: Component { PageNotifications {} } + } + + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } + } + + MbOK { + description: qsTr("Remove disconnected devices") + value: qsTr("Press to remove") + show: deviceList.disconnectedDevices != 0 + editable: true + + function clicked() { + listview.decrementCurrentIndex() + deviceList.removeDisconnected() + } + } + } + } + + Component { + id: vebusPage + PageVebus {} + } + + Component { + id: batteryPage + PageBattery {} + } + + Component { + id: solarChargerPage + PageSolarCharger {} + } + + Component { + id: acInPage + PageAcIn {} + } + + Component { + id: acChargerPage + PageAcCharger {} + } + + Component { + id: tankPage + PageTankSensor {} + } + + Component { + id: motorDrivePage + PageMotorDrive {} + } + + Component { + id: inverterPage + PageInverter {} + } + + Component { + id: pulseCounterPage + PagePulseCounter {} + } + + Component { + id: digitalInputPage + PageDigitalInput {} + } + + Component { + id: temperatureSensorPage + PageTemperatureSensor {} + } + + Component { + id: unsupportedDevicePage + PageUnsupportedDevice {} + } + + Component { + id: meteoDevicePage + PageMeteo {} + } + + Component { + id: evChargerPage + PageEvCharger {} + } + + function addService(service) + { + var name = service.name + + var page + switch(service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + page = vebusPage + break; + case DBusService.DBUS_SERVICE_BATTERY: + page = batteryPage + break; + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + page = solarChargerPage + break; + case DBusService.DBUS_SERVICE_PV_INVERTER: + page = acInPage + break; + case DBusService.DBUS_SERVICE_AC_CHARGER: + page = acChargerPage + break; + case DBusService.DBUS_SERVICE_TANK: + page = tankPage + break; + case DBusService.DBUS_SERVICE_GRIDMETER: + page = acInPage + break + case DBusService.DBUS_SERVICE_GENSET: + page = acInPage + break + case DBusService.DBUS_SERVICE_MOTOR_DRIVE: + page = motorDrivePage + break + case DBusService.DBUS_SERVICE_INVERTER: + page = inverterPage + break; + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + page = temperatureSensorPage + break; + case DBusService.DBUS_SERVICE_SYSTEM_CALC: + return; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + page = digitalInputPage + break; + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + page = pulseCounterPage + break; + case DBusService.DBUS_SERVICE_UNSUPPORTED: + page = unsupportedDevicePage + break; + case DBusService.DBUS_SERVICE_METEO: + page = meteoDevicePage + break; + case DBusService.DBUS_SERVICE_VECAN: + return; + case DBusService.DBUS_SERVICE_EVCHARGER: + page = evChargerPage + break + case DBusService.DBUS_SERVICE_HUB4: + return; + default: + console.log("unknown service " + name) + return; + } + + deviceList.append(service, page.createObject(root, {service: service, bindPrefix: service.name})) + initListView() + } + + Component.onCompleted: { + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + } + + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } +} diff --git a/FileSets/v2.70~3-large-12/main.qml b/FileSets/v2.70~3-large-12/main.qml new file mode 120000 index 00000000..8397b9bc --- /dev/null +++ b/FileSets/v2.70~3-large-12/main.qml @@ -0,0 +1 @@ +../v2.60/main.qml \ No newline at end of file diff --git a/FileSets/v2.70~3-large-12/main.qml.orig b/FileSets/v2.70~3-large-12/main.qml.orig new file mode 120000 index 00000000..dd75715b --- /dev/null +++ b/FileSets/v2.70~3-large-12/main.qml.orig @@ -0,0 +1 @@ +../v2.60/main.qml.orig \ No newline at end of file diff --git a/FileSets/v9.99/PageMain.qml b/FileSets/v9.99/PageMain.qml new file mode 120000 index 00000000..e980c668 --- /dev/null +++ b/FileSets/v9.99/PageMain.qml @@ -0,0 +1 @@ +../v2.70~3-large-12/PageMain.qml \ No newline at end of file diff --git a/FileSets/v9.99/PageMain.qml.orig b/FileSets/v9.99/PageMain.qml.orig new file mode 100644 index 00000000..21d5c7a5 --- /dev/null +++ b/FileSets/v9.99/PageMain.qml.orig @@ -0,0 +1,211 @@ +///////////////////////////////// test - no match +import QtQuick 1.1 +import com.victron.velib 1.0 + +MbPage { + id: root + title: qsTr("Device List") + + model: VisualModels { + VisualDataModel { + model: VeSortFilterProxyModel { + model: DeviceList { + id: deviceList + onRowsAboutToBeRemoved: { + for (var i = first; i <= last; i++) + deviceList.page(i).destroy() + } + } + sortRole: DeviceList.DescriptionRole + dynamicSortFilter: true + naturalSort: true + sortCaseSensitivity: Qt.CaseInsensitive + } + + delegate: MbDevice { + iconId: "icon-toolbar-enter" + service: model.page.service + subpage: model.page + } + } + VisualItemModel { + MbSubMenu { + id: menuNotifications + description: qsTr("Notifications") + item: VBusItem { + property variant active: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + value: active.length > 0 ? active.length : "" + } + subpage: Component { PageNotifications {} } + } + + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } + } + + MbOK { + description: qsTr("Remove disconnected devices") + value: qsTr("Press to remove") + show: deviceList.disconnectedDevices != 0 + editable: true + + function clicked() { + listview.decrementCurrentIndex() + deviceList.removeDisconnected() + } + } + } + } + + Component { + id: vebusPage + PageVebus {} + } + + Component { + id: batteryPage + PageBattery {} + } + + Component { + id: solarChargerPage + PageSolarCharger {} + } + + Component { + id: acInPage + PageAcIn {} + } + + Component { + id: acChargerPage + PageAcCharger {} + } + + Component { + id: tankPage + PageTankSensor {} + } + + Component { + id: motorDrivePage + PageMotorDrive {} + } + + Component { + id: inverterPage + PageInverter {} + } + + Component { + id: pulseCounterPage + PagePulseCounter {} + } + + Component { + id: digitalInputPage + PageDigitalInput {} + } + + Component { + id: temperatureSensorPage + PageTemperatureSensor {} + } + + Component { + id: unsupportedDevicePage + PageUnsupportedDevice {} + } + + Component { + id: meteoDevicePage + PageMeteo {} + } + + Component { + id: evChargerPage + PageEvCharger {} + } + + function addService(service) + { + var name = service.name + + var page + switch(service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + page = vebusPage + break; + case DBusService.DBUS_SERVICE_BATTERY: + page = batteryPage + break; + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + page = solarChargerPage + break; + case DBusService.DBUS_SERVICE_PV_INVERTER: + page = acInPage + break; + case DBusService.DBUS_SERVICE_AC_CHARGER: + page = acChargerPage + break; + case DBusService.DBUS_SERVICE_TANK: + page = tankPage + break; + case DBusService.DBUS_SERVICE_GRIDMETER: + page = acInPage + break + case DBusService.DBUS_SERVICE_GENSET: + page = acInPage + break + case DBusService.DBUS_SERVICE_MOTOR_DRIVE: + page = motorDrivePage + break + case DBusService.DBUS_SERVICE_INVERTER: + page = inverterPage + break; + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + page = temperatureSensorPage + break; + case DBusService.DBUS_SERVICE_SYSTEM_CALC: + return; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + page = digitalInputPage + break; + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + page = pulseCounterPage + break; + case DBusService.DBUS_SERVICE_UNSUPPORTED: + page = unsupportedDevicePage + break; + case DBusService.DBUS_SERVICE_METEO: + page = meteoDevicePage + break; + case DBusService.DBUS_SERVICE_VECAN: + return; + case DBusService.DBUS_SERVICE_EVCHARGER: + page = evChargerPage + break + case DBusService.DBUS_SERVICE_HUB4: + return; + default: + console.log("unknown service " + name) + return; + } + + deviceList.append(service, page.createObject(root, {service: service, bindPrefix: service.name})) + initListView() + } + + Component.onCompleted: { + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + } + + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } +} diff --git a/FileSets/v9.99/main.qml b/FileSets/v9.99/main.qml new file mode 120000 index 00000000..8397b9bc --- /dev/null +++ b/FileSets/v9.99/main.qml @@ -0,0 +1 @@ +../v2.60/main.qml \ No newline at end of file diff --git a/FileSets/v9.99/main.qml.orig b/FileSets/v9.99/main.qml.orig new file mode 120000 index 00000000..dd75715b --- /dev/null +++ b/FileSets/v9.99/main.qml.orig @@ -0,0 +1 @@ +../v2.60/main.qml.orig \ No newline at end of file diff --git a/ReadMe b/ReadMe new file mode 100644 index 00000000..527d3ccb --- /dev/null +++ b/ReadMe @@ -0,0 +1,28 @@ +This package provides the following modificaitons to the VenusOS GUI: + move Settings and Notifications to the top of the list man page + hides the Tiles Overview + replaces the Mobile Overview with an enhanced version + +each of these modifications is optional and + can be selected at install time + +This script should be run manually initally. +It will then called from reinstallMods to reinstall functionality after a Venus update + +Setup: + +Copy the entire repo from GitHub as a zip file to /data on the Venus device +then unzip it. This should populate /data/VeCanSetup with the package contents. + +You must also install SetupHelper from here: + +https://github.com/kwindrem/SetupHelper-for-VenusOs + +Once both packages are installed run setup and follow the prompts. +./setup + +You will need root access to the Venus device. Instructions can be found here: +https://www.victronenergy.com/live/ccgx:root_access +The root password needs to be reentered following a Venus update. +Setting up an authorization key (see documentation referenced above) will save time and avoid having to reset the root password after each update. + diff --git a/setup b/setup new file mode 100755 index 00000000..10c7a2db --- /dev/null +++ b/setup @@ -0,0 +1,124 @@ +#!/bin/bash + +# this script provides the following modificaitons: +# move Settings and Notifications to the top of the list man page +# hides the Tiles Overview +# replaces the Mobile Overview with an enhanced version +# +# each of these modifications is optional and can be selected +# at install time + +qmlDir=/opt/victronenergy/gui/qml +deviceListFile="$qmlDir/PageMain.qml" +mainPagesFile="$qmlDir/main.qml" +overviewMobileEnhancedFile="$qmlDir/OverviewMobileEnhanced.qml" +reasonMessageFile="$qmlDir/SystemReasonMessage.qml" + + +# log file for this package +# if not a null string, options to display the log file are presented +packageLogFile="/var/log/gui/current" + +#### following lines incorporate SetupHelper utilities into this script +# Refer to the SetupHelper ReadMe file for details. + +source "/data/SetupHelper/CommonResources" + +if [ $scriptAction == 'EXIT' ] ; then + exit +fi + +checkFileSets + +#### end of lines to include SetupHelper + +#### running manually and OK to proceed - prompt for input +if [ $scriptAction == 'NONE' ] ; then + # display innitial message + echo + echo "this package:" + echo " puts Settings and Notifications at the begging of the man page" + echo " hides the Tile Overview" + echo " enhances the Mobile Overview" + + standardActionPrompt + + # prompt for remaining parameters needed for activation + if [ $scriptAction == 'INSTALL' ]; then + echo + echo "The Enhanced overview includes the following changes:" + echo " 1) Tiles are arranged to more cloesly follow the power flow through the system" + echo " 2) Voltage, current and frequency values are added to the AC in and out tiles" + echo " 3) Battery remaining time is added to the Battery tile" + echo " 4) ESS reason codes are replaced with a text version to make them more meaningful" + echo " 5) ESS reason text and other notifications are combined into a single "marquee" line" + echo " 6) The pump switch is hidden unless the Venus relay is configured for pump control" + echo " 7) AC Mode switch includes INVERTER ONLY mode" + echo + + yesNoPrompt "Do you wish to install the Enhanced Mobile Overview Page? (y/n): " + if $yesResponse ; then + touch $scriptDir/useEnhancedOverview + else + rm -f $scriptDir/useEnhancedOverview + fi + + yesNoPrompt "Do you wish to move Settings and Notifications to the top of the Device List? (y/n): " + if $yesResponse ; then + touch $scriptDir/moveSettings + else + rm -f $scriptDir/moveSettings + fi + + yesNoPrompt "Do you wish to move hide the Tile Overview? (y/n): " + if $yesResponse ; then + touch $scriptDir/hideTileOverview + else + rm -f $scriptDir/hideTileOverview + fi + fi +fi + +#### installing +if [ $scriptAction == 'INSTALL' ] ; then + if [ -f "$scriptDir/moveSettings" ]; then + logMessage "Moving Settings & Notifications to top of Device List" + updateActiveFile "$deviceListFile" + fi + if [ -f "$scriptDir/hideTileOverview" ]; then + logMessage "Hiding the Tile Overview" + updateActiveFile "$mainPagesFile" + fi + if [ -f "$scriptDir/useEnhancedOverview" ] ; then + logMessage "installing Enhanced Mobile Overview" + updateActiveFile "$overviewMobileEnhancedFile" + logMessage "installing System Reason Message" + updateActiveFile "$reasonMessageFile" + + # backup main.qml if it still stock + if [ ! -f "$scriptDir/hideTileOverview" ]; then + backupActiveFile "$mainPagesFile" + fi + # modify main.qml to show enhanced movile overview + sed -i -e 's/OverviewMobile.qml/OverviewMobileEnhanced.qml/' "$mainPagesFile" + fi + logMessage "++ Package installed" + +fi + +# uninstalling - check scriptAction again +# if an install step failed package needs to be removed +if [ $scriptAction == 'UNINSTALL' ] ; then + restoreActiveFile "$deviceListFile" + restoreActiveFile "$mainPagesFile" + restoreActiveFile "$overviewMobileEnhancedFile" + restoreActiveFile "$reasonMessageFile" + logMessage "++ Package uninstalled" +fi + +if $filesUpdated ; then + restartGui=true +fi + +# thats all folks - SCRIPT EXITS INSIDE THE FUNCTION +endScript