From a2b488c2a744f640b8a07b550dd864b26bd2c419 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Wed, 19 Aug 2026 16:23:42 +0200 Subject: [PATCH] first try in implementing homeassistant --- evcharger-app/CMakeLists.txt | 7 +- evcharger-app/appsettings.h | 1 + evcharger-app/homeassistantclient.cpp | 581 ++++++++++++++++++ evcharger-app/homeassistantclient.h | 143 +++++ evcharger-app/i18n/qml_da.ts | 403 ++++++++++-- evcharger-app/i18n/qml_de.ts | 403 ++++++++++-- evcharger-app/i18n/qml_en.ts | 17 + evcharger-app/i18n/qml_fi.ts | 403 ++++++++++-- evcharger-app/i18n/qml_nb.ts | 403 ++++++++++-- evcharger-app/i18n/qml_sv.ts | 403 ++++++++++-- evcharger-app/i18n/qml_tr.ts | 401 ++++++++++-- evcharger-app/qml/ControllerTabPage.qml | 55 +- .../qml/HomeAssistantSetupDialog.qml | 479 +++++++++++++++ evcharger-app/qml/InformationsTabPage.qml | 212 +++++-- evcharger-app/qml/qmldir | 1 + 15 files changed, 3606 insertions(+), 306 deletions(-) create mode 100644 evcharger-app/homeassistantclient.cpp create mode 100644 evcharger-app/homeassistantclient.h create mode 100644 evcharger-app/qml/HomeAssistantSetupDialog.qml diff --git a/evcharger-app/CMakeLists.txt b/evcharger-app/CMakeLists.txt index b014c9e..ee72922 100644 --- a/evcharger-app/CMakeLists.txt +++ b/evcharger-app/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Quick WebSockets LinguistTools Charts) +find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets Quick WebSockets LinguistTools Charts) qt_standard_project_setup(REQUIRES 6.6 I18N_TRANSLATED_LANGUAGES de sv fi nb da tr) @@ -13,6 +13,8 @@ qt_add_executable(evcharger-app WIN32 MACOSX_BUNDLE deviceconnection.h devicesmodel.cpp devicesmodel.h + homeassistantclient.cpp + homeassistantclient.h main.cpp sendmessagehelper.cpp sendmessagehelper.h @@ -22,6 +24,8 @@ qt_add_executable(evcharger-app WIN32 MACOSX_BUNDLE versionutils.h ) +target_link_libraries(evcharger-app PRIVATE Qt6::Network) + qt6_add_translations(evcharger-app RESOURCE_PREFIX /EVChargerApp/i18n TS_FILE_BASE qml @@ -116,6 +120,7 @@ qt_add_qml_module(evcharger-app qml/GroundCheckPage.qml qml/HardwareInformationPage.qml qml/HotspotPage.qml + qml/HomeAssistantSetupDialog.qml qml/InformationsTabPage.qml qml/InformationChartPanel.qml qml/IndexedEditValueItem.qml diff --git a/evcharger-app/appsettings.h b/evcharger-app/appsettings.h index 66f0551..7a87040 100644 --- a/evcharger-app/appsettings.h +++ b/evcharger-app/appsettings.h @@ -32,4 +32,5 @@ public: signals: void uiLanguageChanged(QLocale::Language uiLanguage); + void homeAssistantConfigurationChanged(); }; diff --git a/evcharger-app/homeassistantclient.cpp b/evcharger-app/homeassistantclient.cpp new file mode 100644 index 0000000..7cb78f7 --- /dev/null +++ b/evcharger-app/homeassistantclient.cpp @@ -0,0 +1,581 @@ +#include "homeassistantclient.h" + +#include "appsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace +{ +constexpr auto urlSetting = "homeAssistant/url"; +constexpr auto tokenSetting = "homeAssistant/accessToken"; +constexpr auto chartsSetting = "homeAssistant/charts"; + +QString normalizedBaseUrl(const QString &input) +{ + QString text = input.trimmed(); + while (text.endsWith('/')) + text.chop(1); + return text; +} + +QDateTime beginningOfPeriod(const QString &range, int offset) +{ + const QDate today = QDate::currentDate(); + QDate start; + if (range == QStringLiteral("week")) + start = today.addDays(1 - today.dayOfWeek()).addDays(offset * 7); + else if (range == QStringLiteral("month")) + start = QDate(today.year(), today.month(), 1).addMonths(offset); + else if (range == QStringLiteral("year")) + start = QDate(today.year(), 1, 1).addYears(offset); + else + start = today.addDays(offset); + return start.startOfDay(); +} + +QDateTime endOfPeriod(const QString &range, const QDateTime &start) +{ + if (range == QStringLiteral("week")) + return start.addDays(7); + if (range == QStringLiteral("month")) + return QDateTime(start.date().addMonths(1), QTime(0, 0)); + if (range == QStringLiteral("year")) + return QDateTime(start.date().addYears(1), QTime(0, 0)); + return start.addDays(1); +} + +QString defaultColor(int index) +{ + static const QStringList colors{ + QStringLiteral("#3274d9"), QStringLiteral("#f2c037"), + QStringLiteral("#2ca02c"), QStringLiteral("#d64f4f"), + QStringLiteral("#8b5cf6"), QStringLiteral("#00a6a6"), + QStringLiteral("#e8792e"), QStringLiteral("#64748b")}; + return colors.at(index % colors.size()); +} +} + +HomeAssistantEntityModel::HomeAssistantEntityModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +int HomeAssistantEntityModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_visibleRows.size(); +} + +QVariant HomeAssistantEntityModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= m_visibleRows.size()) + return {}; + const Entity &entity = m_entities.at(m_visibleRows.at(index.row())); + switch (role) + { + case EntityIdRole: return entity.entityId; + case NameRole: return entity.name; + case UnitRole: return entity.unit; + case DeviceClassRole: return entity.deviceClass; + case StateRole: return entity.state; + case SearchTextRole: return entity.searchText; + default: return {}; + } +} + +QHash HomeAssistantEntityModel::roleNames() const +{ + return {{EntityIdRole, "entityId"}, {NameRole, "name"}, {UnitRole, "unit"}, + {DeviceClassRole, "deviceClass"}, {StateRole, "currentValue"}, + {SearchTextRole, "searchText"}}; +} + +void HomeAssistantEntityModel::setEntities(QList entities) +{ + std::sort(entities.begin(), entities.end(), [](const Entity &left, const Entity &right) { + const int nameOrder = QString::localeAwareCompare(left.name, right.name); + return nameOrder == 0 ? left.entityId < right.entityId : nameOrder < 0; + }); + + beginResetModel(); + m_entities = std::move(entities); + m_entityRows.clear(); + for (int i = 0; i < m_entities.size(); ++i) + m_entityRows.insert(m_entities.at(i).entityId, i); + m_visibleRows.clear(); + const QStringList words = m_filter.split(' ', Qt::SkipEmptyParts); + for (int i = 0; i < m_entities.size(); ++i) + { + bool matches = true; + for (const QString &word : words) + matches = matches && m_entities.at(i).searchText.contains(word, Qt::CaseInsensitive); + if (matches) + m_visibleRows.append(i); + } + endResetModel(); +} + +void HomeAssistantEntityModel::setFilter(const QString &filter) +{ + const QString simplified = filter.simplified(); + if (m_filter == simplified) + return; + m_filter = simplified; + rebuildVisibleRows(); +} + +const HomeAssistantEntityModel::Entity *HomeAssistantEntityModel::entity( + const QString &entityId) const +{ + const auto row = m_entityRows.constFind(entityId); + return row == m_entityRows.cend() ? nullptr : &m_entities.at(*row); +} + +void HomeAssistantEntityModel::rebuildVisibleRows() +{ + beginResetModel(); + m_visibleRows.clear(); + const QStringList words = m_filter.split(' ', Qt::SkipEmptyParts); + for (int i = 0; i < m_entities.size(); ++i) + { + bool matches = true; + for (const QString &word : words) + matches = matches && m_entities.at(i).searchText.contains(word, Qt::CaseInsensitive); + if (matches) + m_visibleRows.append(i); + } + endResetModel(); +} + +HomeAssistantClient::HomeAssistantClient(QObject *parent) + : QObject(parent), m_settings(nullptr), m_entities(this) +{ +} + +void HomeAssistantClient::setSettings(AppSettings *settings) +{ + if (m_settings == settings) + return; + if (m_settings) + disconnect(m_settings, &AppSettings::homeAssistantConfigurationChanged, + this, &HomeAssistantClient::loadConfiguration); + m_settings = settings; + if (m_settings) + connect(m_settings, &AppSettings::homeAssistantConfigurationChanged, + this, &HomeAssistantClient::loadConfiguration); + loadConfiguration(); + emit settingsChanged(); +} + +void HomeAssistantClient::setEntitySearch(const QString &search) +{ + if (m_entitySearch == search) + return; + m_entitySearch = search; + m_entities.setFilter(search); + emit entitySearchChanged(); +} + +void HomeAssistantClient::testAndSaveConnection(const QString &baseUrl, + const QString &accessToken) +{ + if (m_busy) + return; + + m_pendingBaseUrl = normalizedBaseUrl(baseUrl); + m_pendingAccessToken = accessToken.trimmed(); + const QUrl parsedUrl(m_pendingBaseUrl); + if (!parsedUrl.isValid() + || (parsedUrl.scheme() != QStringLiteral("http") + && parsedUrl.scheme() != QStringLiteral("https")) + || parsedUrl.host().isEmpty()) + { + setErrorText(tr("Enter a valid HTTP or HTTPS Home Assistant address.")); + emit connectionTestFinished(false); + return; + } + if (m_pendingAccessToken.isEmpty()) + { + setErrorText(tr("Enter a Home Assistant access token.")); + emit connectionTestFinished(false); + return; + } + + setErrorText({}); + setStatusText(tr("Connecting to Home Assistant…")); + setBusy(true); + + QUrl url = parsedUrl; + QString path = url.path(); + if (path.endsWith('/')) + path.chop(1); + url.setPath(path + QStringLiteral("/api/")); + QNetworkReply *reply = m_network.get(authenticatedRequest( + url, m_pendingAccessToken.toUtf8())); + connect(reply, &QNetworkReply::finished, this, [this, reply] { + const int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + const QByteArray body = reply->readAll(); + const bool success = reply->error() == QNetworkReply::NoError + && status >= 200 && status < 300; + if (success) + { + m_baseUrl = m_pendingBaseUrl; + m_accessToken = m_pendingAccessToken; + persistConfiguration(); + emit configurationChanged(); + setErrorText({}); + setStatusText(tr("Connected to Home Assistant.")); + } + else + { + QString detail = reply->errorString(); + const QJsonDocument errorDocument = QJsonDocument::fromJson(body); + if (errorDocument.isObject() + && !errorDocument.object().value(QStringLiteral("message")).toString().isEmpty()) + detail = errorDocument.object().value(QStringLiteral("message")).toString(); + setErrorText(status == 401 + ? tr("Home Assistant rejected the access token.") + : tr("Could not connect to Home Assistant: %1").arg(detail)); + setStatusText({}); + } + setBusy(false); + reply->deleteLater(); + emit connectionTestFinished(success); + if (success) + loadEntities(); + }); +} + +void HomeAssistantClient::loadEntities() +{ + if (!configured() || m_entitiesLoading) + return; + setErrorText({}); + setEntitiesLoading(true); + setStatusText(tr("Loading Home Assistant entities…")); + QNetworkReply *reply = m_network.get(authenticatedRequest(apiUrl(QStringLiteral("api/states")))); + connect(reply, &QNetworkReply::finished, this, [this, reply] { + const QByteArray body = reply->readAll(); + QJsonParseError parseError; + const QJsonDocument document = QJsonDocument::fromJson(body, &parseError); + if (reply->error() != QNetworkReply::NoError || !document.isArray()) + { + setErrorText(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 401 + ? tr("The Home Assistant credential is no longer valid.") + : tr("Could not load Home Assistant entities: %1") + .arg(reply->errorString())); + } + else + { + QList entities; + entities.reserve(document.array().size()); + for (const QJsonValue &value : document.array()) + { + const QJsonObject object = value.toObject(); + const QString entityId = object.value(QStringLiteral("entity_id")).toString(); + if (entityId.isEmpty()) + continue; + const QJsonObject attributes = object.value(QStringLiteral("attributes")).toObject(); + HomeAssistantEntityModel::Entity entity; + entity.entityId = entityId; + entity.name = attributes.value(QStringLiteral("friendly_name")).toString(); + if (entity.name.isEmpty()) + entity.name = entityId; + entity.unit = attributes.value(QStringLiteral("unit_of_measurement")).toString(); + entity.deviceClass = attributes.value(QStringLiteral("device_class")).toString(); + entity.state = object.value(QStringLiteral("state")).toString(); + entity.searchText = entity.name + QLatin1Char(' ') + entity.entityId + + QLatin1Char(' ') + entity.unit + QLatin1Char(' ') + + entity.deviceClass; + entities.append(std::move(entity)); + } + m_entities.setEntities(std::move(entities)); + setErrorText({}); + setStatusText(tr("Loaded %1 Home Assistant entities.") + .arg(m_entities.rowCount())); + } + setEntitiesLoading(false); + reply->deleteLater(); + }); +} + +void HomeAssistantClient::saveCharts(const QVariantList &charts) +{ + m_charts = charts; + persistConfiguration(); + emit chartsChanged(); +} + +void HomeAssistantClient::clearConfiguration() +{ + if (m_historyReply) + { + disconnect(m_historyReply, nullptr, this, nullptr); + m_historyReply->abort(); + m_historyReply->deleteLater(); + m_historyReply = nullptr; + setHistoryLoading(false); + } + m_baseUrl.clear(); + m_accessToken.clear(); + m_charts.clear(); + m_historyPanels.clear(); + m_entities.setEntities({}); + if (m_settings) + { + m_settings->remove(QStringLiteral("homeAssistant")); + m_settings->sync(); + emit m_settings->homeAssistantConfigurationChanged(); + } + setStatusText({}); + setErrorText({}); + emit configurationChanged(); + emit chartsChanged(); + emit historyPanelsChanged(); +} + +void HomeAssistantClient::requestHistory(const QString &range, int offset) +{ + if (!configured() || m_charts.isEmpty()) + return; + + if (m_historyReply) + { + disconnect(m_historyReply, nullptr, this, nullptr); + m_historyReply->abort(); + m_historyReply->deleteLater(); + m_historyReply = nullptr; + setHistoryLoading(false); + } + + QStringList entityIds; + for (const QVariant &chartValue : m_charts) + { + const QVariantList series = chartValue.toMap().value(QStringLiteral("entities")).toList(); + for (const QVariant &seriesValue : series) + { + const QString entityId = seriesValue.toMap().value(QStringLiteral("entityId")).toString(); + if (!entityId.isEmpty() && !entityIds.contains(entityId)) + entityIds.append(entityId); + } + } + if (entityIds.isEmpty()) + return; + + const QDateTime start = beginningOfPeriod(range, offset); + QDateTime end = endOfPeriod(range, start); + const QDateTime now = QDateTime::currentDateTime(); + if (end > now) + end = now; + if (start > end) + return; + + QUrl url = apiUrl(QStringLiteral("api/history/period/") + + start.toUTC().toString(Qt::ISODate)); + QUrlQuery query; + query.addQueryItem(QStringLiteral("end_time"), end.toUTC().toString(Qt::ISODate)); + query.addQueryItem(QStringLiteral("filter_entity_id"), entityIds.join(',')); + query.addQueryItem(QStringLiteral("minimal_response"), QString()); + query.addQueryItem(QStringLiteral("no_attributes"), QString()); + url.setQuery(query); + + setErrorText({}); + setHistoryLoading(true); + setStatusText(tr("Loading Home Assistant history…")); + QNetworkReply *reply = m_network.get(authenticatedRequest(url)); + m_historyReply = reply; + connect(reply, &QNetworkReply::finished, this, [this, reply] { + if (m_historyReply != reply) + { + reply->deleteLater(); + return; + } + m_historyReply = nullptr; + const QByteArray body = reply->readAll(); + QJsonParseError parseError; + const QJsonDocument document = QJsonDocument::fromJson(body, &parseError); + if (reply->error() != QNetworkReply::NoError || !document.isArray()) + { + setErrorText(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 401 + ? tr("The Home Assistant credential is no longer valid.") + : tr("Could not load Home Assistant history: %1") + .arg(reply->errorString())); + } + else + { + rebuildHistoryPanels(document.array()); + setErrorText({}); + setStatusText(tr("Home Assistant history is up to date.")); + } + setHistoryLoading(false); + reply->deleteLater(); + }); +} + +QUrl HomeAssistantClient::apiUrl(const QString &relativePath) const +{ + QUrl url(m_baseUrl); + QString path = url.path(); + if (!path.endsWith('/')) + path += '/'; + url.setPath(path + relativePath); + return url; +} + +QNetworkRequest HomeAssistantClient::authenticatedRequest(const QUrl &url, + const QByteArray &token) const +{ + QNetworkRequest request(url); + request.setRawHeader("Authorization", "Bearer " + (token.isEmpty() + ? m_accessToken.toUtf8() + : token)); + request.setRawHeader("Accept", "application/json"); + request.setHeader(QNetworkRequest::UserAgentHeader, + QStringLiteral("EVChargerApp HomeAssistantClient")); + request.setTransferTimeout(20000); + return request; +} + +void HomeAssistantClient::setBusy(bool busy) +{ + if (m_busy == busy) + return; + m_busy = busy; + emit busyChanged(); +} + +void HomeAssistantClient::setEntitiesLoading(bool loading) +{ + if (m_entitiesLoading == loading) + return; + m_entitiesLoading = loading; + emit entitiesLoadingChanged(); +} + +void HomeAssistantClient::setHistoryLoading(bool loading) +{ + if (m_historyLoading == loading) + return; + m_historyLoading = loading; + emit historyLoadingChanged(); +} + +void HomeAssistantClient::setStatusText(const QString &text) +{ + if (m_statusText == text) + return; + m_statusText = text; + emit statusTextChanged(); +} + +void HomeAssistantClient::setErrorText(const QString &text) +{ + if (m_errorText == text) + return; + m_errorText = text; + emit errorTextChanged(); +} + +void HomeAssistantClient::persistConfiguration() +{ + if (!m_settings) + return; + m_settings->setValue(urlSetting, m_baseUrl); + m_settings->setValue(tokenSetting, m_accessToken); + m_settings->setValue(chartsSetting, + QJsonDocument::fromVariant(m_charts).toJson(QJsonDocument::Compact)); + m_settings->sync(); + emit m_settings->homeAssistantConfigurationChanged(); +} + +void HomeAssistantClient::loadConfiguration() +{ + const QString previousBaseUrl = m_baseUrl; + const QString previousAccessToken = m_accessToken; + const QVariantList previousCharts = m_charts; + m_baseUrl.clear(); + m_accessToken.clear(); + m_charts.clear(); + if (m_settings) + { + m_baseUrl = normalizedBaseUrl(m_settings->value(urlSetting).toString()); + m_accessToken = m_settings->value(tokenSetting).toString(); + + QJsonParseError error; + const QJsonDocument document = QJsonDocument::fromJson( + m_settings->value(chartsSetting).toByteArray(), &error); + if (error.error == QJsonParseError::NoError && document.isArray()) + m_charts = document.array().toVariantList(); + } + if (previousBaseUrl != m_baseUrl || previousAccessToken != m_accessToken) + emit configurationChanged(); + if (previousCharts != m_charts) + emit chartsChanged(); +} + +void HomeAssistantClient::rebuildHistoryPanels(const QJsonArray &history) +{ + QHash pointsByEntity; + for (const QJsonValue &seriesValue : history) + { + const QJsonArray states = seriesValue.toArray(); + QString entityId; + QVariantList points; + points.reserve(states.size()); + for (const QJsonValue &stateValue : states) + { + const QJsonObject state = stateValue.toObject(); + const QString receivedId = state.value(QStringLiteral("entity_id")).toString(); + if (!receivedId.isEmpty()) + entityId = receivedId; + bool numeric = false; + const double value = state.value(QStringLiteral("state")).toString().toDouble(&numeric); + const QString timestampText = state.value(QStringLiteral("last_changed")).toString( + state.value(QStringLiteral("last_updated")).toString()); + const QDateTime timestamp = QDateTime::fromString(timestampText, Qt::ISODate); + if (numeric && timestamp.isValid()) + points.append(QVariantMap{{QStringLiteral("t"), timestamp.toMSecsSinceEpoch() / 1000.0}, + {QStringLiteral("value"), value}}); + } + if (!entityId.isEmpty()) + pointsByEntity.insert(entityId, points); + } + + QVariantList panels; + for (const QVariant &chartValue : m_charts) + { + const QVariantMap chart = chartValue.toMap(); + QVariantList dataSeries; + const QVariantList configuredSeries = chart.value(QStringLiteral("entities")).toList(); + for (int i = 0; i < configuredSeries.size(); ++i) + { + const QVariantMap configuration = configuredSeries.at(i).toMap(); + const QString entityId = configuration.value(QStringLiteral("entityId")).toString(); + const auto *entity = m_entities.entity(entityId); + const QString label = configuration.value(QStringLiteral("label")).toString(); + dataSeries.append(QVariantMap{ + {QStringLiteral("name"), entityId}, + {QStringLiteral("friendlyName"), !label.isEmpty() ? label + : entity ? entity->name : entityId}, + {QStringLiteral("unit"), entity ? entity->unit : QString()}, + {QStringLiteral("color"), configuration.value(QStringLiteral("color"), + defaultColor(i))}, + {QStringLiteral("showLegend"), true}, + {QStringLiteral("dataSet"), pointsByEntity.value(entityId)}}); + } + panels.append(QVariantMap{{QStringLiteral("title"), chart.value(QStringLiteral("title"))}, + {QStringLiteral("dataseries"), dataSeries}}); + } + m_historyPanels = panels; + m_historyCurrentTime = QDateTime::currentSecsSinceEpoch(); + emit historyPanelsChanged(); +} diff --git a/evcharger-app/homeassistantclient.h b/evcharger-app/homeassistantclient.h new file mode 100644 index 0000000..9ec8036 --- /dev/null +++ b/evcharger-app/homeassistantclient.h @@ -0,0 +1,143 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class AppSettings; + +class HomeAssistantEntityModel final : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Role { + EntityIdRole = Qt::UserRole + 1, + NameRole, + UnitRole, + DeviceClassRole, + StateRole, + SearchTextRole, + }; + + struct Entity { + QString entityId; + QString name; + QString unit; + QString deviceClass; + QString state; + QString searchText; + }; + + explicit HomeAssistantEntityModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = {}) const override; + QVariant data(const QModelIndex &index, int role) const override; + QHash roleNames() const override; + + void setEntities(QList entities); + void setFilter(const QString &filter); + const Entity *entity(const QString &entityId) const; + +private: + void rebuildVisibleRows(); + + QList m_entities; + QList m_visibleRows; + QHash m_entityRows; + QString m_filter; +}; + +class HomeAssistantClient : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(AppSettings *settings READ settings WRITE setSettings NOTIFY settingsChanged FINAL) + Q_PROPERTY(QString baseUrl READ baseUrl NOTIFY configurationChanged FINAL) + Q_PROPERTY(bool configured READ configured NOTIFY configurationChanged FINAL) + Q_PROPERTY(bool busy READ busy NOTIFY busyChanged FINAL) + Q_PROPERTY(bool entitiesLoading READ entitiesLoading NOTIFY entitiesLoadingChanged FINAL) + Q_PROPERTY(bool historyLoading READ historyLoading NOTIFY historyLoadingChanged FINAL) + Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged FINAL) + Q_PROPERTY(QString errorText READ errorText NOTIFY errorTextChanged FINAL) + Q_PROPERTY(QString entitySearch READ entitySearch WRITE setEntitySearch NOTIFY entitySearchChanged FINAL) + Q_PROPERTY(QAbstractItemModel *entitiesModel READ entitiesModel CONSTANT FINAL) + Q_PROPERTY(QVariantList charts READ charts NOTIFY chartsChanged FINAL) + Q_PROPERTY(QVariantList historyPanels READ historyPanels NOTIFY historyPanelsChanged FINAL) + Q_PROPERTY(qreal historyCurrentTime READ historyCurrentTime NOTIFY historyPanelsChanged FINAL) + +public: + explicit HomeAssistantClient(QObject *parent = nullptr); + + AppSettings *settings() const { return m_settings; } + QString baseUrl() const { return m_baseUrl; } + bool configured() const { return !m_baseUrl.isEmpty() && !m_accessToken.isEmpty(); } + bool busy() const { return m_busy; } + bool entitiesLoading() const { return m_entitiesLoading; } + bool historyLoading() const { return m_historyLoading; } + QString statusText() const { return m_statusText; } + QString errorText() const { return m_errorText; } + QString entitySearch() const { return m_entitySearch; } + QAbstractItemModel *entitiesModel() { return &m_entities; } + QVariantList charts() const { return m_charts; } + QVariantList historyPanels() const { return m_historyPanels; } + qreal historyCurrentTime() const { return m_historyCurrentTime; } + + void setEntitySearch(const QString &search); + void setSettings(AppSettings *settings); + + Q_INVOKABLE void testAndSaveConnection(const QString &baseUrl, + const QString &accessToken); + Q_INVOKABLE void loadEntities(); + Q_INVOKABLE void saveCharts(const QVariantList &charts); + Q_INVOKABLE void clearConfiguration(); + Q_INVOKABLE void requestHistory(const QString &range, int offset); + Q_INVOKABLE QString accessTokenForEditing() const { return m_accessToken; } + +signals: + void settingsChanged(); + void configurationChanged(); + void busyChanged(); + void entitiesLoadingChanged(); + void historyLoadingChanged(); + void statusTextChanged(); + void errorTextChanged(); + void entitySearchChanged(); + void chartsChanged(); + void historyPanelsChanged(); + void connectionTestFinished(bool success); + +private: + QUrl apiUrl(const QString &relativePath) const; + QNetworkRequest authenticatedRequest(const QUrl &url, + const QByteArray &token = {}) const; + void setBusy(bool busy); + void setEntitiesLoading(bool loading); + void setHistoryLoading(bool loading); + void setStatusText(const QString &text); + void setErrorText(const QString &text); + void persistConfiguration(); + void loadConfiguration(); + void rebuildHistoryPanels(const QJsonArray &history); + + AppSettings *m_settings; + QNetworkAccessManager m_network; + HomeAssistantEntityModel m_entities; + QString m_baseUrl; + QString m_accessToken; + QString m_pendingBaseUrl; + QString m_pendingAccessToken; + QString m_statusText; + QString m_errorText; + QString m_entitySearch; + QVariantList m_charts; + QVariantList m_historyPanels; + qreal m_historyCurrentTime = 0; + bool m_busy = false; + bool m_entitiesLoading = false; + bool m_historyLoading = false; + QPointer m_historyReply; +}; diff --git a/evcharger-app/i18n/qml_da.ts b/evcharger-app/i18n/qml_da.ts index 52b3434..13d6163 100644 --- a/evcharger-app/i18n/qml_da.ts +++ b/evcharger-app/i18n/qml_da.ts @@ -2856,6 +2856,269 @@ RSSI: + + HomeAssistantClient + + + Enter a valid HTTP or HTTPS Home Assistant address. + Indtast en gyldig HTTP- eller HTTPS-adresse til Home Assistant. + + + + Enter a Home Assistant access token. + Indtast et adgangstoken til Home Assistant. + + + + Connecting to Home Assistant… + Opretter forbindelse til Home Assistant… + + + + Connected to Home Assistant. + Forbundet med Home Assistant. + + + + Home Assistant rejected the access token. + Home Assistant afviste adgangstokenet. + + + + Could not connect to Home Assistant: %1 + Kunne ikke oprette forbindelse til Home Assistant: %1 + + + + Loading Home Assistant entities… + Indlæser Home Assistant-entiteter… + + + + + The Home Assistant credential is no longer valid. + Loginoplysningerne til Home Assistant er ikke længere gyldige. + + + + Could not load Home Assistant entities: %1 + Kunne ikke indlæse Home Assistant-entiteter: %1 + + + + Loaded %1 Home Assistant entities. + Indlæste %1 Home Assistant-entiteter. + + + + Loading Home Assistant history… + Indlæser Home Assistant-historik… + + + + Could not load Home Assistant history: %1 + Kunne ikke indlæse Home Assistant-historik: %1 + + + + Home Assistant history is up to date. + Home Assistant-historikken er opdateret. + + + + HomeAssistantSetupDialog + + + Edit chart + Rediger diagram + + + + Connect Home Assistant + Forbind Home Assistant + + + + Server + Server + + + + Login + Login + + + + Charts + Diagrammer + + + + %1. %2 + %1. %2 + + + + Before you begin + Før du begynder + + + + Home Assistant must already be installed and running. A Raspberry Pi with Home Assistant on an SD card is one option; other supported Home Assistant installations work as well. + +Before charts can receive data: +• Install or configure an MQTT broker, such as the Mosquitto broker app or a separate broker. +• Connect Home Assistant's MQTT integration to that broker. +• Connect the charger to the same broker and enable MQTT. +• Enable Home Assistant MQTT auto-discovery in Home Assistant and on the charger. +• Verify that the charger's entities are visible in Home Assistant. + Home Assistant skal allerede være installeret og køre. En mulighed er en Raspberry Pi med Home Assistant på et SD-kort; andre understøttede Home Assistant-installationer fungerer også. + +Før diagrammer kan modtage data: +• Installer eller konfigurer en MQTT-broker, f.eks. Mosquitto Broker-appen eller en separat broker. +• Forbind Home Assistants MQTT-integration til denne broker. +• Forbind laderen til den samme broker, og aktivér MQTT. +• Aktivér automatisk MQTT-registrering til Home Assistant i Home Assistant og på laderen. +• Kontrollér, at laderens entiteter er synlige i Home Assistant. + + + + Installation guide + Installationsvejledning + + + + MQTT guide + MQTT-vejledning + + + + Enter the address you normally use to open Home Assistant. The app will contact this server directly. + Indtast den adresse, du normalt bruger til at åbne Home Assistant. Appen kontakter denne server direkte. + + + + + Cancel + Annuller + + + + Next + Næste + + + + Create a long-lived access token in your Home Assistant profile and paste it below. Your Home Assistant password is never entered into this app. + Opret et langtidsholdbart adgangstoken i din Home Assistant-profil, og indsæt det nedenfor. Din Home Assistant-adgangskode indtastes aldrig i denne app. + + + + Open Home Assistant profile + Åbn Home Assistant-profil + + + + Long-lived access token + Langtidsholdbart adgangstoken + + + + Hide + Skjul + + + + Show + Vis + + + + Back + Tilbage + + + + Connect + Opret forbindelse + + + + Create charts and choose exactly which Home Assistant entities each chart should display. + Opret diagrammer, og vælg præcist, hvilke Home Assistant-entiteter hvert diagram skal vise. + + + + Change server or access token + Skift server eller adgangstoken + + + + %n entity(s) + + %n entitet + %n entiteter + + + + + Edit + Rediger + + + + Delete + Slet + + + + No charts configured yet. + Der er endnu ikke konfigureret nogen diagrammer. + + + + Add chart + Tilføj diagram + + + + Done + Færdig + + + + Chart title + Diagramtitel + + + + Search by name, entity ID, unit or device class… + Søg efter navn, entitets-id, enhed eller enhedsklasse… + + + + %n entity(s) selected + + %n entitet valgt + %n entiteter valgt + + + + + Reload + Indlæs igen + + + + No matching entities found. + Ingen matchende entiteter fundet. + + + + Save chart + Gem diagram + + HotspotPage @@ -3011,7 +3274,7 @@ Information - + Idle Inaktiv @@ -3024,66 +3287,132 @@ INDLÆSER - + %1 – %2 %1 – %2 - + Receiving manufacturer data… Modtager producentdata… - + Loading manufacturer charts… Indlæser producentdiagrammer… - + %1 images %1 billeder - + %1 charts %1 diagrammer - + Loaded %1 %1 indlæst - + No information data received Ingen informationsdata modtaget - + The manufacturer cloud returned invalid data. Producentens cloud returnerede ugyldige data. - + Requesting manufacturer data… Anmoder om producentdata… - + Information Information - + Manufacturer cloud Producentens cloud + + + Connected to Home Assistant + Forbundet med Home Assistant + + + + Home Assistant is not configured + Home Assistant er ikke konfigureret + + + + Configure + Konfigurer + + + + + Set up + Konfigurer + + + + Refresh + Opdater + + + + Connect Home Assistant to display history + Forbind Home Assistant for at vise historik + + + + Create your first history chart + Opret dit første historikdiagram + + + + The setup wizard connects to your server and lets you select the entities to display. + Opsætningsguiden opretter forbindelse til din server og lader dig vælge de entiteter, der skal vises. + + + + Open the configuration and add a chart with one or more Home Assistant entities. + Åbn konfigurationen, og tilføj et diagram med en eller flere Home Assistant-entiteter. + + + + Add chart + Tilføj diagram + + + + Forget Home Assistant configuration + Glem Home Assistant-konfigurationen + + + + Forget Home Assistant? + Glem Home Assistant? + + + + This removes the server address, access token and all chart definitions stored in this app. + Dette fjerner serveradressen, adgangstokenet og alle diagramdefinitioner, der er gemt i appen. + Limited · unreliable · slow Begrænset · upålidelig · langsom - + Your own cloud Din egen cloud @@ -3092,105 +3421,97 @@ Historik · vedligeholdt · hurtig - + Lacks features · unreliable · slow Mangler funktioner · upålidelig · langsom - + Historical data · properly maintained · fast Historiske data · godt vedligeholdt · hurtig - + The manufacturer cloud provides basic charts, but can be slow, unavailable or limited to the data selected by the manufacturer. Producentens cloud leverer grundlæggende diagrammer, men kan være langsom, utilgængelig eller begrænset til de data, som producenten har valgt. - + Download information Download information - + Own your charging history Bevar ejerskabet over din ladehistorik - + Connect the charger to your own Home Assistant installation for fast, dependable history that remains under your control. You decide how long data is retained and which measurements are recorded. Forbind laderen med din egen Home Assistant-installation for at få hurtig og pålidelig historik, der forbliver under din kontrol. Du bestemmer, hvor længe data gemmes, og hvilke målinger der registreres. - Home Assistant recorder integration is being prepared. The setup below is currently a preview and does not connect yet. - Integrationen med Home Assistant Recorder er under udarbejdelse. Opsætningen nedenfor er i øjeblikket en forhåndsvisning og opretter endnu ingen forbindelse. + Integrationen med Home Assistant Recorder er under udarbejdelse. Opsætningen nedenfor er i øjeblikket en forhåndsvisning og opretter endnu ingen forbindelse. - Set up connection to Home Assistant - Opsæt forbindelse til Home Assistant + Opsæt forbindelse til Home Assistant - + Historical data Historiske data - + Day Dag - + Week Uge - + Month Måned - + Year År - + Previous Forrige - - + Next Næste - Home Assistant history will appear here - Home Assistant-historikken vises her + Home Assistant-historikken vises her - After the recorder integration is connected, this view will request measurements for the selected period. - Når Recorder-integrationen er forbundet, anmoder denne visning om målinger for den valgte periode. + Når Recorder-integrationen er forbundet, anmoder denne visning om målinger for den valgte periode. - Connect Home Assistant - Forbind Home Assistant + Forbind Home Assistant - Enter the address of your Home Assistant server. No connection is made in this preview. - Indtast adressen på din Home Assistant-server. Der oprettes ingen forbindelse i denne forhåndsvisning. + Indtast adressen på din Home Assistant-server. Der oprettes ingen forbindelse i denne forhåndsvisning. - Cancel - Annuller + Annuller Request started... diff --git a/evcharger-app/i18n/qml_de.ts b/evcharger-app/i18n/qml_de.ts index 10aeeb2..ed8148c 100644 --- a/evcharger-app/i18n/qml_de.ts +++ b/evcharger-app/i18n/qml_de.ts @@ -3031,6 +3031,269 @@ RSSI: + + HomeAssistantClient + + + Enter a valid HTTP or HTTPS Home Assistant address. + Gib eine gültige HTTP- oder HTTPS-Adresse für Home Assistant ein. + + + + Enter a Home Assistant access token. + Gib ein Home-Assistant-Zugriffstoken ein. + + + + Connecting to Home Assistant… + Verbindung zu Home Assistant wird hergestellt… + + + + Connected to Home Assistant. + Mit Home Assistant verbunden. + + + + Home Assistant rejected the access token. + Home Assistant hat das Zugriffstoken abgelehnt. + + + + Could not connect to Home Assistant: %1 + Verbindung zu Home Assistant fehlgeschlagen: %1 + + + + Loading Home Assistant entities… + Home-Assistant-Entitäten werden geladen… + + + + + The Home Assistant credential is no longer valid. + Die Home-Assistant-Anmeldedaten sind nicht mehr gültig. + + + + Could not load Home Assistant entities: %1 + Home-Assistant-Entitäten konnten nicht geladen werden: %1 + + + + Loaded %1 Home Assistant entities. + %1 Home-Assistant-Entitäten geladen. + + + + Loading Home Assistant history… + Home-Assistant-Verlauf wird geladen… + + + + Could not load Home Assistant history: %1 + Home-Assistant-Verlauf konnte nicht geladen werden: %1 + + + + Home Assistant history is up to date. + Der Home-Assistant-Verlauf ist aktuell. + + + + HomeAssistantSetupDialog + + + Edit chart + Diagramm bearbeiten + + + + Connect Home Assistant + Home Assistant verbinden + + + + Server + Server + + + + Login + Anmeldung + + + + Charts + Diagramme + + + + %1. %2 + %1. %2 + + + + Before you begin + Bevor du beginnst + + + + Home Assistant must already be installed and running. A Raspberry Pi with Home Assistant on an SD card is one option; other supported Home Assistant installations work as well. + +Before charts can receive data: +• Install or configure an MQTT broker, such as the Mosquitto broker app or a separate broker. +• Connect Home Assistant's MQTT integration to that broker. +• Connect the charger to the same broker and enable MQTT. +• Enable Home Assistant MQTT auto-discovery in Home Assistant and on the charger. +• Verify that the charger's entities are visible in Home Assistant. + Home Assistant muss bereits installiert sein und laufen. Eine Möglichkeit ist ein Raspberry Pi mit Home Assistant auf einer SD-Karte; andere unterstützte Home-Assistant-Installationen funktionieren ebenfalls. + +Bevor Diagramme Daten empfangen können: +• Installiere oder konfiguriere einen MQTT-Broker, beispielsweise die Mosquitto-Broker-App oder einen separaten Broker. +• Verbinde die MQTT-Integration von Home Assistant mit diesem Broker. +• Verbinde den Charger mit demselben Broker und aktiviere MQTT. +• Aktiviere die Home-Assistant-MQTT-Auto-Discovery in Home Assistant und auf dem Charger. +• Prüfe, ob die Entitäten des Chargers in Home Assistant sichtbar sind. + + + + Installation guide + Installationsanleitung + + + + MQTT guide + MQTT-Anleitung + + + + Enter the address you normally use to open Home Assistant. The app will contact this server directly. + Gib die Adresse ein, über die du Home Assistant normalerweise öffnest. Die App verbindet sich direkt mit diesem Server. + + + + + Cancel + Abbrechen + + + + Next + Weiter + + + + Create a long-lived access token in your Home Assistant profile and paste it below. Your Home Assistant password is never entered into this app. + Erstelle in deinem Home-Assistant-Profil ein langlebiges Zugriffstoken und füge es unten ein. Dein Home-Assistant-Passwort wird niemals in diese App eingegeben. + + + + Open Home Assistant profile + Home-Assistant-Profil öffnen + + + + Long-lived access token + Langlebiges Zugriffstoken + + + + Hide + Ausblenden + + + + Show + Anzeigen + + + + Back + Zurück + + + + Connect + Verbinden + + + + Create charts and choose exactly which Home Assistant entities each chart should display. + Erstelle Diagramme und wähle genau aus, welche Home-Assistant-Entitäten jedes Diagramm anzeigen soll. + + + + Change server or access token + Server oder Zugriffstoken ändern + + + + %n entity(s) + + %n Entität + %n Entitäten + + + + + Edit + Bearbeiten + + + + Delete + Löschen + + + + No charts configured yet. + Noch keine Diagramme eingerichtet. + + + + Add chart + Diagramm hinzufügen + + + + Done + Fertig + + + + Chart title + Diagrammtitel + + + + Search by name, entity ID, unit or device class… + Nach Name, Entitäts-ID, Einheit oder Geräteklasse suchen… + + + + %n entity(s) selected + + %n Entität ausgewählt + %n Entitäten ausgewählt + + + + + Reload + Neu laden + + + + No matching entities found. + Keine passenden Entitäten gefunden. + + + + Save chart + Diagramm speichern + + HotspotPage @@ -3186,71 +3449,137 @@ Informationen - + Idle Inaktiv - + %1 – %2 %1 – %2 - + Receiving manufacturer data… Herstellerdaten werden empfangen… - + Loading manufacturer charts… Herstellerdiagramme werden geladen… - + %1 images %1 Bilder - + %1 charts %1 Diagramme - + Loaded %1 %1 geladen - + No information data received Keine Informationsdaten empfangen - + The manufacturer cloud returned invalid data. Die Hersteller-Cloud hat ungültige Daten zurückgegeben. - + Requesting manufacturer data… Herstellerdaten werden angefordert… - + Information Informationen - + Manufacturer cloud Hersteller-Cloud + + + Connected to Home Assistant + Mit Home Assistant verbunden + + + + Home Assistant is not configured + Home Assistant ist nicht eingerichtet + + + + Configure + Konfigurieren + + + + + Set up + Einrichten + + + + Refresh + Aktualisieren + + + + Connect Home Assistant to display history + Verbinde Home Assistant, um den Verlauf anzuzeigen + + + + Create your first history chart + Erstelle dein erstes Verlaufsdiagramm + + + + The setup wizard connects to your server and lets you select the entities to display. + Der Einrichtungsassistent verbindet sich mit deinem Server und lässt dich die anzuzeigenden Entitäten auswählen. + + + + Open the configuration and add a chart with one or more Home Assistant entities. + Öffne die Konfiguration und füge ein Diagramm mit einer oder mehreren Home-Assistant-Entitäten hinzu. + + + + Add chart + Diagramm hinzufügen + + + + Forget Home Assistant configuration + Home-Assistant-Konfiguration vergessen + + + + Forget Home Assistant? + Home Assistant vergessen? + + + + This removes the server address, access token and all chart definitions stored in this app. + Dadurch werden die Serveradresse, das Zugriffstoken und alle in dieser App gespeicherten Diagrammdefinitionen entfernt. + Limited · unreliable · slow Eingeschränkt · unzuverlässig · langsam - + Your own cloud Eigene Cloud @@ -3259,105 +3588,97 @@ Verlauf · gepflegt · schnell - + Lacks features · unreliable · slow Fehlende Funktionen · unzuverlässig · langsam - + Historical data · properly maintained · fast Historische Daten · gut gepflegt · schnell - + The manufacturer cloud provides basic charts, but can be slow, unavailable or limited to the data selected by the manufacturer. Die Hersteller-Cloud stellt grundlegende Diagramme bereit, kann jedoch langsam, nicht erreichbar oder auf vom Hersteller ausgewählte Daten beschränkt sein. - + Download information Informationen herunterladen - + Own your charging history Dein Ladeprotokoll gehört dir - + Connect the charger to your own Home Assistant installation for fast, dependable history that remains under your control. You decide how long data is retained and which measurements are recorded. Verbinde den Charger mit deiner eigenen Home-Assistant-Installation, um einen schnellen, zuverlässigen Verlauf unter deiner Kontrolle zu erhalten. Du bestimmst, wie lange Daten aufbewahrt und welche Messwerte aufgezeichnet werden. - Home Assistant recorder integration is being prepared. The setup below is currently a preview and does not connect yet. - Die Home-Assistant-Recorder-Integration wird vorbereitet. Die Einrichtung unten ist derzeit eine Vorschau und stellt noch keine Verbindung her. + Die Home-Assistant-Recorder-Integration wird vorbereitet. Die Einrichtung unten ist derzeit eine Vorschau und stellt noch keine Verbindung her. - Set up connection to Home Assistant - Verbindung zu Home Assistant einrichten + Verbindung zu Home Assistant einrichten - + Historical data Historische Daten - + Day Tag - + Week Woche - + Month Monat - + Year Jahr - + Previous Zurück - - + Next Weiter - Home Assistant history will appear here - Der Home-Assistant-Verlauf wird hier angezeigt + Der Home-Assistant-Verlauf wird hier angezeigt - After the recorder integration is connected, this view will request measurements for the selected period. - Nach Verbindung der Recorder-Integration ruft diese Ansicht die Messwerte für den ausgewählten Zeitraum ab. + Nach Verbindung der Recorder-Integration ruft diese Ansicht die Messwerte für den ausgewählten Zeitraum ab. - Connect Home Assistant - Home Assistant verbinden + Home Assistant verbinden - Enter the address of your Home Assistant server. No connection is made in this preview. - Gib die Adresse deines Home-Assistant-Servers ein. In dieser Vorschau wird keine Verbindung hergestellt. + Gib die Adresse deines Home-Assistant-Servers ein. In dieser Vorschau wird keine Verbindung hergestellt. - Cancel - Abbrechen + Abbrechen Download informations diff --git a/evcharger-app/i18n/qml_en.ts b/evcharger-app/i18n/qml_en.ts index 6401616..3335b29 100644 --- a/evcharger-app/i18n/qml_en.ts +++ b/evcharger-app/i18n/qml_en.ts @@ -1,4 +1,21 @@ + + HomeAssistantSetupDialog + + + %n entity(s) + + + + + + + %n entity(s) selected + + + + + diff --git a/evcharger-app/i18n/qml_fi.ts b/evcharger-app/i18n/qml_fi.ts index 7d9a6d9..d5e1b51 100644 --- a/evcharger-app/i18n/qml_fi.ts +++ b/evcharger-app/i18n/qml_fi.ts @@ -2856,6 +2856,269 @@ RSSI: + + HomeAssistantClient + + + Enter a valid HTTP or HTTPS Home Assistant address. + Anna kelvollinen Home Assistantin HTTP- tai HTTPS-osoite. + + + + Enter a Home Assistant access token. + Anna Home Assistantin käyttöoikeustunnus. + + + + Connecting to Home Assistant… + Yhdistetään Home Assistantiin… + + + + Connected to Home Assistant. + Yhdistetty Home Assistantiin. + + + + Home Assistant rejected the access token. + Home Assistant hylkäsi käyttöoikeustunnuksen. + + + + Could not connect to Home Assistant: %1 + Home Assistantiin ei voitu yhdistää: %1 + + + + Loading Home Assistant entities… + Ladataan Home Assistant -entiteettejä… + + + + + The Home Assistant credential is no longer valid. + Home Assistantin tunnistetieto ei ole enää voimassa. + + + + Could not load Home Assistant entities: %1 + Home Assistant -entiteettejä ei voitu ladata: %1 + + + + Loaded %1 Home Assistant entities. + Ladattiin %1 Home Assistant -entiteettiä. + + + + Loading Home Assistant history… + Ladataan Home Assistant -historiaa… + + + + Could not load Home Assistant history: %1 + Home Assistant -historiaa ei voitu ladata: %1 + + + + Home Assistant history is up to date. + Home Assistant -historia on ajan tasalla. + + + + HomeAssistantSetupDialog + + + Edit chart + Muokkaa kaaviota + + + + Connect Home Assistant + Yhdistä Home Assistantiin + + + + Server + Palvelin + + + + Login + Kirjautuminen + + + + Charts + Kaaviot + + + + %1. %2 + %1. %2 + + + + Before you begin + Ennen aloittamista + + + + Home Assistant must already be installed and running. A Raspberry Pi with Home Assistant on an SD card is one option; other supported Home Assistant installations work as well. + +Before charts can receive data: +• Install or configure an MQTT broker, such as the Mosquitto broker app or a separate broker. +• Connect Home Assistant's MQTT integration to that broker. +• Connect the charger to the same broker and enable MQTT. +• Enable Home Assistant MQTT auto-discovery in Home Assistant and on the charger. +• Verify that the charger's entities are visible in Home Assistant. + Home Assistantin on oltava jo asennettuna ja käynnissä. Yksi vaihtoehto on Raspberry Pi, jossa Home Assistant on SD-kortilla; myös muut tuetut Home Assistant -asennukset toimivat. + +Ennen kuin kaaviot voivat vastaanottaa tietoja: +• Asenna tai määritä MQTT-välittäjä, kuten Mosquitto Broker -sovellus tai erillinen välittäjä. +• Yhdistä Home Assistantin MQTT-integraatio tähän välittäjään. +• Yhdistä laturi samaan välittäjään ja ota MQTT käyttöön. +• Ota Home Assistantin MQTT-automaattinen tunnistus käyttöön Home Assistantissa ja laturissa. +• Varmista, että laturin entiteetit näkyvät Home Assistantissa. + + + + Installation guide + Asennusohje + + + + MQTT guide + MQTT-ohje + + + + Enter the address you normally use to open Home Assistant. The app will contact this server directly. + Anna osoite, jolla yleensä avaat Home Assistantin. Sovellus ottaa suoraan yhteyden tähän palvelimeen. + + + + + Cancel + Peruuta + + + + Next + Seuraava + + + + Create a long-lived access token in your Home Assistant profile and paste it below. Your Home Assistant password is never entered into this app. + Luo pitkäikäinen käyttöoikeustunnus Home Assistant -profiilissasi ja liitä se alle. Home Assistant -salasanaasi ei koskaan syötetä tähän sovellukseen. + + + + Open Home Assistant profile + Avaa Home Assistant -profiili + + + + Long-lived access token + Pitkäikäinen käyttöoikeustunnus + + + + Hide + Piilota + + + + Show + Näytä + + + + Back + Takaisin + + + + Connect + Yhdistä + + + + Create charts and choose exactly which Home Assistant entities each chart should display. + Luo kaavioita ja valitse tarkasti, mitkä Home Assistant -entiteetit kussakin kaaviossa näytetään. + + + + Change server or access token + Vaihda palvelinta tai käyttöoikeustunnusta + + + + %n entity(s) + + %n entiteetti + %n entiteettiä + + + + + Edit + Muokkaa + + + + Delete + Poista + + + + No charts configured yet. + Kaavioita ei ole vielä määritetty. + + + + Add chart + Lisää kaavio + + + + Done + Valmis + + + + Chart title + Kaavion otsikko + + + + Search by name, entity ID, unit or device class… + Hae nimellä, entiteettitunnuksella, yksiköllä tai laiteluokalla… + + + + %n entity(s) selected + + %n entiteetti valittu + %n entiteettiä valittu + + + + + Reload + Lataa uudelleen + + + + No matching entities found. + Vastaavia entiteettejä ei löytynyt. + + + + Save chart + Tallenna kaavio + + HotspotPage @@ -3011,7 +3274,7 @@ Tiedot - + Idle Valmiustila @@ -3024,66 +3287,132 @@ LADATAAN - + %1 – %2 %1 – %2 - + Receiving manufacturer data… Vastaanotetaan valmistajan tietoja… - + Loading manufacturer charts… Ladataan valmistajan kaavioita… - + %1 images %1 kuvaa - + %1 charts %1 kaaviota - + Loaded %1 Ladattu: %1 - + No information data received Tietoja ei vastaanotettu - + The manufacturer cloud returned invalid data. Valmistajan pilvipalvelu palautti virheellisiä tietoja. - + Requesting manufacturer data… Pyydetään valmistajan tietoja… - + Information Tiedot - + Manufacturer cloud Valmistajan pilvipalvelu + + + Connected to Home Assistant + Yhdistetty Home Assistantiin + + + + Home Assistant is not configured + Home Assistantia ei ole määritetty + + + + Configure + Määritä + + + + + Set up + Määritä + + + + Refresh + Päivitä + + + + Connect Home Assistant to display history + Yhdistä Home Assistant historian näyttämiseksi + + + + Create your first history chart + Luo ensimmäinen historiakaaviosi + + + + The setup wizard connects to your server and lets you select the entities to display. + Ohjattu määritys yhdistää palvelimeesi ja antaa valita näytettävät entiteetit. + + + + Open the configuration and add a chart with one or more Home Assistant entities. + Avaa määritys ja lisää kaavio, jossa on yksi tai useampi Home Assistant -entiteetti. + + + + Add chart + Lisää kaavio + + + + Forget Home Assistant configuration + Unohda Home Assistant -määritys + + + + Forget Home Assistant? + Unohdetaanko Home Assistant? + + + + This removes the server address, access token and all chart definitions stored in this app. + Tämä poistaa palvelimen osoitteen, käyttöoikeustunnuksen ja kaikki tähän sovellukseen tallennetut kaaviomääritykset. + Limited · unreliable · slow Rajallinen · epäluotettava · hidas - + Your own cloud Oma pilvipalvelusi @@ -3092,105 +3421,97 @@ Historia · ylläpidetty · nopea - + Lacks features · unreliable · slow Puutteelliset ominaisuudet · epäluotettava · hidas - + Historical data · properly maintained · fast Historiatiedot · hyvin ylläpidetty · nopea - + The manufacturer cloud provides basic charts, but can be slow, unavailable or limited to the data selected by the manufacturer. Valmistajan pilvipalvelu tarjoaa peruskaavioita, mutta se voi olla hidas, poissa käytöstä tai rajoittunut valmistajan valitsemiin tietoihin. - + Download information Lataa tiedot - + Own your charging history Hallitse lataushistoriaasi - + Connect the charger to your own Home Assistant installation for fast, dependable history that remains under your control. You decide how long data is retained and which measurements are recorded. Yhdistä laturi omaan Home Assistant -asennukseesi, jotta saat nopean ja luotettavan historian, joka pysyy hallinnassasi. Päätät, kuinka kauan tiedot säilytetään ja mitkä mittaukset tallennetaan. - Home Assistant recorder integration is being prepared. The setup below is currently a preview and does not connect yet. - Home Assistant Recorder -integraatiota valmistellaan. Alla oleva määritys on tällä hetkellä esikatselu eikä vielä muodosta yhteyttä. + Home Assistant Recorder -integraatiota valmistellaan. Alla oleva määritys on tällä hetkellä esikatselu eikä vielä muodosta yhteyttä. - Set up connection to Home Assistant - Määritä yhteys Home Assistantiin + Määritä yhteys Home Assistantiin - + Historical data Historiatiedot - + Day Päivä - + Week Viikko - + Month Kuukausi - + Year Vuosi - + Previous Edellinen - - + Next Seuraava - Home Assistant history will appear here - Home Assistant -historia näkyy tässä + Home Assistant -historia näkyy tässä - After the recorder integration is connected, this view will request measurements for the selected period. - Kun Recorder-integraatio on yhdistetty, tämä näkymä pyytää valitun ajanjakson mittaukset. + Kun Recorder-integraatio on yhdistetty, tämä näkymä pyytää valitun ajanjakson mittaukset. - Connect Home Assistant - Yhdistä Home Assistantiin + Yhdistä Home Assistantiin - Enter the address of your Home Assistant server. No connection is made in this preview. - Anna Home Assistant -palvelimesi osoite. Tässä esikatselussa yhteyttä ei muodosteta. + Anna Home Assistant -palvelimesi osoite. Tässä esikatselussa yhteyttä ei muodosteta. - Cancel - Peruuta + Peruuta Request started... diff --git a/evcharger-app/i18n/qml_nb.ts b/evcharger-app/i18n/qml_nb.ts index e90e7c2..9d17b9c 100644 --- a/evcharger-app/i18n/qml_nb.ts +++ b/evcharger-app/i18n/qml_nb.ts @@ -2856,6 +2856,269 @@ RSSI: + + HomeAssistantClient + + + Enter a valid HTTP or HTTPS Home Assistant address. + Skriv inn en gyldig HTTP- eller HTTPS-adresse til Home Assistant. + + + + Enter a Home Assistant access token. + Skriv inn et tilgangstoken for Home Assistant. + + + + Connecting to Home Assistant… + Kobler til Home Assistant… + + + + Connected to Home Assistant. + Koblet til Home Assistant. + + + + Home Assistant rejected the access token. + Home Assistant avviste tilgangstokenet. + + + + Could not connect to Home Assistant: %1 + Kunne ikke koble til Home Assistant: %1 + + + + Loading Home Assistant entities… + Laster inn Home Assistant-entiteter… + + + + + The Home Assistant credential is no longer valid. + Påloggingsinformasjonen for Home Assistant er ikke lenger gyldig. + + + + Could not load Home Assistant entities: %1 + Kunne ikke laste inn Home Assistant-entiteter: %1 + + + + Loaded %1 Home Assistant entities. + Lastet inn %1 Home Assistant-entiteter. + + + + Loading Home Assistant history… + Laster inn Home Assistant-historikk… + + + + Could not load Home Assistant history: %1 + Kunne ikke laste inn Home Assistant-historikk: %1 + + + + Home Assistant history is up to date. + Home Assistant-historikken er oppdatert. + + + + HomeAssistantSetupDialog + + + Edit chart + Rediger diagram + + + + Connect Home Assistant + Koble til Home Assistant + + + + Server + Server + + + + Login + Pålogging + + + + Charts + Diagrammer + + + + %1. %2 + %1. %2 + + + + Before you begin + Før du begynner + + + + Home Assistant must already be installed and running. A Raspberry Pi with Home Assistant on an SD card is one option; other supported Home Assistant installations work as well. + +Before charts can receive data: +• Install or configure an MQTT broker, such as the Mosquitto broker app or a separate broker. +• Connect Home Assistant's MQTT integration to that broker. +• Connect the charger to the same broker and enable MQTT. +• Enable Home Assistant MQTT auto-discovery in Home Assistant and on the charger. +• Verify that the charger's entities are visible in Home Assistant. + Home Assistant må allerede være installert og kjøre. Ett alternativ er en Raspberry Pi med Home Assistant på et SD-kort; andre støttede Home Assistant-installasjoner fungerer også. + +Før diagrammer kan motta data: +• Installer eller konfigurer en MQTT-megler, for eksempel Mosquitto Broker-appen eller en separat megler. +• Koble Home Assistants MQTT-integrasjon til denne megleren. +• Koble laderen til den samme megleren og aktiver MQTT. +• Aktiver automatisk MQTT-oppdagelse for Home Assistant i Home Assistant og på laderen. +• Kontroller at laderens entiteter er synlige i Home Assistant. + + + + Installation guide + Installasjonsveiledning + + + + MQTT guide + MQTT-veiledning + + + + Enter the address you normally use to open Home Assistant. The app will contact this server directly. + Skriv inn adressen du vanligvis bruker for å åpne Home Assistant. Appen kontakter denne serveren direkte. + + + + + Cancel + Avbryt + + + + Next + Neste + + + + Create a long-lived access token in your Home Assistant profile and paste it below. Your Home Assistant password is never entered into this app. + Opprett et langlivet tilgangstoken i Home Assistant-profilen din, og lim det inn nedenfor. Home Assistant-passordet ditt skrives aldri inn i denne appen. + + + + Open Home Assistant profile + Åpne Home Assistant-profil + + + + Long-lived access token + Langlivet tilgangstoken + + + + Hide + Skjul + + + + Show + Vis + + + + Back + Tilbake + + + + Connect + Koble til + + + + Create charts and choose exactly which Home Assistant entities each chart should display. + Opprett diagrammer og velg nøyaktig hvilke Home Assistant-entiteter hvert diagram skal vise. + + + + Change server or access token + Bytt server eller tilgangstoken + + + + %n entity(s) + + %n entitet + %n entiteter + + + + + Edit + Rediger + + + + Delete + Slett + + + + No charts configured yet. + Ingen diagrammer er konfigurert ennå. + + + + Add chart + Legg til diagram + + + + Done + Ferdig + + + + Chart title + Diagramtittel + + + + Search by name, entity ID, unit or device class… + Søk etter navn, entitets-ID, enhet eller enhetsklasse… + + + + %n entity(s) selected + + %n entitet valgt + %n entiteter valgt + + + + + Reload + Last inn på nytt + + + + No matching entities found. + Fant ingen samsvarende entiteter. + + + + Save chart + Lagre diagram + + HotspotPage @@ -3011,7 +3274,7 @@ Informasjon - + Idle Inaktiv @@ -3024,66 +3287,132 @@ LASTER INN - + %1 – %2 %1 – %2 - + Receiving manufacturer data… Mottar produsentdata… - + Loading manufacturer charts… Laster produsentdiagrammer… - + %1 images %1 bilder - + %1 charts %1 diagrammer - + Loaded %1 Lastet inn %1 - + No information data received Ingen informasjonsdata mottatt - + The manufacturer cloud returned invalid data. Produsentskyen returnerte ugyldige data. - + Requesting manufacturer data… Ber om produsentdata… - + Information Informasjon - + Manufacturer cloud Produsentsky + + + Connected to Home Assistant + Koblet til Home Assistant + + + + Home Assistant is not configured + Home Assistant er ikke konfigurert + + + + Configure + Konfigurer + + + + + Set up + Konfigurer + + + + Refresh + Oppdater + + + + Connect Home Assistant to display history + Koble til Home Assistant for å vise historikk + + + + Create your first history chart + Opprett ditt første historikkdiagram + + + + The setup wizard connects to your server and lets you select the entities to display. + Oppsettsveiviseren kobler til serveren og lar deg velge entitetene som skal vises. + + + + Open the configuration and add a chart with one or more Home Assistant entities. + Åpne konfigurasjonen og legg til et diagram med én eller flere Home Assistant-entiteter. + + + + Add chart + Legg til diagram + + + + Forget Home Assistant configuration + Glem Home Assistant-konfigurasjonen + + + + Forget Home Assistant? + Glemme Home Assistant? + + + + This removes the server address, access token and all chart definitions stored in this app. + Dette fjerner serveradressen, tilgangstokenet og alle diagramdefinisjoner som er lagret i appen. + Limited · unreliable · slow Begrenset · upålitelig · treg - + Your own cloud Din egen sky @@ -3092,105 +3421,97 @@ Historikk · vedlikeholdt · rask - + Lacks features · unreliable · slow Mangler funksjoner · upålitelig · treg - + Historical data · properly maintained · fast Historiske data · godt vedlikeholdt · rask - + The manufacturer cloud provides basic charts, but can be slow, unavailable or limited to the data selected by the manufacturer. Produsentskyen tilbyr grunnleggende diagrammer, men kan være treg, utilgjengelig eller begrenset til data som produsenten har valgt. - + Download information Last ned informasjon - + Own your charging history Ha kontroll over ladehistorikken - + Connect the charger to your own Home Assistant installation for fast, dependable history that remains under your control. You decide how long data is retained and which measurements are recorded. Koble laderen til din egen Home Assistant-installasjon for rask, pålitelig historikk som forblir under din kontroll. Du bestemmer hvor lenge data beholdes og hvilke målinger som registreres. - Home Assistant recorder integration is being prepared. The setup below is currently a preview and does not connect yet. - Integrasjonen med Home Assistant Recorder er under utvikling. Oppsettet nedenfor er foreløpig en forhåndsvisning og kobler ikke til ennå. + Integrasjonen med Home Assistant Recorder er under utvikling. Oppsettet nedenfor er foreløpig en forhåndsvisning og kobler ikke til ennå. - Set up connection to Home Assistant - Konfigurer tilkobling til Home Assistant + Konfigurer tilkobling til Home Assistant - + Historical data Historiske data - + Day Dag - + Week Uke - + Month Måned - + Year År - + Previous Forrige - - + Next Neste - Home Assistant history will appear here - Home Assistant-historikken vises her + Home Assistant-historikken vises her - After the recorder integration is connected, this view will request measurements for the selected period. - Når Recorder-integrasjonen er tilkoblet, vil denne visningen be om målinger for den valgte perioden. + Når Recorder-integrasjonen er tilkoblet, vil denne visningen be om målinger for den valgte perioden. - Connect Home Assistant - Koble til Home Assistant + Koble til Home Assistant - Enter the address of your Home Assistant server. No connection is made in this preview. - Skriv inn adressen til Home Assistant-serveren. Ingen tilkobling opprettes i denne forhåndsvisningen. + Skriv inn adressen til Home Assistant-serveren. Ingen tilkobling opprettes i denne forhåndsvisningen. - Cancel - Avbryt + Avbryt Request started... diff --git a/evcharger-app/i18n/qml_sv.ts b/evcharger-app/i18n/qml_sv.ts index 42987d1..e4309f6 100644 --- a/evcharger-app/i18n/qml_sv.ts +++ b/evcharger-app/i18n/qml_sv.ts @@ -2856,6 +2856,269 @@ RSSI: + + HomeAssistantClient + + + Enter a valid HTTP or HTTPS Home Assistant address. + Ange en giltig HTTP- eller HTTPS-adress till Home Assistant. + + + + Enter a Home Assistant access token. + Ange en åtkomsttoken för Home Assistant. + + + + Connecting to Home Assistant… + Ansluter till Home Assistant… + + + + Connected to Home Assistant. + Ansluten till Home Assistant. + + + + Home Assistant rejected the access token. + Home Assistant avvisade åtkomsttoken. + + + + Could not connect to Home Assistant: %1 + Det gick inte att ansluta till Home Assistant: %1 + + + + Loading Home Assistant entities… + Läser in Home Assistant-entiteter… + + + + + The Home Assistant credential is no longer valid. + Autentiseringsuppgiften för Home Assistant är inte längre giltig. + + + + Could not load Home Assistant entities: %1 + Det gick inte att läsa in Home Assistant-entiteter: %1 + + + + Loaded %1 Home Assistant entities. + %1 Home Assistant-entiteter har lästs in. + + + + Loading Home Assistant history… + Läser in Home Assistant-historik… + + + + Could not load Home Assistant history: %1 + Det gick inte att läsa in Home Assistant-historik: %1 + + + + Home Assistant history is up to date. + Home Assistant-historiken är aktuell. + + + + HomeAssistantSetupDialog + + + Edit chart + Redigera diagram + + + + Connect Home Assistant + Anslut Home Assistant + + + + Server + Server + + + + Login + Inloggning + + + + Charts + Diagram + + + + %1. %2 + %1. %2 + + + + Before you begin + Innan du börjar + + + + Home Assistant must already be installed and running. A Raspberry Pi with Home Assistant on an SD card is one option; other supported Home Assistant installations work as well. + +Before charts can receive data: +• Install or configure an MQTT broker, such as the Mosquitto broker app or a separate broker. +• Connect Home Assistant's MQTT integration to that broker. +• Connect the charger to the same broker and enable MQTT. +• Enable Home Assistant MQTT auto-discovery in Home Assistant and on the charger. +• Verify that the charger's entities are visible in Home Assistant. + Home Assistant måste redan vara installerat och igång. Ett alternativ är en Raspberry Pi med Home Assistant på ett SD-kort; även andra Home Assistant-installationer som stöds fungerar. + +Innan diagram kan ta emot data: +• Installera eller konfigurera en MQTT-mäklare, till exempel Mosquitto Broker-appen eller en separat mäklare. +• Anslut Home Assistants MQTT-integration till den mäklaren. +• Anslut laddaren till samma mäklare och aktivera MQTT. +• Aktivera automatisk MQTT-identifiering för Home Assistant i Home Assistant och på laddaren. +• Kontrollera att laddarens entiteter är synliga i Home Assistant. + + + + Installation guide + Installationsguide + + + + MQTT guide + MQTT-guide + + + + Enter the address you normally use to open Home Assistant. The app will contact this server directly. + Ange adressen som du normalt använder för att öppna Home Assistant. Appen kontaktar servern direkt. + + + + + Cancel + Avbryt + + + + Next + Nästa + + + + Create a long-lived access token in your Home Assistant profile and paste it below. Your Home Assistant password is never entered into this app. + Skapa en långlivad åtkomsttoken i din Home Assistant-profil och klistra in den nedan. Ditt Home Assistant-lösenord anges aldrig i appen. + + + + Open Home Assistant profile + Öppna Home Assistant-profil + + + + Long-lived access token + Långlivad åtkomsttoken + + + + Hide + Dölj + + + + Show + Visa + + + + Back + Tillbaka + + + + Connect + Anslut + + + + Create charts and choose exactly which Home Assistant entities each chart should display. + Skapa diagram och välj exakt vilka Home Assistant-entiteter varje diagram ska visa. + + + + Change server or access token + Byt server eller åtkomsttoken + + + + %n entity(s) + + %n entitet + %n entiteter + + + + + Edit + Redigera + + + + Delete + Ta bort + + + + No charts configured yet. + Inga diagram har konfigurerats ännu. + + + + Add chart + Lägg till diagram + + + + Done + Klar + + + + Chart title + Diagramrubrik + + + + Search by name, entity ID, unit or device class… + Sök efter namn, entitets-ID, enhet eller enhetsklass… + + + + %n entity(s) selected + + %n entitet vald + %n entiteter valda + + + + + Reload + Läs in igen + + + + No matching entities found. + Inga matchande entiteter hittades. + + + + Save chart + Spara diagram + + HotspotPage @@ -3011,7 +3274,7 @@ Information - + Idle Viloläge @@ -3024,66 +3287,132 @@ LÄSER IN - + %1 – %2 %1 – %2 - + Receiving manufacturer data… Tar emot tillverkardata… - + Loading manufacturer charts… Läser in tillverkarens diagram… - + %1 images %1 bilder - + %1 charts %1 diagram - + Loaded %1 %1 inläst - + No information data received Ingen informationsdata mottogs - + The manufacturer cloud returned invalid data. Tillverkarens molntjänst returnerade ogiltiga data. - + Requesting manufacturer data… Begär tillverkardata… - + Information Information - + Manufacturer cloud Tillverkarens moln + + + Connected to Home Assistant + Ansluten till Home Assistant + + + + Home Assistant is not configured + Home Assistant är inte konfigurerat + + + + Configure + Konfigurera + + + + + Set up + Konfigurera + + + + Refresh + Uppdatera + + + + Connect Home Assistant to display history + Anslut Home Assistant för att visa historik + + + + Create your first history chart + Skapa ditt första historikdiagram + + + + The setup wizard connects to your server and lets you select the entities to display. + Installationsguiden ansluter till servern och låter dig välja vilka entiteter som ska visas. + + + + Open the configuration and add a chart with one or more Home Assistant entities. + Öppna konfigurationen och lägg till ett diagram med en eller flera Home Assistant-entiteter. + + + + Add chart + Lägg till diagram + + + + Forget Home Assistant configuration + Glöm Home Assistant-konfiguration + + + + Forget Home Assistant? + Glöm Home Assistant? + + + + This removes the server address, access token and all chart definitions stored in this app. + Detta tar bort serveradressen, åtkomsttoken och alla diagramdefinitioner som lagras i appen. + Limited · unreliable · slow Begränsat · opålitligt · långsamt - + Your own cloud Ditt eget moln @@ -3092,105 +3421,97 @@ Historik · underhållet · snabbt - + Lacks features · unreliable · slow Saknar funktioner · opålitligt · långsamt - + Historical data · properly maintained · fast Historiska data · väl underhållet · snabbt - + The manufacturer cloud provides basic charts, but can be slow, unavailable or limited to the data selected by the manufacturer. Tillverkarens molntjänst erbjuder grundläggande diagram, men kan vara långsam, otillgänglig eller begränsad till data som tillverkaren har valt. - + Download information Hämta information - + Own your charging history Äg din laddningshistorik - + Connect the charger to your own Home Assistant installation for fast, dependable history that remains under your control. You decide how long data is retained and which measurements are recorded. Anslut laddaren till din egen Home Assistant-installation för snabb, tillförlitlig historik som förblir under din kontroll. Du bestämmer hur länge data sparas och vilka mätvärden som registreras. - Home Assistant recorder integration is being prepared. The setup below is currently a preview and does not connect yet. - Integrationen med Home Assistant Recorder förbereds. Inställningen nedan är för närvarande en förhandsvisning och ansluter inte ännu. + Integrationen med Home Assistant Recorder förbereds. Inställningen nedan är för närvarande en förhandsvisning och ansluter inte ännu. - Set up connection to Home Assistant - Konfigurera anslutning till Home Assistant + Konfigurera anslutning till Home Assistant - + Historical data Historiska data - + Day Dag - + Week Vecka - + Month Månad - + Year År - + Previous Föregående - - + Next Nästa - Home Assistant history will appear here - Home Assistant-historiken visas här + Home Assistant-historiken visas här - After the recorder integration is connected, this view will request measurements for the selected period. - När Recorder-integrationen har anslutits begär den här vyn mätvärden för den valda perioden. + När Recorder-integrationen har anslutits begär den här vyn mätvärden för den valda perioden. - Connect Home Assistant - Anslut Home Assistant + Anslut Home Assistant - Enter the address of your Home Assistant server. No connection is made in this preview. - Ange adressen till din Home Assistant-server. Ingen anslutning görs i denna förhandsvisning. + Ange adressen till din Home Assistant-server. Ingen anslutning görs i denna förhandsvisning. - Cancel - Avbryt + Avbryt Request started... diff --git a/evcharger-app/i18n/qml_tr.ts b/evcharger-app/i18n/qml_tr.ts index 9d57025..d08610a 100644 --- a/evcharger-app/i18n/qml_tr.ts +++ b/evcharger-app/i18n/qml_tr.ts @@ -2856,6 +2856,267 @@ RSSI: + + HomeAssistantClient + + + Enter a valid HTTP or HTTPS Home Assistant address. + Geçerli bir HTTP veya HTTPS Home Assistant adresi girin. + + + + Enter a Home Assistant access token. + Bir Home Assistant erişim belirteci girin. + + + + Connecting to Home Assistant… + Home Assistant'a bağlanılıyor… + + + + Connected to Home Assistant. + Home Assistant'a bağlandı. + + + + Home Assistant rejected the access token. + Home Assistant erişim belirtecini reddetti. + + + + Could not connect to Home Assistant: %1 + Home Assistant'a bağlanılamadı: %1 + + + + Loading Home Assistant entities… + Home Assistant varlıkları yükleniyor… + + + + + The Home Assistant credential is no longer valid. + Home Assistant kimlik bilgisi artık geçerli değil. + + + + Could not load Home Assistant entities: %1 + Home Assistant varlıkları yüklenemedi: %1 + + + + Loaded %1 Home Assistant entities. + %1 Home Assistant varlığı yüklendi. + + + + Loading Home Assistant history… + Home Assistant geçmişi yükleniyor… + + + + Could not load Home Assistant history: %1 + Home Assistant geçmişi yüklenemedi: %1 + + + + Home Assistant history is up to date. + Home Assistant geçmişi güncel. + + + + HomeAssistantSetupDialog + + + Edit chart + Grafiği düzenle + + + + Connect Home Assistant + Home Assistant'ı bağla + + + + Server + Sunucu + + + + Login + Oturum açma + + + + Charts + Grafikler + + + + %1. %2 + %1. %2 + + + + Before you begin + Başlamadan önce + + + + Home Assistant must already be installed and running. A Raspberry Pi with Home Assistant on an SD card is one option; other supported Home Assistant installations work as well. + +Before charts can receive data: +• Install or configure an MQTT broker, such as the Mosquitto broker app or a separate broker. +• Connect Home Assistant's MQTT integration to that broker. +• Connect the charger to the same broker and enable MQTT. +• Enable Home Assistant MQTT auto-discovery in Home Assistant and on the charger. +• Verify that the charger's entities are visible in Home Assistant. + Home Assistant önceden kurulmuş ve çalışıyor olmalıdır. SD kart üzerinde Home Assistant bulunan bir Raspberry Pi seçeneklerden biridir; desteklenen diğer Home Assistant kurulumları da kullanılabilir. + +Grafiklerin veri alabilmesi için: +• Mosquitto Broker uygulaması veya ayrı bir aracı gibi bir MQTT aracısı kurun ya da yapılandırın. +• Home Assistant'ın MQTT entegrasyonunu bu aracıya bağlayın. +• Şarj cihazını aynı aracıya bağlayın ve MQTT'yi etkinleştirin. +• Home Assistant'ta ve şarj cihazında Home Assistant MQTT otomatik keşfini etkinleştirin. +• Şarj cihazının varlıklarının Home Assistant'ta göründüğünü doğrulayın. + + + + Installation guide + Kurulum kılavuzu + + + + MQTT guide + MQTT kılavuzu + + + + Enter the address you normally use to open Home Assistant. The app will contact this server directly. + Home Assistant'ı açmak için normalde kullandığınız adresi girin. Uygulama bu sunucuyla doğrudan iletişim kuracaktır. + + + + + Cancel + İptal + + + + Next + Sonraki + + + + Create a long-lived access token in your Home Assistant profile and paste it below. Your Home Assistant password is never entered into this app. + Home Assistant profilinizde uzun ömürlü bir erişim belirteci oluşturup aşağıya yapıştırın. Home Assistant parolanız bu uygulamaya hiçbir zaman girilmez. + + + + Open Home Assistant profile + Home Assistant profilini aç + + + + Long-lived access token + Uzun ömürlü erişim belirteci + + + + Hide + Gizle + + + + Show + Göster + + + + Back + Geri + + + + Connect + Bağlan + + + + Create charts and choose exactly which Home Assistant entities each chart should display. + Grafikler oluşturun ve her grafiğin hangi Home Assistant varlıklarını göstereceğini tam olarak seçin. + + + + Change server or access token + Sunucuyu veya erişim belirtecini değiştir + + + + %n entity(s) + + %n varlık + + + + + Edit + Düzenle + + + + Delete + Sil + + + + No charts configured yet. + Henüz yapılandırılmış grafik yok. + + + + Add chart + Grafik ekle + + + + Done + Bitti + + + + Chart title + Grafik başlığı + + + + Search by name, entity ID, unit or device class… + Ada, varlık kimliğine, birime veya cihaz sınıfına göre ara… + + + + %n entity(s) selected + + %n varlık seçildi + + + + + Reload + Yeniden yükle + + + + No matching entities found. + Eşleşen varlık bulunamadı. + + + + Save chart + Grafiği kaydet + + HotspotPage @@ -3011,7 +3272,7 @@ Bilgiler - + Idle Boşta @@ -3024,66 +3285,132 @@ YÜKLENİYOR - + %1 – %2 %1 – %2 - + Receiving manufacturer data… Üretici verileri alınıyor… - + Loading manufacturer charts… Üretici grafikleri yükleniyor… - + %1 images %1 resim - + %1 charts %1 grafik - + Loaded %1 %1 yüklendi - + No information data received Bilgi verisi alınmadı - + The manufacturer cloud returned invalid data. Üretici bulutu geçersiz veri döndürdü. - + Requesting manufacturer data… Üretici verileri isteniyor… - + Information Bilgi - + Manufacturer cloud Üretici bulutu + + + Connected to Home Assistant + Home Assistant'a bağlı + + + + Home Assistant is not configured + Home Assistant yapılandırılmadı + + + + Configure + Yapılandır + + + + + Set up + Kur + + + + Refresh + Yenile + + + + Connect Home Assistant to display history + Geçmişi görüntülemek için Home Assistant'ı bağlayın + + + + Create your first history chart + İlk geçmiş grafiğinizi oluşturun + + + + The setup wizard connects to your server and lets you select the entities to display. + Kurulum sihirbazı sunucunuza bağlanır ve görüntülenecek varlıkları seçmenizi sağlar. + + + + Open the configuration and add a chart with one or more Home Assistant entities. + Yapılandırmayı açın ve bir veya daha fazla Home Assistant varlığı içeren bir grafik ekleyin. + + + + Add chart + Grafik ekle + + + + Forget Home Assistant configuration + Home Assistant yapılandırmasını unut + + + + Forget Home Assistant? + Home Assistant unutulsun mu? + + + + This removes the server address, access token and all chart definitions stored in this app. + Bu işlem sunucu adresini, erişim belirtecini ve bu uygulamada saklanan tüm grafik tanımlarını kaldırır. + Limited · unreliable · slow Kısıtlı · güvenilmez · yavaş - + Your own cloud Kendi bulutunuz @@ -3092,105 +3419,97 @@ Geçmiş · bakımlı · hızlı - + Lacks features · unreliable · slow Eksik özellikler · güvenilmez · yavaş - + Historical data · properly maintained · fast Geçmiş veriler · iyi bakımlı · hızlı - + The manufacturer cloud provides basic charts, but can be slow, unavailable or limited to the data selected by the manufacturer. Üretici bulutu temel grafikler sağlar, ancak yavaş veya erişilemez olabilir ya da üreticinin seçtiği verilerle sınırlı kalabilir. - + Download information Bilgileri indir - + Own your charging history Şarj geçmişiniz size ait olsun - + Connect the charger to your own Home Assistant installation for fast, dependable history that remains under your control. You decide how long data is retained and which measurements are recorded. Şarj cihazını kendi Home Assistant kurulumunuza bağlayarak kontrolünüz altında kalan hızlı ve güvenilir bir geçmiş elde edin. Verilerin ne kadar süre saklanacağına ve hangi ölçümlerin kaydedileceğine siz karar verin. - Home Assistant recorder integration is being prepared. The setup below is currently a preview and does not connect yet. - Home Assistant Recorder entegrasyonu hazırlanmaktadır. Aşağıdaki kurulum şu anda yalnızca bir ön izlemedir ve henüz bağlantı kurmaz. + Home Assistant Recorder entegrasyonu hazırlanmaktadır. Aşağıdaki kurulum şu anda yalnızca bir ön izlemedir ve henüz bağlantı kurmaz. - Set up connection to Home Assistant - Home Assistant bağlantısını kur + Home Assistant bağlantısını kur - + Historical data Geçmiş veriler - + Day Gün - + Week Hafta - + Month Ay - + Year Yıl - + Previous Önceki - - + Next Sonraki - Home Assistant history will appear here - Home Assistant geçmişi burada görünecek + Home Assistant geçmişi burada görünecek - After the recorder integration is connected, this view will request measurements for the selected period. - Recorder entegrasyonu bağlandıktan sonra bu görünüm seçilen dönem için ölçümleri isteyecektir. + Recorder entegrasyonu bağlandıktan sonra bu görünüm seçilen dönem için ölçümleri isteyecektir. - Connect Home Assistant - Home Assistant'ı bağla + Home Assistant'ı bağla - Enter the address of your Home Assistant server. No connection is made in this preview. - Home Assistant sunucunuzun adresini girin. Bu ön izlemede bağlantı kurulmaz. + Home Assistant sunucunuzun adresini girin. Bu ön izlemede bağlantı kurulmaz. - Cancel - İptal + İptal Request started... diff --git a/evcharger-app/qml/ControllerTabPage.qml b/evcharger-app/qml/ControllerTabPage.qml index 19bfe1e..4454fa2 100644 --- a/evcharger-app/qml/ControllerTabPage.qml +++ b/evcharger-app/qml/ControllerTabPage.qml @@ -35,6 +35,15 @@ AnimatedStackView { arrayLength(categoryPowers.value), customCategoryIcons.exists ? 6 + arrayLength(customCategoryIcons.value) : 0) + property var activeCategories: ({}) + readonly property var visibleCategoryIndexes: { + const result = [] + for (let i = 0; i < categoryCount; ++i) { + if (activeCategories[i] === true) + result.push(i) + } + return result + } function arrayLength(value) { return value !== null && value !== undefined && value.length !== undefined @@ -49,11 +58,37 @@ AnimatedStackView { function categorySeriesDefinitions() { const result = [] - for (let i = 0; i < categoryCount; ++i) - result.push({name: categoryName(i)}) + for (let i = 0; i < visibleCategoryIndexes.length; ++i) + result.push({name: categoryName(visibleCategoryIndexes[i])}) return result } + function categorySeriesValues() { + const powers = categoryPowers.value || [] + const result = [] + for (let i = 0; i < visibleCategoryIndexes.length; ++i) + result.push(powers[visibleCategoryIndexes[i]]) + return result + } + + function rememberActiveCategories(powers) { + if (!powers || powers.length === undefined) + return + + const updated = Object.assign({}, activeCategories) + let changed = false + for (let i = 0; i < powers.length; ++i) { + const power = Number(powers[i]) + if (isFinite(power) && Math.abs(power) > 1 + && updated[i] !== true) { + updated[i] = true + changed = true + } + } + if (changed) + activeCategories = updated + } + RowLayout { Layout.fillWidth: true @@ -101,6 +136,8 @@ AnimatedStackView { id: categoryPowers deviceConnection: theDeviceConnection apiKey: "ccp" + onValueChanged: controllerPage.rememberActiveCategories(value) + Component.onCompleted: controllerPage.rememberActiveCategories(value) } ApiKeyValueHelper { @@ -110,13 +147,14 @@ AnimatedStackView { } Repeater { - model: controllerPage.categoryCount + model: controllerPage.visibleCategoryIndexes Pane { - required property int index + required property int modelData + readonly property int categoryIndex: modelData property var power: categoryPowers.value - && categoryPowers.value.length > index - ? categoryPowers.value[index] : null + && categoryPowers.value.length > categoryIndex + ? categoryPowers.value[categoryIndex] : null Layout.fillWidth: true Layout.preferredWidth: gridLayout.width / 3 @@ -133,7 +171,7 @@ AnimatedStackView { Text { Layout.fillWidth: true - text: controllerPage.categoryName(index) + text: controllerPage.categoryName(categoryIndex) } Text { @@ -149,6 +187,7 @@ AnimatedStackView { SettingsCard { visible: categoryPowers.exists + && controllerPage.visibleCategoryIndexes.length > 0 Layout.fillWidth: true LiveHistoryChart { @@ -156,7 +195,7 @@ AnimatedStackView { unit: qsTr("W") historyMilliseconds: 60000 seriesDefinitions: controllerPage.categorySeriesDefinitions() - values: categoryPowers.value || [] + values: controllerPage.categorySeriesValues() } } diff --git a/evcharger-app/qml/HomeAssistantSetupDialog.qml b/evcharger-app/qml/HomeAssistantSetupDialog.qml new file mode 100644 index 0000000..41c319e --- /dev/null +++ b/evcharger-app/qml/HomeAssistantSetupDialog.qml @@ -0,0 +1,479 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import EVChargerApp + +CenteredDialog { + id: dialog + + property int wizardStep: 0 + required property HomeAssistantClient client + property int editingChartIndex: -1 + property var selectedEntityIds: [] + property var originalEntityConfigurations: [] + property int selectionRevision: 0 + property bool tokenVisible: false + + title: wizardStep === 3 ? qsTr("Edit chart") : qsTr("Connect Home Assistant") + modal: true + focus: true + width: Math.min(620, Overlay.overlay.width - 20) + height: Math.min(760, Overlay.overlay.height - 20) + closePolicy: Popup.CloseOnEscape + + function selected(entityId) { + const unusedRevision = selectionRevision + return selectedEntityIds.indexOf(entityId) >= 0 + } + + function setSelected(entityId, enabled) { + const values = selectedEntityIds.slice() + const index = values.indexOf(entityId) + if (enabled && index < 0) + values.push(entityId) + else if (!enabled && index >= 0) + values.splice(index, 1) + selectedEntityIds = values + ++selectionRevision + } + + function editChart(index) { + editingChartIndex = index + const chart = dialog.client.charts[index] + chartTitleInput.text = chart && chart.title ? chart.title : "" + originalEntityConfigurations = chart && chart.entities + ? chart.entities.slice() : [] + const ids = [] + for (let i = 0; i < originalEntityConfigurations.length; ++i) + ids.push(originalEntityConfigurations[i].entityId) + selectedEntityIds = ids + ++selectionRevision + entitySearchInput.text = "" + dialog.client.entitySearch = "" + wizardStep = 3 + if (!dialog.client.entitiesLoading) + dialog.client.loadEntities() + } + + function newChart() { + editingChartIndex = -1 + chartTitleInput.text = "" + originalEntityConfigurations = [] + selectedEntityIds = [] + ++selectionRevision + entitySearchInput.text = "" + dialog.client.entitySearch = "" + wizardStep = 3 + if (!dialog.client.entitiesLoading) + dialog.client.loadEntities() + } + + function saveChart() { + const entities = [] + for (let i = 0; i < selectedEntityIds.length; ++i) { + const entityId = selectedEntityIds[i] + let saved = undefined + for (let j = 0; j < originalEntityConfigurations.length; ++j) { + if (originalEntityConfigurations[j].entityId === entityId) { + saved = originalEntityConfigurations[j] + break + } + } + entities.push(saved || { entityId: entityId }) + } + + const charts = dialog.client.charts.slice() + const chart = { + id: editingChartIndex >= 0 && charts[editingChartIndex].id + ? charts[editingChartIndex].id + : Date.now().toString(36), + title: chartTitleInput.text.trim(), + entities: entities + } + if (editingChartIndex >= 0) + charts[editingChartIndex] = chart + else + charts.push(chart) + dialog.client.saveCharts(charts) + wizardStep = 2 + } + + function removeChart(index) { + const charts = dialog.client.charts.slice() + charts.splice(index, 1) + dialog.client.saveCharts(charts) + } + + onOpened: { + urlInput.text = dialog.client.baseUrl || "" + tokenInput.text = dialog.client.accessTokenForEditing() + wizardStep = dialog.client.configured ? 2 : 0 + if (dialog.client.configured) + dialog.client.loadEntities() + } + + onClosed: { + dialog.client.entitySearch = "" + entitySearchInput.text = "" + } + + Connections { + target: dialog.client + function onConnectionTestFinished(success) { + if (success) + dialog.wizardStep = 2 + } + } + + contentItem: ColumnLayout { + spacing: 10 + + RowLayout { + Layout.fillWidth: true + visible: dialog.wizardStep < 3 + + Repeater { + model: [qsTr("Server"), qsTr("Login"), qsTr("Charts")] + + Label { + required property int index + required property string modelData + Layout.fillWidth: true + text: qsTr("%1. %2").arg(index + 1).arg(modelData) + font.bold: index === dialog.wizardStep + color: index <= dialog.wizardStep + ? palette.text : palette.placeholderText + horizontalAlignment: Text.AlignHCenter + } + } + } + + Rectangle { + Layout.fillWidth: true + visible: dialog.wizardStep < 3 + Layout.preferredHeight: 1 + color: dialog.palette.mid + } + + StackLayout { + Layout.fillWidth: true + Layout.fillHeight: true + currentIndex: dialog.wizardStep + + ColumnLayout { + spacing: 12 + + ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + + ColumnLayout { + width: parent.availableWidth + spacing: 10 + + SettingsCard { + SettingsSectionLabel { text: qsTr("Before you begin") } + + Text { + Layout.fillWidth: true + text: qsTr("Home Assistant must already be installed and running. A Raspberry Pi with Home Assistant on an SD card is one option; other supported Home Assistant installations work as well.\n\nBefore charts can receive data:\n• Install or configure an MQTT broker, such as the Mosquitto broker app or a separate broker.\n• Connect Home Assistant's MQTT integration to that broker.\n• Connect the charger to the same broker and enable MQTT.\n• Enable Home Assistant MQTT auto-discovery in Home Assistant and on the charger.\n• Verify that the charger's entities are visible in Home Assistant.") + wrapMode: Text.Wrap + } + + RowLayout { + Layout.fillWidth: true + Button { + Layout.fillWidth: true + text: qsTr("Installation guide") + onClicked: Qt.openUrlExternally("https://www.home-assistant.io/installation/") + } + Button { + Layout.fillWidth: true + text: qsTr("MQTT guide") + onClicked: Qt.openUrlExternally("https://www.home-assistant.io/integrations/mqtt/") + } + } + } + + Text { + Layout.fillWidth: true + text: qsTr("Enter the address you normally use to open Home Assistant. The app will contact this server directly.") + wrapMode: Text.Wrap + } + + TextField { + id: urlInput + Layout.fillWidth: true + placeholderText: "https://homeassistant.local:8123" + inputMethodHints: Qt.ImhUrlCharactersOnly + selectByMouse: true + } + } + } + + RowLayout { + Layout.fillWidth: true + Button { text: qsTr("Cancel"); onClicked: dialog.close() } + Item { Layout.fillWidth: true } + Button { + text: qsTr("Next") + enabled: urlInput.text.trim().length > 0 + onClicked: dialog.wizardStep = 1 + } + } + } + + ColumnLayout { + spacing: 10 + + Text { + Layout.fillWidth: true + text: qsTr("Create a long-lived access token in your Home Assistant profile and paste it below. Your Home Assistant password is never entered into this app.") + wrapMode: Text.Wrap + } + + Button { + Layout.fillWidth: true + text: qsTr("Open Home Assistant profile") + onClicked: Qt.openUrlExternally(urlInput.text.trim().replace(/\/$/, "") + + "/profile/security") + } + + RowLayout { + Layout.fillWidth: true + + TextField { + id: tokenInput + Layout.fillWidth: true + placeholderText: qsTr("Long-lived access token") + echoMode: dialog.tokenVisible ? TextInput.Normal : TextInput.Password + selectByMouse: true + } + + ToolButton { + text: dialog.tokenVisible ? qsTr("Hide") : qsTr("Show") + onClicked: dialog.tokenVisible = !dialog.tokenVisible + } + } + + Label { + Layout.fillWidth: true + visible: dialog.client.errorText.length > 0 + text: dialog.client.errorText + color: palette.brightText + wrapMode: Text.Wrap + } + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + visible: dialog.client.busy + running: visible + } + + Item { Layout.fillHeight: true } + + RowLayout { + Layout.fillWidth: true + Button { text: qsTr("Back"); onClicked: dialog.wizardStep = 0 } + Item { Layout.fillWidth: true } + Button { + text: qsTr("Connect") + enabled: !dialog.client.busy && tokenInput.text.trim().length > 0 + onClicked: dialog.client.testAndSaveConnection(urlInput.text, + tokenInput.text) + } + } + } + + ColumnLayout { + spacing: 8 + + Text { + Layout.fillWidth: true + text: qsTr("Create charts and choose exactly which Home Assistant entities each chart should display.") + wrapMode: Text.Wrap + } + + Label { + Layout.fillWidth: true + text: dialog.client.baseUrl + elide: Text.ElideMiddle + color: palette.placeholderText + } + + Button { + Layout.fillWidth: true + text: qsTr("Change server or access token") + onClicked: dialog.wizardStep = 0 + } + + ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + + ListView { + spacing: 6 + model: dialog.client.charts + + delegate: WhiteBox { + required property int index + required property var modelData + width: ListView.view.width + implicitHeight: chartRow.implicitHeight + 16 + + RowLayout { + id: chartRow + anchors.fill: parent + anchors.margins: 8 + + ColumnLayout { + Layout.fillWidth: true + Label { + Layout.fillWidth: true + text: modelData.title + font.bold: true + elide: Text.ElideRight + } + Label { + text: qsTr("%n entity(s)", "", modelData.entities.length) + color: palette.placeholderText + } + } + ToolButton { + text: qsTr("Edit") + onClicked: dialog.editChart(index) + } + ToolButton { + text: qsTr("Delete") + onClicked: dialog.removeChart(index) + } + } + } + + Label { + anchors.centerIn: parent + visible: parent.count === 0 + text: qsTr("No charts configured yet.") + color: palette.placeholderText + } + } + } + + RowLayout { + Layout.fillWidth: true + Button { text: qsTr("Add chart"); onClicked: dialog.newChart() } + Item { Layout.fillWidth: true } + Button { text: qsTr("Done"); onClicked: dialog.close() } + } + } + + ColumnLayout { + spacing: 7 + + TextField { + id: chartTitleInput + Layout.fillWidth: true + placeholderText: qsTr("Chart title") + selectByMouse: true + } + + TextField { + id: entitySearchInput + Layout.fillWidth: true + placeholderText: qsTr("Search by name, entity ID, unit or device class…") + selectByMouse: true + onTextChanged: dialog.client.entitySearch = text + } + + RowLayout { + Layout.fillWidth: true + Label { + Layout.fillWidth: true + text: qsTr("%n entity(s) selected", "", dialog.selectedEntityIds.length) + font.bold: true + } + BusyIndicator { + implicitWidth: 28 + implicitHeight: 28 + visible: dialog.client.entitiesLoading + running: visible + } + ToolButton { + text: qsTr("Reload") + enabled: !dialog.client.entitiesLoading + onClicked: dialog.client.loadEntities() + } + } + + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + spacing: 1 + model: dialog.client.entitiesModel + + delegate: CheckDelegate { + id: entityDelegate + required property string entityId + required property string name + required property string unit + required property string deviceClass + required property string currentValue + width: ListView.view.width + checked: dialog.selected(entityId) + onClicked: dialog.setSelected(entityId, checked) + + contentItem: Column { + spacing: 1 + Label { + width: parent.width + text: entityDelegate.name + font.bold: true + elide: Text.ElideRight + } + Label { + width: parent.width + text: { + let details = entityDelegate.entityId + if (entityDelegate.currentValue.length > 0) + details += " · " + entityDelegate.currentValue + if (entityDelegate.unit.length > 0) + details += " " + entityDelegate.unit + return details + } + color: palette.placeholderText + font.pixelSize: 11 + elide: Text.ElideMiddle + } + } + } + + Label { + anchors.centerIn: parent + visible: parent.count === 0 && !dialog.client.entitiesLoading + width: parent.width - 20 + text: dialog.client.errorText.length > 0 + ? dialog.client.errorText + : qsTr("No matching entities found.") + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + } + } + + RowLayout { + Layout.fillWidth: true + Button { text: qsTr("Cancel"); onClicked: dialog.wizardStep = 2 } + Item { Layout.fillWidth: true } + Button { + text: qsTr("Save chart") + enabled: chartTitleInput.text.trim().length > 0 + && dialog.selectedEntityIds.length > 0 + onClicked: dialog.saveChart() + } + } + } + } + } +} diff --git a/evcharger-app/qml/InformationsTabPage.qml b/evcharger-app/qml/InformationsTabPage.qml index c88a24d..4731d83 100644 --- a/evcharger-app/qml/InformationsTabPage.qml +++ b/evcharger-app/qml/InformationsTabPage.qml @@ -11,13 +11,21 @@ ScrollableTabPage { property real chartCurrentTime: 0 property string historyRange: "day" property int historyOffset: 0 - property string homeAssistantUrl + + HomeAssistantClient { + id: homeAssistant + settings: theSettings + } function backPressed() { if (homeAssistantDialog.opened) { homeAssistantDialog.close() return true } + if (forgetHomeAssistantDialog.opened) { + forgetHomeAssistantDialog.close() + return true + } return false } @@ -45,6 +53,18 @@ ScrollableTabPage { } } + function manufacturerImageSource(value) { + if (value === null || value === undefined) + return "" + const sourceText = String(value) + if (sourceText.indexOf("data:") === 0) + return sourceText + if (sourceText.trim().indexOf(" 0 && implicitHeight > 0 + ? width * implicitHeight / implicitWidth : 220 + source: page.manufacturerImageSource(modelData) fillMode: Image.PreserveAspectFit + asynchronous: true } } @@ -277,24 +339,42 @@ ScrollableTabPage { wrapMode: Text.Wrap } - Text { + RowLayout { Layout.fillWidth: true - text: qsTr("Home Assistant recorder integration is being prepared. The setup below is currently a preview and does not connect yet.") - wrapMode: Text.Wrap - color: AppTheme.sectionLabelColor - } - Button { - Layout.fillWidth: true - text: qsTr("Set up connection to Home Assistant") - onClicked: homeAssistantDialog.open() + ColumnLayout { + Layout.fillWidth: true + Label { + Layout.fillWidth: true + text: homeAssistant.configured + ? qsTr("Connected to Home Assistant") + : qsTr("Home Assistant is not configured") + font.bold: true + } + Label { + Layout.fillWidth: true + visible: homeAssistant.configured + text: homeAssistant.baseUrl + color: palette.placeholderText + elide: Text.ElideMiddle + } + } + + Button { + text: homeAssistant.configured ? qsTr("Configure") : qsTr("Set up") + onClicked: homeAssistantDialog.open() + } } } - SettingsSectionLabel { text: qsTr("Historical data") } + SettingsSectionLabel { + visible: homeAssistant.configured && homeAssistant.charts.length > 0 + text: qsTr("Historical data") + } RowLayout { Layout.fillWidth: true + visible: homeAssistant.configured && homeAssistant.charts.length > 0 spacing: 2 Repeater { @@ -318,10 +398,14 @@ ScrollableTabPage { RowLayout { Layout.fillWidth: true + visible: homeAssistant.configured && homeAssistant.charts.length > 0 Button { text: qsTr("Previous") - onClicked: --page.historyOffset + onClicked: { + --page.historyOffset + page.refreshHomeAssistantHistory() + } } Label { @@ -334,12 +418,46 @@ ScrollableTabPage { Button { text: qsTr("Next") enabled: page.historyOffset < 0 - onClicked: ++page.historyOffset + onClicked: { + ++page.historyOffset + page.refreshHomeAssistantHistory() + } + } + + ToolButton { + text: qsTr("Refresh") + enabled: !homeAssistant.historyLoading + onClicked: page.refreshHomeAssistantHistory() + } + } + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + visible: homeAssistant.historyLoading + running: visible + } + + Label { + Layout.fillWidth: true + visible: homeAssistant.errorText.length > 0 + text: homeAssistant.errorText + color: palette.brightText + wrapMode: Text.Wrap + } + + Repeater { + model: homeAssistant.historyPanels + + InformationChartPanel { + required property var modelData + panelData: modelData + currentTime: homeAssistant.historyCurrentTime } } WhiteBox { Layout.fillWidth: true + visible: !homeAssistant.configured || homeAssistant.charts.length === 0 implicitHeight: historyPlaceholder.implicitHeight + 30 ColumnLayout { @@ -356,63 +474,55 @@ ScrollableTabPage { SettingsLabel { Layout.fillWidth: true - text: qsTr("Home Assistant history will appear here") + text: !homeAssistant.configured + ? qsTr("Connect Home Assistant to display history") + : qsTr("Create your first history chart") horizontalAlignment: Text.AlignHCenter } Text { Layout.fillWidth: true - text: qsTr("After the recorder integration is connected, this view will request measurements for the selected period.") + text: !homeAssistant.configured + ? qsTr("The setup wizard connects to your server and lets you select the entities to display.") + : qsTr("Open the configuration and add a chart with one or more Home Assistant entities.") wrapMode: Text.Wrap horizontalAlignment: Text.AlignHCenter color: AppTheme.sectionLabelColor } + + Button { + Layout.alignment: Qt.AlignHCenter + text: !homeAssistant.configured ? qsTr("Set up") : qsTr("Add chart") + onClicked: homeAssistantDialog.open() + } } } + + Button { + Layout.fillWidth: true + visible: homeAssistant.configured + text: qsTr("Forget Home Assistant configuration") + onClicked: forgetHomeAssistantDialog.open() + } } } - CenteredDialog { + HomeAssistantSetupDialog { id: homeAssistantDialog - title: qsTr("Connect Home Assistant") - modal: true - focus: true - width: parent.width + client: homeAssistant + } + CenteredDialog { + id: forgetHomeAssistantDialog + title: qsTr("Forget Home Assistant?") + standardButtons: Dialog.Cancel | Dialog.Ok + onAccepted: homeAssistant.clearConfiguration() contentItem: ColumnLayout { - spacing: 12 - Text { - Layout.fillWidth: true - text: qsTr("Enter the address of your Home Assistant server. No connection is made in this preview.") - wrapMode: Text.Wrap - } - - TextField { - id: homeAssistantUrlInput Layout.fillWidth: true Layout.minimumWidth: 260 - placeholderText: "https://homeassistant.local:8123" - text: page.homeAssistantUrl - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: page.homeAssistantUrl = text - } - - RowLayout { - Layout.fillWidth: true - - Item { Layout.fillWidth: true } - - Button { - text: qsTr("Cancel") - onClicked: homeAssistantDialog.close() - } - - Button { - text: qsTr("Next") - enabled: homeAssistantUrlInput.text.trim().length > 0 - onClicked: {} - } + text: qsTr("This removes the server address, access token and all chart definitions stored in this app.") + wrapMode: Text.Wrap } } } diff --git a/evcharger-app/qml/qmldir b/evcharger-app/qml/qmldir index 1e510f5..7facace 100644 --- a/evcharger-app/qml/qmldir +++ b/evcharger-app/qml/qmldir @@ -50,6 +50,7 @@ EVChargerApp 1.0 GridPage.qml EVChargerApp 1.0 GroundCheckPage.qml EVChargerApp 1.0 HardwareInformationPage.qml EVChargerApp 1.0 HotspotPage.qml +EVChargerApp 1.0 HomeAssistantSetupDialog.qml EVChargerApp 1.0 InformationsTabPage.qml EVChargerApp 1.0 InformationChartPanel.qml EVChargerApp 1.0 IndexedEditValueItem.qml