diff --git a/CMakeLists.txt b/CMakeLists.txt index 7968786..ff5ac2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ endif(CCACHE_FOUND) add_definitions(-DQT_GUI_LIB) add_subdirectory(3rdparty) -find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick WebSockets LinguistTools) +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Quick WebSockets LinguistTools) qt_standard_project_setup(REQUIRES 6.6 I18N_TRANSLATED_LANGUAGES de) @@ -114,6 +114,7 @@ qt_add_qml_module(evcharger-app RequestStatusText.qml SchedulerDayPage.qml SchedulerPage.qml + ScrollableTabPage.qml SecurityPage.qml SelectLogicModeItem.qml SelectPhaseSwitchModeItem.qml @@ -162,6 +163,7 @@ set_source_files_properties(Constants.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE) target_link_libraries(evcharger-app PUBLIC Qt6::Core Qt6::Gui + Qt6::Widgets Qt6::Quick Qt6::WebSockets qmsgpack diff --git a/ChargerTabPage.qml b/ChargerTabPage.qml index a2c0fad..cfc2825 100644 --- a/ChargerTabPage.qml +++ b/ChargerTabPage.qml @@ -25,228 +25,218 @@ AnimatedStackView { onCloseRequested: stackView.closeRequested() } - Flickable { - id: flickable + ScrollableTabPage { Layout.fillWidth: true Layout.fillHeight: true - contentHeight: columnLayout.implicitHeight - clip: true - ColumnLayout { - id: columnLayout - width: flickable.width - 30 - x: 15 - spacing: 5 + RowLayout { + Layout.fillWidth: true - RowLayout { + ColumnLayout { Layout.fillWidth: true - ColumnLayout { + Text { Layout.fillWidth: true - Text { - Layout.fillWidth: true - - ApiKeyValueHelper { - id: errApiKeyHelper - deviceConnection: theDeviceConnection - apiKey: "err" - } - - text: { - switch (carApiKeyHelper.value) - { - case 0: return qsTr("Internal error") - case 1: return qsTr("No car connected") - case 2: return qsTr("Car is charging") - case 3: return qsTr("Connecting to your car...") - case 4: return qsTr("Charging completed") - case 5: return qsTr("Unknown error %0").arg(errApiKeyHelper.value) - } - } - font.pixelSize: 20 - font.bold: true - wrapMode: Text.Wrap - } - - Text { - ApiKeyValueHelper { - id: chargingDurationInfo - deviceConnection: theDeviceConnection - apiKey: "cdi" - } - ApiKeyValueHelper { - id: powerInfo - deviceConnection: theDeviceConnection - apiKey: "nrg" - } - ApiKeyValueHelper { - id: energyInfo - deviceConnection: theDeviceConnection - apiKey: "wh" - } - - Layout.fillWidth: true - - text: { - switch (carApiKeyHelper.value) - { - case 0: return null - case 1: return qsTr("Plug in the cable to start charging your car") - case 2: return qsTr("Duration: %0").arg(getDurationInfo()) + "\n" + - qsTr("Speed: %0").arg(getChargingSpeed()) + "\n" + - qsTr("Amount: %0").arg(getChargingAmount()) - case 3: return qsTr("Charger is connecting to your car, it usually takes a few seconds") - case 4: return qsTr("Let's go-e :)") - case 5: return null - } - } - wrapMode: Text.Wrap - - function getDurationInfo() { - if (!chargingDurationInfo.exists) - return qsTr("(api key doesnt exist)") - if (typeof chargingDurationInfo.value == null) - return qsTr("(api key is null)") - if (typeof chargingDurationInfo.value !== 'object') - return qsTr("(api key is not an object)") - if (!('type' in chargingDurationInfo.value)) - return qsTr("(api key does not contain a type)") - switch (chargingDurationInfo.value.type) - { - case 0: // counter going up, use rbt - return formatDuration(rebootTime.value - chargingDurationInfo.value.value) - case 1: // counter frozen, absolute duration - return formatDuration(chargingDurationInfo.value.value) - } - return qsTr("(api key has unknown type %0)").arg(chargingDurationInfo.value.type) - } - - function getChargingSpeed() { - return qsTr("%0 %1 %2") - .arg(qsTr("%0 A").arg(Qt.locale().toString(powerInfo.value[4], 'f', 1))) - .arg(qsTr("%0 A").arg(Qt.locale().toString(powerInfo.value[5], 'f', 1))) - .arg(qsTr("%0 A").arg(Qt.locale().toString(powerInfo.value[6], 'f', 1))) - } - - function getChargingAmount() { - return qsTr("%0 kWh").arg(Qt.locale().toString(energyInfo.value / 1000., 'f', 1)) - } - } - } - - Image { - Layout.preferredWidth: parent.width / 4 - Layout.preferredHeight: paintedHeight - - fillMode: Image.PreserveAspectFit - ApiKeyValueHelper { - id: devicetype + id: errApiKeyHelper deviceConnection: theDeviceConnection - apiKey: "typ" + apiKey: "err" } - ApiKeyValueHelper { - id: isgo - deviceConnection: theDeviceConnection - apiKey: "isgo" - } - - source: { - if (devicetype.value == "go-eCharger_V5" || - devicetype.value == "go-eCharger_V4") + text: { + switch (carApiKeyHelper.value) { - if (isgo.value) - return "images/geminiFlex.png" - else - return "images/geminiFix.png" - } else if (devicetype.value == "wattpilot_V2") { - return "images/wattpilot.png" - } else if (devicetype.value == "go-eCharger" || - devicetype.value == "wattpilot") { - return "images/homeFix.png" - } else if (devicetype.value == "go-eCharger_Phoenix") { - return "images/phoenix.png" + case 0: return qsTr("Internal error") + case 1: return qsTr("No car connected") + case 2: return qsTr("Car is charging") + case 3: return qsTr("Connecting to your car...") + case 4: return qsTr("Charging completed") + case 5: return qsTr("Unknown error %0").arg(errApiKeyHelper.value) } + } + font.pixelSize: 20 + font.bold: true + wrapMode: Text.Wrap + } - return "material-icons/grid_guides.svg" + Text { + ApiKeyValueHelper { + id: chargingDurationInfo + deviceConnection: theDeviceConnection + apiKey: "cdi" + } + ApiKeyValueHelper { + id: powerInfo + deviceConnection: theDeviceConnection + apiKey: "nrg" + } + ApiKeyValueHelper { + id: energyInfo + deviceConnection: theDeviceConnection + apiKey: "wh" + } + + Layout.fillWidth: true + + text: { + switch (carApiKeyHelper.value) + { + case 0: return null + case 1: return qsTr("Plug in the cable to start charging your car") + case 2: return qsTr("Duration: %0").arg(getDurationInfo()) + "\n" + + qsTr("Speed: %0").arg(getChargingSpeed()) + "\n" + + qsTr("Amount: %0").arg(getChargingAmount()) + case 3: return qsTr("Charger is connecting to your car, it usually takes a few seconds") + case 4: return qsTr("Let's go-e :)") + case 5: return null + } + } + wrapMode: Text.Wrap + + function getDurationInfo() { + if (!chargingDurationInfo.exists) + return qsTr("(api key doesnt exist)") + if (typeof chargingDurationInfo.value == null) + return qsTr("(api key is null)") + if (typeof chargingDurationInfo.value !== 'object') + return qsTr("(api key is not an object)") + if (!('type' in chargingDurationInfo.value)) + return qsTr("(api key does not contain a type)") + switch (chargingDurationInfo.value.type) + { + case 0: // counter going up, use rbt + return formatDuration(rebootTime.value - chargingDurationInfo.value.value) + case 1: // counter frozen, absolute duration + return formatDuration(chargingDurationInfo.value.value) + } + return qsTr("(api key has unknown type %0)").arg(chargingDurationInfo.value.type) + } + + function getChargingSpeed() { + return qsTr("%0 %1 %2") + .arg(qsTr("%0 A").arg(Qt.locale().toString(powerInfo.value[4], 'f', 1))) + .arg(qsTr("%0 A").arg(Qt.locale().toString(powerInfo.value[5], 'f', 1))) + .arg(qsTr("%0 A").arg(Qt.locale().toString(powerInfo.value[6], 'f', 1))) + } + + function getChargingAmount() { + return qsTr("%0 kWh").arg(Qt.locale().toString(energyInfo.value / 1000., 'f', 1)) } } } - StartStopButton { - Layout.fillWidth: true - deviceConnection: theDeviceConnection - } + Image { + Layout.preferredWidth: parent.width / 4 + Layout.preferredHeight: paintedHeight - ApiKeyValueHelper { - id: logicMode - deviceConnection: theDeviceConnection - apiKey: "lmo" - } + fillMode: Image.PreserveAspectFit - SelectLogicModeItem { - Layout.fillWidth: true - } - - NavigationItem { ApiKeyValueHelper { - id: priceLimitHelper + id: devicetype deviceConnection: theDeviceConnection - apiKey: "awp" + apiKey: "typ" } - visible: logicMode.value == 4 - iconSource: "material-icons/grid_guides.svg" - title: qsTr("Price limit") - description: qsTr("%0 ct/kWh").arg(Qt.locale().toString(priceLimitHelper.value, 'f', 1)) - component: Component { - SetPriceLimitPage { + ApiKeyValueHelper { + id: isgo + deviceConnection: theDeviceConnection + apiKey: "isgo" + } + + source: { + if (devicetype.value == "go-eCharger_V5" || + devicetype.value == "go-eCharger_V4") + { + if (isgo.value) + return "images/geminiFlex.png" + else + return "images/geminiFix.png" + } else if (devicetype.value == "wattpilot_V2") { + return "images/wattpilot.png" + } else if (devicetype.value == "go-eCharger" || + devicetype.value == "wattpilot") { + return "images/homeFix.png" + } else if (devicetype.value == "go-eCharger_Phoenix") { + return "images/phoenix.png" } + + return "material-icons/grid_guides.svg" } } + } - NavigationItem { - visible: logicMode.value == 5 - iconSource: "material-icons/grid_guides.svg" - title: qsTr("Daily trip") - description: qsTr("By %0 with %1").arg("08:00").arg(qsTr("%0 km").arg(100)) - component: Component { - DailyTripPage { - } - } - } + StartStopButton { + Layout.fillWidth: true + deviceConnection: theDeviceConnection + } + ApiKeyValueHelper { + id: logicMode + deviceConnection: theDeviceConnection + apiKey: "lmo" + } + + SelectLogicModeItem { + Layout.fillWidth: true + } + + NavigationItem { ApiKeyValueHelper { - id: requestedCurrent + id: priceLimitHelper deviceConnection: theDeviceConnection - apiKey: "amp" + apiKey: "awp" } - ApiKeyValueHelper { - id: phaseSwitchMode - deviceConnection: theDeviceConnection - apiKey: "psm" + visible: logicMode.value == 4 + iconSource: "material-icons/grid_guides.svg" + title: qsTr("Price limit") + description: qsTr("%0 ct/kWh").arg(Qt.locale().toString(priceLimitHelper.value, 'f', 1)) + component: Component { + SetPriceLimitPage { + } } + } - NavigationItem { - iconSource: "material-icons/grid_guides.svg" - title: qsTr("Charging speed") - description: qsTr("%0 & %1") - .arg(qsTr("%0 Ampere").arg(requestedCurrent.value)) - .arg((function(){ - switch (phaseSwitchMode.value) - { - case 0: return qsTr("Automatic phase selection"); - case 1: return qsTr("1-phase"); - case 2: return qsTr("3-phase"); - case 3: return qsTr("Unknown phase selection (%0)").arg(phaseSwitchMode.value); - } - })()) - component: Component { - ChargingSpeedPage { + NavigationItem { + visible: logicMode.value == 5 + iconSource: "material-icons/grid_guides.svg" + title: qsTr("Daily trip") + description: qsTr("By %0 with %1").arg("08:00").arg(qsTr("%0 km").arg(100)) + component: Component { + DailyTripPage { + } + } + } + + ApiKeyValueHelper { + id: requestedCurrent + deviceConnection: theDeviceConnection + apiKey: "amp" + } + + ApiKeyValueHelper { + id: phaseSwitchMode + deviceConnection: theDeviceConnection + apiKey: "psm" + } + + NavigationItem { + iconSource: "material-icons/grid_guides.svg" + title: qsTr("Charging speed") + description: qsTr("%0 & %1") + .arg(qsTr("%0 Ampere").arg(requestedCurrent.value)) + .arg((function(){ + switch (phaseSwitchMode.value) + { + case 0: return qsTr("Automatic phase selection"); + case 1: return qsTr("1-phase"); + case 2: return qsTr("3-phase"); + case 3: return qsTr("Unknown phase selection (%0)").arg(phaseSwitchMode.value); } + })()) + component: Component { + ChargingSpeedPage { } } } diff --git a/ControllerTabPage.qml b/ControllerTabPage.qml index 28a643d..00de53a 100644 --- a/ControllerTabPage.qml +++ b/ControllerTabPage.qml @@ -24,112 +24,102 @@ AnimatedStackView { onCloseRequested: stackView.closeRequested() } - Flickable { - id: flickable + ScrollableTabPage { Layout.fillWidth: true Layout.fillHeight: true - contentHeight: columnLayout.implicitHeight - clip: true - ColumnLayout { - id: columnLayout - width: flickable.width - 30 - x: 15 - spacing: 5 + RowLayout { + Layout.fillWidth: true - RowLayout { + ColumnLayout { Layout.fillWidth: true + Layout.fillHeight: true - ColumnLayout { + Text { Layout.fillWidth: true - Layout.fillHeight: true + width: 100 - Text { - Layout.fillWidth: true - width: 100 - - text: qsTr("Connected") - font.pixelSize: 20 - font.bold: true - wrapMode: Text.Wrap - } - } - - Image { - Layout.preferredWidth: parent.width / 3 - Layout.preferredHeight: paintedHeight - - fillMode: Image.PreserveAspectFit - - source: "images/controller.png" + text: qsTr("Connected") + font.pixelSize: 20 + font.bold: true + wrapMode: Text.Wrap } } - WhiteBox { - Layout.fillWidth: true - Layout.preferredHeight: gridLayout.implicitHeight + 20 + Image { + Layout.preferredWidth: parent.width / 3 + Layout.preferredHeight: paintedHeight - GridLayout { - id: gridLayout - anchors.fill: parent - columns: 3 + fillMode: Image.PreserveAspectFit - Repeater { - model: 9 + source: "images/controller.png" + } + } - Pane { - Layout.fillWidth: true - Layout.preferredHeight: 50 + WhiteBox { + Layout.fillWidth: true + Layout.preferredHeight: gridLayout.implicitHeight + 20 - Component.onCompleted: { - background.color = "grey" - background.radius = 5 - } + GridLayout { + id: gridLayout + anchors.fill: parent + columns: 3 - Text { - anchors.fill: parent - text: qsTr("Category") - } + Repeater { + model: 9 + + Pane { + Layout.fillWidth: true + Layout.preferredHeight: 50 + + Component.onCompleted: { + background.color = "grey" + background.radius = 5 + } + + Text { + anchors.fill: parent + text: qsTr("Category") } } } } + } - Text { - Layout.fillWidth: true + Text { + Layout.fillWidth: true - text: "Controller TODO" - } + text: "Controller TODO" + } - Text { - Layout.fillWidth: true + Text { + Layout.fillWidth: true - text: "Controller TODO" - } + text: "Controller TODO" + } - Text { - Layout.fillWidth: true + Text { + Layout.fillWidth: true - text: "Controller TODO" - } + text: "Controller TODO" + } - Text { - Layout.fillWidth: true + Text { + Layout.fillWidth: true - text: "Controller TODO" - } + text: "Controller TODO" + } - Text { - Layout.fillWidth: true + Text { + Layout.fillWidth: true - text: "Controller TODO" - } + text: "Controller TODO" + } - Text { - Layout.fillWidth: true + Text { + Layout.fillWidth: true - text: "Controller TODO" - } + text: "Controller TODO" } } } diff --git a/InformationsTabPage.qml b/InformationsTabPage.qml index d6da9fb..e000ec4 100644 --- a/InformationsTabPage.qml +++ b/InformationsTabPage.qml @@ -1,7 +1,34 @@ import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtCharts + +ScrollableTabPage { + id: page -Item { function backPressed() { return false } + + Repeater { + model: 5 + + ChartView { + title: "Line" + Layout.fillWidth: true + Layout.preferredHeight: page.height * 0.75 + antialiasing: true + + LineSeries { + name: "LineSeries" + XYPoint { x: 0; y: 0 } + XYPoint { x: 1.1; y: 2.1 } + XYPoint { x: 1.9; y: 3.3 } + XYPoint { x: 2.1; y: 2.1 } + XYPoint { x: 2.9; y: 4.9 } + XYPoint { x: 3.4; y: 3.0 } + XYPoint { x: 4.1; y: 3.3 } + } + } + } } diff --git a/ScrollableTabPage.qml b/ScrollableTabPage.qml new file mode 100644 index 0000000..fc88f32 --- /dev/null +++ b/ScrollableTabPage.qml @@ -0,0 +1,20 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Flickable { + default property alias data: columnLayout.data + + id: flickable + Layout.fillWidth: true + Layout.fillHeight: true + contentHeight: columnLayout.implicitHeight + clip: true + + ColumnLayout { + id: columnLayout + width: flickable.width - 30 + x: 15 + spacing: 5 + } +} diff --git a/i18n/qml_de.ts b/i18n/qml_de.ts index 724a625..30faba1 100644 --- a/i18n/qml_de.ts +++ b/i18n/qml_de.ts @@ -367,192 +367,192 @@ Geräte - - + + Internal error Interner Fehler - - + + No car connected Kein Auto angeschlossen - - + + Car is charging Auto wird geladen - - + + Connecting to your car... Verbinde mit Auto... - - + + Charging completed Ladevorgang abgeschlossen - - + + Unknown error %0 Unbekannter Fehler %0 - - + + Plug in the cable to start charging your car Stecke das Kabel ein, um dein Auto aufzuladen - - + + Duration: %0 Dauer: %0 - - + + Speed: %0 Geschwindigkeit: %0 - - + + Amount: %0 Menge: %0 - - + + Charger is connecting to your car, it usually takes a few seconds Der Charger stellt die Verbindung zu deinem Auto her, das dauert in der Regel ein paar Sekunden - - + + Let's go-e :) Let's go-e :) - - + + (api key doesnt exist) (api key existiert nicht) - - + + (api key is null) (api key is null) - - + + (api key is not an object) (api key ist kein Objekt) - - + + (api key does not contain a type) (api key beinhaltet keinen Typ) - - + + (api key has unknown type %0) (api key hat unbekannten Typ %0) - - + + %0 %1 %2 %0 %1 %2 - - - - - - + + + + + + %0 A %0 A - - + + %0 kWh %0 kWh - - + + Price limit Preisgrenze - - + + %0 ct/kWh %0 ct/kWh - - + + By %0 with %1 Bis %0 mit %1 - - + + %0 km %0 km - - + + Charging speed Ladegeschwindigkeit - - + + %0 & %1 %0 & %1 - - + + %0 Ampere %0 Ampere - - + + Automatic phase selection Automatische Phasenwahl - - + + 1-phase 1-phasig - - + + 3-phase 3-phasig - - + + Unknown phase selection (%0) Unbekannte Phasen Selektion (%0) @@ -569,8 +569,8 @@ Start - - + + Daily trip Daily trip @@ -852,14 +852,14 @@ Geräte - - + + Connected Verbunden - - + + Category Kategorie diff --git a/main.cpp b/main.cpp index 8985c96..577a0f3 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -20,7 +20,7 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationDomain("brunner.ninja"); QCoreApplication::setApplicationName("evcharger-app"); - QGuiApplication app(argc, argv); + QApplication app(argc, argv); QTranslator translator; if (!translator.load(QLocale(), "qml", "_", ":/EVChargerApp/i18n/")) diff --git a/qmldir b/qmldir index 8f00eba..5cd9771 100644 --- a/qmldir +++ b/qmldir @@ -64,6 +64,7 @@ EVChargerApp 1.0 RebootPage.qml EVChargerApp 1.0 RequestStatusText.qml EVChargerApp 1.0 SchedulerDayPage.qml EVChargerApp 1.0 SchedulerPage.qml +EVChargerApp 1.0 ScrollableTabPage.qml EVChargerApp 1.0 SecurityPage.qml EVChargerApp 1.0 SelectLogicModeItem.qml EVChargerApp 1.0 SelectPhaseSwitchModeItem.qml