Make columns in flotten-updater configurable

This commit is contained in:
2024-10-22 16:04:08 +02:00
parent e655a4e89f
commit 62451113f4
13 changed files with 239 additions and 65 deletions

View File

@ -15,10 +15,6 @@ endif(CCACHE_FOUND)
add_definitions(-DQT_GUI_LIB)
add_subdirectory(3rdparty)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Quick WebSockets LinguistTools)
qt_standard_project_setup(REQUIRES 6.6 I18N_TRANSLATED_LANGUAGES de)
add_subdirectory(evcharger-app)
if (NOT ANDROID)
add_subdirectory(flotten-updater)

View File

@ -1,3 +1,7 @@
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Quick WebSockets LinguistTools)
qt_standard_project_setup(REQUIRES 6.6 I18N_TRANSLATED_LANGUAGES de)
qt_add_executable(evcharger-app WIN32 MACOSX_BUNDLE
appsettings.cpp
appsettings.h

View File

@ -576,92 +576,92 @@
<context>
<name>ChargersModel</name>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="264"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="286"/>
<source>Serial</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="265"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="287"/>
<source>WS Status</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="266"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="288"/>
<source>Status</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="267"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="289"/>
<source>Variant</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="268"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="290"/>
<source>IsGo</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="269"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="291"/>
<source>IsAustralien</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="270"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="292"/>
<source>ResetCard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="271"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="293"/>
<source>ConnectedWifi</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="272"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="294"/>
<source>Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="273"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="295"/>
<source>Version</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="274"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="296"/>
<source>IDF Version</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="275"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="297"/>
<source>Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="276"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="298"/>
<source>Reboots</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="277"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="299"/>
<source>Uptime</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="278"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="300"/>
<source>Current Partition</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="279"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="301"/>
<source>Car state</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="280"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="302"/>
<source>Energy</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/chargersmodel.cpp" line="281"/>
<location filename="../../flotten-updater/chargersmodel.cpp" line="303"/>
<source>Livedata</source>
<translation type="unfinished"></translation>
</message>
@ -1687,64 +1687,64 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="26"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="30"/>
<source>Yes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="26"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="30"/>
<source>No</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="51"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="55"/>
<source>Select certificate...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="58"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="62"/>
<source>Could not open file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="63"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="67"/>
<source>Please enter password</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="63"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="67"/>
<source>Certificate password:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="77"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="82"/>
<source>Failed to load openssl legacy provider!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="82"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="88"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="94"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="100"/>
<source>Failed processing certificate!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="82"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="88"/>
<source>Possible reasons: openssl has a problem, the file is corrupt/invalid or the password is incorrect.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="88"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="94"/>
<source>The key seems to be invalid.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="94"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="100"/>
<source>The cert seems to be invalid.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="149"/>
<location filename="../../flotten-updater/importcertificatedialog.cpp" line="155"/>
<source>Could not change active backend</source>
<translation type="unfinished"></translation>
</message>
@ -1869,72 +1869,97 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="46"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="48"/>
<source>Serial</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="55"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="57"/>
<source>Not yet implemented!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="60"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="62"/>
<source>%0 selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="82"/>
<source>Set update url...</source>
<location filename="../../flotten-updater/mainwindow.cpp" line="69"/>
<source>Add new column...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="83"/>
<source>Start update...</source>
<location filename="../../flotten-updater/mainwindow.cpp" line="76"/>
<source>Remove column %0</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="84"/>
<source>Enter api key</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="84"/>
<source>Api key:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="115"/>
<source>Set update url...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="116"/>
<source>Start update...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="117"/>
<source>Reboot...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="85"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="118"/>
<source>Set chargectrl override...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="86"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="119"/>
<source>Set abitrary api key...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="90"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="120"/>
<source>Open app(s)...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="124"/>
<source>Enter update url...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="90"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="124"/>
<source>Update url:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="106"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="140"/>
<source>Select update release...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="108"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="142"/>
<source>Update release</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="125"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="159"/>
<source>Are you sure?</source>
<translation type="unfinished">Sind Sie sicher?</translation>
</message>
<message>
<location filename="../../flotten-updater/mainwindow.cpp" line="125"/>
<location filename="../../flotten-updater/mainwindow.cpp" line="159"/>
<source>Do you really want to reboot selected devices?</source>
<translation type="unfinished"></translation>
</message>

View File

@ -1,4 +1,8 @@
add_executable(flotten-updater
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Quick WebSockets LinguistTools)
qt_standard_project_setup(REQUIRES 6.6 I18N_TRANSLATED_LANGUAGES de)
qt_add_executable(flotten-updater WIN32 MACOSX_BUNDLE
chargerconnection.cpp
chargerconnection.h
chargersmodel.cpp
@ -22,6 +26,12 @@ add_executable(flotten-updater
setarbitraryapikeydialog.ui
)
qt_add_resources(flotten-updater
PREFIX /
FILES
goe-root-ca.pem
)
find_package(OpenSSL)
if (OpenSSL_Found)
target_compile_definitions(flotten-updater HAS_OPENSSL)
@ -35,12 +45,13 @@ target_link_libraries(flotten-updater PUBLIC
Qt6::Core
Qt6::Gui
Qt6::Widgets
Qt6::Quick
Qt6::WebSockets
goecommon
)
qt_add_resources(flotten-updater
PREFIX /
FILES
goe-root-ca.pem
install(TARGETS flotten-updater
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

View File

@ -346,6 +346,11 @@ QString ChargerConnection::livedataText() const
return {};
}
QVariant ChargerConnection::getApiKey(const QString &apiKey) const
{
return m_fullStatus.value(apiKey);
}
void ChargerConnection::sendMessage(const QJsonDocument &doc)
{
sendMessage(QString::fromUtf8(doc.toJson()));
@ -384,6 +389,7 @@ void ChargerConnection::init()
connect(this, &ChargerConnection::carStateChanged, model, &ChargersModel::carStateChanged);
connect(this, &ChargerConnection::energyChanged, model, &ChargersModel::energyChanged);
connect(this, &ChargerConnection::livedataChanged, model, &ChargersModel::livedataChanged);
connect(this, &ChargerConnection::apiKeyChanged, model, &ChargersModel::apiKeyChanged);
}
else
qWarning() << "unexpected parent";
@ -396,7 +402,7 @@ void ChargerConnection::init()
auto caCerts = QSslCertificate::fromPath(":/goe-root-ca.pem");
if (caCerts.empty())
qFatal("could not parse root ca");
for (const auto &caCert : qAsConst(caCerts))
for (const auto &caCert : std::as_const(caCerts))
qDebug() << caCert.issuerDisplayName();
sslConfig.setCaCertificates(std::move(caCerts));
}
@ -411,7 +417,7 @@ void ChargerConnection::init()
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<QAbstractSocket::SocketError>(&QWebSocket::error), this, &ChargerConnection::error);
connect(&m_websocket, &QWebSocket::errorOccurred, this, &ChargerConnection::errorOccurred);
connect(&m_websocket, &QWebSocket::peerVerifyError, this, &ChargerConnection::peerVerifyError);
connect(&m_websocket, &QWebSocket::sslErrors, this, &ChargerConnection::sslErrors);
connect(&m_websocket, &QWebSocket::alertReceived, this, &ChargerConnection::alertReceived);
@ -465,6 +471,8 @@ void ChargerConnection::maintainStatus(const QJsonObject &msg, bool forceChange)
energyChanged = true;
else if (iter.key() == "nrg")
livedataChanged = true;
emit apiKeyChanged(iter.key());
}
if (variantChanged)
@ -611,7 +619,7 @@ void ChargerConnection::binaryMessageReceived(const QByteArray &message)
qDebug() << "called" << message;
}
void ChargerConnection::error(QAbstractSocket::SocketError error)
void ChargerConnection::errorOccurred(QAbstractSocket::SocketError error)
{
qDebug() << "called" << QMetaEnum::fromType<QAbstractSocket::SocketError>().valueToKey(error) << m_websocket.errorString();
}

View File

@ -66,6 +66,8 @@ public:
QString livedataText() const;
QVariant getApiKey(const QString &apiKey) const;
void sendMessage(const QJsonDocument &doc);
void sendMessage(const QJsonObject &obj);
void sendMessage(const QString &msg);
@ -90,6 +92,7 @@ signals:
void carStateChanged();
void energyChanged();
void livedataChanged();
void apiKeyChanged(const QString &apiKey);
private:
void init();
@ -101,7 +104,7 @@ private slots:
void stateChanged(QAbstractSocket::SocketState state);
void textMessageReceived(const QString &message);
void binaryMessageReceived(const QByteArray &message);
void error(QAbstractSocket::SocketError error);
void errorOccurred(QAbstractSocket::SocketError error);
void peerVerifyError(const QSslError &error);
void sslErrors(const QList<QSslError> &errors);
void alertReceived(QSsl::AlertLevel level, QSsl::AlertType type, const QString &description);

View File

@ -2,10 +2,12 @@
#include <QDebug>
#include <QBrush>
#include <QJsonDocument>
#include <algorithm>
#include "chargerconnection.h"
#include "flottenupdatersettings.h"
namespace {
enum {
@ -31,10 +33,13 @@ enum {
};
}
ChargersModel::ChargersModel(const QSslKey &key, const QSslCertificate &cert, QObject *parent) :
ChargersModel::ChargersModel(FlottenUpdaterSettings &settings, const QSslKey &key,
const QSslCertificate &cert, QObject *parent) :
QAbstractTableModel{parent},
m_settings{settings},
m_key{key},
m_cert{cert}
m_cert{cert},
m_customColumns{settings.customColumns()}
{
constexpr const char *serials[] {
"096850", "10000003",
@ -49,6 +54,7 @@ ChargersModel::ChargersModel(const QSslKey &key, const QSslCertificate &cert, QO
"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",
"91100000", "91100001", "91100002", "91100003", "91100004", "91100005", "91100006", "91100007", "91100008", "91100009", "91100010", "91100011", "91100012", "91100013", "91100014", "91100015", "91100016", "91100017", "91100018", "91100019", "91100020", "91100021", "91100022", "91100023", "91100024", "91100025", "91100026", "91100027", "91100028", "91100029", "91100030", "91100031"
};
for (const auto &serial : serials)
{
@ -68,7 +74,7 @@ int ChargersModel::rowCount(const QModelIndex &parent) const
int ChargersModel::columnCount(const QModelIndex &parent) const
{
return NumberOfColumns;
return NumberOfColumns + m_customColumns.size();
}
QVariant ChargersModel::data(const QModelIndex &index, int role) const
@ -248,6 +254,22 @@ QVariant ChargersModel::data(const QModelIndex &index, int role) const
}
return {};
}
if (index.column() >= NumberOfColumns && index.column() - NumberOfColumns < m_customColumns.size())
switch (role)
{
case Qt::DisplayRole:
{
auto variant = charger.getApiKey(m_customColumns.at(index.column() - NumberOfColumns));
auto str = variant.toString();
if (str.isEmpty())
str = QJsonDocument::fromVariant(variant).toJson(QJsonDocument::Compact);
return str;
}
case Qt::EditRole:
return charger.getApiKey(m_customColumns.at(index.column() - NumberOfColumns));
}
return {};
}
@ -280,8 +302,14 @@ QVariant ChargersModel::headerData(int section, Qt::Orientation orientation, int
case ColumnEnergy: return tr("Energy");
case ColumnLivedata: return tr("Livedata");
}
if (section >= NumberOfColumns && section - NumberOfColumns < m_customColumns.size())
return m_customColumns[section - NumberOfColumns];
return {};
}
return {};
}
void ChargersModel::addClient(const QString &serial)
@ -312,6 +340,30 @@ std::shared_ptr<const ChargerConnection> ChargersModel::getCharger(QModelIndex i
return m_chargers.at(index.row());
}
void ChargersModel::addCustomColumn(const QString &apiKey)
{
beginInsertColumns({}, NumberOfColumns + m_customColumns.size(), NumberOfColumns + m_customColumns.size());
m_customColumns.push_back(apiKey);
endInsertColumns();
m_settings.setCustomColumns(m_customColumns);
}
bool ChargersModel::customColumnRemovable(int section)
{
return section >= NumberOfColumns && section - NumberOfColumns < m_customColumns.size();
}
void ChargersModel::removeCustomColumn(int section)
{
if (section < NumberOfColumns || section - NumberOfColumns >= m_customColumns.size())
return;
beginRemoveColumns({}, section, section);
m_customColumns.erase(std::next(std::begin(m_customColumns), section - NumberOfColumns));
endRemoveColumns();
m_settings.setCustomColumns(m_customColumns);
}
void ChargersModel::connectAll()
{
for (auto &charger : m_chargers)
@ -409,6 +461,13 @@ void ChargersModel::livedataChanged()
columnChanged(ColumnLivedata, {Qt::DisplayRole, Qt::EditRole});
}
void ChargersModel::apiKeyChanged(const QString &apiKey)
{
for (auto iter = std::cbegin(m_customColumns); iter != std::cend(m_customColumns); iter++)
if (*iter == apiKey)
columnChanged(NumberOfColumns + std::distance(std::cbegin(m_customColumns), iter), {Qt::DisplayRole, Qt::EditRole});
}
void ChargersModel::columnChanged(int column, const QList<int> &roles)
{
auto charger = qobject_cast<ChargerConnection*>(sender());
@ -430,5 +489,5 @@ void ChargersModel::columnChanged(int column, const QList<int> &roles)
auto row = std::distance(std::cbegin(m_chargers), iter);
auto index = createIndex(row, column);
dataChanged(index, index, roles);
emit dataChanged(index, index, roles);
}

View File

@ -8,6 +8,7 @@
class QSslKey;
class QSslCertificate;
class FlottenUpdaterSettings;
class ChargerConnection;
class ChargersModel : public QAbstractTableModel
@ -15,7 +16,8 @@ class ChargersModel : public QAbstractTableModel
Q_OBJECT
public:
explicit ChargersModel(const QSslKey &key, const QSslCertificate &cert, QObject *parent = nullptr);
explicit ChargersModel(FlottenUpdaterSettings &settings, const QSslKey &key,
const QSslCertificate &cert, QObject *parent = nullptr);
~ChargersModel() override;
int rowCount(const QModelIndex &parent) const override;
@ -28,6 +30,10 @@ public:
std::shared_ptr<ChargerConnection> getCharger(QModelIndex index);
std::shared_ptr<const ChargerConnection> getCharger(QModelIndex index) const;
void addCustomColumn(const QString &apiKey);
bool customColumnRemovable(int section);
void removeCustomColumn(int section);
public slots:
void connectAll();
void disconnectAll();
@ -49,11 +55,15 @@ public slots:
void carStateChanged();
void energyChanged();
void livedataChanged();
void apiKeyChanged(const QString &apiKey);
private:
FlottenUpdaterSettings &m_settings;
const QSslKey &m_key;
const QSslCertificate &m_cert;
void columnChanged(int column, const QList<int> &roles = QList<int>());
std::vector<std::shared_ptr<ChargerConnection>> m_chargers;
QStringList m_customColumns;
};

View File

@ -19,3 +19,13 @@ void FlottenUpdaterSettings::setPrivateCert(const QByteArray &cert)
{
setValue("privateCert", cert);
}
QStringList FlottenUpdaterSettings::customColumns() const
{
return value("customColumns").toStringList();
}
void FlottenUpdaterSettings::setCustomColumns(const QStringList &customColumns)
{
setValue("customColumns", customColumns);
}

View File

@ -14,4 +14,7 @@ public:
QByteArray privateCert() const;
void setPrivateCert(const QByteArray &cert);
QStringList customColumns() const;
void setCustomColumns(const QStringList &customColumns);
};

View File

@ -21,7 +21,7 @@ MainWindow::MainWindow(FlottenUpdaterSettings &settings, const QSslKey &key,
QMainWindow{parent},
m_ui{std::make_unique<Ui::MainWindow>()},
m_settings{settings},
m_model{std::make_unique<ChargersModel>(key, cert, this)},
m_model{std::make_unique<ChargersModel>(settings, key, cert, this)},
m_proxyModel{std::make_unique<QSortFilterProxyModel>(this)}
{
m_ui->setupUi(this);
@ -35,6 +35,8 @@ MainWindow::MainWindow(FlottenUpdaterSettings &settings, const QSslKey &key,
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);
m_ui->treeView->header()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui->treeView->header(), &QTreeView::customContextMenuRequested, this, &MainWindow::headerContextMenuRequested);
connect(m_ui->treeView, &QTreeView::customContextMenuRequested, this, &MainWindow::contextMenuRequested);
}
@ -60,6 +62,37 @@ void MainWindow::selectionChanged()
m_ui->statusbar->showMessage(tr("%0 selected").arg(m_ui->treeView->selectionModel()->selectedRows().count()));
}
void MainWindow::headerContextMenuRequested(const QPoint &pos)
{
QMenu menu;
auto addColumnAction = menu.addAction(tr("Add new column..."));
QAction *removeColumnAction{};
auto header = m_ui->treeView->header();
const auto section = header->logicalIndexAt(pos);
if (section >= 0)
{
removeColumnAction = menu.addAction(tr("Remove column %0").arg(header->model()->headerData(section, Qt::Horizontal).toString()));
if (!m_model->customColumnRemovable(section))
removeColumnAction->setEnabled(false);
}
if (auto selectedAction = menu.exec(header->mapToGlobal(pos)); selectedAction == addColumnAction)
{
bool ok{};
auto apiKey = QInputDialog::getText(this, tr("Enter api key"), tr("Api key:"), QLineEdit::Normal, {}, &ok);
if (!ok)
return;
m_model->addCustomColumn(apiKey);
}
else if (removeColumnAction && selectedAction == removeColumnAction)
{
m_model->removeCustomColumn(section);
}
}
void MainWindow::contextMenuRequested(const QPoint &pos)
{
auto selectedRows = m_ui->treeView->selectionModel()->selectedRows();
@ -84,6 +117,7 @@ void MainWindow::contextMenuRequested(const QPoint &pos)
auto actionReboot = menu.addAction(tr("Reboot..."));
auto actionSetChargectrlOverride = menu.addAction(tr("Set chargectrl override..."));
auto actionSetAbitraryApiKey = menu.addAction(tr("Set abitrary api key..."));
auto actionOpenApps = menu.addAction(tr("Open app(s)..."));
if (const auto selected = menu.exec(m_ui->treeView->viewport()->mapToGlobal(pos)); selected == actionSetUpdateUrl)
{
bool ok{};
@ -186,4 +220,8 @@ void MainWindow::contextMenuRequested(const QPoint &pos)
RequestDialog{std::move(msg), std::move(chargers), this}.exec();
}
}
else if (selected == actionOpenApps)
{
}
}

View File

@ -25,6 +25,7 @@ private slots:
void doAdd();
void doRemove();
void selectionChanged();
void headerContextMenuRequested(const QPoint &pos);
void contextMenuRequested(const QPoint &pos);
private:

View File

@ -1,10 +1,16 @@
add_library(goecommon
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Quick WebSockets LinguistTools)
qt_standard_project_setup(REQUIRES 6.6 I18N_TRANSLATED_LANGUAGES de)
qt_add_library(goecommon
goesettings.cpp
goesettings.h
)
target_link_libraries(goecommon
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
target_include_directories(goecommon PUBLIC