From f622d76ec84552a67326d71dbfc94a822523d067 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Fri, 11 Oct 2024 09:43:29 +0200 Subject: [PATCH] Add flotten-updater and goecommon lib --- CMakeLists.txt | 179 +---- android/src/QZeroConfNsdManager.java | 1 - evcharger-app/CMakeLists.txt | 171 +++++ .../android}/AndroidManifest.xml | 0 .../android}/build.gradle | 0 .../android}/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 {android => evcharger-app/android}/gradlew | 0 .../android}/gradlew.bat | 0 .../android}/res/drawable-hdpi/icon.png | Bin .../android}/res/drawable-hdpi/logo.png | Bin .../android}/res/drawable-hdpi/logo_land.png | Bin .../android}/res/drawable-hdpi/logo_port.png | Bin .../android}/res/drawable-ldpi/icon.png | Bin .../android}/res/drawable-ldpi/logo.png | Bin .../android}/res/drawable-ldpi/logo_land.png | Bin .../android}/res/drawable-ldpi/logo_port.png | Bin .../android}/res/drawable-mdpi/icon.png | Bin .../android}/res/drawable-mdpi/logo.png | Bin .../android}/res/drawable-mdpi/logo_land.png | Bin .../android}/res/drawable-mdpi/logo_port.png | Bin .../android}/res/drawable-xhdpi/icon.png | Bin .../android}/res/drawable-xhdpi/logo.png | Bin .../android}/res/drawable-xhdpi/logo_land.png | Bin .../android}/res/drawable-xhdpi/logo_port.png | Bin .../android}/res/drawable-xxhdpi/icon.png | Bin .../android}/res/drawable-xxhdpi/logo.png | Bin .../res/drawable-xxhdpi/logo_land.png | Bin .../res/drawable-xxhdpi/logo_port.png | Bin .../android}/res/drawable-xxxhdpi/icon.png | Bin .../android}/res/drawable-xxxhdpi/logo.png | Bin .../res/drawable-xxxhdpi/logo_land.png | Bin .../res/drawable-xxxhdpi/logo_port.png | Bin .../android}/res/drawable/splashscreen.xml | 0 .../res/drawable/splashscreen_land.xml | 0 .../res/drawable/splashscreen_port.xml | 0 .../res/values-land/splashscreentheme.xml | 0 .../res/values-port/splashscreentheme.xml | 0 .../android}/res/values/libs.xml | 0 .../android}/res/values/splashscreentheme.xml | 0 .../android}/res/xml/qtprovider_paths.xml | 0 .../android/src/QZeroConfNsdManager.java | 1 + {src => evcharger-app}/apikeyvaluehelper.cpp | 0 {src => evcharger-app}/apikeyvaluehelper.h | 0 evcharger-app/appsettings.cpp | 32 + evcharger-app/appsettings.h | 15 + {src => evcharger-app}/deviceconnection.cpp | 0 {src => evcharger-app}/deviceconnection.h | 0 {src => evcharger-app}/devicesmodel.cpp | 0 {src => evcharger-app}/devicesmodel.h | 0 {i18n => evcharger-app/i18n}/qml_de.ts | 599 ++++++++++++++-- {i18n => evcharger-app/i18n}/qml_en.ts | 0 {src => evcharger-app}/main.cpp | 0 {qml => evcharger-app/qml}/AboutPage.qml | 0 {qml => evcharger-app/qml}/AccessPage.qml | 0 .../qml}/AddDeviceScreen.qml | 0 .../qml}/AnimatedStackLayout.qml | 0 .../qml}/AnimatedStackView.qml | 0 .../qml}/ApiKeyValueItem.qml | 0 .../qml}/ApiSettingsPage.qml | 0 {qml => evcharger-app/qml}/AppInstance.qml | 0 .../qml}/AppSettingsPage.qml | 0 .../qml}/BaseNavigationPage.qml | 0 {qml => evcharger-app/qml}/CablePage.qml | 0 {qml => evcharger-app/qml}/CarPage.qml | 0 {qml => evcharger-app/qml}/CellularPage.qml | 0 {qml => evcharger-app/qml}/CenteredDialog.qml | 0 {qml => evcharger-app/qml}/ChargerTabPage.qml | 0 .../qml}/ChargingConfigurationPage.qml | 0 .../qml}/ChargingSpeedPage.qml | 0 .../qml}/CloudApiQrCodePage.qml | 0 {qml => evcharger-app/qml}/CloudPage.qml | 0 {qml => evcharger-app/qml}/CloudUrlsModel.qml | 0 .../qml}/ConfirmingOnOffSwitch.qml | 0 .../qml}/ConnectingScreen.qml | 0 {qml => evcharger-app/qml}/ConnectionPage.qml | 0 {qml => evcharger-app/qml}/Constants.qml | 0 {qml => evcharger-app/qml}/ControllerPage.qml | 0 .../qml}/ControllerTabPage.qml | 0 .../qml}/CurrentLevelsPage.qml | 0 {qml => evcharger-app/qml}/DailyTripPage.qml | 0 .../qml}/DateAndTimePage.qml | 0 .../qml}/DeviceHeaderBar.qml | 0 .../qml}/DeviceListScreen.qml | 0 {qml => evcharger-app/qml}/DeviceScreen.qml | 0 .../qml}/DisplaySettingsPage.qml | 0 {qml => evcharger-app/qml}/DoubleSpinBox.qml | 0 {qml => evcharger-app/qml}/EVChargerApp.qml | 0 {qml => evcharger-app/qml}/EcoTabPage.qml | 0 {qml => evcharger-app/qml}/EditValueItem.qml | 0 {qml => evcharger-app/qml}/EthernetPage.qml | 0 {qml => evcharger-app/qml}/FirmwarePage.qml | 0 .../qml}/FlexibleEnergyTariffPage.qml | 0 .../qml}/GeneralOnOffSwitch.qml | 0 {qml => evcharger-app/qml}/GeneralPage.qml | 0 {qml => evcharger-app/qml}/GridPage.qml | 0 .../qml}/GroundCheckPage.qml | 0 .../qml}/HardwareInformationPage.qml | 0 {qml => evcharger-app/qml}/HotspotPage.qml | 0 .../qml}/InformationsTabPage.qml | 0 {qml => evcharger-app/qml}/KwhLimitPage.qml | 0 {qml => evcharger-app/qml}/LedPage.qml | 0 {qml => evcharger-app/qml}/LicensesPage.qml | 0 .../qml}/LoadBalancingPage.qml | 0 .../qml}/LogicModeButton.qml | 0 {qml => evcharger-app/qml}/MainScreen.qml | 0 {qml => evcharger-app/qml}/MqttPage.qml | 0 {qml => evcharger-app/qml}/NamePage.qml | 0 {qml => evcharger-app/qml}/NavigationItem.qml | 0 {qml => evcharger-app/qml}/NavigationPage.qml | 0 .../qml}/NotificationsPage.qml | 0 {qml => evcharger-app/qml}/OcppPage.qml | 0 {qml => evcharger-app/qml}/OpenLinkButton.qml | 0 {qml => evcharger-app/qml}/PasswordPage.qml | 0 {qml => evcharger-app/qml}/PvSurplusPage.qml | 0 {qml => evcharger-app/qml}/QrCode.qml | 0 {qml => evcharger-app/qml}/RebootPage.qml | 0 .../qml}/RequestStatusText.qml | 0 .../qml}/SchedulerDayPage.qml | 0 {qml => evcharger-app/qml}/SchedulerPage.qml | 0 .../qml}/ScrollableTabPage.qml | 0 {qml => evcharger-app/qml}/SecurityPage.qml | 0 .../qml}/SelectLogicModeItem.qml | 0 .../qml}/SelectPhaseSwitchModeItem.qml | 0 .../qml}/SensorsConfigurationPage.qml | 0 .../qml}/SetPriceLimitPage.qml | 0 {qml => evcharger-app/qml}/SetValueHelper.qml | 0 .../qml}/SettingsTabPage.qml | 0 .../qml}/SimpleNavigationItem.qml | 0 .../qml}/StartStopButton.qml | 0 .../qml}/SwitchLanguagePage.qml | 0 .../qml}/TimeComponentLabel.qml | 0 {qml => evcharger-app/qml}/TimePicker.qml | 0 .../qml}/TimePickerDialog.qml | 0 .../qml}/TimePickerLabel.qml | 0 {qml => evcharger-app/qml}/TitleText.qml | 0 .../qml}/VerticalTabButton.qml | 0 {qml => evcharger-app/qml}/WhiteBox.qml | 0 .../qml}/WhiteCheckDelegate.qml | 0 .../qml}/WhiteItemDelegate.qml | 0 .../qml}/WhiteSwipeDelegate.qml | 0 {qml => evcharger-app/qml}/WiFiErrorsPage.qml | 0 {qml => evcharger-app/qml}/WiFiPage.qml | 0 {qml => evcharger-app/qml}/WiFiScanPage.qml | 0 {qml => evcharger-app/qml}/icons/Alarm.svg | 0 {qml => evcharger-app/qml}/icons/Charger.svg | 0 .../qml}/icons/ChargerV3.svg | 0 .../qml}/icons/ChargerV4.svg | 0 {qml => evcharger-app/qml}/icons/Charts.svg | 0 .../qml}/icons/Controller.svg | 0 .../qml}/icons/EcoModeFilled.svg | 0 .../qml}/images/controller.png | Bin .../qml}/images/geminiFix.png | Bin .../qml}/images/geminiFlex.png | Bin {qml => evcharger-app/qml}/images/homeFix.png | Bin .../qml}/images/homePlus.png | Bin {qml => evcharger-app/qml}/images/phoenix.png | Bin .../qml}/images/wattpilot.png | Bin evcharger-app/qml/js/qrcode.min.js | 1 + .../qml}/material-icons/add.svg | 0 .../qml}/material-icons/grid_guides.svg | 0 .../qml}/material-icons/open_in_new.svg | 0 .../qml}/material-icons/settings.svg | 0 {qml => evcharger-app/qml}/qmldir | 0 .../qml}/ui-icons/MaterialIcons-Regular.ttf | Bin .../ui-icons/material-design-icons.LICENSE | 0 {qml => evcharger-app/qml}/update_qmldir.sh | 0 {src => evcharger-app}/sendmessagehelper.cpp | 0 {src => evcharger-app}/sendmessagehelper.h | 0 flotten-updater/CMakeLists.txt | 39 ++ flotten-updater/chargerconnection.cpp | 637 ++++++++++++++++++ flotten-updater/chargerconnection.h | 126 ++++ flotten-updater/chargersmodel.cpp | 434 ++++++++++++ flotten-updater/chargersmodel.h | 59 ++ flotten-updater/flottenupdatersettings.cpp | 21 + flotten-updater/flottenupdatersettings.h | 17 + flotten-updater/goe-root-ca.pem | 32 + flotten-updater/importcertificatedialog.cpp | 151 +++++ flotten-updater/importcertificatedialog.h | 36 + flotten-updater/importcertificatedialog.ui | 184 +++++ flotten-updater/main.cpp | 86 +++ flotten-updater/mainwindow.cpp | 189 ++++++ flotten-updater/mainwindow.h | 35 + flotten-updater/mainwindow.ui | 82 +++ flotten-updater/requestdialog.cpp | 19 + flotten-updater/requestdialog.h | 24 + flotten-updater/requestdialog.ui | 71 ++ flotten-updater/requestmodel.cpp | 177 +++++ flotten-updater/requestmodel.h | 37 + flotten-updater/setarbitraryapikeydialog.cpp | 61 ++ flotten-updater/setarbitraryapikeydialog.h | 28 + flotten-updater/setarbitraryapikeydialog.ui | 127 ++++ goecommon/CMakeLists.txt | 12 + .../goesettings.cpp | 57 +- src/appsettings.h => goecommon/goesettings.h | 6 +- qml/js/qrcode.min.js | 1 - 197 files changed, 3475 insertions(+), 272 deletions(-) delete mode 120000 android/src/QZeroConfNsdManager.java create mode 100644 evcharger-app/CMakeLists.txt rename {android => evcharger-app/android}/AndroidManifest.xml (100%) rename {android => evcharger-app/android}/build.gradle (100%) rename {android => evcharger-app/android}/gradle.properties (100%) rename {android => evcharger-app/android}/gradle/wrapper/gradle-wrapper.jar (100%) rename {android => evcharger-app/android}/gradle/wrapper/gradle-wrapper.properties (100%) rename {android => evcharger-app/android}/gradlew (100%) rename {android => evcharger-app/android}/gradlew.bat (100%) rename {android => evcharger-app/android}/res/drawable-hdpi/icon.png (100%) rename {android => evcharger-app/android}/res/drawable-hdpi/logo.png (100%) rename {android => evcharger-app/android}/res/drawable-hdpi/logo_land.png (100%) rename {android => evcharger-app/android}/res/drawable-hdpi/logo_port.png (100%) rename {android => evcharger-app/android}/res/drawable-ldpi/icon.png (100%) rename {android => evcharger-app/android}/res/drawable-ldpi/logo.png (100%) rename {android => evcharger-app/android}/res/drawable-ldpi/logo_land.png (100%) rename {android => evcharger-app/android}/res/drawable-ldpi/logo_port.png (100%) rename {android => evcharger-app/android}/res/drawable-mdpi/icon.png (100%) rename {android => evcharger-app/android}/res/drawable-mdpi/logo.png (100%) rename {android => evcharger-app/android}/res/drawable-mdpi/logo_land.png (100%) rename {android => evcharger-app/android}/res/drawable-mdpi/logo_port.png (100%) rename {android => evcharger-app/android}/res/drawable-xhdpi/icon.png (100%) rename {android => evcharger-app/android}/res/drawable-xhdpi/logo.png (100%) rename {android => evcharger-app/android}/res/drawable-xhdpi/logo_land.png (100%) rename {android => evcharger-app/android}/res/drawable-xhdpi/logo_port.png (100%) rename {android => evcharger-app/android}/res/drawable-xxhdpi/icon.png (100%) rename {android => evcharger-app/android}/res/drawable-xxhdpi/logo.png (100%) rename {android => evcharger-app/android}/res/drawable-xxhdpi/logo_land.png (100%) rename {android => evcharger-app/android}/res/drawable-xxhdpi/logo_port.png (100%) rename {android => evcharger-app/android}/res/drawable-xxxhdpi/icon.png (100%) rename {android => evcharger-app/android}/res/drawable-xxxhdpi/logo.png (100%) rename {android => evcharger-app/android}/res/drawable-xxxhdpi/logo_land.png (100%) rename {android => evcharger-app/android}/res/drawable-xxxhdpi/logo_port.png (100%) rename {android => evcharger-app/android}/res/drawable/splashscreen.xml (100%) rename {android => evcharger-app/android}/res/drawable/splashscreen_land.xml (100%) rename {android => evcharger-app/android}/res/drawable/splashscreen_port.xml (100%) rename {android => evcharger-app/android}/res/values-land/splashscreentheme.xml (100%) rename {android => evcharger-app/android}/res/values-port/splashscreentheme.xml (100%) rename {android => evcharger-app/android}/res/values/libs.xml (100%) rename {android => evcharger-app/android}/res/values/splashscreentheme.xml (100%) rename {android => evcharger-app/android}/res/xml/qtprovider_paths.xml (100%) create mode 120000 evcharger-app/android/src/QZeroConfNsdManager.java rename {src => evcharger-app}/apikeyvaluehelper.cpp (100%) rename {src => evcharger-app}/apikeyvaluehelper.h (100%) create mode 100644 evcharger-app/appsettings.cpp create mode 100644 evcharger-app/appsettings.h rename {src => evcharger-app}/deviceconnection.cpp (100%) rename {src => evcharger-app}/deviceconnection.h (100%) rename {src => evcharger-app}/devicesmodel.cpp (100%) rename {src => evcharger-app}/devicesmodel.h (100%) rename {i18n => evcharger-app/i18n}/qml_de.ts (79%) rename {i18n => evcharger-app/i18n}/qml_en.ts (100%) rename {src => evcharger-app}/main.cpp (100%) rename {qml => evcharger-app/qml}/AboutPage.qml (100%) rename {qml => evcharger-app/qml}/AccessPage.qml (100%) rename {qml => evcharger-app/qml}/AddDeviceScreen.qml (100%) rename {qml => evcharger-app/qml}/AnimatedStackLayout.qml (100%) rename {qml => evcharger-app/qml}/AnimatedStackView.qml (100%) rename {qml => evcharger-app/qml}/ApiKeyValueItem.qml (100%) rename {qml => evcharger-app/qml}/ApiSettingsPage.qml (100%) rename {qml => evcharger-app/qml}/AppInstance.qml (100%) rename {qml => evcharger-app/qml}/AppSettingsPage.qml (100%) rename {qml => evcharger-app/qml}/BaseNavigationPage.qml (100%) rename {qml => evcharger-app/qml}/CablePage.qml (100%) rename {qml => evcharger-app/qml}/CarPage.qml (100%) rename {qml => evcharger-app/qml}/CellularPage.qml (100%) rename {qml => evcharger-app/qml}/CenteredDialog.qml (100%) rename {qml => evcharger-app/qml}/ChargerTabPage.qml (100%) rename {qml => evcharger-app/qml}/ChargingConfigurationPage.qml (100%) rename {qml => evcharger-app/qml}/ChargingSpeedPage.qml (100%) rename {qml => evcharger-app/qml}/CloudApiQrCodePage.qml (100%) rename {qml => evcharger-app/qml}/CloudPage.qml (100%) rename {qml => evcharger-app/qml}/CloudUrlsModel.qml (100%) rename {qml => evcharger-app/qml}/ConfirmingOnOffSwitch.qml (100%) rename {qml => evcharger-app/qml}/ConnectingScreen.qml (100%) rename {qml => evcharger-app/qml}/ConnectionPage.qml (100%) rename {qml => evcharger-app/qml}/Constants.qml (100%) rename {qml => evcharger-app/qml}/ControllerPage.qml (100%) rename {qml => evcharger-app/qml}/ControllerTabPage.qml (100%) rename {qml => evcharger-app/qml}/CurrentLevelsPage.qml (100%) rename {qml => evcharger-app/qml}/DailyTripPage.qml (100%) rename {qml => evcharger-app/qml}/DateAndTimePage.qml (100%) rename {qml => evcharger-app/qml}/DeviceHeaderBar.qml (100%) rename {qml => evcharger-app/qml}/DeviceListScreen.qml (100%) rename {qml => evcharger-app/qml}/DeviceScreen.qml (100%) rename {qml => evcharger-app/qml}/DisplaySettingsPage.qml (100%) rename {qml => evcharger-app/qml}/DoubleSpinBox.qml (100%) rename {qml => evcharger-app/qml}/EVChargerApp.qml (100%) rename {qml => evcharger-app/qml}/EcoTabPage.qml (100%) rename {qml => evcharger-app/qml}/EditValueItem.qml (100%) rename {qml => evcharger-app/qml}/EthernetPage.qml (100%) rename {qml => evcharger-app/qml}/FirmwarePage.qml (100%) rename {qml => evcharger-app/qml}/FlexibleEnergyTariffPage.qml (100%) rename {qml => evcharger-app/qml}/GeneralOnOffSwitch.qml (100%) rename {qml => evcharger-app/qml}/GeneralPage.qml (100%) rename {qml => evcharger-app/qml}/GridPage.qml (100%) rename {qml => evcharger-app/qml}/GroundCheckPage.qml (100%) rename {qml => evcharger-app/qml}/HardwareInformationPage.qml (100%) rename {qml => evcharger-app/qml}/HotspotPage.qml (100%) rename {qml => evcharger-app/qml}/InformationsTabPage.qml (100%) rename {qml => evcharger-app/qml}/KwhLimitPage.qml (100%) rename {qml => evcharger-app/qml}/LedPage.qml (100%) rename {qml => evcharger-app/qml}/LicensesPage.qml (100%) rename {qml => evcharger-app/qml}/LoadBalancingPage.qml (100%) rename {qml => evcharger-app/qml}/LogicModeButton.qml (100%) rename {qml => evcharger-app/qml}/MainScreen.qml (100%) rename {qml => evcharger-app/qml}/MqttPage.qml (100%) rename {qml => evcharger-app/qml}/NamePage.qml (100%) rename {qml => evcharger-app/qml}/NavigationItem.qml (100%) rename {qml => evcharger-app/qml}/NavigationPage.qml (100%) rename {qml => evcharger-app/qml}/NotificationsPage.qml (100%) rename {qml => evcharger-app/qml}/OcppPage.qml (100%) rename {qml => evcharger-app/qml}/OpenLinkButton.qml (100%) rename {qml => evcharger-app/qml}/PasswordPage.qml (100%) rename {qml => evcharger-app/qml}/PvSurplusPage.qml (100%) rename {qml => evcharger-app/qml}/QrCode.qml (100%) rename {qml => evcharger-app/qml}/RebootPage.qml (100%) rename {qml => evcharger-app/qml}/RequestStatusText.qml (100%) rename {qml => evcharger-app/qml}/SchedulerDayPage.qml (100%) rename {qml => evcharger-app/qml}/SchedulerPage.qml (100%) rename {qml => evcharger-app/qml}/ScrollableTabPage.qml (100%) rename {qml => evcharger-app/qml}/SecurityPage.qml (100%) rename {qml => evcharger-app/qml}/SelectLogicModeItem.qml (100%) rename {qml => evcharger-app/qml}/SelectPhaseSwitchModeItem.qml (100%) rename {qml => evcharger-app/qml}/SensorsConfigurationPage.qml (100%) rename {qml => evcharger-app/qml}/SetPriceLimitPage.qml (100%) rename {qml => evcharger-app/qml}/SetValueHelper.qml (100%) rename {qml => evcharger-app/qml}/SettingsTabPage.qml (100%) rename {qml => evcharger-app/qml}/SimpleNavigationItem.qml (100%) rename {qml => evcharger-app/qml}/StartStopButton.qml (100%) rename {qml => evcharger-app/qml}/SwitchLanguagePage.qml (100%) rename {qml => evcharger-app/qml}/TimeComponentLabel.qml (100%) rename {qml => evcharger-app/qml}/TimePicker.qml (100%) rename {qml => evcharger-app/qml}/TimePickerDialog.qml (100%) rename {qml => evcharger-app/qml}/TimePickerLabel.qml (100%) rename {qml => evcharger-app/qml}/TitleText.qml (100%) rename {qml => evcharger-app/qml}/VerticalTabButton.qml (100%) rename {qml => evcharger-app/qml}/WhiteBox.qml (100%) rename {qml => evcharger-app/qml}/WhiteCheckDelegate.qml (100%) rename {qml => evcharger-app/qml}/WhiteItemDelegate.qml (100%) rename {qml => evcharger-app/qml}/WhiteSwipeDelegate.qml (100%) rename {qml => evcharger-app/qml}/WiFiErrorsPage.qml (100%) rename {qml => evcharger-app/qml}/WiFiPage.qml (100%) rename {qml => evcharger-app/qml}/WiFiScanPage.qml (100%) rename {qml => evcharger-app/qml}/icons/Alarm.svg (100%) rename {qml => evcharger-app/qml}/icons/Charger.svg (100%) rename {qml => evcharger-app/qml}/icons/ChargerV3.svg (100%) rename {qml => evcharger-app/qml}/icons/ChargerV4.svg (100%) rename {qml => evcharger-app/qml}/icons/Charts.svg (100%) rename {qml => evcharger-app/qml}/icons/Controller.svg (100%) rename {qml => evcharger-app/qml}/icons/EcoModeFilled.svg (100%) rename {qml => evcharger-app/qml}/images/controller.png (100%) rename {qml => evcharger-app/qml}/images/geminiFix.png (100%) rename {qml => evcharger-app/qml}/images/geminiFlex.png (100%) rename {qml => evcharger-app/qml}/images/homeFix.png (100%) rename {qml => evcharger-app/qml}/images/homePlus.png (100%) rename {qml => evcharger-app/qml}/images/phoenix.png (100%) rename {qml => evcharger-app/qml}/images/wattpilot.png (100%) create mode 120000 evcharger-app/qml/js/qrcode.min.js rename {qml => evcharger-app/qml}/material-icons/add.svg (100%) rename {qml => evcharger-app/qml}/material-icons/grid_guides.svg (100%) rename {qml => evcharger-app/qml}/material-icons/open_in_new.svg (100%) rename {qml => evcharger-app/qml}/material-icons/settings.svg (100%) rename {qml => evcharger-app/qml}/qmldir (100%) rename {qml => evcharger-app/qml}/ui-icons/MaterialIcons-Regular.ttf (100%) rename {qml => evcharger-app/qml}/ui-icons/material-design-icons.LICENSE (100%) rename {qml => evcharger-app/qml}/update_qmldir.sh (100%) rename {src => evcharger-app}/sendmessagehelper.cpp (100%) rename {src => evcharger-app}/sendmessagehelper.h (100%) create mode 100644 flotten-updater/CMakeLists.txt create mode 100644 flotten-updater/chargerconnection.cpp create mode 100644 flotten-updater/chargerconnection.h create mode 100644 flotten-updater/chargersmodel.cpp create mode 100644 flotten-updater/chargersmodel.h create mode 100644 flotten-updater/flottenupdatersettings.cpp create mode 100644 flotten-updater/flottenupdatersettings.h create mode 100644 flotten-updater/goe-root-ca.pem create mode 100644 flotten-updater/importcertificatedialog.cpp create mode 100644 flotten-updater/importcertificatedialog.h create mode 100644 flotten-updater/importcertificatedialog.ui create mode 100644 flotten-updater/main.cpp create mode 100644 flotten-updater/mainwindow.cpp create mode 100644 flotten-updater/mainwindow.h create mode 100644 flotten-updater/mainwindow.ui create mode 100644 flotten-updater/requestdialog.cpp create mode 100644 flotten-updater/requestdialog.h create mode 100644 flotten-updater/requestdialog.ui create mode 100644 flotten-updater/requestmodel.cpp create mode 100644 flotten-updater/requestmodel.h create mode 100644 flotten-updater/setarbitraryapikeydialog.cpp create mode 100644 flotten-updater/setarbitraryapikeydialog.h create mode 100644 flotten-updater/setarbitraryapikeydialog.ui create mode 100644 goecommon/CMakeLists.txt rename src/appsettings.cpp => goecommon/goesettings.cpp (72%) rename src/appsettings.h => goecommon/goesettings.h (89%) delete mode 120000 qml/js/qrcode.min.js diff --git a/CMakeLists.txt b/CMakeLists.txt index ec3e813..7f9d3b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,179 +19,8 @@ find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Quick WebSockets LinguistT qt_standard_project_setup(REQUIRES 6.6 I18N_TRANSLATED_LANGUAGES de) -qt_add_executable(evcharger-app WIN32 MACOSX_BUNDLE - src/apikeyvaluehelper.cpp - src/apikeyvaluehelper.h - src/appsettings.cpp - src/appsettings.h - src/deviceconnection.cpp - src/deviceconnection.h - src/devicesmodel.cpp - src/devicesmodel.h - src/main.cpp - src/sendmessagehelper.cpp - src/sendmessagehelper.h -) - -target_include_directories(evcharger-app - PRIVATE - src -) - -qt6_add_translations(evcharger-app - RESOURCE_PREFIX /EVChargerApp/i18n - TS_FILE_BASE qml - TS_FILE_DIR i18n -) - -set_property(TARGET evcharger-app APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android) - -if (ANDROID) - include(${ANDROID_SDK_ROOT}/android_openssl/CMakeLists.txt) - set_target_properties(evcharger-app PROPERTIES QT_ANDROID_EXTRA_LIBS "${ANDROID_EXTRA_LIBS}") +add_subdirectory(evcharger-app) +if (NOT ANDROID) +add_subdirectory(flotten-updater) endif() - -qt_add_qml_module(evcharger-app - URI EVChargerApp - VERSION 1.0 - QML_FILES - qml/AboutPage.qml - qml/AccessPage.qml - qml/AddDeviceScreen.qml - qml/AnimatedStackLayout.qml - qml/AnimatedStackView.qml - qml/ApiKeyValueItem.qml - qml/ApiSettingsPage.qml - qml/AppInstance.qml - qml/AppSettingsPage.qml - qml/BaseNavigationPage.qml - qml/CablePage.qml - qml/CarPage.qml - qml/CellularPage.qml - qml/CenteredDialog.qml - qml/ChargerTabPage.qml - qml/ChargingConfigurationPage.qml - qml/ChargingSpeedPage.qml - qml/CloudApiQrCodePage.qml - qml/CloudPage.qml - qml/CloudUrlsModel.qml - qml/ConfirmingOnOffSwitch.qml - qml/ConnectingScreen.qml - qml/ConnectionPage.qml - qml/Constants.qml - qml/ControllerPage.qml - qml/ControllerTabPage.qml - qml/CurrentLevelsPage.qml - qml/DailyTripPage.qml - qml/DateAndTimePage.qml - qml/DeviceHeaderBar.qml - qml/DeviceListScreen.qml - qml/DeviceScreen.qml - qml/DisplaySettingsPage.qml - qml/DoubleSpinBox.qml - qml/EcoTabPage.qml - qml/EditValueItem.qml - qml/EthernetPage.qml - qml/EVChargerApp.qml - qml/FirmwarePage.qml - qml/FlexibleEnergyTariffPage.qml - qml/GeneralOnOffSwitch.qml - qml/GeneralPage.qml - qml/GridPage.qml - qml/GroundCheckPage.qml - qml/HardwareInformationPage.qml - qml/HotspotPage.qml - qml/InformationsTabPage.qml - qml/KwhLimitPage.qml - qml/LedPage.qml - qml/LicensesPage.qml - qml/LoadBalancingPage.qml - qml/LogicModeButton.qml - qml/MainScreen.qml - qml/MqttPage.qml - qml/NamePage.qml - qml/NavigationItem.qml - qml/NavigationPage.qml - qml/NotificationsPage.qml - qml/OcppPage.qml - qml/OpenLinkButton.qml - qml/PasswordPage.qml - qml/PvSurplusPage.qml - qml/QrCode.qml - qml/RebootPage.qml - qml/RequestStatusText.qml - qml/SchedulerDayPage.qml - qml/SchedulerPage.qml - qml/ScrollableTabPage.qml - qml/SecurityPage.qml - qml/SelectLogicModeItem.qml - qml/SelectPhaseSwitchModeItem.qml - qml/SensorsConfigurationPage.qml - qml/SetPriceLimitPage.qml - qml/SettingsTabPage.qml - qml/SetValueHelper.qml - qml/SimpleNavigationItem.qml - qml/StartStopButton.qml - qml/SwitchLanguagePage.qml - qml/TimeComponentLabel.qml - qml/TimePickerDialog.qml - qml/TimePickerLabel.qml - qml/TimePicker.qml - qml/TitleText.qml - qml/VerticalTabButton.qml - qml/WhiteBox.qml - qml/WhiteCheckDelegate.qml - qml/WhiteItemDelegate.qml - qml/WhiteSwipeDelegate.qml - qml/WiFiErrorsPage.qml - qml/WiFiPage.qml - qml/WiFiScanPage.qml - RESOURCES - qml/icons/Charger.svg - qml/icons/ChargerV3.svg - qml/icons/ChargerV4.svg - qml/icons/Controller.svg - qml/icons/Alarm.svg - qml/icons/EcoModeFilled.svg - qml/icons/Charts.svg - qml/images/controller.png - qml/images/geminiFlex.png - qml/images/homeFix.png - qml/images/homePlus.png - qml/images/wattpilot.png - qml/images/phoenix.png - qml/images/geminiFix.png - qml/js/qrcode.min.js - qml/material-icons/add.svg - qml/material-icons/grid_guides.svg - qml/material-icons/open_in_new.svg - qml/material-icons/settings.svg - qml/ui-icons/MaterialIcons-Regular.ttf -) - -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 - QtZeroConf -) - -install(TARGETS evcharger-app - BUNDLE DESTINATION . - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} -) - -qt_generate_deploy_qml_app_script( - TARGET evcharger-app - OUTPUT_SCRIPT deploy_script - MACOS_BUNDLE_POST_BUILD - NO_UNSUPPORTED_PLATFORM_ERROR - DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM -) -install(SCRIPT ${deploy_script}) +add_subdirectory(goecommon) diff --git a/android/src/QZeroConfNsdManager.java b/android/src/QZeroConfNsdManager.java deleted file mode 120000 index 337cc26..0000000 --- a/android/src/QZeroConfNsdManager.java +++ /dev/null @@ -1 +0,0 @@ -../../3rdparty/QtZeroConf/QZeroConfNsdManager.java \ No newline at end of file diff --git a/evcharger-app/CMakeLists.txt b/evcharger-app/CMakeLists.txt new file mode 100644 index 0000000..c76c8e6 --- /dev/null +++ b/evcharger-app/CMakeLists.txt @@ -0,0 +1,171 @@ +qt_add_executable(evcharger-app WIN32 MACOSX_BUNDLE + appsettings.cpp + appsettings.h + apikeyvaluehelper.cpp + apikeyvaluehelper.h + deviceconnection.cpp + deviceconnection.h + devicesmodel.cpp + devicesmodel.h + main.cpp + sendmessagehelper.cpp + sendmessagehelper.h +) + +qt6_add_translations(evcharger-app + RESOURCE_PREFIX /EVChargerApp/i18n + TS_FILE_BASE qml + TS_FILE_DIR i18n +) + +if (ANDROID) + set_property(TARGET evcharger-app APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android) + include(${ANDROID_SDK_ROOT}/android_openssl/CMakeLists.txt) + set_target_properties(evcharger-app PROPERTIES QT_ANDROID_EXTRA_LIBS "${ANDROID_EXTRA_LIBS}") +endif() + +qt_add_qml_module(evcharger-app + URI EVChargerApp + VERSION 1.0 + QML_FILES + qml/AboutPage.qml + qml/AccessPage.qml + qml/AddDeviceScreen.qml + qml/AnimatedStackLayout.qml + qml/AnimatedStackView.qml + qml/ApiKeyValueItem.qml + qml/ApiSettingsPage.qml + qml/AppInstance.qml + qml/AppSettingsPage.qml + qml/BaseNavigationPage.qml + qml/CablePage.qml + qml/CarPage.qml + qml/CellularPage.qml + qml/CenteredDialog.qml + qml/ChargerTabPage.qml + qml/ChargingConfigurationPage.qml + qml/ChargingSpeedPage.qml + qml/CloudApiQrCodePage.qml + qml/CloudPage.qml + qml/CloudUrlsModel.qml + qml/ConfirmingOnOffSwitch.qml + qml/ConnectingScreen.qml + qml/ConnectionPage.qml + qml/Constants.qml + qml/ControllerPage.qml + qml/ControllerTabPage.qml + qml/CurrentLevelsPage.qml + qml/DailyTripPage.qml + qml/DateAndTimePage.qml + qml/DeviceHeaderBar.qml + qml/DeviceListScreen.qml + qml/DeviceScreen.qml + qml/DisplaySettingsPage.qml + qml/DoubleSpinBox.qml + qml/EcoTabPage.qml + qml/EditValueItem.qml + qml/EthernetPage.qml + qml/EVChargerApp.qml + qml/FirmwarePage.qml + qml/FlexibleEnergyTariffPage.qml + qml/GeneralOnOffSwitch.qml + qml/GeneralPage.qml + qml/GridPage.qml + qml/GroundCheckPage.qml + qml/HardwareInformationPage.qml + qml/HotspotPage.qml + qml/InformationsTabPage.qml + qml/KwhLimitPage.qml + qml/LedPage.qml + qml/LicensesPage.qml + qml/LoadBalancingPage.qml + qml/LogicModeButton.qml + qml/MainScreen.qml + qml/MqttPage.qml + qml/NamePage.qml + qml/NavigationItem.qml + qml/NavigationPage.qml + qml/NotificationsPage.qml + qml/OcppPage.qml + qml/OpenLinkButton.qml + qml/PasswordPage.qml + qml/PvSurplusPage.qml + qml/QrCode.qml + qml/RebootPage.qml + qml/RequestStatusText.qml + qml/SchedulerDayPage.qml + qml/SchedulerPage.qml + qml/ScrollableTabPage.qml + qml/SecurityPage.qml + qml/SelectLogicModeItem.qml + qml/SelectPhaseSwitchModeItem.qml + qml/SensorsConfigurationPage.qml + qml/SetPriceLimitPage.qml + qml/SettingsTabPage.qml + qml/SetValueHelper.qml + qml/SimpleNavigationItem.qml + qml/StartStopButton.qml + qml/SwitchLanguagePage.qml + qml/TimeComponentLabel.qml + qml/TimePickerDialog.qml + qml/TimePickerLabel.qml + qml/TimePicker.qml + qml/TitleText.qml + qml/VerticalTabButton.qml + qml/WhiteBox.qml + qml/WhiteCheckDelegate.qml + qml/WhiteItemDelegate.qml + qml/WhiteSwipeDelegate.qml + qml/WiFiErrorsPage.qml + qml/WiFiPage.qml + qml/WiFiScanPage.qml + RESOURCES + qml/icons/Charger.svg + qml/icons/ChargerV3.svg + qml/icons/ChargerV4.svg + qml/icons/Controller.svg + qml/icons/Alarm.svg + qml/icons/EcoModeFilled.svg + qml/icons/Charts.svg + qml/images/controller.png + qml/images/geminiFlex.png + qml/images/homeFix.png + qml/images/homePlus.png + qml/images/wattpilot.png + qml/images/phoenix.png + qml/images/geminiFix.png + qml/js/qrcode.min.js + qml/material-icons/add.svg + qml/material-icons/grid_guides.svg + qml/material-icons/open_in_new.svg + qml/material-icons/settings.svg + qml/ui-icons/MaterialIcons-Regular.ttf +) + +set_source_files_properties(qml/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 + QtZeroConf + goecommon +) + +install(TARGETS evcharger-app + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET evcharger-app + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/android/AndroidManifest.xml b/evcharger-app/android/AndroidManifest.xml similarity index 100% rename from android/AndroidManifest.xml rename to evcharger-app/android/AndroidManifest.xml diff --git a/android/build.gradle b/evcharger-app/android/build.gradle similarity index 100% rename from android/build.gradle rename to evcharger-app/android/build.gradle diff --git a/android/gradle.properties b/evcharger-app/android/gradle.properties similarity index 100% rename from android/gradle.properties rename to evcharger-app/android/gradle.properties diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/evcharger-app/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from android/gradle/wrapper/gradle-wrapper.jar rename to evcharger-app/android/gradle/wrapper/gradle-wrapper.jar diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/evcharger-app/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from android/gradle/wrapper/gradle-wrapper.properties rename to evcharger-app/android/gradle/wrapper/gradle-wrapper.properties diff --git a/android/gradlew b/evcharger-app/android/gradlew similarity index 100% rename from android/gradlew rename to evcharger-app/android/gradlew diff --git a/android/gradlew.bat b/evcharger-app/android/gradlew.bat similarity index 100% rename from android/gradlew.bat rename to evcharger-app/android/gradlew.bat diff --git a/android/res/drawable-hdpi/icon.png b/evcharger-app/android/res/drawable-hdpi/icon.png similarity index 100% rename from android/res/drawable-hdpi/icon.png rename to evcharger-app/android/res/drawable-hdpi/icon.png diff --git a/android/res/drawable-hdpi/logo.png b/evcharger-app/android/res/drawable-hdpi/logo.png similarity index 100% rename from android/res/drawable-hdpi/logo.png rename to evcharger-app/android/res/drawable-hdpi/logo.png diff --git a/android/res/drawable-hdpi/logo_land.png b/evcharger-app/android/res/drawable-hdpi/logo_land.png similarity index 100% rename from android/res/drawable-hdpi/logo_land.png rename to evcharger-app/android/res/drawable-hdpi/logo_land.png diff --git a/android/res/drawable-hdpi/logo_port.png b/evcharger-app/android/res/drawable-hdpi/logo_port.png similarity index 100% rename from android/res/drawable-hdpi/logo_port.png rename to evcharger-app/android/res/drawable-hdpi/logo_port.png diff --git a/android/res/drawable-ldpi/icon.png b/evcharger-app/android/res/drawable-ldpi/icon.png similarity index 100% rename from android/res/drawable-ldpi/icon.png rename to evcharger-app/android/res/drawable-ldpi/icon.png diff --git a/android/res/drawable-ldpi/logo.png b/evcharger-app/android/res/drawable-ldpi/logo.png similarity index 100% rename from android/res/drawable-ldpi/logo.png rename to evcharger-app/android/res/drawable-ldpi/logo.png diff --git a/android/res/drawable-ldpi/logo_land.png b/evcharger-app/android/res/drawable-ldpi/logo_land.png similarity index 100% rename from android/res/drawable-ldpi/logo_land.png rename to evcharger-app/android/res/drawable-ldpi/logo_land.png diff --git a/android/res/drawable-ldpi/logo_port.png b/evcharger-app/android/res/drawable-ldpi/logo_port.png similarity index 100% rename from android/res/drawable-ldpi/logo_port.png rename to evcharger-app/android/res/drawable-ldpi/logo_port.png diff --git a/android/res/drawable-mdpi/icon.png b/evcharger-app/android/res/drawable-mdpi/icon.png similarity index 100% rename from android/res/drawable-mdpi/icon.png rename to evcharger-app/android/res/drawable-mdpi/icon.png diff --git a/android/res/drawable-mdpi/logo.png b/evcharger-app/android/res/drawable-mdpi/logo.png similarity index 100% rename from android/res/drawable-mdpi/logo.png rename to evcharger-app/android/res/drawable-mdpi/logo.png diff --git a/android/res/drawable-mdpi/logo_land.png b/evcharger-app/android/res/drawable-mdpi/logo_land.png similarity index 100% rename from android/res/drawable-mdpi/logo_land.png rename to evcharger-app/android/res/drawable-mdpi/logo_land.png diff --git a/android/res/drawable-mdpi/logo_port.png b/evcharger-app/android/res/drawable-mdpi/logo_port.png similarity index 100% rename from android/res/drawable-mdpi/logo_port.png rename to evcharger-app/android/res/drawable-mdpi/logo_port.png diff --git a/android/res/drawable-xhdpi/icon.png b/evcharger-app/android/res/drawable-xhdpi/icon.png similarity index 100% rename from android/res/drawable-xhdpi/icon.png rename to evcharger-app/android/res/drawable-xhdpi/icon.png diff --git a/android/res/drawable-xhdpi/logo.png b/evcharger-app/android/res/drawable-xhdpi/logo.png similarity index 100% rename from android/res/drawable-xhdpi/logo.png rename to evcharger-app/android/res/drawable-xhdpi/logo.png diff --git a/android/res/drawable-xhdpi/logo_land.png b/evcharger-app/android/res/drawable-xhdpi/logo_land.png similarity index 100% rename from android/res/drawable-xhdpi/logo_land.png rename to evcharger-app/android/res/drawable-xhdpi/logo_land.png diff --git a/android/res/drawable-xhdpi/logo_port.png b/evcharger-app/android/res/drawable-xhdpi/logo_port.png similarity index 100% rename from android/res/drawable-xhdpi/logo_port.png rename to evcharger-app/android/res/drawable-xhdpi/logo_port.png diff --git a/android/res/drawable-xxhdpi/icon.png b/evcharger-app/android/res/drawable-xxhdpi/icon.png similarity index 100% rename from android/res/drawable-xxhdpi/icon.png rename to evcharger-app/android/res/drawable-xxhdpi/icon.png diff --git a/android/res/drawable-xxhdpi/logo.png b/evcharger-app/android/res/drawable-xxhdpi/logo.png similarity index 100% rename from android/res/drawable-xxhdpi/logo.png rename to evcharger-app/android/res/drawable-xxhdpi/logo.png diff --git a/android/res/drawable-xxhdpi/logo_land.png b/evcharger-app/android/res/drawable-xxhdpi/logo_land.png similarity index 100% rename from android/res/drawable-xxhdpi/logo_land.png rename to evcharger-app/android/res/drawable-xxhdpi/logo_land.png diff --git a/android/res/drawable-xxhdpi/logo_port.png b/evcharger-app/android/res/drawable-xxhdpi/logo_port.png similarity index 100% rename from android/res/drawable-xxhdpi/logo_port.png rename to evcharger-app/android/res/drawable-xxhdpi/logo_port.png diff --git a/android/res/drawable-xxxhdpi/icon.png b/evcharger-app/android/res/drawable-xxxhdpi/icon.png similarity index 100% rename from android/res/drawable-xxxhdpi/icon.png rename to evcharger-app/android/res/drawable-xxxhdpi/icon.png diff --git a/android/res/drawable-xxxhdpi/logo.png b/evcharger-app/android/res/drawable-xxxhdpi/logo.png similarity index 100% rename from android/res/drawable-xxxhdpi/logo.png rename to evcharger-app/android/res/drawable-xxxhdpi/logo.png diff --git a/android/res/drawable-xxxhdpi/logo_land.png b/evcharger-app/android/res/drawable-xxxhdpi/logo_land.png similarity index 100% rename from android/res/drawable-xxxhdpi/logo_land.png rename to evcharger-app/android/res/drawable-xxxhdpi/logo_land.png diff --git a/android/res/drawable-xxxhdpi/logo_port.png b/evcharger-app/android/res/drawable-xxxhdpi/logo_port.png similarity index 100% rename from android/res/drawable-xxxhdpi/logo_port.png rename to evcharger-app/android/res/drawable-xxxhdpi/logo_port.png diff --git a/android/res/drawable/splashscreen.xml b/evcharger-app/android/res/drawable/splashscreen.xml similarity index 100% rename from android/res/drawable/splashscreen.xml rename to evcharger-app/android/res/drawable/splashscreen.xml diff --git a/android/res/drawable/splashscreen_land.xml b/evcharger-app/android/res/drawable/splashscreen_land.xml similarity index 100% rename from android/res/drawable/splashscreen_land.xml rename to evcharger-app/android/res/drawable/splashscreen_land.xml diff --git a/android/res/drawable/splashscreen_port.xml b/evcharger-app/android/res/drawable/splashscreen_port.xml similarity index 100% rename from android/res/drawable/splashscreen_port.xml rename to evcharger-app/android/res/drawable/splashscreen_port.xml diff --git a/android/res/values-land/splashscreentheme.xml b/evcharger-app/android/res/values-land/splashscreentheme.xml similarity index 100% rename from android/res/values-land/splashscreentheme.xml rename to evcharger-app/android/res/values-land/splashscreentheme.xml diff --git a/android/res/values-port/splashscreentheme.xml b/evcharger-app/android/res/values-port/splashscreentheme.xml similarity index 100% rename from android/res/values-port/splashscreentheme.xml rename to evcharger-app/android/res/values-port/splashscreentheme.xml diff --git a/android/res/values/libs.xml b/evcharger-app/android/res/values/libs.xml similarity index 100% rename from android/res/values/libs.xml rename to evcharger-app/android/res/values/libs.xml diff --git a/android/res/values/splashscreentheme.xml b/evcharger-app/android/res/values/splashscreentheme.xml similarity index 100% rename from android/res/values/splashscreentheme.xml rename to evcharger-app/android/res/values/splashscreentheme.xml diff --git a/android/res/xml/qtprovider_paths.xml b/evcharger-app/android/res/xml/qtprovider_paths.xml similarity index 100% rename from android/res/xml/qtprovider_paths.xml rename to evcharger-app/android/res/xml/qtprovider_paths.xml diff --git a/evcharger-app/android/src/QZeroConfNsdManager.java b/evcharger-app/android/src/QZeroConfNsdManager.java new file mode 120000 index 0000000..14ba9c6 --- /dev/null +++ b/evcharger-app/android/src/QZeroConfNsdManager.java @@ -0,0 +1 @@ +../../../3rdparty/QtZeroConf/QZeroConfNsdManager.java \ No newline at end of file diff --git a/src/apikeyvaluehelper.cpp b/evcharger-app/apikeyvaluehelper.cpp similarity index 100% rename from src/apikeyvaluehelper.cpp rename to evcharger-app/apikeyvaluehelper.cpp diff --git a/src/apikeyvaluehelper.h b/evcharger-app/apikeyvaluehelper.h similarity index 100% rename from src/apikeyvaluehelper.h rename to evcharger-app/apikeyvaluehelper.h diff --git a/evcharger-app/appsettings.cpp b/evcharger-app/appsettings.cpp new file mode 100644 index 0000000..fbf691c --- /dev/null +++ b/evcharger-app/appsettings.cpp @@ -0,0 +1,32 @@ +#include "appsettings.h" + +#include +#include + +bool AppSettings::loadSolalawebKey(const QString &url) +{ + QFile file{QQmlFile::urlToLocalFileOrQrc(url)}; + if (!file.open(QFile::ReadOnly)) + { + qWarning() << "Could not open file:" << file.errorString(); + return false; + } + + setSolalawebKey(file.readAll()); + + return true; +} + +bool AppSettings::loadSolalawebCert(const QString &url) +{ + QFile file{QQmlFile::urlToLocalFileOrQrc(url)}; + if (!file.open(QFile::ReadOnly)) + { + qWarning() << "Could not open file:" << file.errorString(); + return false; + } + + setSolalawebCert(file.readAll()); + + return true; +} diff --git a/evcharger-app/appsettings.h b/evcharger-app/appsettings.h new file mode 100644 index 0000000..d0c711b --- /dev/null +++ b/evcharger-app/appsettings.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "goesettings.h" + +class AppSettings : public GoeSettings +{ + Q_OBJECT + QML_ELEMENT + +public: + Q_INVOKABLE bool loadSolalawebKey(const QString &url); + Q_INVOKABLE bool loadSolalawebCert(const QString &url); +}; diff --git a/src/deviceconnection.cpp b/evcharger-app/deviceconnection.cpp similarity index 100% rename from src/deviceconnection.cpp rename to evcharger-app/deviceconnection.cpp diff --git a/src/deviceconnection.h b/evcharger-app/deviceconnection.h similarity index 100% rename from src/deviceconnection.h rename to evcharger-app/deviceconnection.h diff --git a/src/devicesmodel.cpp b/evcharger-app/devicesmodel.cpp similarity index 100% rename from src/devicesmodel.cpp rename to evcharger-app/devicesmodel.cpp diff --git a/src/devicesmodel.h b/evcharger-app/devicesmodel.h similarity index 100% rename from src/devicesmodel.h rename to evcharger-app/devicesmodel.h diff --git a/i18n/qml_de.ts b/evcharger-app/i18n/qml_de.ts similarity index 79% rename from i18n/qml_de.ts rename to evcharger-app/i18n/qml_de.ts index 2fa4705..17c1ceb 100644 --- a/i18n/qml_de.ts +++ b/evcharger-app/i18n/qml_de.ts @@ -307,6 +307,91 @@ IP Adresse: + + ChargerConnection + + + + Unknown + Unbekannt + + + + Ok + + + + + Offline + + + + + + Idle + + + + + Updating + + + + + Failed + + + + + Succeeded + + + + + NotReady + + + + + Verifying + + + + + %0 %1% + + + + + %0 %1/? + + + + + %0 ?/%1 + + + + + Charging + + + + + WaitCar + + + + + Complete + + + + + Error + + + ChargerTabPage @@ -488,6 +573,99 @@ Daily trip + + ChargersModel + + + Serial + + + + + WS Status + + + + + Status + + + + + Variant + + + + + IsGo + + + + + IsAustralien + + + + + ResetCard + + + + + ConnectedWifi + + + + + Project + + + + + Version + + + + + IDF Version + + + + + Update + + + + + Reboots + + + + + Uptime + + + + + Current Partition + + + + + Car state + + + + + Energy + + + + + Livedata + + + ChargingConfigurationPage @@ -562,7 +740,7 @@ - + Open integrations page Integrationsseite öffnen @@ -783,212 +961,212 @@ DeviceConnection - + Connecting to %0 Verbinde zu %0 - + Received something that is not a json object! - + Received something that does not contain a type! - + Received something with a non-string type! - + Received hello without serial! - + Received hello with a non-string serial! - + Received hello with a non-bool secured! - + Received hello without manufacturer! - + Received hello with a non-string manufacturer! - + Received hello without friendly_name! - + Received hello with a non-string friendly_name! - + Received hello without devicetype! - + Could not parse solalaweb certificate! - + Could not parse solalaweb key! - + Received hello with a non-string devicetype! - + Received authRequired without token1! - + Received authRequired with a non-string token1! - + Received authRequired without token2! - + Received authRequired with a non-string token2! - + Received fullStatus with a non-bool partial! - + Received fullStatus without a status! - + Received fullStatus with a non-object status! - + Received partial fullStatus - + Received last fullStatus - + Received deltaStatus without a status! - + Received deltaStatus with a non-object status! - + Received message type %0 %1 - + Received message with a non-string requestId! - + Sending %0 - + Connected! Verbunden! - + Disconnected! Verbindung verloren! - + Reconnecting to %0 Verbinde wieder zu %0 - + state changed: %0 - + could not parse received json: %0 at offset %1 - + unknown binary message type %0 received - + error occured: %0 - + ssl peer verify error - + ssl errors - + ssl alert sent level=%0 type=%1 description=%2 - + ssl alert received level=%0 type=%1 description=%2 - + ssl handshake interrupted on error - + unknown response type %0 @@ -1158,8 +1336,8 @@ DevicesModel - - + + Device %0 Gerät %0 @@ -1449,6 +1627,128 @@ Kanal + + ImportCertificateDialog + + + Import Certificate + + + + + Supports Ssl: + + + + + + + TextLabel + + + + + Ssl Library Version: + + + + + Ssl Library Build Version: + + + + + Ssl Backend: + + + + + Automatic Import: + + + + + Load p12 file... + + + + + Private Key: + + + + + Private Cert: + + + + + Warning: Your private key and cert will be stored on this machine, but can later be erased from the main window. + + + + + Yes + + + + + No + + + + + Select certificate... + + + + + Could not open file! + + + + + Please enter password + + + + + Certificate password: + + + + + Failed to load openssl legacy provider! + + + + + + + Failed processing certificate! + + + + + Possible reasons: openssl has a problem, the file is corrupt/invalid or the password is incorrect. + + + + + The key seems to be invalid. + + + + + The cert seems to be invalid. + + + + + Could not change active backend + + + InformationsTabPage @@ -1541,6 +1841,104 @@ Einstellungen + + MainWindow + + + MainWindow + + + + + Connect All + + + + + Disconnect All + + + + + Add + + + + + Remove + + + + + Serial + + + + + Not yet implemented! + + + + + %0 selected + + + + + Set update url... + + + + + Start update... + + + + + Reboot... + + + + + Set chargectrl override... + + + + + Set abitrary api key... + + + + + Enter update url... + + + + + Update url: + + + + + Select update release... + + + + + Update release + + + + + Are you sure? + Sind Sie sicher? + + + + Do you really want to reboot selected devices? + + + MqttPage @@ -1855,6 +2253,52 @@ Neustart + + RequestDialog + + + Pending requests... + + + + + RequestModel + + + Pending + + + + + Failed + + + + + Succeeded + + + + + Serial + + + + + RequestId + + + + + Status + + + + + Message + + + SavedDevicesModel @@ -2055,7 +2499,7 @@ SendMessageHelper - + No device connection set! @@ -2068,6 +2512,54 @@ Sensorkonfiguration + + SetArbitraryApiKeyDialog + + + Dialog + + + + + ApiKey: + + + + + Value: + + + + + Sudo: + + + + + Enabled + + + + + Could not parse JSON + + + + + Unexpected not array! + + + + + Unexpected array length! + + + + + Unexpected array length: %0! + + + SetPriceLimitPage @@ -2540,4 +3032,19 @@ BSSID: + + main + + + + Could not parse private key! + + + + + + Could not parse private cert! + + + diff --git a/i18n/qml_en.ts b/evcharger-app/i18n/qml_en.ts similarity index 100% rename from i18n/qml_en.ts rename to evcharger-app/i18n/qml_en.ts diff --git a/src/main.cpp b/evcharger-app/main.cpp similarity index 100% rename from src/main.cpp rename to evcharger-app/main.cpp diff --git a/qml/AboutPage.qml b/evcharger-app/qml/AboutPage.qml similarity index 100% rename from qml/AboutPage.qml rename to evcharger-app/qml/AboutPage.qml diff --git a/qml/AccessPage.qml b/evcharger-app/qml/AccessPage.qml similarity index 100% rename from qml/AccessPage.qml rename to evcharger-app/qml/AccessPage.qml diff --git a/qml/AddDeviceScreen.qml b/evcharger-app/qml/AddDeviceScreen.qml similarity index 100% rename from qml/AddDeviceScreen.qml rename to evcharger-app/qml/AddDeviceScreen.qml diff --git a/qml/AnimatedStackLayout.qml b/evcharger-app/qml/AnimatedStackLayout.qml similarity index 100% rename from qml/AnimatedStackLayout.qml rename to evcharger-app/qml/AnimatedStackLayout.qml diff --git a/qml/AnimatedStackView.qml b/evcharger-app/qml/AnimatedStackView.qml similarity index 100% rename from qml/AnimatedStackView.qml rename to evcharger-app/qml/AnimatedStackView.qml diff --git a/qml/ApiKeyValueItem.qml b/evcharger-app/qml/ApiKeyValueItem.qml similarity index 100% rename from qml/ApiKeyValueItem.qml rename to evcharger-app/qml/ApiKeyValueItem.qml diff --git a/qml/ApiSettingsPage.qml b/evcharger-app/qml/ApiSettingsPage.qml similarity index 100% rename from qml/ApiSettingsPage.qml rename to evcharger-app/qml/ApiSettingsPage.qml diff --git a/qml/AppInstance.qml b/evcharger-app/qml/AppInstance.qml similarity index 100% rename from qml/AppInstance.qml rename to evcharger-app/qml/AppInstance.qml diff --git a/qml/AppSettingsPage.qml b/evcharger-app/qml/AppSettingsPage.qml similarity index 100% rename from qml/AppSettingsPage.qml rename to evcharger-app/qml/AppSettingsPage.qml diff --git a/qml/BaseNavigationPage.qml b/evcharger-app/qml/BaseNavigationPage.qml similarity index 100% rename from qml/BaseNavigationPage.qml rename to evcharger-app/qml/BaseNavigationPage.qml diff --git a/qml/CablePage.qml b/evcharger-app/qml/CablePage.qml similarity index 100% rename from qml/CablePage.qml rename to evcharger-app/qml/CablePage.qml diff --git a/qml/CarPage.qml b/evcharger-app/qml/CarPage.qml similarity index 100% rename from qml/CarPage.qml rename to evcharger-app/qml/CarPage.qml diff --git a/qml/CellularPage.qml b/evcharger-app/qml/CellularPage.qml similarity index 100% rename from qml/CellularPage.qml rename to evcharger-app/qml/CellularPage.qml diff --git a/qml/CenteredDialog.qml b/evcharger-app/qml/CenteredDialog.qml similarity index 100% rename from qml/CenteredDialog.qml rename to evcharger-app/qml/CenteredDialog.qml diff --git a/qml/ChargerTabPage.qml b/evcharger-app/qml/ChargerTabPage.qml similarity index 100% rename from qml/ChargerTabPage.qml rename to evcharger-app/qml/ChargerTabPage.qml diff --git a/qml/ChargingConfigurationPage.qml b/evcharger-app/qml/ChargingConfigurationPage.qml similarity index 100% rename from qml/ChargingConfigurationPage.qml rename to evcharger-app/qml/ChargingConfigurationPage.qml diff --git a/qml/ChargingSpeedPage.qml b/evcharger-app/qml/ChargingSpeedPage.qml similarity index 100% rename from qml/ChargingSpeedPage.qml rename to evcharger-app/qml/ChargingSpeedPage.qml diff --git a/qml/CloudApiQrCodePage.qml b/evcharger-app/qml/CloudApiQrCodePage.qml similarity index 100% rename from qml/CloudApiQrCodePage.qml rename to evcharger-app/qml/CloudApiQrCodePage.qml diff --git a/qml/CloudPage.qml b/evcharger-app/qml/CloudPage.qml similarity index 100% rename from qml/CloudPage.qml rename to evcharger-app/qml/CloudPage.qml diff --git a/qml/CloudUrlsModel.qml b/evcharger-app/qml/CloudUrlsModel.qml similarity index 100% rename from qml/CloudUrlsModel.qml rename to evcharger-app/qml/CloudUrlsModel.qml diff --git a/qml/ConfirmingOnOffSwitch.qml b/evcharger-app/qml/ConfirmingOnOffSwitch.qml similarity index 100% rename from qml/ConfirmingOnOffSwitch.qml rename to evcharger-app/qml/ConfirmingOnOffSwitch.qml diff --git a/qml/ConnectingScreen.qml b/evcharger-app/qml/ConnectingScreen.qml similarity index 100% rename from qml/ConnectingScreen.qml rename to evcharger-app/qml/ConnectingScreen.qml diff --git a/qml/ConnectionPage.qml b/evcharger-app/qml/ConnectionPage.qml similarity index 100% rename from qml/ConnectionPage.qml rename to evcharger-app/qml/ConnectionPage.qml diff --git a/qml/Constants.qml b/evcharger-app/qml/Constants.qml similarity index 100% rename from qml/Constants.qml rename to evcharger-app/qml/Constants.qml diff --git a/qml/ControllerPage.qml b/evcharger-app/qml/ControllerPage.qml similarity index 100% rename from qml/ControllerPage.qml rename to evcharger-app/qml/ControllerPage.qml diff --git a/qml/ControllerTabPage.qml b/evcharger-app/qml/ControllerTabPage.qml similarity index 100% rename from qml/ControllerTabPage.qml rename to evcharger-app/qml/ControllerTabPage.qml diff --git a/qml/CurrentLevelsPage.qml b/evcharger-app/qml/CurrentLevelsPage.qml similarity index 100% rename from qml/CurrentLevelsPage.qml rename to evcharger-app/qml/CurrentLevelsPage.qml diff --git a/qml/DailyTripPage.qml b/evcharger-app/qml/DailyTripPage.qml similarity index 100% rename from qml/DailyTripPage.qml rename to evcharger-app/qml/DailyTripPage.qml diff --git a/qml/DateAndTimePage.qml b/evcharger-app/qml/DateAndTimePage.qml similarity index 100% rename from qml/DateAndTimePage.qml rename to evcharger-app/qml/DateAndTimePage.qml diff --git a/qml/DeviceHeaderBar.qml b/evcharger-app/qml/DeviceHeaderBar.qml similarity index 100% rename from qml/DeviceHeaderBar.qml rename to evcharger-app/qml/DeviceHeaderBar.qml diff --git a/qml/DeviceListScreen.qml b/evcharger-app/qml/DeviceListScreen.qml similarity index 100% rename from qml/DeviceListScreen.qml rename to evcharger-app/qml/DeviceListScreen.qml diff --git a/qml/DeviceScreen.qml b/evcharger-app/qml/DeviceScreen.qml similarity index 100% rename from qml/DeviceScreen.qml rename to evcharger-app/qml/DeviceScreen.qml diff --git a/qml/DisplaySettingsPage.qml b/evcharger-app/qml/DisplaySettingsPage.qml similarity index 100% rename from qml/DisplaySettingsPage.qml rename to evcharger-app/qml/DisplaySettingsPage.qml diff --git a/qml/DoubleSpinBox.qml b/evcharger-app/qml/DoubleSpinBox.qml similarity index 100% rename from qml/DoubleSpinBox.qml rename to evcharger-app/qml/DoubleSpinBox.qml diff --git a/qml/EVChargerApp.qml b/evcharger-app/qml/EVChargerApp.qml similarity index 100% rename from qml/EVChargerApp.qml rename to evcharger-app/qml/EVChargerApp.qml diff --git a/qml/EcoTabPage.qml b/evcharger-app/qml/EcoTabPage.qml similarity index 100% rename from qml/EcoTabPage.qml rename to evcharger-app/qml/EcoTabPage.qml diff --git a/qml/EditValueItem.qml b/evcharger-app/qml/EditValueItem.qml similarity index 100% rename from qml/EditValueItem.qml rename to evcharger-app/qml/EditValueItem.qml diff --git a/qml/EthernetPage.qml b/evcharger-app/qml/EthernetPage.qml similarity index 100% rename from qml/EthernetPage.qml rename to evcharger-app/qml/EthernetPage.qml diff --git a/qml/FirmwarePage.qml b/evcharger-app/qml/FirmwarePage.qml similarity index 100% rename from qml/FirmwarePage.qml rename to evcharger-app/qml/FirmwarePage.qml diff --git a/qml/FlexibleEnergyTariffPage.qml b/evcharger-app/qml/FlexibleEnergyTariffPage.qml similarity index 100% rename from qml/FlexibleEnergyTariffPage.qml rename to evcharger-app/qml/FlexibleEnergyTariffPage.qml diff --git a/qml/GeneralOnOffSwitch.qml b/evcharger-app/qml/GeneralOnOffSwitch.qml similarity index 100% rename from qml/GeneralOnOffSwitch.qml rename to evcharger-app/qml/GeneralOnOffSwitch.qml diff --git a/qml/GeneralPage.qml b/evcharger-app/qml/GeneralPage.qml similarity index 100% rename from qml/GeneralPage.qml rename to evcharger-app/qml/GeneralPage.qml diff --git a/qml/GridPage.qml b/evcharger-app/qml/GridPage.qml similarity index 100% rename from qml/GridPage.qml rename to evcharger-app/qml/GridPage.qml diff --git a/qml/GroundCheckPage.qml b/evcharger-app/qml/GroundCheckPage.qml similarity index 100% rename from qml/GroundCheckPage.qml rename to evcharger-app/qml/GroundCheckPage.qml diff --git a/qml/HardwareInformationPage.qml b/evcharger-app/qml/HardwareInformationPage.qml similarity index 100% rename from qml/HardwareInformationPage.qml rename to evcharger-app/qml/HardwareInformationPage.qml diff --git a/qml/HotspotPage.qml b/evcharger-app/qml/HotspotPage.qml similarity index 100% rename from qml/HotspotPage.qml rename to evcharger-app/qml/HotspotPage.qml diff --git a/qml/InformationsTabPage.qml b/evcharger-app/qml/InformationsTabPage.qml similarity index 100% rename from qml/InformationsTabPage.qml rename to evcharger-app/qml/InformationsTabPage.qml diff --git a/qml/KwhLimitPage.qml b/evcharger-app/qml/KwhLimitPage.qml similarity index 100% rename from qml/KwhLimitPage.qml rename to evcharger-app/qml/KwhLimitPage.qml diff --git a/qml/LedPage.qml b/evcharger-app/qml/LedPage.qml similarity index 100% rename from qml/LedPage.qml rename to evcharger-app/qml/LedPage.qml diff --git a/qml/LicensesPage.qml b/evcharger-app/qml/LicensesPage.qml similarity index 100% rename from qml/LicensesPage.qml rename to evcharger-app/qml/LicensesPage.qml diff --git a/qml/LoadBalancingPage.qml b/evcharger-app/qml/LoadBalancingPage.qml similarity index 100% rename from qml/LoadBalancingPage.qml rename to evcharger-app/qml/LoadBalancingPage.qml diff --git a/qml/LogicModeButton.qml b/evcharger-app/qml/LogicModeButton.qml similarity index 100% rename from qml/LogicModeButton.qml rename to evcharger-app/qml/LogicModeButton.qml diff --git a/qml/MainScreen.qml b/evcharger-app/qml/MainScreen.qml similarity index 100% rename from qml/MainScreen.qml rename to evcharger-app/qml/MainScreen.qml diff --git a/qml/MqttPage.qml b/evcharger-app/qml/MqttPage.qml similarity index 100% rename from qml/MqttPage.qml rename to evcharger-app/qml/MqttPage.qml diff --git a/qml/NamePage.qml b/evcharger-app/qml/NamePage.qml similarity index 100% rename from qml/NamePage.qml rename to evcharger-app/qml/NamePage.qml diff --git a/qml/NavigationItem.qml b/evcharger-app/qml/NavigationItem.qml similarity index 100% rename from qml/NavigationItem.qml rename to evcharger-app/qml/NavigationItem.qml diff --git a/qml/NavigationPage.qml b/evcharger-app/qml/NavigationPage.qml similarity index 100% rename from qml/NavigationPage.qml rename to evcharger-app/qml/NavigationPage.qml diff --git a/qml/NotificationsPage.qml b/evcharger-app/qml/NotificationsPage.qml similarity index 100% rename from qml/NotificationsPage.qml rename to evcharger-app/qml/NotificationsPage.qml diff --git a/qml/OcppPage.qml b/evcharger-app/qml/OcppPage.qml similarity index 100% rename from qml/OcppPage.qml rename to evcharger-app/qml/OcppPage.qml diff --git a/qml/OpenLinkButton.qml b/evcharger-app/qml/OpenLinkButton.qml similarity index 100% rename from qml/OpenLinkButton.qml rename to evcharger-app/qml/OpenLinkButton.qml diff --git a/qml/PasswordPage.qml b/evcharger-app/qml/PasswordPage.qml similarity index 100% rename from qml/PasswordPage.qml rename to evcharger-app/qml/PasswordPage.qml diff --git a/qml/PvSurplusPage.qml b/evcharger-app/qml/PvSurplusPage.qml similarity index 100% rename from qml/PvSurplusPage.qml rename to evcharger-app/qml/PvSurplusPage.qml diff --git a/qml/QrCode.qml b/evcharger-app/qml/QrCode.qml similarity index 100% rename from qml/QrCode.qml rename to evcharger-app/qml/QrCode.qml diff --git a/qml/RebootPage.qml b/evcharger-app/qml/RebootPage.qml similarity index 100% rename from qml/RebootPage.qml rename to evcharger-app/qml/RebootPage.qml diff --git a/qml/RequestStatusText.qml b/evcharger-app/qml/RequestStatusText.qml similarity index 100% rename from qml/RequestStatusText.qml rename to evcharger-app/qml/RequestStatusText.qml diff --git a/qml/SchedulerDayPage.qml b/evcharger-app/qml/SchedulerDayPage.qml similarity index 100% rename from qml/SchedulerDayPage.qml rename to evcharger-app/qml/SchedulerDayPage.qml diff --git a/qml/SchedulerPage.qml b/evcharger-app/qml/SchedulerPage.qml similarity index 100% rename from qml/SchedulerPage.qml rename to evcharger-app/qml/SchedulerPage.qml diff --git a/qml/ScrollableTabPage.qml b/evcharger-app/qml/ScrollableTabPage.qml similarity index 100% rename from qml/ScrollableTabPage.qml rename to evcharger-app/qml/ScrollableTabPage.qml diff --git a/qml/SecurityPage.qml b/evcharger-app/qml/SecurityPage.qml similarity index 100% rename from qml/SecurityPage.qml rename to evcharger-app/qml/SecurityPage.qml diff --git a/qml/SelectLogicModeItem.qml b/evcharger-app/qml/SelectLogicModeItem.qml similarity index 100% rename from qml/SelectLogicModeItem.qml rename to evcharger-app/qml/SelectLogicModeItem.qml diff --git a/qml/SelectPhaseSwitchModeItem.qml b/evcharger-app/qml/SelectPhaseSwitchModeItem.qml similarity index 100% rename from qml/SelectPhaseSwitchModeItem.qml rename to evcharger-app/qml/SelectPhaseSwitchModeItem.qml diff --git a/qml/SensorsConfigurationPage.qml b/evcharger-app/qml/SensorsConfigurationPage.qml similarity index 100% rename from qml/SensorsConfigurationPage.qml rename to evcharger-app/qml/SensorsConfigurationPage.qml diff --git a/qml/SetPriceLimitPage.qml b/evcharger-app/qml/SetPriceLimitPage.qml similarity index 100% rename from qml/SetPriceLimitPage.qml rename to evcharger-app/qml/SetPriceLimitPage.qml diff --git a/qml/SetValueHelper.qml b/evcharger-app/qml/SetValueHelper.qml similarity index 100% rename from qml/SetValueHelper.qml rename to evcharger-app/qml/SetValueHelper.qml diff --git a/qml/SettingsTabPage.qml b/evcharger-app/qml/SettingsTabPage.qml similarity index 100% rename from qml/SettingsTabPage.qml rename to evcharger-app/qml/SettingsTabPage.qml diff --git a/qml/SimpleNavigationItem.qml b/evcharger-app/qml/SimpleNavigationItem.qml similarity index 100% rename from qml/SimpleNavigationItem.qml rename to evcharger-app/qml/SimpleNavigationItem.qml diff --git a/qml/StartStopButton.qml b/evcharger-app/qml/StartStopButton.qml similarity index 100% rename from qml/StartStopButton.qml rename to evcharger-app/qml/StartStopButton.qml diff --git a/qml/SwitchLanguagePage.qml b/evcharger-app/qml/SwitchLanguagePage.qml similarity index 100% rename from qml/SwitchLanguagePage.qml rename to evcharger-app/qml/SwitchLanguagePage.qml diff --git a/qml/TimeComponentLabel.qml b/evcharger-app/qml/TimeComponentLabel.qml similarity index 100% rename from qml/TimeComponentLabel.qml rename to evcharger-app/qml/TimeComponentLabel.qml diff --git a/qml/TimePicker.qml b/evcharger-app/qml/TimePicker.qml similarity index 100% rename from qml/TimePicker.qml rename to evcharger-app/qml/TimePicker.qml diff --git a/qml/TimePickerDialog.qml b/evcharger-app/qml/TimePickerDialog.qml similarity index 100% rename from qml/TimePickerDialog.qml rename to evcharger-app/qml/TimePickerDialog.qml diff --git a/qml/TimePickerLabel.qml b/evcharger-app/qml/TimePickerLabel.qml similarity index 100% rename from qml/TimePickerLabel.qml rename to evcharger-app/qml/TimePickerLabel.qml diff --git a/qml/TitleText.qml b/evcharger-app/qml/TitleText.qml similarity index 100% rename from qml/TitleText.qml rename to evcharger-app/qml/TitleText.qml diff --git a/qml/VerticalTabButton.qml b/evcharger-app/qml/VerticalTabButton.qml similarity index 100% rename from qml/VerticalTabButton.qml rename to evcharger-app/qml/VerticalTabButton.qml diff --git a/qml/WhiteBox.qml b/evcharger-app/qml/WhiteBox.qml similarity index 100% rename from qml/WhiteBox.qml rename to evcharger-app/qml/WhiteBox.qml diff --git a/qml/WhiteCheckDelegate.qml b/evcharger-app/qml/WhiteCheckDelegate.qml similarity index 100% rename from qml/WhiteCheckDelegate.qml rename to evcharger-app/qml/WhiteCheckDelegate.qml diff --git a/qml/WhiteItemDelegate.qml b/evcharger-app/qml/WhiteItemDelegate.qml similarity index 100% rename from qml/WhiteItemDelegate.qml rename to evcharger-app/qml/WhiteItemDelegate.qml diff --git a/qml/WhiteSwipeDelegate.qml b/evcharger-app/qml/WhiteSwipeDelegate.qml similarity index 100% rename from qml/WhiteSwipeDelegate.qml rename to evcharger-app/qml/WhiteSwipeDelegate.qml diff --git a/qml/WiFiErrorsPage.qml b/evcharger-app/qml/WiFiErrorsPage.qml similarity index 100% rename from qml/WiFiErrorsPage.qml rename to evcharger-app/qml/WiFiErrorsPage.qml diff --git a/qml/WiFiPage.qml b/evcharger-app/qml/WiFiPage.qml similarity index 100% rename from qml/WiFiPage.qml rename to evcharger-app/qml/WiFiPage.qml diff --git a/qml/WiFiScanPage.qml b/evcharger-app/qml/WiFiScanPage.qml similarity index 100% rename from qml/WiFiScanPage.qml rename to evcharger-app/qml/WiFiScanPage.qml diff --git a/qml/icons/Alarm.svg b/evcharger-app/qml/icons/Alarm.svg similarity index 100% rename from qml/icons/Alarm.svg rename to evcharger-app/qml/icons/Alarm.svg diff --git a/qml/icons/Charger.svg b/evcharger-app/qml/icons/Charger.svg similarity index 100% rename from qml/icons/Charger.svg rename to evcharger-app/qml/icons/Charger.svg diff --git a/qml/icons/ChargerV3.svg b/evcharger-app/qml/icons/ChargerV3.svg similarity index 100% rename from qml/icons/ChargerV3.svg rename to evcharger-app/qml/icons/ChargerV3.svg diff --git a/qml/icons/ChargerV4.svg b/evcharger-app/qml/icons/ChargerV4.svg similarity index 100% rename from qml/icons/ChargerV4.svg rename to evcharger-app/qml/icons/ChargerV4.svg diff --git a/qml/icons/Charts.svg b/evcharger-app/qml/icons/Charts.svg similarity index 100% rename from qml/icons/Charts.svg rename to evcharger-app/qml/icons/Charts.svg diff --git a/qml/icons/Controller.svg b/evcharger-app/qml/icons/Controller.svg similarity index 100% rename from qml/icons/Controller.svg rename to evcharger-app/qml/icons/Controller.svg diff --git a/qml/icons/EcoModeFilled.svg b/evcharger-app/qml/icons/EcoModeFilled.svg similarity index 100% rename from qml/icons/EcoModeFilled.svg rename to evcharger-app/qml/icons/EcoModeFilled.svg diff --git a/qml/images/controller.png b/evcharger-app/qml/images/controller.png similarity index 100% rename from qml/images/controller.png rename to evcharger-app/qml/images/controller.png diff --git a/qml/images/geminiFix.png b/evcharger-app/qml/images/geminiFix.png similarity index 100% rename from qml/images/geminiFix.png rename to evcharger-app/qml/images/geminiFix.png diff --git a/qml/images/geminiFlex.png b/evcharger-app/qml/images/geminiFlex.png similarity index 100% rename from qml/images/geminiFlex.png rename to evcharger-app/qml/images/geminiFlex.png diff --git a/qml/images/homeFix.png b/evcharger-app/qml/images/homeFix.png similarity index 100% rename from qml/images/homeFix.png rename to evcharger-app/qml/images/homeFix.png diff --git a/qml/images/homePlus.png b/evcharger-app/qml/images/homePlus.png similarity index 100% rename from qml/images/homePlus.png rename to evcharger-app/qml/images/homePlus.png diff --git a/qml/images/phoenix.png b/evcharger-app/qml/images/phoenix.png similarity index 100% rename from qml/images/phoenix.png rename to evcharger-app/qml/images/phoenix.png diff --git a/qml/images/wattpilot.png b/evcharger-app/qml/images/wattpilot.png similarity index 100% rename from qml/images/wattpilot.png rename to evcharger-app/qml/images/wattpilot.png diff --git a/evcharger-app/qml/js/qrcode.min.js b/evcharger-app/qml/js/qrcode.min.js new file mode 120000 index 0000000..fa044ff --- /dev/null +++ b/evcharger-app/qml/js/qrcode.min.js @@ -0,0 +1 @@ +../../../3rdparty/qrcode-svg/dist/qrcode.min.js \ No newline at end of file diff --git a/qml/material-icons/add.svg b/evcharger-app/qml/material-icons/add.svg similarity index 100% rename from qml/material-icons/add.svg rename to evcharger-app/qml/material-icons/add.svg diff --git a/qml/material-icons/grid_guides.svg b/evcharger-app/qml/material-icons/grid_guides.svg similarity index 100% rename from qml/material-icons/grid_guides.svg rename to evcharger-app/qml/material-icons/grid_guides.svg diff --git a/qml/material-icons/open_in_new.svg b/evcharger-app/qml/material-icons/open_in_new.svg similarity index 100% rename from qml/material-icons/open_in_new.svg rename to evcharger-app/qml/material-icons/open_in_new.svg diff --git a/qml/material-icons/settings.svg b/evcharger-app/qml/material-icons/settings.svg similarity index 100% rename from qml/material-icons/settings.svg rename to evcharger-app/qml/material-icons/settings.svg diff --git a/qml/qmldir b/evcharger-app/qml/qmldir similarity index 100% rename from qml/qmldir rename to evcharger-app/qml/qmldir diff --git a/qml/ui-icons/MaterialIcons-Regular.ttf b/evcharger-app/qml/ui-icons/MaterialIcons-Regular.ttf similarity index 100% rename from qml/ui-icons/MaterialIcons-Regular.ttf rename to evcharger-app/qml/ui-icons/MaterialIcons-Regular.ttf diff --git a/qml/ui-icons/material-design-icons.LICENSE b/evcharger-app/qml/ui-icons/material-design-icons.LICENSE similarity index 100% rename from qml/ui-icons/material-design-icons.LICENSE rename to evcharger-app/qml/ui-icons/material-design-icons.LICENSE diff --git a/qml/update_qmldir.sh b/evcharger-app/qml/update_qmldir.sh similarity index 100% rename from qml/update_qmldir.sh rename to evcharger-app/qml/update_qmldir.sh diff --git a/src/sendmessagehelper.cpp b/evcharger-app/sendmessagehelper.cpp similarity index 100% rename from src/sendmessagehelper.cpp rename to evcharger-app/sendmessagehelper.cpp diff --git a/src/sendmessagehelper.h b/evcharger-app/sendmessagehelper.h similarity index 100% rename from src/sendmessagehelper.h rename to evcharger-app/sendmessagehelper.h diff --git a/flotten-updater/CMakeLists.txt b/flotten-updater/CMakeLists.txt new file mode 100644 index 0000000..383ccaa --- /dev/null +++ b/flotten-updater/CMakeLists.txt @@ -0,0 +1,39 @@ +add_executable(flotten-updater + chargerconnection.cpp + chargerconnection.h + chargersmodel.cpp + chargersmodel.h + flottenupdatersettings.cpp + flottenupdatersettings.h + importcertificatedialog.cpp + importcertificatedialog.h + importcertificatedialog.ui + main.cpp + mainwindow.cpp + mainwindow.h + mainwindow.ui + requestdialog.cpp + requestdialog.h + requestdialog.ui + requestmodel.cpp + requestmodel.h + setarbitraryapikeydialog.cpp + setarbitraryapikeydialog.h + setarbitraryapikeydialog.ui +) + +target_link_libraries(flotten-updater PUBLIC + Qt6::Core + Qt6::Gui + Qt6::Widgets + Qt6::WebSockets + ssl + crypto + goecommon +) + +qt_add_resources(flotten-updater + PREFIX / + FILES + goe-root-ca.pem +) diff --git a/flotten-updater/chargerconnection.cpp b/flotten-updater/chargerconnection.cpp new file mode 100644 index 0000000..8801ab6 --- /dev/null +++ b/flotten-updater/chargerconnection.cpp @@ -0,0 +1,637 @@ +#include "chargerconnection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "chargersmodel.h" + +namespace { +const constexpr QColor red{255, 150, 150}; +const constexpr QColor yellow{255, 255, 150}; +const constexpr QColor green{150, 255, 150}; +const constexpr QColor cyan{150, 255, 255}; +const constexpr QColor blue{150, 150, 255}; +const constexpr QColor magenta{255, 150, 255}; +} + +ChargerConnection::ChargerConnection(const QSslKey &key, const QSslCertificate &cert, QString &&serial, QObject *parent) : + QObject{parent}, + m_serial(std::move(serial)), + m_key{key}, + m_cert{cert} +{ + init(); +} + +ChargerConnection::ChargerConnection(const QSslKey &key, const QSslCertificate &cert, const QString &serial, QObject *parent) : + QObject{parent}, + m_serial(serial), + m_key{key}, + m_cert{cert} +{ + init(); +} + +void ChargerConnection::start() +{ + const QUrl url{QString("wss://solalaweb.com/%0").arg(m_serial)}; + qDebug() << url; + m_websocket.open(url); +} + +void ChargerConnection::stop() +{ + m_websocket.close(); +} + +QString ChargerConnection::wsStatusText() const +{ + return QMetaEnum::fromType().valueToKey(m_websocket.state()); +} + +QBrush ChargerConnection::wsStatusBackground() const +{ + switch (m_websocket.state()) + { + case QAbstractSocket::UnconnectedState: return red; + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: return yellow; + case QAbstractSocket::ConnectedState: return green; + case QAbstractSocket::ClosingState: return red; + } + return {}; +} + +QString ChargerConnection::statusText() const +{ + switch (m_status) + { + case Status::Unknown: return tr("Unknown"); + case Status::Ok: return tr("Ok"); + case Status::Offline: return tr("Offline"); + } + + return QString::number(std::to_underlying(m_status)); +} + +QBrush ChargerConnection::statusBackground() const +{ + switch (m_status) + { + case Status::Unknown: return yellow; + case Status::Ok: return green; + case Status::Offline: return red; + } + return {}; +} + +QString ChargerConnection::variantText() const +{ + if (!m_fullStatus.contains("var")) + return {}; + + return m_fullStatus.value("var").toString(); +} + +QBrush ChargerConnection::variantBackground() const +{ + if (!m_fullStatus.contains("var")) + return {}; + + switch (m_fullStatus.value("var").toInt()) + { + case 11: return blue; + case 22: return yellow; + } + return red; +} + +QString ChargerConnection::isGoText() const +{ + if (!m_fullStatus.contains("isgo")) + return {}; + + return m_fullStatus.value("isgo").toBool() ? "yes" : "no"; +} + +QBrush ChargerConnection::isGoBackground() const +{ + if (!m_fullStatus.contains("isgo")) + return {}; + + return m_fullStatus.value("isgo").toBool() ? blue : yellow; +} + +QString ChargerConnection::isAustralienText() const +{ + if (!m_fullStatus.contains("aus")) + return {}; + + return m_fullStatus.value("aus").toBool() ? "yes" : "no"; +} + +QBrush ChargerConnection::isAustralienBackground() const +{ + if (!m_fullStatus.contains("aus")) + return {}; + + return m_fullStatus.value("aus").toBool() ? yellow : blue; +} + +QString ChargerConnection::resetCardText() const +{ + if (!m_fullStatus.contains("frci")) + return {}; + + return m_fullStatus.value("frci").toBool() ? "yes" : "no"; +} + +QBrush ChargerConnection::resetCardBackground() const +{ + if (!m_fullStatus.contains("frci")) + return {}; + + return m_fullStatus.value("frci").toBool() ? green : red; +} + +QString ChargerConnection::connectedWifiText() const +{ + if (!m_fullStatus.contains("ccw")) + return {}; + + const auto &apd = m_fullStatus.value("ccw").toJsonObject(); + if (!apd.contains("ssid")) + return {}; + + return apd.value("ssid").toString(); +} + +QString ChargerConnection::projectText() const +{ + if (!m_fullStatus.contains("apd")) + return {}; + + const auto &apd = m_fullStatus.value("apd").toJsonObject(); + if (!apd.contains("project_name")) + return {}; + + return apd.value("project_name").toString(); +} + +QString ChargerConnection::versionText() const +{ + if (!m_fullStatus.contains("apd")) + return {}; + + const auto &apd = m_fullStatus.value("apd").toJsonObject(); + if (!apd.contains("version")) + return {}; + + return apd.value("version").toString(); +} + +QString ChargerConnection::idfVersionText() const +{ + if (!m_fullStatus.contains("apd")) + return {}; + + const auto &apd = m_fullStatus.value("apd").toJsonObject(); + if (!apd.contains("idf_ver")) + return {}; + + return apd.value("idf_ver").toString(); +} + +QString ChargerConnection::updateText() const +{ + if (!m_fullStatus.contains("ocs")) + return {}; + + const QStringList updateStates { tr("Idle"), tr("Updating"), tr("Failed"), tr("Succeeded"), tr("NotReady"), tr("Verifying") }; + const auto ocs = m_fullStatus.value("ocs").toInt(); + if (ocs >= 0 && ocs < updateStates.size()) + { + if (ocs == 1) + { + std::optional progress; + if (m_fullStatus.contains("ocp")) + progress = m_fullStatus.value("ocp").toInt(); + std::optional total; + if (m_fullStatus.contains("ocl")) + total = m_fullStatus.value("ocl").toInt(); + if (progress && total) + return (tr("%0 %1%").arg(updateStates.at(ocs)).arg(*progress ? *progress * 100 / *total : 0)); + else if (progress) + return tr("%0 %1/?").arg(updateStates.at(ocs)).arg(*progress); + else if (total) + return tr("%0 ?/%1").arg(updateStates.at(ocs)).arg(*total); + } + return updateStates.at(ocs); + } + return QString::number(ocs); +} + +QBrush ChargerConnection::updateBackground() const +{ + if (!m_fullStatus.contains("ocs")) + return {}; + + const auto ocs = m_fullStatus.value("ocs").toInt(); + switch (ocs) + { + case 1: return yellow; + case 2: return red; + case 3: return green; + case 4: return red; + case 5: return yellow; + } + + return {}; +} + +std::optional ChargerConnection::uptime() const +{ + if (!m_fullStatus.contains("rbt")) + return {}; + + return m_fullStatus.value("rbt").toULongLong(); +} + +QString ChargerConnection::uptimeText() const +{ + if (!m_fullStatus.contains("rbt")) + return {}; + + return QString::number(m_fullStatus.value("rbt").toULongLong()); +} + +QString ChargerConnection::currentPartition() const +{ + if (!m_fullStatus.contains("otap")) + return {}; + + const auto &otap = m_fullStatus.value("otap").toJsonObject(); + if (!otap.contains("label")) + return {}; + + return otap.value("label").toString(); +} + +std::optional ChargerConnection::reboots() const +{ + if (!m_fullStatus.contains("rbc")) + return {}; + + return m_fullStatus.value("rbc").toInt(); +} + +QString ChargerConnection::rebootsText() const +{ + if (!m_fullStatus.contains("rbc")) + return {}; + + return QString::number(m_fullStatus.value("rbc").toInt()); +} + +QString ChargerConnection::carStateText() const +{ + if (!m_fullStatus.contains("car")) + return {}; + + const QStringList carStates { tr("Unknown"), tr("Idle"), tr("Charging"), tr("WaitCar"), tr("Complete"), tr("Error") }; + const auto car = m_fullStatus.value("car").toInt(); + if (car >= 0 && car < carStates.size()) + return carStates.at(car); + return QString::number(car); +} + +std::optional ChargerConnection::energy() const +{ + if (!m_fullStatus.contains("eto")) + return {}; + + return m_fullStatus.value("eto").toDouble(); +} + +QString ChargerConnection::energyText() const +{ + if (!m_fullStatus.contains("eto")) + return {}; + + return QString("%0kWh").arg(m_fullStatus.value("eto").toDouble() / 1000.); +} + +QString ChargerConnection::livedataText() const +{ + if (!m_fullStatus.contains("nrg")) + return {}; + + const auto nrg = m_fullStatus.value("nrg").toList(); + if (nrg.size() > 3) + return QString{"%0V %1V %2V %3V"} + .arg(nrg.at(0).toDouble()) + .arg(nrg.at(1).toDouble()) + .arg(nrg.at(2).toDouble()) + .arg(nrg.at(3).toDouble()); + return {}; +} + +void ChargerConnection::sendMessage(const QJsonDocument &doc) +{ + sendMessage(QString::fromUtf8(doc.toJson())); +} + +void ChargerConnection::sendMessage(const QJsonObject &obj) +{ + sendMessage(QJsonDocument{obj}); +} + +void ChargerConnection::sendMessage(const QString &msg) +{ + qDebug() << msg << m_websocket.errorString(); + if (const auto written = m_websocket.sendTextMessage(msg); written != msg.size()) + qCritical() << "sending message failed" << written << "(expected:" << msg.size() << ')'; +} + +void ChargerConnection::init() +{ + if (auto model = qobject_cast(parent())) + { + connect(this, &ChargerConnection::wsStatusChanged, model, &ChargersModel::wsStatusChanged); + connect(this, &ChargerConnection::statusChanged, model, &ChargersModel::statusChanged); + connect(this, &ChargerConnection::variantChanged, model, &ChargersModel::variantChanged); + connect(this, &ChargerConnection::isGoChanged, model, &ChargersModel::isGoChanged); + connect(this, &ChargerConnection::isAustralienChanged, model, &ChargersModel::isAustralienChanged); + connect(this, &ChargerConnection::resetCardChanged, model, &ChargersModel::resetCardChanged); + connect(this, &ChargerConnection::connectedWifiChanged, model, &ChargersModel::connectedWifiChanged); + connect(this, &ChargerConnection::projectChanged, model, &ChargersModel::projectChanged); + connect(this, &ChargerConnection::versionChanged, model, &ChargersModel::versionChanged); + connect(this, &ChargerConnection::idfVersionChanged, model, &ChargersModel::idfVersionChanged); + connect(this, &ChargerConnection::updateChanged, model, &ChargersModel::updateChanged); + connect(this, &ChargerConnection::uptimeChanged, model, &ChargersModel::uptimeChanged); + connect(this, &ChargerConnection::currentPartitionChanged, model, &ChargersModel::currentPartitionChanged); + connect(this, &ChargerConnection::rebootsChanged, model, &ChargersModel::rebootsChanged); + connect(this, &ChargerConnection::carStateChanged, model, &ChargersModel::carStateChanged); + connect(this, &ChargerConnection::energyChanged, model, &ChargersModel::energyChanged); + connect(this, &ChargerConnection::livedataChanged, model, &ChargersModel::livedataChanged); + } + else + qWarning() << "unexpected parent"; + + { + auto sslConfig = m_websocket.sslConfiguration(); + sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone); + //sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer); + { + auto caCerts = QSslCertificate::fromPath(":/goe-root-ca.pem"); + if (caCerts.empty()) + qFatal("could not parse root ca"); + for (const auto &caCert : qAsConst(caCerts)) + qDebug() << caCert.issuerDisplayName(); + sslConfig.setCaCertificates(std::move(caCerts)); + } + sslConfig.setPrivateKey(m_key); + sslConfig.setLocalCertificate(m_cert); + m_websocket.setSslConfiguration(sslConfig); + m_websocket.ignoreSslErrors(); + } + + connect(&m_websocket, &QWebSocket::connected, this, &ChargerConnection::connected); + connect(&m_websocket, &QWebSocket::disconnected, this, &ChargerConnection::disconnected); + connect(&m_websocket, &QWebSocket::stateChanged, this, &ChargerConnection::stateChanged); + connect(&m_websocket, &QWebSocket::textMessageReceived, this, &ChargerConnection::textMessageReceived); + connect(&m_websocket, &QWebSocket::binaryMessageReceived, this, &ChargerConnection::binaryMessageReceived); + connect(&m_websocket, qOverload(&QWebSocket::error), this, &ChargerConnection::error); + connect(&m_websocket, &QWebSocket::peerVerifyError, this, &ChargerConnection::peerVerifyError); + connect(&m_websocket, &QWebSocket::sslErrors, this, &ChargerConnection::sslErrors); + connect(&m_websocket, &QWebSocket::alertReceived, this, &ChargerConnection::alertReceived); + connect(&m_websocket, &QWebSocket::handshakeInterruptedOnError, this, &ChargerConnection::handshakeInterruptedOnError); +} + +void ChargerConnection::maintainStatus(const QJsonObject &msg, bool forceChange) +{ + bool variantChanged{forceChange}; + bool isGoChanged{forceChange}; + bool isAustralienChanged{forceChange}; + bool resetCardChanged{forceChange}; + bool connectedWifiChanged{forceChange}; + bool versionChanged{forceChange}; + bool updateChanged{forceChange}; + bool uptimeChanged{forceChange}; + bool currentPartitionChanged{forceChange}; + bool rebootsChanged{forceChange}; + bool carStateChanged{forceChange}; + bool energyChanged{forceChange}; + bool livedataChanged{forceChange}; + + const auto status = msg.value("status").toObject(); + for (auto iter = std::cbegin(status); iter != std::cend(status); iter++) + { + m_fullStatus[iter.key()] = iter.value().toVariant(); + + if (iter.key() == "var") + variantChanged = true; + else if (iter.key() == "isgo") + isGoChanged = true; + else if (iter.key() == "aus") + isAustralienChanged = true; + else if (iter.key() == "frci") + resetCardChanged = true; + else if (iter.key() == "ccw") + connectedWifiChanged = true; + else if (iter.key() == "apd") + versionChanged = true; + else if (iter.key() == "ocs" || iter.key() == "ocp" || iter.key() == "ocl") + updateChanged = true; + else if (iter.key() == "rbt") + uptimeChanged = true; + else if (iter.key() == "otap") + currentPartitionChanged = true; + else if (iter.key() == "rbc") + rebootsChanged = true; + else if (iter.key() == "car") + carStateChanged = true; + else if (iter.key() == "eto") + energyChanged = true; + else if (iter.key() == "nrg") + livedataChanged = true; + } + + if (variantChanged) + emit this->variantChanged(); + if (isGoChanged) + emit this->isGoChanged(); + if (isAustralienChanged) + emit this->isAustralienChanged(); + if (resetCardChanged) + emit this->resetCardChanged(); + if (connectedWifiChanged) + emit this->connectedWifiChanged(); + if (versionChanged) + { + emit this->projectChanged(); + emit this->versionChanged(); + emit this->idfVersionChanged(); + } + if (updateChanged) + emit this->updateChanged(); + if (uptimeChanged) + emit this->uptimeChanged(); + if (currentPartitionChanged) + emit this->currentPartitionChanged(); + if (rebootsChanged) + emit this->rebootsChanged(); + if (carStateChanged) + emit this->carStateChanged(); + if (energyChanged) + emit this->energyChanged(); + if (livedataChanged) + emit this->livedataChanged(); +} + +void ChargerConnection::connected() +{ + qDebug() << "called"; +} + +void ChargerConnection::disconnected() +{ + qDebug() << "called"; +} + +void ChargerConnection::stateChanged(QAbstractSocket::SocketState state) +{ +// qDebug() << "called" << state; + + if (state != QAbstractSocket::ConnectedState) + { + if (m_status != Status::Unknown) + { + m_status = Status::Unknown; + emit statusChanged(); + } + } + + emit wsStatusChanged(); +} + +void ChargerConnection::textMessageReceived(const QString &message) +{ +// qDebug() << "called" << message; + + QJsonParseError error; + const auto doc = QJsonDocument::fromJson(message.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) + { + qWarning() << m_serial << "could not parse json:" << error.errorString(); + return; + } + + if (!doc.isObject()) + { + qWarning() << m_serial << "json is not an object"; + return; + } + + const auto msgObj = doc.object(); + + if (!msgObj.contains("type")) + { + qWarning() << "msg does not contain a type"; + return; + } + + const auto typeVal = msgObj.value("type"); + + if (!typeVal.isString()) + { + qWarning() << "msg type is not a string"; + return; + } + + const auto type = typeVal.toString(); + + bool response{}; + if (msgObj.contains("requestId")) + { + response = true; + + qDebug() << "response to a request" << message; + + emit responseReceived(msgObj.value("requestId").toString(), msgObj); + } + + if (type == "hello") + { +// qDebug() << "hello received" << msgObj; + m_status = Status::Ok; + emit statusChanged(); + } + else if (type == "offline") + { +// qDebug() << "offline received" << msgObj; + m_status = Status::Offline; + emit statusChanged(); + } + else if (type == "fullStatus") + { + bool forceChange{false}; + + if (m_receivedDelta) + { + m_receivedDelta = false; + m_fullStatus.clear(); + forceChange = true; + } + + maintainStatus(msgObj, forceChange); + } + else if (type == "deltaStatus") + { + m_receivedDelta = true; + + maintainStatus(msgObj, false); + } + else if (!response) + qWarning() << "unknown message type" << msgObj; +} + +void ChargerConnection::binaryMessageReceived(const QByteArray &message) +{ + qDebug() << "called" << message; +} + +void ChargerConnection::error(QAbstractSocket::SocketError error) +{ + qDebug() << "called" << QMetaEnum::fromType().valueToKey(error) << m_websocket.errorString(); +} + +void ChargerConnection::peerVerifyError(const QSslError &error) +{ + qDebug() << "called" << error; +} + +void ChargerConnection::sslErrors(const QList &errors) +{ + qDebug() << "called" << errors; +} + +void ChargerConnection::alertReceived(QSsl::AlertLevel level, QSsl::AlertType type, const QString &description) +{ + qDebug() << "called" << std::to_underlying(level) << std::to_underlying(type) << description; +} + +void ChargerConnection::handshakeInterruptedOnError(const QSslError &error) +{ + qDebug() << "called" << error; +} diff --git a/flotten-updater/chargerconnection.h b/flotten-updater/chargerconnection.h new file mode 100644 index 0000000..d211111 --- /dev/null +++ b/flotten-updater/chargerconnection.h @@ -0,0 +1,126 @@ +#pragma once + +#include +#include +#include +#include + +class QSslKey; +class QSslCertificate; +class QJsonObject; + +class ChargerConnection : public QObject +{ + Q_OBJECT + +public: + explicit ChargerConnection(const QSslKey &key, const QSslCertificate &cert, QString &&serial, QObject *parent = nullptr); + explicit ChargerConnection(const QSslKey &key, const QSslCertificate &cert, const QString &serial, QObject *parent = nullptr); + + void start(); + void stop(); + + const QString &serial() const { return m_serial; } + + QString wsStatusText() const; + QBrush wsStatusBackground() const; + + QString statusText() const; + QBrush statusBackground() const; + + QString variantText() const; + QBrush variantBackground() const; + + QString isGoText() const; + QBrush isGoBackground() const; + + QString isAustralienText() const; + QBrush isAustralienBackground() const; + + QString resetCardText() const; + QBrush resetCardBackground() const; + + QString connectedWifiText() const; + + QString projectText() const; + + QString versionText() const; + + QString idfVersionText() const; + + QString updateText() const; + QBrush updateBackground() const; + + std::optional uptime() const; + QString uptimeText() const; + + QString currentPartition() const; + + std::optional reboots() const; + QString rebootsText() const; + + QString carStateText() const; + + std::optional energy() const; + QString energyText() const; + + QString livedataText() const; + + void sendMessage(const QJsonDocument &doc); + void sendMessage(const QJsonObject &obj); + void sendMessage(const QString &msg); + +signals: + void responseReceived(const QString &requestId, const QJsonObject &msg); + + void wsStatusChanged(); + void statusChanged(); + void variantChanged(); + void isGoChanged(); + void isAustralienChanged(); + void resetCardChanged(); + void connectedWifiChanged(); + void projectChanged(); + void versionChanged(); + void idfVersionChanged(); + void updateChanged(); + void uptimeChanged(); + void currentPartitionChanged(); + void rebootsChanged(); + void carStateChanged(); + void energyChanged(); + void livedataChanged(); + +private: + void init(); + void maintainStatus(const QJsonObject &msg, bool forceChange); + +private slots: + void connected(); + void disconnected(); + void stateChanged(QAbstractSocket::SocketState state); + void textMessageReceived(const QString &message); + void binaryMessageReceived(const QByteArray &message); + void error(QAbstractSocket::SocketError error); + void peerVerifyError(const QSslError &error); + void sslErrors(const QList &errors); + void alertReceived(QSsl::AlertLevel level, QSsl::AlertType type, const QString &description); + void handshakeInterruptedOnError(const QSslError &error); + +private: + const QString m_serial; + const QSslKey &m_key; + const QSslCertificate &m_cert; + + enum Status { + Unknown, + Ok, + Offline + }; + Status m_status{Status::Unknown}; + + QWebSocket m_websocket; + + QVariantMap m_fullStatus; + bool m_receivedDelta{false}; +}; diff --git a/flotten-updater/chargersmodel.cpp b/flotten-updater/chargersmodel.cpp new file mode 100644 index 0000000..83206e7 --- /dev/null +++ b/flotten-updater/chargersmodel.cpp @@ -0,0 +1,434 @@ +#include "chargersmodel.h" + +#include +#include + +#include + +#include "chargerconnection.h" + +namespace { +enum { + ColumnSerial, + ColumnWsStatus, + ColumnStatus, + ColumnVariant, + ColumnIsGo, + ColumnIsAustralien, + ColumnResetCard, + ColumnConnectedWifi, + ColumnProject, + ColumnVersion, + ColumnIdfVersion, + ColumnUpdate, + ColumnReboots, + ColumnUptime, + ColumnCurrentPartition, + ColumnCarState, + ColumnEnergy, + ColumnLivedata, + NumberOfColumns +}; +} + +ChargersModel::ChargersModel(const QSslKey &key, const QSslCertificate &cert, QObject *parent) : + QAbstractTableModel{parent}, + m_key{key}, + m_cert{cert} +{ + constexpr const char *serials[] { + "096850", "10000003", + "000010", "000011", "000012", "000013", "000014", "000015", "000016", "000017", "000018", "000019", "000020", "000021", "000022", "000023", "000024", "000025", "000026", "000027", "000028", "000029", + "000030", "000031", "000032", "000033", "000034", "000035", "000036", "000037", "000038", "000039", + "999900", "999901", "999902", "999903", "999904", "999905", "999906", "999907", "999908", "999909", + "999910", "999911", "999912", "999913", "999914", "00999915", "00999916", "00999917", "00999918", "00999919", + "999920", "00999921", "00999922", "999923", "00999924", "00999925", "999926", "999927", "999928", "999929", + "91000286", + "31416778", "31416779", "31416780", "31416781", "31416783", "31416784", "31416786", "31416787", "31416788", "31416789", "31416790", "31416791", "31416793", "31416794", "31416795", "31416796", "31416797", "31416798", "31416799", "31416800", "31416801", "31416802", "31416803", "31416804", "31416806", "31416807", "31416808", "31416809", "31416810", "31416811", "31416814", "31416815", "31416816", "31416817", "31416818", "31416819", + "91000001", "91000002", "91000003", "91000004", "91000005", "91000006", "91000007", "91000008", "91000009", "91000010", "91000011", "91000012", "91000013", "91000014", "91000015", "91000016", "91000017", "91000018", "91000019", "91000020", "91000021", "91000022", "91000023", "91000024", "91000025", "91000026", "91000027", "91000028", "91000029", "91000030", "91000031", "91000032", "91000033", "91000034", "91000035", "91000036", "91000037", "91000038", "91000039", "91000040", + "000043", "000044", "000047", "000050", "900001", "900103", "900104", "900105", "900107", "900108", "900113", "900117", "900118", "900123", "900126", "900127", + "91028339", "91028457", "91028482", "91028368", "91028336", "91028374", "91028371", "91028452", "91028481", "91028455", "91028334", "91028456", "91028351", "91028367", "91028346", "91028459", "91028366", "91028335", "91028483", "91028372", "91028337", "91028338", + "91008954", "91008978", "91008282", "91009008", "91008953", "91009000", "91009024", "91048840", "91048873", "91045590", "91045593", "91045586", "91048874", "91048879", "91048882", "91048878", "91048860", "91048865", "91048853", "91048864", "91048867", "91021261", "91021260", "91021379", "91021135", "91021266", "91021259", "91021374", "91021381", "91021258", "91021275", "91021380", "91021382", "91021377", "91021240", "91021371", "91021376", "91021255", "91021378", "91021383", "91021354", "91021195", "91021370", "91021278", "91021234", "91021256", "91021257", "91021360", "91021248", "91021363", "91021270", "91021267", "91021239", "91021193", "91021268", "91028339", "91028457", "91028482", "91028368", "91036167", "91028374", "91028371", "91028452", "91028481", "91028455", "91028334", "91028456", "91028351", "91028367", "91028346", "91028459", "91028366", "91028335", "91028483", "91028372", "91028337", "91028338", + }; + for (const auto &serial : serials) + { + if (std::none_of(std::cbegin(m_chargers), std::cend(m_chargers), [&serial](auto &ptr){ + return ptr->serial() == serial; + })) + m_chargers.emplace_back(std::make_shared(m_key, m_cert, serial, this)); + } +} + +ChargersModel::~ChargersModel() = default; + +int ChargersModel::rowCount(const QModelIndex &parent) const +{ + return m_chargers.size(); +} + +int ChargersModel::columnCount(const QModelIndex &parent) const +{ + return NumberOfColumns; +} + +QVariant ChargersModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= m_chargers.size()) + { + qWarning() << "row out of bounds"; + return {}; + } + + const ChargerConnection &charger = **std::next(std::begin(m_chargers), index.row()); + + switch (index.column()) + { + case ColumnSerial: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.serial(); + } + return {}; + case ColumnWsStatus: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.wsStatusText(); + case Qt::BackgroundRole: + return charger.wsStatusBackground(); + } + return {}; + case ColumnStatus: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.statusText(); + case Qt::BackgroundRole: + return charger.statusBackground(); + } + return {}; + case ColumnVariant: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.variantText(); + case Qt::BackgroundRole: + return charger.variantBackground(); + } + return {}; + case ColumnIsGo: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.isGoText(); + case Qt::BackgroundRole: + return charger.isGoBackground(); + } + return {}; + case ColumnIsAustralien: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.isAustralienText(); + case Qt::BackgroundRole: + return charger.isAustralienBackground(); + } + return {}; + case ColumnResetCard: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.resetCardText(); + case Qt::BackgroundRole: + return charger.resetCardBackground(); + } + return {}; + case ColumnConnectedWifi: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.connectedWifiText(); + } + return {}; + case ColumnProject: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.projectText(); + } + return {}; + case ColumnVersion: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.versionText(); + } + return {}; + case ColumnIdfVersion: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.idfVersionText(); + } + return {}; + case ColumnUpdate: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.updateText(); + case Qt::BackgroundRole: + return charger.updateBackground(); + } + return {}; + case ColumnUptime: + switch (role) + { + case Qt::DisplayRole: + return charger.uptimeText(); + case Qt::EditRole: + if (const auto uptime = charger.uptime(); uptime) + return *uptime; + } + return {}; + case ColumnCurrentPartition: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.currentPartition(); + } + return {}; + case ColumnReboots: + switch (role) + { + case Qt::DisplayRole: + return charger.rebootsText(); + case Qt::EditRole: + if (const auto reboots = charger.reboots(); reboots) + return *reboots; + } + return {}; + case ColumnCarState: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.carStateText(); + } + return {}; + case ColumnEnergy: + switch (role) + { + case Qt::DisplayRole: + return charger.energyText(); + case Qt::EditRole: + if (const auto energy = charger.energy(); energy) + return *energy; + } + return {}; + case ColumnLivedata: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return charger.livedataText(); + } + return {}; + } + return {}; +} + +QVariant ChargersModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole && role != Qt::EditRole) + return {}; + + switch (orientation) + { + case Qt::Horizontal: + switch (section) + { + case ColumnSerial: return tr("Serial"); + case ColumnWsStatus: return tr("WS Status"); + case ColumnStatus: return tr("Status"); + case ColumnVariant: return tr("Variant"); + case ColumnIsGo: return tr("IsGo"); + case ColumnIsAustralien: return tr("IsAustralien"); + case ColumnResetCard: return tr("ResetCard"); + case ColumnConnectedWifi: return tr("ConnectedWifi"); + case ColumnProject: return tr("Project"); + case ColumnVersion: return tr("Version"); + case ColumnIdfVersion: return tr("IDF Version"); + case ColumnUpdate: return tr("Update"); + case ColumnReboots: return tr("Reboots"); + case ColumnUptime: return tr("Uptime"); + case ColumnCurrentPartition: return tr("Current Partition"); + case ColumnCarState: return tr("Car state"); + case ColumnEnergy: return tr("Energy"); + case ColumnLivedata: return tr("Livedata"); + } + return {}; + } +} + +void ChargersModel::addClient(const QString &serial) +{ + beginInsertRows({}, m_chargers.size(), m_chargers.size()); + + auto clientPtr = std::make_shared(m_key, m_cert, serial, this); + auto client = clientPtr.get(); + m_chargers.emplace_back(std::move(clientPtr)); + + endInsertRows(); + + client->start(); +} + +std::shared_ptr ChargersModel::getCharger(QModelIndex index) +{ + Q_ASSERT(!index.parent().isValid()); + Q_ASSERT(index.row() >= 0 && index.row() < m_chargers.size()); + auto charger = m_chargers.at(index.row()); + Q_ASSERT(charger); + return charger; +} + +std::shared_ptr ChargersModel::getCharger(QModelIndex index) const +{ + Q_ASSERT(!index.parent().isValid()); + return m_chargers.at(index.row()); +} + +void ChargersModel::connectAll() +{ + for (auto &charger : m_chargers) + charger->start(); +} + +void ChargersModel::disconnectAll() +{ + for (auto &charger : m_chargers) + charger->stop(); +} + +void ChargersModel::wsStatusChanged() +{ + columnChanged(ColumnWsStatus, {Qt::DisplayRole, Qt::EditRole, Qt::BackgroundRole}); +} + +void ChargersModel::statusChanged() +{ + columnChanged(ColumnStatus, {Qt::DisplayRole, Qt::EditRole, Qt::BackgroundRole}); +} + +void ChargersModel::variantChanged() +{ + columnChanged(ColumnVariant, {Qt::DisplayRole, Qt::EditRole, Qt::BackgroundRole}); +} + +void ChargersModel::isGoChanged() +{ + columnChanged(ColumnIsGo, {Qt::DisplayRole, Qt::EditRole, Qt::BackgroundRole}); +} + +void ChargersModel::isAustralienChanged() +{ + columnChanged(ColumnIsAustralien, {Qt::DisplayRole, Qt::EditRole, Qt::BackgroundRole}); +} + +void ChargersModel::resetCardChanged() +{ + columnChanged(ColumnResetCard, {Qt::DisplayRole, Qt::EditRole, Qt::BackgroundRole}); +} + +void ChargersModel::connectedWifiChanged() +{ + columnChanged(ColumnConnectedWifi, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::projectChanged() +{ + columnChanged(ColumnProject, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::versionChanged() +{ + columnChanged(ColumnVersion, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::idfVersionChanged() +{ + columnChanged(ColumnIdfVersion, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::updateChanged() +{ + columnChanged(ColumnUpdate, {Qt::DisplayRole, Qt::EditRole, Qt::BackgroundRole}); +} + +void ChargersModel::uptimeChanged() +{ + columnChanged(ColumnUptime, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::currentPartitionChanged() +{ + columnChanged(ColumnCurrentPartition, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::rebootsChanged() +{ + columnChanged(ColumnReboots, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::carStateChanged() +{ + columnChanged(ColumnCarState, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::energyChanged() +{ + columnChanged(ColumnEnergy, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::livedataChanged() +{ + columnChanged(ColumnLivedata, {Qt::DisplayRole, Qt::EditRole}); +} + +void ChargersModel::columnChanged(int column, const QList &roles) +{ + auto charger = qobject_cast(sender()); + if (!charger) + { + qWarning() << "unknown sender" << sender(); + return; + } + + auto iter = std::find_if(std::cbegin(m_chargers), std::cend(m_chargers), [&charger](const std::shared_ptr &ptr){ return charger == ptr.get(); }); + if (iter == std::cend(m_chargers)) + { + qWarning() << "unknown sender" << charger; + for (const auto &charger : m_chargers) + qDebug() << charger.get(); + return; + } + + auto row = std::distance(std::cbegin(m_chargers), iter); + + auto index = createIndex(row, column); + dataChanged(index, index, roles); +} diff --git a/flotten-updater/chargersmodel.h b/flotten-updater/chargersmodel.h new file mode 100644 index 0000000..0415277 --- /dev/null +++ b/flotten-updater/chargersmodel.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include +#include + +class QSslKey; +class QSslCertificate; + +class ChargerConnection; + +class ChargersModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit ChargersModel(const QSslKey &key, const QSslCertificate &cert, QObject *parent = nullptr); + ~ChargersModel() override; + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + void addClient(const QString &serial); + + std::shared_ptr getCharger(QModelIndex index); + std::shared_ptr getCharger(QModelIndex index) const; + +public slots: + void connectAll(); + void disconnectAll(); + + void wsStatusChanged(); + void statusChanged(); + void variantChanged(); + void isGoChanged(); + void isAustralienChanged(); + void resetCardChanged(); + void connectedWifiChanged(); + void projectChanged(); + void versionChanged(); + void idfVersionChanged(); + void updateChanged(); + void uptimeChanged(); + void currentPartitionChanged(); + void rebootsChanged(); + void carStateChanged(); + void energyChanged(); + void livedataChanged(); + +private: + const QSslKey &m_key; + const QSslCertificate &m_cert; + void columnChanged(int column, const QList &roles = QList()); + + std::vector> m_chargers; +}; diff --git a/flotten-updater/flottenupdatersettings.cpp b/flotten-updater/flottenupdatersettings.cpp new file mode 100644 index 0000000..4c7985a --- /dev/null +++ b/flotten-updater/flottenupdatersettings.cpp @@ -0,0 +1,21 @@ +#include "flottenupdatersettings.h" + +QByteArray FlottenUpdaterSettings::privateKey() const +{ + return value("privateKey").toByteArray(); +} + +void FlottenUpdaterSettings::setPrivateKey(const QByteArray &key) +{ + setValue("privateKey", key); +} + +QByteArray FlottenUpdaterSettings::privateCert() const +{ + return value("privateCert").toByteArray(); +} + +void FlottenUpdaterSettings::setPrivateCert(const QByteArray &cert) +{ + setValue("privateCert", cert); +} diff --git a/flotten-updater/flottenupdatersettings.h b/flotten-updater/flottenupdatersettings.h new file mode 100644 index 0000000..f344e2a --- /dev/null +++ b/flotten-updater/flottenupdatersettings.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +class FlottenUpdaterSettings : public QSettings +{ + Q_OBJECT + +public: + using QSettings::QSettings; + + QByteArray privateKey() const; + void setPrivateKey(const QByteArray &key); + + QByteArray privateCert() const; + void setPrivateCert(const QByteArray &cert); +}; diff --git a/flotten-updater/goe-root-ca.pem b/flotten-updater/goe-root-ca.pem new file mode 100644 index 0000000..e752706 --- /dev/null +++ b/flotten-updater/goe-root-ca.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFlTCCA32gAwIBAgIUcZ/DDQS4/ix7lYlys+2zMAZnHwowDQYJKoZIhvcNAQEN +BQAwZTELMAkGA1UEBhMCQVQxETAPBgNVBAgMCEthZXJudGVuMRQwEgYDVQQHDAtG +ZWxka2lyY2hlbjESMBAGA1UECgwJZ28tZSBHbWJIMRkwFwYDVQQDDBBnby1lIElP +VCBST09UIENBMCAXDTIwMTEyOTE5MTU0MFoYDzIwOTAxMTEyMTkxNTQwWjBlMQsw +CQYDVQQGEwJBVDERMA8GA1UECAwIS2Flcm50ZW4xFDASBgNVBAcMC0ZlbGRraXJj +aGVuMRIwEAYDVQQKDAlnby1lIEdtYkgxGTAXBgNVBAMMEGdvLWUgSU9UIFJPT1Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDIPhF0MflVFuGUtyI0 +UjyQpjAQStP9k4Wlx+czab6vPXbDbdVEXxXPhoHBeW0oT0g8UuOtMsdfGy3RsNCd +MLR8KiwqysaYeDmHWvVG2Bmpc8+BUm3p2zg63MMCrjp4Syfytgg4hORZjAvbRgDv +Jmh+EgjtD0nakyhBWwCkbDqQN0AXumvSaWRgvcDppI94i4aFGeg8xUBRLzsHbdHY +CuP4hCM/75FqIbOvUuXAMC/FfUxGAt/evxrQRoqyLzF0MrPvzQUTWaOuVH4WPxRM +zDm7kEBUmFL/nYcxkVjLoGqhDAjiMX1Xo4IgsUA0zUVHNHPOKyc7P2E2jgeVDe7o +WCjifoO0OQXiS/uaJmz+33dFe5hBQmKPFEit45ijrsieo1hBasUWHToaa/o/iArp +iSjKTy7YzLFKhvFVZs5iBOOc0K2KglYSBii7d7cT2wArfGpTtuA+eBthIPvM432a +KuW0IGl1BwzjlqIwnSCNd43s4KXg3sUgzW4GtIYSjI897nLlBeJ8Y5zsuEWPRaDG +FVLQJQ1lEwo740CFRSCEkGKdmKMT5TmuA4aApAT0xl8R06lbri9mY0BFWyLrGf3q +rrJO8LDcgGm5fIch+XlNb6H7nf6nP2AJOJRNha9ziNkjDI8jaKlBGveEzw6a+O4X +npsTdKS6hGJ8V74PvlDsZg5nUwIDAQABozswOTAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBhjAWBgNVHREEDzANggtpb3QuZ28tZS5jbzANBgkqhkiG9w0B +AQ0FAAOCAgEAsuGAB7wTy3+9/7/DfBWUkPHvLmR2MmkfBljt/cxFz6pFAVe+QDnb +9EmUWOmWjw7i2rvs5mvcErynj2/R+VDFyorufXMnfm7BXGl/68xIvRszpJe2snPq +cfhC48R7JX7jJcm5xMHkAg8oc3ziRH634/Lo/KXkRhSfxbbujpdAeEhO68ekLKuq +1V8RuJ+MNspLsIrn366kN6pPAQWUM0TUTAs2W0cIDgVx4pnRQd31ccBiCoRcNaer +5mA85KdTkzF0E3/jLuvB4BZRKI0apTg+/hO2a3XQhjbdAKw8C8rYu3Db+t6KRnsa +tyl2Nxeh7nfmfQgdgKZ0zA4osjREzObycZYdUgBFQOf67pU7TCxpzk3iflLSGL92 +AXRpcf3oSw3DLm+fAgaNVVHeSQ3ipB0YLGmf4l/105DAL6o4jTGFcsbPkkrtC8rT +E5+lJYyTQzvb6gLsBLKBTrpcC2BNli5f5tmZJdKOrpY9lb8XFO3W31OTl71FdwlE +KtJrht8K/9xvAUzYHXeTORl0gR7B/yNzSg4p8nJOhSJD9ktUrXErFLu4S2GMqcBM +U/YOJkFc93cSZf0TKM3TaUUslHYFKruBKH8sd15UhijnixED111F63Kwcar87QES +8ipCcGWUtufRHKaUhmswP7lbR5hbQJwIpyBg+FfzcrPUAFrXdllxe4A= +-----END CERTIFICATE----- diff --git a/flotten-updater/importcertificatedialog.cpp b/flotten-updater/importcertificatedialog.cpp new file mode 100644 index 0000000..65cb954 --- /dev/null +++ b/flotten-updater/importcertificatedialog.cpp @@ -0,0 +1,151 @@ +#include "importcertificatedialog.h" +#include "ui_importcertificatedialog.h" + +#include +#include +#include +#include +#include + +#include + +namespace { +OSSL_PROVIDER *legacy{}; +} + +ImportCertificateDialog::ImportCertificateDialog(QWidget *parent) : + QDialog{parent}, + m_ui{std::make_unique()} +{ + m_ui->setupUi(this); + + connect(m_ui->pushButtonImport, &QPushButton::clicked, this, &ImportCertificateDialog::loadP12File); + connect(m_ui->plainTextEditKey, &QPlainTextEdit::textChanged, this, &ImportCertificateDialog::keyChanged); + connect(m_ui->plainTextEditCert, &QPlainTextEdit::textChanged, this, &ImportCertificateDialog::certChanged); + + m_ui->labelSupportsSslValue->setText(QSslSocket::supportsSsl() ? tr("Yes") : tr("No")); + m_ui->labelSslLibraryVersionValue->setText(QSslSocket::sslLibraryVersionString()); + m_ui->labelSslLibraryBuildVersionValue->setText(QSslSocket::sslLibraryBuildVersionString()); + const auto &availableBackends = QSslSocket::availableBackends(); + for (const auto &backend : availableBackends) + m_ui->comboBoxSslBackend->addItem(backend); + m_ui->comboBoxSslBackend->setCurrentText(QSslSocket::activeBackend()); + connect(m_ui->comboBoxSslBackend, &QComboBox::currentIndexChanged, + this, &ImportCertificateDialog::updateActiveBackend); +} + +ImportCertificateDialog::~ImportCertificateDialog() = default; + +void ImportCertificateDialog::accept() +{ + if (m_key.isNull()) + return; + if (m_cert.isNull()) + return; + + QDialog::accept(); +} + +void ImportCertificateDialog::loadP12File() +{ + auto selected = QFileDialog::getOpenFileName(this, tr("Select certificate..."), {}, "Certificates (*.p12)"); + if (selected.isEmpty()) + return; + + QFile file{selected}; + if (!file.open(QIODevice::ReadOnly)) + { + QMessageBox::warning(this, tr("Could not open file!"), tr("Could not open file!") + "\n\n" + file.errorString()); + return; + } + + bool ok{}; + QString passwordStr = QInputDialog::getText(this, tr("Please enter password"), tr("Certificate password:"), QLineEdit::PasswordEchoOnEdit, {}, &ok); + if (!ok) + return; + + auto password = passwordStr.toUtf8(); + + QSslKey key; + QSslCertificate cert; + QList certChain; + + if (QSslSocket::activeBackend() == "openssl" && !legacy) + { + legacy = OSSL_PROVIDER_load(NULL, "legacy"); + if (!legacy) + QMessageBox::warning(this, tr("Failed to load openssl legacy provider!"), tr("Failed to load openssl legacy provider!")); + } + + if (!QSslCertificate::importPkcs12(&file, &key, &cert, &certChain, password)) + { + QMessageBox::warning(this, tr("Failed processing certificate!"), tr("Failed processing certificate!") + "\n\n" + tr("Possible reasons: openssl has a problem, the file is corrupt/invalid or the password is incorrect.")); + return; + } + + if (key.isNull()) + { + QMessageBox::warning(this, tr("Failed processing certificate!"), tr("Failed processing certificate!") + "\n\n" + tr("The key seems to be invalid.")); + return; + } + + if (cert.isNull()) + { + QMessageBox::warning(this, tr("Failed processing certificate!"), tr("Failed processing certificate!") + "\n\n" + tr("The cert seems to be invalid.")); + return; + } + + m_key = std::move(key); + m_cert = std::move(cert); + + { + QSignalBlocker blocker{m_ui->plainTextEditKey}; + m_ui->plainTextEditKey->setPlainText(m_key.toPem()); + } + + { + auto palette = m_ui->plainTextEditKey->palette(); + palette.setBrush(QPalette::Base, QColor{200, 255, 200}); + palette.setBrush(QPalette::Window, QColor{200, 255, 200}); + m_ui->plainTextEditKey->setPalette(palette); + } + + { + QSignalBlocker blocker{m_ui->plainTextEditCert}; + m_ui->plainTextEditCert->setPlainText(m_cert.toPem()); + } + + { + auto palette = m_ui->plainTextEditCert->palette(); + palette.setBrush(QPalette::Base, QColor{200, 255, 200}); + palette.setBrush(QPalette::Window, QColor{200, 255, 200}); + m_ui->plainTextEditCert->setPalette(palette); + } +} + +void ImportCertificateDialog::keyChanged() +{ + m_key = QSslKey{m_ui->plainTextEditKey->toPlainText().toUtf8(), QSsl::KeyAlgorithm::Rsa, QSsl::Pem}; + + auto palette = m_ui->plainTextEditKey->palette(); + palette.setBrush(QPalette::Base, m_key.isNull() ? QColor{255, 200, 200} : QColor{200, 255, 200}); + palette.setBrush(QPalette::Window, m_key.isNull() ? QColor{255, 200, 200} : QColor{200, 255, 200}); + m_ui->plainTextEditKey->setPalette(palette); +} + +void ImportCertificateDialog::certChanged() +{ + m_cert = QSslCertificate{m_ui->plainTextEditCert->toPlainText().toUtf8()}; + + auto palette = m_ui->plainTextEditCert->palette(); + palette.setBrush(QPalette::Base, m_cert.isNull() ? QColor{255, 200, 200} : QColor{200, 255, 200}); + palette.setBrush(QPalette::Window, m_cert.isNull() ? QColor{255, 200, 200} : QColor{200, 255, 200}); + m_ui->plainTextEditCert->setPalette(palette); +} + +void ImportCertificateDialog::updateActiveBackend() +{ + if (!QSslSocket::setActiveBackend(m_ui->comboBoxSslBackend->currentText())) + QMessageBox::warning(this, tr("Could not change active backend"), tr("Could not change active backend")); + m_ui->comboBoxSslBackend->setCurrentText(QSslSocket::activeBackend()); +} diff --git a/flotten-updater/importcertificatedialog.h b/flotten-updater/importcertificatedialog.h new file mode 100644 index 0000000..78e65da --- /dev/null +++ b/flotten-updater/importcertificatedialog.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#include + +namespace Ui { class ImportCertificateDialog; } + +class ImportCertificateDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ImportCertificateDialog(QWidget *parent = nullptr); + ~ImportCertificateDialog(); + + const QSslKey &privateKey() const { return m_key; } + const QSslCertificate &privateCert() const { return m_cert; } + +public slots: + void accept() override; + +private slots: + void loadP12File(); + void keyChanged(); + void certChanged(); + void updateActiveBackend(); + +private: + const std::unique_ptr m_ui; + + QSslKey m_key; + QSslCertificate m_cert; +}; diff --git a/flotten-updater/importcertificatedialog.ui b/flotten-updater/importcertificatedialog.ui new file mode 100644 index 0000000..f63d515 --- /dev/null +++ b/flotten-updater/importcertificatedialog.ui @@ -0,0 +1,184 @@ + + + ImportCertificateDialog + + + + 0 + 0 + 459 + 429 + + + + Import Certificate + + + + + + + + Supports Ssl: + + + + + + + TextLabel + + + + + + + Ssl Library Version: + + + + + + + TextLabel + + + + + + + Ssl Library Build Version: + + + + + + + TextLabel + + + + + + + Ssl Backend: + + + comboBoxSslBackend + + + + + + + + + + Automatic Import: + + + pushButtonImport + + + + + + + Load p12 file... + + + + + + + Private Key: + + + plainTextEditKey + + + + + + + true + + + + + + + Private Cert: + + + plainTextEditCert + + + + + + + true + + + + + + + + + Warning: Your private key and cert will be stored on this machine, but can later be erased from the main window. + + + true + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Save + + + + + + + + + buttonBox + accepted() + ImportCertificateDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ImportCertificateDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/flotten-updater/main.cpp b/flotten-updater/main.cpp new file mode 100644 index 0000000..ca6b2fc --- /dev/null +++ b/flotten-updater/main.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#ifdef Q_OS_WIN +#include +#endif + +#include "flottenupdatersettings.h" +#include "importcertificatedialog.h" +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} " + "[" + "%{if-debug}D%{endif}" + "%{if-info}I%{endif}" + "%{if-warning}W%{endif}" + "%{if-critical}C%{endif}" + "%{if-fatal}F%{endif}" + "] " + "%{function}(): " + "%{message}")); + + QApplication app{argc, argv}; + + QCoreApplication::setOrganizationName("feedc0de"); + QCoreApplication::setOrganizationDomain("brunner.ninja"); + QCoreApplication::setApplicationName("flotten-updater"); + +#ifdef Q_OS_WIN + if (QSslSocket::availableBackends().contains("schannel")) + QSslSocket::setActiveBackend("schannel"); +#endif + + FlottenUpdaterSettings settings; + QByteArray keyBuf = settings.privateKey(); + QByteArray certBuf = settings.privateCert(); + QSslKey key{keyBuf, QSsl::KeyAlgorithm::Rsa, QSsl::Pem}; + QSslCertificate cert{certBuf}; + + if (keyBuf.isEmpty()) + goto loadCert; + + if (certBuf.isEmpty()) + goto loadCert; + + if (key.isNull()) + { + QMessageBox::warning(nullptr, + QCoreApplication::translate("main", "Could not parse private key!"), + QCoreApplication::translate("main", "Could not parse private key!")); + goto loadCert; + } + + if (cert.isNull()) + { + QMessageBox::warning(nullptr, + QCoreApplication::translate("main", "Could not parse private cert!"), + QCoreApplication::translate("main", "Could not parse private cert!")); + goto loadCert; + } + + goto showMainWindow; + + { + loadCert: + ImportCertificateDialog dialog; + if (dialog.exec() != QDialog::Accepted) + return 0; + + key = dialog.privateKey(); + cert = dialog.privateCert(); + settings.setPrivateKey(key.toPem()); + settings.setPrivateCert(cert.toPem()); + } + + showMainWindow: + + MainWindow mainWindow{settings, key, cert}; + mainWindow.show(); + + return app.exec(); +} diff --git a/flotten-updater/mainwindow.cpp b/flotten-updater/mainwindow.cpp new file mode 100644 index 0000000..8858e5d --- /dev/null +++ b/flotten-updater/mainwindow.cpp @@ -0,0 +1,189 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "flottenupdatersettings.h" +#include "chargersmodel.h" +#include "requestdialog.h" +#include "setarbitraryapikeydialog.h" + +MainWindow::MainWindow(FlottenUpdaterSettings &settings, const QSslKey &key, + const QSslCertificate &cert, QWidget *parent) : + QMainWindow{parent}, + m_ui{std::make_unique()}, + m_settings{settings}, + m_model{std::make_unique(key, cert, this)}, + m_proxyModel{std::make_unique(this)} +{ + m_ui->setupUi(this); + + m_proxyModel->setSourceModel(m_model.get()); + m_proxyModel->setSortRole(Qt::EditRole); + m_ui->treeView->setModel(m_proxyModel.get()); + + connect(m_ui->pushButtonConnectAll, &QAbstractButton::pressed, m_model.get(), &ChargersModel::connectAll); + connect(m_ui->pushButtonDisconnectAll, &QAbstractButton::pressed, m_model.get(), &ChargersModel::disconnectAll); + connect(m_ui->pushButtonAdd, &QAbstractButton::pressed, this, &MainWindow::doAdd); + connect(m_ui->pushButtonRemove, &QAbstractButton::pressed, this, &MainWindow::doRemove); + connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::selectionChanged); + connect(m_ui->treeView, &QTreeView::customContextMenuRequested, this, &MainWindow::contextMenuRequested); +} + +MainWindow::~MainWindow() = default; + +void MainWindow::doAdd() +{ + bool ok{}; + const auto serial = QInputDialog::getText(this, tr("Serial"), tr("Serial"), QLineEdit::Normal, {}, &ok); + if (!ok) + return; + + m_model->addClient(serial); +} + +void MainWindow::doRemove() +{ + QMessageBox::warning(this, tr("Not yet implemented!"), tr("Not yet implemented!")); +} + +void MainWindow::selectionChanged() +{ + m_ui->statusbar->showMessage(tr("%0 selected").arg(m_ui->treeView->selectionModel()->selectedRows().count())); +} + +void MainWindow::contextMenuRequested(const QPoint &pos) +{ + auto selectedRows = m_ui->treeView->selectionModel()->selectedRows(); + if (selectedRows.isEmpty()) + return; + + // map proxied indices to those from the model + std::transform(std::begin(selectedRows), std::end(selectedRows), std::begin(selectedRows), + [&](const QModelIndex &index){ return m_proxyModel->mapToSource(index); }); + + // get all the chargers for selected indices + std::vector> chargers; + chargers.reserve(selectedRows.size()); + std::transform(std::begin(selectedRows), std::end(selectedRows), std::back_inserter(chargers), + [&](const QModelIndex &index){ auto charger = m_model->getCharger(index); Q_ASSERT(charger); return charger; }); + + Q_ASSERT(std::all_of(std::begin(chargers), std::end(chargers), [](const auto &charger)->bool{ return charger.get(); })); + + QMenu menu; + auto actionSetUpdateUrl = menu.addAction(tr("Set update url...")); + auto actionStartUpdate = menu.addAction(tr("Start update...")); + auto actionReboot = menu.addAction(tr("Reboot...")); + auto actionSetChargectrlOverride = menu.addAction(tr("Set chargectrl override...")); + auto actionSetAbitraryApiKey = menu.addAction(tr("Set abitrary api key...")); + if (const auto selected = menu.exec(m_ui->treeView->viewport()->mapToGlobal(pos)); selected == actionSetUpdateUrl) + { + bool ok{}; + const auto url = QInputDialog::getText(this, tr("Enter update url..."), tr("Update url:"), QLineEdit::Normal, {}, &ok); + if (!ok) + return; + QJsonObject msg { + { "type", "setValue" }, + { "key", "ocu" }, + { "value", url }, + { "sudo", true } + }; + RequestDialog{std::move(msg), std::move(chargers), this}.exec(); + } + else if (selected == actionStartUpdate) + { + QString branch; + { + QInputDialog dialog{this}; + dialog.setWindowTitle(tr("Select update release...")); + dialog.setInputMode(QInputDialog::TextInput); + dialog.setLabelText(tr("Update release")); + dialog.setComboBoxEditable(true); + dialog.setComboBoxItems({QStringLiteral("__default")}); + if (dialog.exec() != QDialog::Accepted) + return; + branch = dialog.textValue(); + } + QJsonObject msg { + { "type", "setValue" }, + { "key", "oct" }, + { "value", branch }, + { "sudo", true } + }; + RequestDialog{std::move(msg), std::move(chargers), this}.exec(); + } + else if (selected == actionReboot) + { + if (const auto result = QMessageBox::question(this, tr("Are you sure?"), tr("Do you really want to reboot selected devices?")); result == QMessageBox::Yes) + { + QJsonObject msg { + { "type", "setValue" }, + { "key", "rst" }, + { "value", 1 }, + { "sudo", true } + }; + RequestDialog{std::move(msg), std::move(chargers), this}.exec(); + } + } + else if (selected == actionSetChargectrlOverride) + { + QJsonObject msg { + { "type", "setValue" }, + { "key", "stao" }, + { "value", QJsonObject { + { "car", 2 }, + { "error", 0 }, + { "phases", QJsonArray { + true, + true, + true, + true, + true, + true + } }, + { "temperature", 30 }, + { "voltageL1", 230 }, + { "voltageL2", 230 }, + { "voltageL3", 230 }, + { "voltageN", 0 }, + { "ampereL1", 32 }, + { "ampereL2", 32 }, + { "ampereL3", 32 }, + { "powerL1", 7360 }, + { "powerL2", 7360 }, + { "powerL3", 7360 }, + { "powerN", 0 }, + { "powerTotal", 22080 }, + { "powerFactorL1", 90 }, + { "powerFactorL2", 90 }, + { "powerFactorL3", 90 }, + { "powerFactorN", 0 } + } }, + { "sudo", true } + }; + RequestDialog{std::move(msg), std::move(chargers), this}.exec(); + } + else if (selected == actionSetAbitraryApiKey) + { + SetArbitraryApiKeyDialog dialog{this}; + if (dialog.exec() == QDialog::Accepted) + { + QJsonObject msg { + { "type", "setValue" }, + { "key", dialog.apiKey() }, + { "value", dialog.value() } + }; + if (dialog.sudo()) + msg["sudo"] = true; + RequestDialog{std::move(msg), std::move(chargers), this}.exec(); + } + } +} diff --git a/flotten-updater/mainwindow.h b/flotten-updater/mainwindow.h new file mode 100644 index 0000000..e75a2a1 --- /dev/null +++ b/flotten-updater/mainwindow.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include + +namespace Ui { class MainWindow; } +class QSslKey; +class QSslCertificate; +class QItemSelection; +class ChargersModel; +class QSortFilterProxyModel; +class FlottenUpdaterSettings; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(FlottenUpdaterSettings &settings, const QSslKey &key, + const QSslCertificate &cert, QWidget *parent = nullptr); + ~MainWindow() override; + +private slots: + void doAdd(); + void doRemove(); + void selectionChanged(); + void contextMenuRequested(const QPoint &pos); + +private: + const std::unique_ptr m_ui; + FlottenUpdaterSettings &m_settings; + const std::unique_ptr m_model; + const std::unique_ptr m_proxyModel; +}; diff --git a/flotten-updater/mainwindow.ui b/flotten-updater/mainwindow.ui new file mode 100644 index 0000000..9ed4fdd --- /dev/null +++ b/flotten-updater/mainwindow.ui @@ -0,0 +1,82 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + + + Connect All + + + + + + + Disconnect All + + + + + + + Add + + + + + + + Remove + + + + + + + + + Qt::ContextMenuPolicy::CustomContextMenu + + + QAbstractItemView::SelectionMode::ExtendedSelection + + + false + + + true + + + + + + + + + 0 + 0 + 800 + 22 + + + + + + + + diff --git a/flotten-updater/requestdialog.cpp b/flotten-updater/requestdialog.cpp new file mode 100644 index 0000000..7394363 --- /dev/null +++ b/flotten-updater/requestdialog.cpp @@ -0,0 +1,19 @@ +#include "requestdialog.h" +#include "ui_requestdialog.h" + +#include +#include + +#include "chargerconnection.h" +#include "requestmodel.h" + +RequestDialog::RequestDialog(QJsonObject &&msg, std::vector> &&chargers, QWidget *parent) : + QDialog{parent}, + m_ui{std::make_unique()}, + m_model{std::make_unique(std::move(msg), std::move(chargers), this)} +{ + m_ui->setupUi(this); + m_ui->treeView->setModel(m_model.get()); +} + +RequestDialog::~RequestDialog() = default; diff --git a/flotten-updater/requestdialog.h b/flotten-updater/requestdialog.h new file mode 100644 index 0000000..9bd213f --- /dev/null +++ b/flotten-updater/requestdialog.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +class QJsonObject; +class ChargerConnection; +class RequestModel; + +namespace Ui { class RequestDialog; } + +class RequestDialog : public QDialog +{ + Q_OBJECT + +public: + explicit RequestDialog(QJsonObject &&msg, std::vector> &&chargers, QWidget *parent = nullptr); + ~RequestDialog(); + +private: + const std::unique_ptr m_ui; + const std::unique_ptr m_model; +}; diff --git a/flotten-updater/requestdialog.ui b/flotten-updater/requestdialog.ui new file mode 100644 index 0000000..07091c3 --- /dev/null +++ b/flotten-updater/requestdialog.ui @@ -0,0 +1,71 @@ + + + RequestDialog + + + + 0 + 0 + 400 + 300 + + + + Pending requests... + + + + + + false + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + RequestDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + RequestDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/flotten-updater/requestmodel.cpp b/flotten-updater/requestmodel.cpp new file mode 100644 index 0000000..2c8bd7d --- /dev/null +++ b/flotten-updater/requestmodel.cpp @@ -0,0 +1,177 @@ +#include "requestmodel.h" + +#include +#include +#include + +#include +#include + +#include "chargerconnection.h" + +namespace { +enum { + ColumnSerial, + ColumnRequestId, + ColumnStatus, + ColumnMessage, + NumberOfColumns +}; + +QString getRandomString(); +} + +RequestModel::RequestModel(QJsonObject &&msg, std::vector> &&chargers, QObject *parent) : + QAbstractTableModel{parent} +{ + m_requests.reserve(chargers.size()); + + for (auto &charger : chargers) + { + Request request { + .charger = std::move(charger), + .requestId = getRandomString() + }; + + connect(request.charger.get(), &ChargerConnection::responseReceived, this, &RequestModel::responseReceived); + + { + QJsonObject msg2 = msg; + msg2["requestId"] = request.requestId; + request.charger->sendMessage(msg2); + } + + m_requests.emplace_back(std::move(request)); + } +} + +RequestModel::~RequestModel() = default; + +int RequestModel::rowCount(const QModelIndex &parent) const +{ + return m_requests.size(); +} + +int RequestModel::columnCount(const QModelIndex &parent) const +{ + return NumberOfColumns; +} + +QVariant RequestModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= m_requests.size()) + { + qWarning() << "row out of bounds"; + return {}; + } + + const Request &request = *std::next(std::begin(m_requests), index.row()); + + switch (index.column()) + { + case ColumnSerial: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return request.charger->serial(); + } + return {}; + case ColumnRequestId: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return request.requestId; + } + return {}; + case ColumnStatus: + switch (role) + { + case Qt::DisplayRole: + switch (request.status) + { + case Request::Status::Pending: return tr("Pending"); + case Request::Status::Failed: return tr("Failed"); + case Request::Status::Succeeded: return tr("Succeeded"); + } + return QString::number(std::to_underlying(request.status)); + case Qt::EditRole: + return std::to_underlying(request.status); + } + return {}; + case ColumnMessage: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return request.message; + } + return {}; + } + return {}; +} + +QVariant RequestModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole && role != Qt::EditRole) + return {}; + + switch (orientation) + { + case Qt::Horizontal: + switch (section) + { + case ColumnSerial: return tr("Serial"); + case ColumnRequestId: return tr("RequestId"); + case ColumnStatus: return tr("Status"); + case ColumnMessage: return tr("Message"); + } + return {}; + } + + return {}; +} + +void RequestModel::responseReceived(const QString &requestId, const QJsonObject &msg) +{ + qDebug() << requestId << msg; + + auto iter = std::find_if(std::begin(m_requests), std::end(m_requests), + [&requestId](const auto &request){ return request.requestId == requestId; }); + if (iter == std::end(m_requests)) + { + qWarning() << "unknown request id" << requestId; + return; + } + + const auto row = std::distance(std::begin(m_requests), iter); + if (msg.value("success").toBool()) + { + iter->status = Request::Status::Succeeded; + emit dataChanged(index(row, ColumnStatus), index(row, ColumnStatus), { Qt::DisplayRole, Qt::EditRole }); + } + else + { + iter->status = Request::Status::Failed; + iter->message = msg.value("message").toString(); + emit dataChanged(index(row, ColumnStatus), index(row, ColumnMessage), { Qt::DisplayRole, Qt::EditRole }); + } +} + +namespace { +QString getRandomString() +{ + const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + const int randomStringLength = 12; // assuming you want random strings of 12 characters + + QString randomString; + for(int i = 0; ibounded(possibleCharacters.length()); + QChar nextChar = possibleCharacters.at(index); + randomString.append(nextChar); + } + return randomString; +} +} diff --git a/flotten-updater/requestmodel.h b/flotten-updater/requestmodel.h new file mode 100644 index 0000000..c2d7438 --- /dev/null +++ b/flotten-updater/requestmodel.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include + +class QJsonObject; +class ChargerConnection; + +class RequestModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit RequestModel(QJsonObject &&msg, std::vector> &&chargers, QObject *parent = nullptr); + ~RequestModel() override; + + // QAbstractItemModel interface + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + +private slots: + void responseReceived(const QString &requestId, const QJsonObject &msg); + +private: + struct Request { + std::shared_ptr charger; + QString requestId; + enum class Status { Pending, Failed, Succeeded }; + Status status{Status::Pending}; + QString message; + }; + std::vector m_requests; +}; diff --git a/flotten-updater/setarbitraryapikeydialog.cpp b/flotten-updater/setarbitraryapikeydialog.cpp new file mode 100644 index 0000000..b3be469 --- /dev/null +++ b/flotten-updater/setarbitraryapikeydialog.cpp @@ -0,0 +1,61 @@ +#include "setarbitraryapikeydialog.h" +#include "ui_setarbitraryapikeydialog.h" + +#include +#include +#include +#include +#include + +SetArbitraryApiKeyDialog::SetArbitraryApiKeyDialog(QWidget *parent) : + QDialog{parent}, + m_ui{std::make_unique()} +{ + m_ui->setupUi(this); +} + +SetArbitraryApiKeyDialog::~SetArbitraryApiKeyDialog() = default; + +QString SetArbitraryApiKeyDialog::apiKey() const +{ + return m_ui->lineEditApiKey->text(); +} + +QJsonValue SetArbitraryApiKeyDialog::value() const +{ + return m_value; +} + +bool SetArbitraryApiKeyDialog::sudo() const +{ + return m_ui->checkBoxSudo->isChecked(); +} + +void SetArbitraryApiKeyDialog::accept() +{ + QJsonParseError error; + const auto doc = QJsonDocument::fromJson("[" + m_ui->plainTextEditValue->toPlainText().toUtf8() + "]", &error); + if (error.error != QJsonParseError::NoError) + { + QMessageBox::warning(this, tr("Could not parse JSON"), tr("Could not parse JSON") + "\n\n" + error.errorString()); + return; + } + + if (!doc.isArray()) + { + QMessageBox::warning(this, tr("Unexpected not array!"), tr("Unexpected not array!")); + return; + } + + const auto array = doc.array(); + + if (array.size() != 1) + { + QMessageBox::warning(this, tr("Unexpected array length!"), tr("Unexpected array length: %0!").arg(array.size())); + return; + } + + m_value = array.first(); + + QDialog::accept(); +} diff --git a/flotten-updater/setarbitraryapikeydialog.h b/flotten-updater/setarbitraryapikeydialog.h new file mode 100644 index 0000000..10c7d24 --- /dev/null +++ b/flotten-updater/setarbitraryapikeydialog.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +namespace Ui { class SetArbitraryApiKeyDialog; } + +class SetArbitraryApiKeyDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SetArbitraryApiKeyDialog(QWidget *parent = nullptr); + ~SetArbitraryApiKeyDialog() override; + + QString apiKey() const; + QJsonValue value() const; + bool sudo() const; + +protected: + void accept() override; + +private: + const std::unique_ptr m_ui; + + QJsonValue m_value; +}; diff --git a/flotten-updater/setarbitraryapikeydialog.ui b/flotten-updater/setarbitraryapikeydialog.ui new file mode 100644 index 0000000..5f4cdf7 --- /dev/null +++ b/flotten-updater/setarbitraryapikeydialog.ui @@ -0,0 +1,127 @@ + + + SetArbitraryApiKeyDialog + + + + 0 + 0 + 285 + 224 + + + + Dialog + + + + + + + + ApiKey: + + + lineEditApiKey + + + + + + + Value: + + + plainTextEditValue + + + + + + + + + + + + + Sudo: + + + checkBoxSudo + + + + + + + Enabled + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SetArbitraryApiKeyDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SetArbitraryApiKeyDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/goecommon/CMakeLists.txt b/goecommon/CMakeLists.txt new file mode 100644 index 0000000..3095b6a --- /dev/null +++ b/goecommon/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(goecommon + goesettings.cpp + goesettings.h +) + +target_link_libraries(goecommon + Qt6::Core +) + +target_include_directories(goecommon PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/appsettings.cpp b/goecommon/goesettings.cpp similarity index 72% rename from src/appsettings.cpp rename to goecommon/goesettings.cpp index 0ebc2ec..3bb102c 100644 --- a/src/appsettings.cpp +++ b/goecommon/goesettings.cpp @@ -1,11 +1,8 @@ -#include "appsettings.h" - -#include -#include +#include "goesettings.h" #include -std::vector AppSettings::getSavedDevices() +std::vector GoeSettings::getSavedDevices() { std::vector savedDevices; @@ -26,7 +23,7 @@ std::vector AppSettings::getSavedDevices() return savedDevices; } -void AppSettings::saveSavedDevices(const std::vector &savedDevices) +void GoeSettings::saveSavedDevices(const std::vector &savedDevices) { beginWriteArray("devices"); for (qsizetype i = 0; i < savedDevices.size(); ++i) { @@ -40,7 +37,7 @@ void AppSettings::saveSavedDevices(const std::vector &savedDevices) endArray(); } -void AppSettings::refreshSavedDevice(SavedDevice &&savedDevice) +void GoeSettings::refreshSavedDevice(SavedDevice &&savedDevice) { auto savedDevices = getSavedDevices(); @@ -58,7 +55,7 @@ void AppSettings::refreshSavedDevice(SavedDevice &&savedDevice) saveSavedDevices(savedDevices); } -void AppSettings::removeSavedDevice(const QString &serial) +void GoeSettings::removeSavedDevice(const QString &serial) { auto savedDevices = getSavedDevices(); @@ -72,7 +69,7 @@ void AppSettings::removeSavedDevice(const QString &serial) saveSavedDevices(savedDevices); } -int AppSettings::numberOfAppInstances() const +int GoeSettings::numberOfAppInstances() const { if (m_numberOfAppInstances) return *m_numberOfAppInstances; @@ -85,7 +82,7 @@ int AppSettings::numberOfAppInstances() const return numberOfAppInstances; } -void AppSettings::setNumberOfAppInstances(int numberOfAppInstances) +void GoeSettings::setNumberOfAppInstances(int numberOfAppInstances) { if (numberOfAppInstances < 1) return; @@ -94,7 +91,7 @@ void AppSettings::setNumberOfAppInstances(int numberOfAppInstances) emit numberOfAppInstancesChanged(numberOfAppInstances); } -QString AppSettings::solalawebKey() const +QString GoeSettings::solalawebKey() const { if (m_solalawebKey) return *m_solalawebKey; @@ -104,28 +101,14 @@ QString AppSettings::solalawebKey() const return solalawebKey; } -void AppSettings::setSolalawebKey(const QString &solalawebKey) +void GoeSettings::setSolalawebKey(const QString &solalawebKey) { setValue("solalawebKey", solalawebKey); m_solalawebKey = solalawebKey; emit solalawebKeyChanged(solalawebKey); } -bool AppSettings::loadSolalawebKey(const QString &url) -{ - QFile file{QQmlFile::urlToLocalFileOrQrc(url)}; - if (!file.open(QFile::ReadOnly)) - { - qWarning() << "Could not open file:" << file.errorString(); - return false; - } - - setSolalawebKey(file.readAll()); - - return true; -} - -QString AppSettings::solalawebCert() const +QString GoeSettings::solalawebCert() const { if (m_solalawebCert) return *m_solalawebCert; @@ -135,28 +118,14 @@ QString AppSettings::solalawebCert() const return solalawebCert; } -void AppSettings::setSolalawebCert(const QString &solalawebCert) +void GoeSettings::setSolalawebCert(const QString &solalawebCert) { setValue("solalawebCert", solalawebCert); m_solalawebCert = solalawebCert; emit solalawebCertChanged(solalawebCert); } -bool AppSettings::loadSolalawebCert(const QString &url) -{ - QFile file{QQmlFile::urlToLocalFileOrQrc(url)}; - if (!file.open(QFile::ReadOnly)) - { - qWarning() << "Could not open file:" << file.errorString(); - return false; - } - - setSolalawebCert(file.readAll()); - - return true; -} - -bool AppSettings::showSolalaweb() const +bool GoeSettings::showSolalaweb() const { if (m_showSolalaweb) return *m_showSolalaweb; @@ -166,7 +135,7 @@ bool AppSettings::showSolalaweb() const return showSolalaweb; } -void AppSettings::setShowSolalaweb(bool showSolalaweb) +void GoeSettings::setShowSolalaweb(bool showSolalaweb) { setValue("showSolalaweb", showSolalaweb); m_showSolalaweb = showSolalaweb; diff --git a/src/appsettings.h b/goecommon/goesettings.h similarity index 89% rename from src/appsettings.h rename to goecommon/goesettings.h index 5a1fad4..0712b36 100644 --- a/src/appsettings.h +++ b/goecommon/goesettings.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include @@ -14,10 +13,9 @@ struct SavedDevice QString password; }; -class AppSettings : public QSettings +class GoeSettings : public QSettings { Q_OBJECT - QML_ELEMENT Q_PROPERTY(int numberOfAppInstances READ numberOfAppInstances WRITE setNumberOfAppInstances NOTIFY numberOfAppInstancesChanged FINAL) Q_PROPERTY(QString solalawebKey READ solalawebKey WRITE setSolalawebKey NOTIFY solalawebKeyChanged FINAL) Q_PROPERTY(QString solalawebCert READ solalawebCert WRITE setSolalawebCert NOTIFY solalawebCertChanged FINAL) @@ -34,11 +32,9 @@ public: QString solalawebKey() const; void setSolalawebKey(const QString &solalawebKey); - Q_INVOKABLE bool loadSolalawebKey(const QString &url); QString solalawebCert() const; void setSolalawebCert(const QString &solalawebCert); - Q_INVOKABLE bool loadSolalawebCert(const QString &url); bool showSolalaweb() const; void setShowSolalaweb(bool showSolalaweb); diff --git a/qml/js/qrcode.min.js b/qml/js/qrcode.min.js deleted file mode 120000 index e1ac4a9..0000000 --- a/qml/js/qrcode.min.js +++ /dev/null @@ -1 +0,0 @@ -../../3rdparty/qrcode-svg/dist/qrcode.min.js \ No newline at end of file