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