Merge PR #1136: Introduce system based updates and add progress and changelog
This commit is contained in:
commit
43857e6512
@ -25,20 +25,23 @@
|
||||
#include "systemcontroller.h"
|
||||
|
||||
#include "types/package.h"
|
||||
#include "types/repository.h"
|
||||
#include "types/packages.h"
|
||||
#include "types/repositories.h"
|
||||
#include "types/repository.h"
|
||||
|
||||
#include <QTimeZone>
|
||||
#include <QJsonDocument>
|
||||
#include <QMetaEnum>
|
||||
#include <QTimeZone>
|
||||
|
||||
#include "logging.h"
|
||||
NYMEA_LOGGING_CATEGORY(dcSystemController, "SystemController")
|
||||
|
||||
SystemController::SystemController(JsonRpcClient *jsonRpcClient, QObject *parent):
|
||||
QObject(parent),
|
||||
m_jsonRpcClient(jsonRpcClient)
|
||||
SystemController::SystemController(JsonRpcClient *jsonRpcClient, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_jsonRpcClient(jsonRpcClient)
|
||||
{
|
||||
m_jsonRpcClient->registerNotificationHandler(this, "System", "notificationReceived");
|
||||
|
||||
m_packages = new Packages(this);
|
||||
m_repositories = new Repositories(this);
|
||||
|
||||
@ -73,6 +76,11 @@ bool SystemController::updateManagementAvailable() const
|
||||
return m_updateManagementAvailable;
|
||||
}
|
||||
|
||||
SystemController::UpdateType SystemController::updateManagementType() const
|
||||
{
|
||||
return m_updateManagementType;
|
||||
}
|
||||
|
||||
int SystemController::restart()
|
||||
{
|
||||
return m_jsonRpcClient->sendCommand("System.Restart", this, "restartResponse");
|
||||
@ -98,6 +106,11 @@ bool SystemController::updateRunning() const
|
||||
return m_updateRunning;
|
||||
}
|
||||
|
||||
int SystemController::updateProgress() const
|
||||
{
|
||||
return m_updateProgress;
|
||||
}
|
||||
|
||||
void SystemController::checkForUpdates()
|
||||
{
|
||||
m_jsonRpcClient->sendCommand("System.CheckForUpdates");
|
||||
@ -172,7 +185,7 @@ QString SystemController::serverTimeZone() const
|
||||
// NOTE: Ideally we'd just set the TimeZone of our serverTime prooperly, however, there's a bug on Android
|
||||
// Which doesn't allow to create QTimeZone objects by IANA id.... So, let's keep that separated in a string
|
||||
// https://bugreports.qt.io/browse/QTBUG-83438
|
||||
// return m_serverTime.timeZone().id();
|
||||
// return m_serverTime.timeZone().id();
|
||||
return m_serverTimeZone;
|
||||
}
|
||||
|
||||
@ -207,12 +220,26 @@ QString SystemController::deviceSerialNumber() const
|
||||
|
||||
void SystemController::getCapabilitiesResponse(int /*commandId*/, const QVariantMap &data)
|
||||
{
|
||||
qCDebug(dcSystemController()) << data;
|
||||
m_powerManagementAvailable = data.value("powerManagement").toBool();
|
||||
emit powerManagementAvailableChanged();
|
||||
|
||||
m_updateManagementAvailable = data.value("updateManagement").toBool();
|
||||
emit updateManagementAvailableChanged();
|
||||
|
||||
// Since API version 8.5
|
||||
if (data.contains("updateManagementType")) {
|
||||
QMetaEnum updateTypeEnum = QMetaEnum::fromType<SystemController::UpdateType>();
|
||||
m_updateManagementType = static_cast<SystemController::UpdateType>(updateTypeEnum.keyToValue(data.value("updateManagementType").toByteArray()));
|
||||
} else {
|
||||
// Property exists since API 8.5, if there is an update management available, default to UpdateTypePackageManager
|
||||
if (m_updateManagementAvailable) {
|
||||
m_updateManagementType = UpdateTypePackageManager;
|
||||
} else {
|
||||
m_updateManagementType = UpdateTypeNone;
|
||||
}
|
||||
}
|
||||
|
||||
m_timeManagementAvailable = data.value("timeManagement").toBool();
|
||||
emit timeManagementAvailableChanged();
|
||||
|
||||
@ -226,7 +253,15 @@ void SystemController::getCapabilitiesResponse(int /*commandId*/, const QVariant
|
||||
m_jsonRpcClient->sendCommand("System.GetTime", this, "getServerTimeResponse");
|
||||
}
|
||||
|
||||
qCDebug(dcSystemController) << "nymea:core capabilities: Power management:" << m_powerManagementAvailable << "Update management:" << m_updateManagementAvailable << "Time management:" << m_timeManagementAvailable;
|
||||
qCDebug(dcSystemController())
|
||||
<< "nymea:core capabilities: Power management:"
|
||||
<< m_powerManagementAvailable
|
||||
<< "Update management:"
|
||||
<< m_updateManagementAvailable
|
||||
<< "Update management type:"
|
||||
<< m_updateManagementType
|
||||
<< "Time management:"
|
||||
<< m_timeManagementAvailable;
|
||||
}
|
||||
|
||||
void SystemController::getUpdateStatusResponse(int /*commandId*/, const QVariantMap &data)
|
||||
@ -234,13 +269,17 @@ void SystemController::getUpdateStatusResponse(int /*commandId*/, const QVariant
|
||||
qCDebug(dcSystemController()) << "Update status:" << qUtf8Printable(QJsonDocument::fromVariant(data).toJson(QJsonDocument::Indented));
|
||||
m_updateManagementBusy = data.value("busy").toBool();
|
||||
m_updateRunning = data.value("updateRunning").toBool();
|
||||
m_updateProgress = data.value("updateProgress", -1).toInt(); // Since API 8.5, optional
|
||||
|
||||
emit updateRunningChanged();
|
||||
emit updateManagementBusyChanged();
|
||||
emit updateProgressChanged();
|
||||
}
|
||||
|
||||
void SystemController::getPackagesResponse(int commandId, const QVariantMap &data)
|
||||
{
|
||||
Q_UNUSED(commandId)
|
||||
qCDebug(dcSystemController) << "Packages:" << qUtf8Printable(QJsonDocument::fromVariant(data).toJson(QJsonDocument::Indented));
|
||||
qCDebug(dcSystemController()) << "Packages:" << qUtf8Printable(QJsonDocument::fromVariant(data).toJson(QJsonDocument::Indented));
|
||||
foreach (const QVariant &packageVariant, data.value("packages").toList()) {
|
||||
QString id = packageVariant.toMap().value("id").toString();
|
||||
QString displayName = packageVariant.toMap().value("displayName").toString();
|
||||
@ -269,12 +308,12 @@ void SystemController::getRepositoriesResponse(int /*commandId*/, const QVariant
|
||||
|
||||
void SystemController::removePackageResponse(int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
qCDebug(dcSystemController) << "Remove result" << commandId << params;
|
||||
qCDebug(dcSystemController()) << "Remove result" << commandId << params;
|
||||
}
|
||||
|
||||
void SystemController::enableRepositoryResponse(int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
qCDebug(dcSystemController) << "Enable repo response" << params;
|
||||
qCDebug(dcSystemController()) << "Enable repo response" << params;
|
||||
emit enableRepositoryFinished(commandId, params.value("success").toBool());
|
||||
}
|
||||
|
||||
@ -286,7 +325,7 @@ void SystemController::getServerTimeResponse(int commandId, const QVariantMap &p
|
||||
// Which doesn't allow to create QTimeZone objects by IANA id.... So, let's keep that separated in a string
|
||||
// https://bugreports.qt.io/browse/QTBUG-83438
|
||||
|
||||
// m_serverTime.setTimeZone(QTimeZone(params.value("timeZone").toString().toUtf8()));
|
||||
// m_serverTime.setTimeZone(QTimeZone(params.value("timeZone").toString().toUtf8()));
|
||||
m_serverTimeZone = params.value("timeZone").toString();
|
||||
emit serverTimeZoneChanged();
|
||||
|
||||
@ -302,12 +341,12 @@ void SystemController::getServerTimeResponse(int commandId, const QVariantMap &p
|
||||
emit automaticTimeAvailableChanged();
|
||||
m_automaticTime = params.value("automaticTime").toBool();
|
||||
emit automaticTimeChanged();
|
||||
qCDebug(dcSystemController) << "Server time:" << m_serverTime << "Automatic Time available:" << m_automaticTimeAvailable << "Automatic time:" << m_automaticTime;
|
||||
qCDebug(dcSystemController()) << "Server time:" << m_serverTime << "Automatic Time available:" << m_automaticTimeAvailable << "Automatic time:" << m_automaticTime;
|
||||
}
|
||||
|
||||
void SystemController::setTimeResponse(int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
qCDebug(dcSystemController) << "set time response" << commandId << params;
|
||||
qCDebug(dcSystemController()) << "set time response" << commandId << params;
|
||||
}
|
||||
|
||||
void SystemController::restartResponse(int commandId, const QVariantMap ¶ms)
|
||||
@ -338,18 +377,26 @@ void SystemController::getSystemInfoResponse(int commandId, const QVariantMap &p
|
||||
void SystemController::notificationReceived(const QVariantMap &data)
|
||||
{
|
||||
QString notification = data.value("notification").toString();
|
||||
const QVariantMap paramsMap = data.value("params").toMap();
|
||||
if (notification == "System.UpdateStatusChanged") {
|
||||
qCDebug(dcSystemController) << "System.UpdateStatusChanged:" << data.value("params").toMap();
|
||||
if (m_updateManagementBusy != data.value("params").toMap().value("busy").toBool()) {
|
||||
m_updateManagementBusy = data.value("params").toMap().value("busy").toBool();
|
||||
qCDebug(dcSystemController()) << "System.UpdateStatusChanged:" << paramsMap;
|
||||
if (m_updateManagementBusy != paramsMap.value("busy").toBool()) {
|
||||
m_updateManagementBusy = paramsMap.value("busy").toBool();
|
||||
emit updateManagementBusyChanged();
|
||||
}
|
||||
if (m_updateRunning != data.value("params").toMap().value("updateRunning").toBool()) {
|
||||
m_updateRunning = data.value("params").toMap().value("updateRunning").toBool();
|
||||
if (m_updateRunning != paramsMap.value("updateRunning").toBool()) {
|
||||
m_updateRunning = paramsMap.value("updateRunning").toBool();
|
||||
emit updateRunningChanged();
|
||||
}
|
||||
|
||||
// Since API 8.5, optional, not supported or not running = -1
|
||||
if (m_updateProgress != paramsMap.value("updateProgress", -1).toInt()) {
|
||||
m_updateProgress = paramsMap.value("updateProgress").toInt();
|
||||
emit updateProgressChanged();
|
||||
}
|
||||
|
||||
} else if (notification == "System.PackageAdded") {
|
||||
QVariantMap packageMap = data.value("params").toMap().value("package").toMap();
|
||||
QVariantMap packageMap = paramsMap.value("package").toMap();
|
||||
QString id = packageMap.value("id").toString();
|
||||
QString displayName = packageMap.value("displayName").toString();
|
||||
Package *p = new Package(id, displayName);
|
||||
@ -362,7 +409,7 @@ void SystemController::notificationReceived(const QVariantMap &data)
|
||||
p->setCanRemove(packageMap.value("canRemove").toBool());
|
||||
m_packages->addPackage(p);
|
||||
} else if (notification == "System.PackageChanged") {
|
||||
QVariantMap packageMap = data.value("params").toMap().value("package").toMap();
|
||||
QVariantMap packageMap = paramsMap.value("package").toMap();
|
||||
QString id = packageMap.value("id").toString();
|
||||
Package *p = m_packages->getPackage(id);
|
||||
if (!p) {
|
||||
@ -377,17 +424,17 @@ void SystemController::notificationReceived(const QVariantMap &data)
|
||||
p->setRollbackAvailable(packageMap.value("rollbackAvailable").toBool());
|
||||
p->setCanRemove(packageMap.value("canRemove").toBool());
|
||||
} else if (notification == "System.PackageRemoved") {
|
||||
QString packageId = data.value("params").toMap().value("packageId").toString();
|
||||
QString packageId = paramsMap.value("packageId").toString();
|
||||
m_packages->removePackage(packageId);
|
||||
} else if (notification == "System.RepositoryAdded") {
|
||||
QVariantMap repoMap = data.value("params").toMap().value("repository").toMap();
|
||||
QVariantMap repoMap = paramsMap.value("repository").toMap();
|
||||
QString id = repoMap.value("id").toString();
|
||||
QString displayName = repoMap.value("displayName").toString();
|
||||
Repository *repo = new Repository(id, displayName);
|
||||
repo->setEnabled(repoMap.value("enabled").toBool());
|
||||
m_repositories->addRepository(repo);
|
||||
} else if (notification == "System.RepositoryChanged") {
|
||||
QVariantMap repoMap = data.value("params").toMap().value("repository").toMap();
|
||||
QVariantMap repoMap = paramsMap.value("repository").toMap();
|
||||
QString id = repoMap.value("id").toString();
|
||||
Repository *repo = m_repositories->getRepository(id);
|
||||
if (!repo) {
|
||||
@ -396,16 +443,36 @@ void SystemController::notificationReceived(const QVariantMap &data)
|
||||
}
|
||||
repo->setEnabled(repoMap.value("enabled").toBool());
|
||||
} else if (notification == "System.RepositoryRemoved") {
|
||||
QString repositoryId = data.value("params").toMap().value("repositoryId").toString();
|
||||
QString repositoryId = paramsMap.value("repositoryId").toString();
|
||||
m_repositories->removeRepository(repositoryId);
|
||||
} else if (notification == "System.CapabilitiesChanged") {
|
||||
m_powerManagementAvailable = data.value("params").toMap().value("powerManagement").toBool();
|
||||
m_updateManagementAvailable = data.value("params").toMap().value("updateManagement").toBool();
|
||||
qWarning() << "System capabilites changed: power management:" << m_powerManagementAvailable << "update management:" << m_updateManagementAvailable;
|
||||
m_powerManagementAvailable = paramsMap.value("powerManagement").toBool();
|
||||
m_updateManagementAvailable = paramsMap.value("updateManagement").toBool();
|
||||
if (paramsMap.contains("updateManagementType")) {
|
||||
QMetaEnum updateTypeEnum = QMetaEnum::fromType<SystemController::UpdateType>();
|
||||
m_updateManagementType = static_cast<SystemController::UpdateType>(updateTypeEnum.keyToValue(paramsMap.value("updateManagementType").toByteArray()));
|
||||
} else {
|
||||
// Property exists since API 8.5, if there is an update management available, default to UpdateTypePackageManager
|
||||
if (m_updateManagementAvailable) {
|
||||
m_updateManagementType = UpdateTypePackageManager;
|
||||
} else {
|
||||
m_updateManagementType = UpdateTypeNone;
|
||||
}
|
||||
}
|
||||
|
||||
qCDebug(dcSystemController())
|
||||
<< "System capabilites changed: power management:"
|
||||
<< m_powerManagementAvailable
|
||||
<< "update management:"
|
||||
<< m_updateManagementAvailable
|
||||
<< "update management type:"
|
||||
<< m_updateManagementType;
|
||||
emit powerManagementAvailableChanged();
|
||||
emit updateManagementAvailableChanged();
|
||||
emit updateManagementTypeChanged();
|
||||
|
||||
} else if (notification == "System.TimeConfigurationChanged") {
|
||||
qCDebug(dcSystemController) << "System time configuration changed" << data.value("params").toMap().value("timeZone").toByteArray();
|
||||
qCDebug(dcSystemController()) << "System time configuration changed" << data.value("params").toMap().value("timeZone").toByteArray();
|
||||
|
||||
// NOTE: Ideally we'd just set the TimeZone of our serverTime prooperly, however, there's a bug on Android
|
||||
// Which doesn't allow to create QTimeZone objects by IANA id.... So, let's keep that separated in a string
|
||||
|
||||
@ -35,14 +35,18 @@ class SystemController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool powerManagementAvailable READ powerManagementAvailable NOTIFY powerManagementAvailableChanged)
|
||||
|
||||
// Whether the update mechanism is available in the connected core
|
||||
Q_PROPERTY(bool updateManagementAvailable READ updateManagementAvailable NOTIFY updateManagementAvailableChanged)
|
||||
Q_PROPERTY(UpdateType updateManagementType READ updateManagementType NOTIFY updateManagementTypeChanged FINAL)
|
||||
Q_PROPERTY(bool timeManagementAvailable READ timeManagementAvailable NOTIFY timeManagementAvailableChanged)
|
||||
|
||||
Q_PROPERTY(bool updateManagementBusy READ updateManagementBusy NOTIFY updateManagementBusyChanged)
|
||||
Q_PROPERTY(bool updateRunning READ updateRunning NOTIFY updateRunningChanged)
|
||||
Q_PROPERTY(Packages* packages READ packages CONSTANT)
|
||||
Q_PROPERTY(Repositories* repositories READ repositories CONSTANT)
|
||||
Q_PROPERTY(int updateProgress READ updateProgress NOTIFY updateProgressChanged)
|
||||
|
||||
Q_PROPERTY(Packages *packages READ packages CONSTANT)
|
||||
Q_PROPERTY(Repositories *repositories READ repositories CONSTANT)
|
||||
|
||||
Q_PROPERTY(QDateTime serverTime READ serverTime WRITE setServerTime NOTIFY serverTimeChanged)
|
||||
Q_PROPERTY(QString serverTimeZone READ serverTimeZone WRITE setServerTimeZone NOTIFY serverTimeZoneChanged)
|
||||
@ -53,23 +57,34 @@ class SystemController : public QObject
|
||||
Q_PROPERTY(QString deviceSerialNumber READ deviceSerialNumber NOTIFY deviceSerialNumberChanged)
|
||||
|
||||
public:
|
||||
enum UpdateType {
|
||||
UpdateTypeNone,
|
||||
UpdateTypeSystem,
|
||||
UpdateTypePackageManager
|
||||
};
|
||||
Q_ENUM(UpdateType)
|
||||
|
||||
explicit SystemController(JsonRpcClient *jsonRpcClient, QObject *parent = nullptr);
|
||||
|
||||
void init();
|
||||
|
||||
bool powerManagementAvailable() const;
|
||||
|
||||
Q_INVOKABLE int restart();
|
||||
Q_INVOKABLE int reboot();
|
||||
Q_INVOKABLE int shutdown();
|
||||
|
||||
bool updateManagementAvailable() const;
|
||||
UpdateType updateManagementType() const;
|
||||
|
||||
bool updateManagementBusy() const;
|
||||
bool updateRunning() const;
|
||||
int updateProgress() const;
|
||||
Q_INVOKABLE void checkForUpdates();
|
||||
Packages* packages() const;
|
||||
Packages *packages() const;
|
||||
Q_INVOKABLE void updatePackages(const QString packageId = QString());
|
||||
Q_INVOKABLE void removePackages(const QString packageId = QString());
|
||||
Repositories* repositories() const;
|
||||
Repositories *repositories() const;
|
||||
Q_INVOKABLE int enableRepository(const QString &id, bool enabled);
|
||||
|
||||
bool timeManagementAvailable() const;
|
||||
@ -87,9 +102,11 @@ public:
|
||||
signals:
|
||||
void powerManagementAvailableChanged();
|
||||
void updateManagementAvailableChanged();
|
||||
void updateManagementTypeChanged();
|
||||
void timeManagementAvailableChanged();
|
||||
void updateManagementBusyChanged();
|
||||
void updateRunningChanged();
|
||||
void updateProgressChanged();
|
||||
void enableRepositoryFinished(int id, bool success);
|
||||
void serverTimeChanged();
|
||||
void serverTimeZoneChanged();
|
||||
@ -117,7 +134,6 @@ private slots:
|
||||
|
||||
void notificationReceived(const QVariantMap &data);
|
||||
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
|
||||
@ -126,10 +142,12 @@ private:
|
||||
|
||||
bool m_powerManagementAvailable = false;
|
||||
bool m_updateManagementAvailable = false;
|
||||
UpdateType m_updateManagementType = UpdateTypeNone;
|
||||
bool m_timeManagementAvailable = false;
|
||||
|
||||
bool m_updateManagementBusy = false;
|
||||
bool m_updateRunning = false;
|
||||
int m_updateProgress = -1;
|
||||
Packages *m_packages = nullptr;
|
||||
Repositories *m_repositories = nullptr;
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
* Copyright (C) 2024 - 2026, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of libnymea-app.
|
||||
*
|
||||
@ -26,10 +26,9 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
ThingClass::ThingClass(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
ThingClass::ThingClass(QObject *parent)
|
||||
: QObject(parent)
|
||||
{}
|
||||
|
||||
QUuid ThingClass::id() const
|
||||
{
|
||||
@ -330,3 +329,8 @@ bool ThingClass::hasActionType(const QUuid &actionTypeId)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThingClass::isAutoCreated() const
|
||||
{
|
||||
return m_createMethods.contains("CreateMethodAuto");
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2013 - 2024, nymea GmbH
|
||||
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
||||
* Copyright (C) 2024 - 2026, chargebyte austria GmbH
|
||||
*
|
||||
* This file is part of libnymea-app.
|
||||
*
|
||||
@ -25,15 +25,15 @@
|
||||
#ifndef THINGCLASS_H
|
||||
#define THINGCLASS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUuid>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QUuid>
|
||||
|
||||
#include "actiontypes.h"
|
||||
#include "eventtypes.h"
|
||||
#include "paramtypes.h"
|
||||
#include "statetypes.h"
|
||||
#include "eventtypes.h"
|
||||
#include "actiontypes.h"
|
||||
|
||||
class ThingClass : public QObject
|
||||
{
|
||||
@ -135,6 +135,8 @@ public:
|
||||
|
||||
Q_INVOKABLE bool hasActionType(const QUuid &actionTypeId);
|
||||
|
||||
Q_INVOKABLE bool isAutoCreated() const;
|
||||
|
||||
signals:
|
||||
void paramTypesChanged();
|
||||
void settingsTypesChanged();
|
||||
|
||||
@ -32,6 +32,7 @@ ConfigurationBase {
|
||||
company: "chargebyte GmbH"
|
||||
|
||||
connectionWizard: "/ui/connection/ConnectionWizard.qml"
|
||||
iosSafeAreaBottomMarginScale: 1.0
|
||||
|
||||
magicEnabled: true
|
||||
networkSettingsEnabled: true
|
||||
|
||||
@ -53,6 +53,9 @@ Item {
|
||||
|
||||
property string alternativeMainPage: ""
|
||||
|
||||
// iOS only: scales the safe area bottom inset before applying it as bottom margin.
|
||||
property real iosSafeAreaBottomMarginScale: 1.0
|
||||
|
||||
property var mainMenuLinks: null
|
||||
property bool closedSource: false
|
||||
|
||||
|
||||
@ -43,6 +43,8 @@ Page {
|
||||
// a deepter layer as we need to include it in the blurring of the header and footer.
|
||||
// We don't want to paint the background on the entire screen twice (overdraw is costly)
|
||||
background: null
|
||||
readonly property bool applyRootItemBottomMarginCompaction: true
|
||||
readonly property int bottomMargin: footer.shown ? contentContainer.footerSize : 0
|
||||
|
||||
function configureViews() {
|
||||
if (Configuration.hasOwnProperty("mainViewsFilter")) {
|
||||
|
||||
@ -37,6 +37,27 @@ import "connection"
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property var currentPage: swipeView.currentItem ? swipeView.currentItem.pageStack.currentItem : null
|
||||
readonly property bool currentPageCompactsBottomMargin: currentPage
|
||||
&& currentPage.hasOwnProperty("applyRootItemBottomMarginCompaction")
|
||||
&& currentPage.applyRootItemBottomMarginCompaction
|
||||
readonly property bool currentPageDefinesBottomMargin: currentPage && currentPage.hasOwnProperty("bottomMargin")
|
||||
readonly property int currentPageBottomMargin: currentPageDefinesBottomMargin ? currentPage.bottomMargin : 0
|
||||
|
||||
readonly property int safeAreaBottomMargin: {
|
||||
var margin = PlatformHelper.bottomPadding
|
||||
|
||||
if (Qt.platform.os === "ios") {
|
||||
margin = Math.round(PlatformHelper.bottomPadding * Math.max(0, Configuration.iosSafeAreaBottomMarginScale))
|
||||
|
||||
if (currentPageCompactsBottomMargin && !app.landscape && currentPageBottomMargin > 0 && margin > 0) {
|
||||
margin = Math.floor(margin * 0.5)
|
||||
}
|
||||
}
|
||||
|
||||
return margin
|
||||
}
|
||||
|
||||
function handleAndroidBackButton() {
|
||||
return swipeView.currentItem.handleAndroidBackButton()
|
||||
}
|
||||
@ -80,7 +101,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
|
||||
anchors.topMargin: PlatformHelper.topPadding
|
||||
anchors.bottomMargin: PlatformHelper.bottomPadding
|
||||
anchors.bottomMargin: root.safeAreaBottomMargin
|
||||
anchors.leftMargin: PlatformHelper.leftPadding
|
||||
anchors.rightMargin: PlatformHelper.rightPadding
|
||||
|
||||
|
||||
@ -69,12 +69,16 @@ SettingsPageBase {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
|
||||
visible: !kioskMode && Qt.platform.os !== "ios"
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("View mode")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
Layout.minimumWidth: 200
|
||||
model: [qsTr("Windowed"), qsTr("Maximized"), qsTr("Fullscreen"), qsTr("Automatic")]
|
||||
currentIndex: {
|
||||
switch (settings.viewMode) {
|
||||
|
||||
@ -37,6 +37,7 @@ Rectangle {
|
||||
property color selectionColor: Style.tileOverlayColor
|
||||
property alias model: repeater.model
|
||||
property var selectedItems: []
|
||||
property int tabHeight: 0
|
||||
|
||||
Rectangle {
|
||||
id: clipMask
|
||||
@ -56,7 +57,7 @@ Rectangle {
|
||||
|
||||
delegate: Item {
|
||||
Layout.fillWidth: true
|
||||
height: label.implicitHeight + Style.smallMargins
|
||||
height: root.tabHeight > 0 ? root.tabHeight : label.implicitHeight + Style.smallMargins
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: root.selectionColor
|
||||
@ -99,4 +100,3 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -56,6 +56,42 @@ ColumnLayout {
|
||||
readonly property bool hasNumbers: passwordTextField.text.search(/[0-9]/) >= 0
|
||||
readonly property bool hasSpecialChar: passwordTextField.text.search(/(?=.*?[$*.\[\]{}()?\-'"!@#%&/\\,><':;|_~`^])/) >= 0
|
||||
readonly property bool confirmationMatches: passwordTextField.text === confirmationPasswordTextField.text
|
||||
readonly property var passwordRequirements: {
|
||||
var requirements = []
|
||||
requirements.push({
|
||||
text: qsTr("Minimum %1 characters").arg(root.minPasswordLength),
|
||||
fulfilled: root.isLongEnough
|
||||
})
|
||||
|
||||
if (root.requireLowerCaseLetter) {
|
||||
requirements.push({
|
||||
text: qsTr("Lowercase letters"),
|
||||
fulfilled: root.hasLower
|
||||
})
|
||||
}
|
||||
|
||||
if (root.requireUpperCaseLetter) {
|
||||
requirements.push({
|
||||
text: qsTr("Uppercase letters"),
|
||||
fulfilled: root.hasUpper
|
||||
})
|
||||
}
|
||||
|
||||
if (root.requireNumber) {
|
||||
requirements.push({
|
||||
text: qsTr("Numbers"),
|
||||
fulfilled: root.hasNumbers
|
||||
})
|
||||
}
|
||||
|
||||
if (root.requireSpecialChar) {
|
||||
requirements.push({
|
||||
text: qsTr("Special characters"),
|
||||
fulfilled: root.hasSpecialChar
|
||||
})
|
||||
}
|
||||
return requirements
|
||||
}
|
||||
|
||||
property bool hiddenPassword: true
|
||||
|
||||
@ -73,39 +109,49 @@ ColumnLayout {
|
||||
placeholderText: root.signup ? qsTr("Pick a password") : qsTr("Password")
|
||||
|
||||
error: root.showErrors && !root.isValidPassword
|
||||
// palette.toolTipBase: Style.tooltipBackgroundColor
|
||||
ToolTip.visible: root.signup && focus && !root.isValidPassword
|
||||
ToolTip.delay: 1000
|
||||
ToolTip.onVisibleChanged: print("Tooltip visible changed:", ToolTip.visible, focus, root.isValidPassword)
|
||||
ToolTip.text: {
|
||||
var texts = []
|
||||
var checks = []
|
||||
texts.push(qsTr("Minimum %1 characters").arg(root.minPasswordLength))
|
||||
checks.push(root.isLongEnough)
|
||||
if (root.requireLowerCaseLetter) {
|
||||
texts.push(qsTr("Lowercase letters"))
|
||||
checks.push(root.hasLower)
|
||||
|
||||
ToolTip {
|
||||
id: passwordRequirementsToolTip
|
||||
parent: passwordTextField
|
||||
visible: root.signup && passwordTextField.focus && !root.isValidPassword
|
||||
delay: 1000
|
||||
timeout: -1
|
||||
x: 0
|
||||
y: passwordTextField.height + Style.smallMargins
|
||||
leftPadding: Style.smallMargins
|
||||
rightPadding: Style.smallMargins
|
||||
topPadding: Style.smallMargins
|
||||
bottomPadding: Style.smallMargins
|
||||
|
||||
background: Rectangle {
|
||||
color: Style.tooltipBackgroundColor
|
||||
radius: Style.smallCornerRadius
|
||||
}
|
||||
if (root.requireUpperCaseLetter) {
|
||||
texts.push(qsTr("Uppercase letters"))
|
||||
checks.push(root.hasUpper)
|
||||
|
||||
contentItem: Column {
|
||||
spacing: Style.extraSmallMargins
|
||||
|
||||
Repeater {
|
||||
model: root.passwordRequirements
|
||||
|
||||
delegate: Row {
|
||||
spacing: Style.extraSmallMargins
|
||||
|
||||
CheckBox {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checked: modelData.fulfilled
|
||||
checkable: false
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: modelData.text
|
||||
color: Style.foregroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (root.requireNumber) {
|
||||
texts.push(qsTr("Numbers"))
|
||||
checks.push(root.hasNumbers)
|
||||
}
|
||||
if (root.requireSpecialChar) {
|
||||
texts.push(qsTr("Special characters"))
|
||||
checks.push(root.hasSpecialChar)
|
||||
}
|
||||
var ret = []
|
||||
for (var i = 0; i < texts.length; i++) {
|
||||
var entry = "<font color=\"%1\">• ".arg(checks[i] ? "#ffffff" : Style.red)
|
||||
entry += texts[i]
|
||||
entry += "</font>"
|
||||
ret.push(entry)
|
||||
}
|
||||
return ret.join("<br>")
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
|
||||
@ -64,6 +64,15 @@ Rectangle {
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
visible: engine.systemController.updateProgress >= 0
|
||||
value: engine.systemController.updateProgress / 100.0
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins * 2
|
||||
|
||||
@ -153,7 +153,7 @@ WizardPageBase {
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
if (PlatformPermissions.bluetoothPermission != PlatformPermissions.PermissionStatusGranted) {
|
||||
if (PlatformPermissions.bluetoothPermission !== PlatformPermissions.PermissionStatusGranted) {
|
||||
PlatformPermissions.requestPermission(PlatformPermissions.PermissionBluetooth)
|
||||
}
|
||||
pageStack.push(wirelessInstructionsComponent)
|
||||
@ -219,7 +219,7 @@ WizardPageBase {
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
visible: hostsProxy.count == 0
|
||||
visible: hostsProxy.count === 0
|
||||
spacing: Style.margins
|
||||
BusyIndicator {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
@ -49,23 +49,23 @@ ColumnLayout {
|
||||
port = portTextInput.text
|
||||
}
|
||||
|
||||
if (connectionTypeComboBox.currentIndex == 0) {
|
||||
if (connectionTypeComboBox.currentIndex === 0) {
|
||||
if (secureCheckBox.checked) {
|
||||
rpcUrl = "nymeas://" + hostAddress + ":" + port
|
||||
} else {
|
||||
rpcUrl = "nymea://" + hostAddress + ":" + port
|
||||
}
|
||||
} else if (connectionTypeComboBox.currentIndex == 1) {
|
||||
} else if (connectionTypeComboBox.currentIndex === 1) {
|
||||
if (secureCheckBox.checked) {
|
||||
rpcUrl = "wss://" + hostAddress + ":" + port
|
||||
} else {
|
||||
rpcUrl = "ws://" + hostAddress + ":" + port
|
||||
}
|
||||
} else if (connectionTypeComboBox.currentIndex == 2) {
|
||||
} else if (connectionTypeComboBox.currentIndex === 2) {
|
||||
if (secureCheckBox.checked) {
|
||||
rpcUrl = "tunnels://" + hostAddress + ":" + port + "?uuid=" + serverUuidTextInput.text
|
||||
rpcUrl = "tunnels://" + hostAddress + ":" + port + "?uuid=" + serverUuidTextInput.text.replace('{', '').replace('}', '')
|
||||
} else {
|
||||
rpcUrl = "tunnel://" + hostAddress + ":" + port + "?uuid=" + serverUuidTextInput.text
|
||||
rpcUrl = "tunnel://" + hostAddress + ":" + port + "?uuid=" + serverUuidTextInput.text.replace('{', '').replace('}', '')
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,41 +90,72 @@ ColumnLayout {
|
||||
Label {
|
||||
text: connectionTypeComboBox.currentIndex < 2 ? qsTr("Address:") : qsTr("Proxy address:")
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: addressTextInput
|
||||
|
||||
objectName: "addressTextInput"
|
||||
Layout.fillWidth: true
|
||||
placeholderText: connectionTypeComboBox.currentIndex < 2 ? "127.0.0.1" : Configuration.tunnelProxyUrl
|
||||
}
|
||||
placeholderText: {
|
||||
if (focus || text)
|
||||
return ""
|
||||
|
||||
Label {
|
||||
text: qsTr("%1 UUID:").arg(Configuration.systemName)
|
||||
visible: connectionTypeComboBox.currentIndex == 2
|
||||
}
|
||||
TextField {
|
||||
id: serverUuidTextInput
|
||||
Layout.fillWidth: true
|
||||
visible: connectionTypeComboBox.currentIndex == 2
|
||||
}
|
||||
Label { text: qsTr("Port:") }
|
||||
TextField {
|
||||
id: portTextInput
|
||||
Layout.fillWidth: true
|
||||
placeholderText: connectionTypeComboBox.currentIndex === 0
|
||||
? "2222"
|
||||
: connectionTypeComboBox.currentIndex == 1
|
||||
? "4444"
|
||||
: Configuration.tunnelProxyPort
|
||||
validator: IntValidator{bottom: 1; top: 65535;}
|
||||
return connectionTypeComboBox.currentIndex < 2 ? "127.0.0.1" : Configuration.tunnelProxyUrl
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("SSL:")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: secureCheckBox
|
||||
|
||||
checked: true
|
||||
}
|
||||
|
||||
Label { text: qsTr("Port:") }
|
||||
TextField {
|
||||
id: portTextInput
|
||||
|
||||
Layout.fillWidth: true
|
||||
validator: IntValidator{bottom: 1; top: 65535;}
|
||||
placeholderText: {
|
||||
if (focus || text)
|
||||
return ""
|
||||
|
||||
if (connectionTypeComboBox.currentIndex === 0) {
|
||||
if (secureCheckBox.checked) {
|
||||
return "2222"
|
||||
} else {
|
||||
return "2223"
|
||||
}
|
||||
}
|
||||
|
||||
if (connectionTypeComboBox.currentIndex === 1) {
|
||||
if (secureCheckBox.checked) {
|
||||
return "4444"
|
||||
} else {
|
||||
return "4445"
|
||||
}
|
||||
}
|
||||
|
||||
if (connectionTypeComboBox.currentIndex === 2)
|
||||
return Configuration.tunnelProxyPort
|
||||
|
||||
return "2222"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TextField {
|
||||
id: serverUuidTextInput
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: 2
|
||||
placeholderText: qsTr("%1 UUID:").arg(Configuration.systemName)
|
||||
visible: connectionTypeComboBox.currentIndex === 2
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ Item {
|
||||
ValueAxis {
|
||||
id: yAxis
|
||||
max: {
|
||||
if (root.stateType && root.stateType.type.toLowerCase() == "bool") {
|
||||
if (root.stateType && root.stateType.type.toLowerCase() === "bool") {
|
||||
return 1;
|
||||
} else {
|
||||
Math.ceil(logsModelNg.maxValue + Math.abs(logsModelNg.maxValue * .05))
|
||||
@ -137,7 +137,7 @@ Item {
|
||||
// onMaxChanged: applyNiceNumbers();
|
||||
labelsFont: Style.extraSmallFont
|
||||
labelFormat: {
|
||||
if (root.stateType && root.stateType.type.toLowerCase() == "bool") {
|
||||
if (root.stateType && root.stateType.type.toLowerCase() === "bool") {
|
||||
return "x";
|
||||
} else {
|
||||
return "%d";
|
||||
|
||||
@ -147,7 +147,7 @@ Item {
|
||||
// y: chartView.plotArea.y
|
||||
// height: chartView.plotArea.height
|
||||
// width: chartView.plotArea.x - x
|
||||
// visible: root.stateType.type.toLowerCase() != "bool" && logsModel.minValue != logsModel.maxValue
|
||||
// visible: root.stateType.type.toLowerCase() !== "bool" && logsModel.minValue != logsModel.maxValue
|
||||
// property double range: Math.abs(valueAxis.max - valueAxis.min)
|
||||
// property double stepSize: range / (valueAxis.tickCount - 1)
|
||||
// property int precision: valueAxis.max - valueAxis.min < 5 ? 2 : 0
|
||||
@ -256,7 +256,7 @@ Item {
|
||||
print("entry", entry.timestamp, entry.source, JSON.stringify(entry.values))
|
||||
d.ensureValue(zeroSeries, entry.timestamp)
|
||||
|
||||
if (stateType.type.toLowerCase() == "bool") {
|
||||
if (stateType.type.toLowerCase() === "bool") {
|
||||
var value = entry.values[stateType.name]
|
||||
if (value == null) {
|
||||
value = false;
|
||||
@ -290,7 +290,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
if (stateType.type.toLowerCase() == "bool") {
|
||||
if (stateType.type.toLowerCase() === "bool") {
|
||||
|
||||
var last = series.at(series.count-1);
|
||||
if (last.x < d.endTime) {
|
||||
@ -304,7 +304,7 @@ Item {
|
||||
}
|
||||
onEntriesRemoved: (index, count) => {
|
||||
print("removing:", index, count, series.count)
|
||||
if (stateType.type.toLowerCase() == "bool") {
|
||||
if (stateType.type.toLowerCase() === "bool") {
|
||||
series.removePoints(index * 2, count * 2)
|
||||
if (series.count == 1) {
|
||||
series.removePoints(0, 1);
|
||||
|
||||
@ -57,7 +57,7 @@ Item {
|
||||
if (root.interfaceName == "closablesensor") {
|
||||
return true
|
||||
}
|
||||
return sensorStateType && sensorStateType.type.toLowerCase() == "bool" && sensorState.value === true
|
||||
return sensorStateType && sensorStateType.type.toLowerCase() === "bool" && sensorState.value === true
|
||||
}
|
||||
iconSource: {
|
||||
if (root.interfaceName == "closablesensor") {
|
||||
@ -100,7 +100,7 @@ Item {
|
||||
}
|
||||
|
||||
sourceComponent: {
|
||||
if (stateType.type.toLowerCase() == "bool") {
|
||||
if (stateType.type.toLowerCase() === "bool") {
|
||||
return boolComponent;
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ Item {
|
||||
property date now: new Date()
|
||||
|
||||
readonly property int range: selectionTabs.currentValue.range
|
||||
readonly property int sampleRate: root.stateType == null || root.stateType.type.toLowerCase() == "bool" ? NewLogsModel.SampleRateAny : selectionTabs.currentValue.sampleRate
|
||||
readonly property int sampleRate: root.stateType == null || root.stateType.type.toLowerCase() === "bool" ? NewLogsModel.SampleRateAny : selectionTabs.currentValue.sampleRate
|
||||
|
||||
readonly property int visibleValues: range / sampleRate
|
||||
|
||||
@ -114,7 +114,7 @@ Item {
|
||||
// print("entry", entry.timestamp, entry.source, JSON.stringify(entry.values))
|
||||
zeroSeries.ensureValue(entry.timestamp)
|
||||
|
||||
if (root.stateType.type.toLowerCase() == "bool") {
|
||||
if (root.stateType.type.toLowerCase() === "bool") {
|
||||
var value = entry.values[root.stateType.name]
|
||||
if (value == null) {
|
||||
value = false;
|
||||
@ -148,7 +148,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
if (root.stateType.type.toLowerCase() == "bool") {
|
||||
if (root.stateType.type.toLowerCase() === "bool") {
|
||||
var last = valueSeries.at(valueSeries.count-1);
|
||||
if (last.x < d.endTime) {
|
||||
valueSeries.append(d.endTime, last.y)
|
||||
@ -160,7 +160,7 @@ Item {
|
||||
}
|
||||
onEntriesRemoved: (index, count) => {
|
||||
print("removing:", index, count, valueSeries.count)
|
||||
if (root.stateType.type.toLowerCase() == "bool") {
|
||||
if (root.stateType.type.toLowerCase() === "bool") {
|
||||
valueSeries.removePoints(index * 2, count * 2)
|
||||
if (valueSeries.count == 1) {
|
||||
valueSeries.removePoints(0, 1);
|
||||
@ -419,7 +419,7 @@ Item {
|
||||
y: chartView.y + chartView.plotArea.y
|
||||
height: chartView.plotArea.height
|
||||
width: Math.max(0, chartContainer.yAxisLabelAreaWidth - Style.smallMargins)
|
||||
visible: root.stateType && root.stateType.type.toLowerCase() != "bool" && logsModel.minValue != logsModel.maxValue
|
||||
visible: root.stateType && root.stateType.type.toLowerCase() !== "bool" && logsModel.minValue != logsModel.maxValue
|
||||
property double range: Math.abs(valueAxis.max - valueAxis.min)
|
||||
property double stepSize: range / (valueAxis.tickCount - 1)
|
||||
property int precision: valueAxis.max - valueAxis.min < 5 ? 2 : 0
|
||||
@ -589,7 +589,7 @@ Item {
|
||||
elide: Text.ElideRight
|
||||
text: toolTip.value === null
|
||||
? qsTr("No data")
|
||||
: root.stateType.type.toLowerCase() == "bool"
|
||||
: root.stateType.type.toLowerCase() === "bool"
|
||||
? root.stateType.displayName + ": " + (toolTip.value ? qsTr("Yes") : qsTr("No"))
|
||||
: Types.toUiValue(toolTip.value, root.stateType.unit).toFixed(root.roundTo) + Types.toUiUnit(root.stateType.unit)
|
||||
font: Style.extraSmallFont
|
||||
|
||||
@ -37,6 +37,7 @@ ItemDelegate {
|
||||
property alias value: d.value
|
||||
property Param param: Param {
|
||||
id: d
|
||||
|
||||
paramTypeId: paramType.id
|
||||
value: paramType.defaultValue
|
||||
}
|
||||
@ -48,9 +49,11 @@ ItemDelegate {
|
||||
bottomPadding: 0
|
||||
contentItem: ColumnLayout {
|
||||
id: contentItemColumn
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.margins
|
||||
anchors.rightMargin: Style.margins
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.margins
|
||||
@ -70,8 +73,6 @@ ItemDelegate {
|
||||
Layout.fillWidth: true//!parent.labelFillsWidth
|
||||
Layout.maximumWidth: root.nameVisible ? contentItemColumn.width / 2 : contentItemColumn.width
|
||||
sourceComponent: {
|
||||
print("Loading ParamDelegate");
|
||||
print("Writable:", root.writable, "type:", root.paramType.type, "min:", root.paramType.minValue, "max:", root.paramType.maxValue, "value:", root.param.value)
|
||||
if (!root.writable) {
|
||||
return stringComponent;
|
||||
}
|
||||
@ -123,12 +124,11 @@ ItemDelegate {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: stringComponent
|
||||
|
||||
Label {
|
||||
text: {
|
||||
switch (root.paramType.type.toLowerCase()) {
|
||||
@ -141,15 +141,17 @@ ItemDelegate {
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: boolComponent
|
||||
|
||||
Item {
|
||||
implicitHeight: theSwitch.implicitHeight
|
||||
implicitWidth: theSwitch.implicitWidth
|
||||
Switch {
|
||||
id: theSwitch
|
||||
anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
|
||||
width: Math.min(parent.width, implicitiWidth)
|
||||
width: Math.min(parent.width, implicitWidth)
|
||||
checked: root.param.value === true
|
||||
Component.onCompleted: {
|
||||
if (root.param.value === undefined) {
|
||||
@ -162,10 +164,11 @@ ItemDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: sliderComponent
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.margins
|
||||
|
||||
@ -206,6 +209,7 @@ ItemDelegate {
|
||||
root.param.value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: Types.toUiValue(root.param.value, root.paramType.unit).toFixed(slider.decimals) + Types.toUiUnit(root.paramType.unit)
|
||||
}
|
||||
@ -231,18 +235,19 @@ ItemDelegate {
|
||||
: 2000000000
|
||||
editable: true
|
||||
width: 150
|
||||
onValueModified: root.param.value = value
|
||||
floatingPoint: root.paramType.type.toLowerCase() === "double"
|
||||
|
||||
floatingPoint: root.paramType.type.toLowerCase() == "double"
|
||||
onValueModified: (value) => { root.param.value = value }
|
||||
|
||||
Component.onCompleted: {
|
||||
print("from:", from, "min", root.paramType.minValue)
|
||||
print("to:", to, "max", root.paramType.maxValue)
|
||||
if (root.value === undefined) {
|
||||
root.value = value
|
||||
if (root.param.value === undefined) {
|
||||
root.param.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: Types.toUiUnit(root.paramType.unit)
|
||||
visible: text.length > 0
|
||||
@ -255,40 +260,48 @@ ItemDelegate {
|
||||
TextField {
|
||||
text: root.param.value !== undefined
|
||||
? root.param.value
|
||||
: root.paramType.defaultValue
|
||||
: root.paramType.defaultValue !== undefined
|
||||
? root.paramType.defaultValue
|
||||
: ""
|
||||
|
||||
placeholderText: root.placeholderText
|
||||
onEditingFinished: {
|
||||
root.param.value = text
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.param.value === undefined) {
|
||||
root.param.value = text;
|
||||
}
|
||||
}
|
||||
placeholderText: root.placeholderText
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: comboBoxComponent
|
||||
|
||||
ComboBox {
|
||||
id: control
|
||||
Layout.fillWidth: true
|
||||
model: root.paramType.allowedValues
|
||||
displayText: currentText + ( root.paramType.unit != Types.UnitNone ? " " + Types.toUiUnit(root.paramType.unit) : "")
|
||||
displayText: {
|
||||
if (currentIndex < 0 || currentIndex >= root.paramType.allowedValues.length) {
|
||||
return "";
|
||||
}
|
||||
return Types.toUiValue(root.paramType.allowedValues[currentIndex], root.paramType.unit)
|
||||
+ (root.paramType.unit !== Types.UnitNone ? " " + Types.toUiUnit(root.paramType.unit) : "");
|
||||
}
|
||||
currentIndex: root.paramType.allowedValues.indexOf(root.param.value !== undefined ? root.param.value : root.paramType.defaultValue)
|
||||
delegate: ItemDelegate {
|
||||
width: control.width
|
||||
text: Types.toUiValue(modelData, root.paramType.unit) + ( root.paramType.unit != Types.UnitNone ? " " + Types.toUiUnit(root.paramType.unit) : "")
|
||||
text: Types.toUiValue(modelData, root.paramType.unit) + ( root.paramType.unit !== Types.UnitNone ? " " + Types.toUiUnit(root.paramType.unit) : "")
|
||||
highlighted: control.highlightedIndex === index
|
||||
}
|
||||
onActivated: (index) => {
|
||||
root.param.value = root.paramType.allowedValues[index]
|
||||
}
|
||||
onActivated: (index) => { root.param.value = root.paramType.allowedValues[index] }
|
||||
Component.onCompleted: {
|
||||
if (root.value === undefined) {
|
||||
root.value = model[0]
|
||||
if (root.param.value === undefined) {
|
||||
root.param.value = model[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -296,6 +309,7 @@ ItemDelegate {
|
||||
|
||||
Component {
|
||||
id: colorPickerComponent
|
||||
|
||||
ColorPickerPre510 {
|
||||
id: colorPicker
|
||||
implicitHeight: 200
|
||||
@ -333,6 +347,7 @@ ItemDelegate {
|
||||
|
||||
Component {
|
||||
id: colorTemperaturePickerComponent
|
||||
|
||||
ColorPickerCt {
|
||||
id: colorPickerCt
|
||||
implicitHeight: 50
|
||||
@ -340,7 +355,7 @@ ItemDelegate {
|
||||
maxCt: root.paramType.maxValue
|
||||
ct: root.param.value !== undefined
|
||||
? root.param.value
|
||||
: root.paramType.defaultValue
|
||||
: root.paramType.defaultValue !== undefined
|
||||
? root.paramType.defaultValue
|
||||
: root.paramType.minValue
|
||||
|
||||
@ -359,6 +374,7 @@ ItemDelegate {
|
||||
|
||||
Component {
|
||||
id: colorPreviewComponent
|
||||
|
||||
Rectangle {
|
||||
implicitHeight: app.mediumFont
|
||||
implicitWidth: implicitHeight
|
||||
|
||||
@ -63,7 +63,7 @@ ItemDelegate {
|
||||
id: loader
|
||||
Layout.fillWidth: !parent.labelFillsWidth
|
||||
sourceComponent: {
|
||||
print("Loading ParamDelegate");
|
||||
print("Loading StateDelegate");
|
||||
print("Writable:", root.writable, "type:", root.stateType.type, "min:", root.stateType.minValue, "max:", root.stateType.maxValue, "value:", root.param.value)
|
||||
if (!root.writable) {
|
||||
return stringComponent;
|
||||
@ -222,7 +222,7 @@ ItemDelegate {
|
||||
width: 150
|
||||
onValueModified: root.param.value = value
|
||||
|
||||
floatingPoint: root.stateType.type.toLowerCase() == "double"
|
||||
floatingPoint: root.stateType.type.toLowerCase() === "double"
|
||||
|
||||
Component.onCompleted: {
|
||||
print("from:", from, "min", root.stateType.minValue)
|
||||
@ -264,11 +264,11 @@ ItemDelegate {
|
||||
id: control
|
||||
Layout.fillWidth: true
|
||||
model: root.stateType.allowedValues
|
||||
displayText: currentText + ( root.stateType.unit != Types.UnitNone ? " " + Types.toUiUnit(root.stateType.unit) : "")
|
||||
displayText: currentText + ( root.stateType.unit !== Types.UnitNone ? " " + Types.toUiUnit(root.stateType.unit) : "")
|
||||
currentIndex: root.stateType.allowedValues.indexOf(root.param.value !== undefined ? root.param.value : root.stateType.defaultValue)
|
||||
delegate: ItemDelegate {
|
||||
width: control.width
|
||||
text: Types.toUiValue(modelData, root.stateType.unit) + ( root.stateType.unit != Types.UnitNone ? " " + Types.toUiUnit(root.stateType.unit) : "")
|
||||
text: Types.toUiValue(modelData, root.stateType.unit) + ( root.stateType.unit !== Types.UnitNone ? " " + Types.toUiUnit(root.stateType.unit) : "")
|
||||
highlighted: control.highlightedIndex === index
|
||||
}
|
||||
onActivated: (index) => {
|
||||
|
||||
@ -36,7 +36,7 @@ SpinBox {
|
||||
stepSize: Math.min(10, (to - from) / 10)
|
||||
property var unit: Types.UnitNone
|
||||
editable: true
|
||||
onValueModified: {
|
||||
onValueModified: (value) => {
|
||||
changed(value)
|
||||
}
|
||||
textFromValue: function(value) {
|
||||
|
||||
@ -172,17 +172,9 @@ Page {
|
||||
var maxValue = stateDelegate.stateType.maxValue !== undefined
|
||||
? stateDelegate.stateType.maxValue
|
||||
: 2000000000;
|
||||
print("pushing delegate for", stateDelegate.stateType.name, "from:", minValue, "to:", maxValue, "possible:", stateDelegate.stateType.possibleValuesDisplayNames)
|
||||
stateDelegateLoader.setSource("../delegates/statedelegates/" + sourceComp,
|
||||
{
|
||||
value: root.thing.states.getState(stateType.id).value,
|
||||
possibleValues: stateDelegate.stateType.possibleValues,
|
||||
possibleValuesDisplayNames: stateDelegate.stateType.possibleValuesDisplayNames,
|
||||
from: minValue,
|
||||
to: maxValue,
|
||||
unit: stateDelegate.stateType.unit,
|
||||
writable: false,
|
||||
stateType: stateDelegate.stateType
|
||||
console.log("pushing delegate for", stateDelegate.stateType.name, "from:", minValue, "to:", maxValue, "possible:", stateDelegate.stateType.possibleValuesDisplayNames)
|
||||
stateDelegateLoader.setSource("../delegates/statedelegates/" + sourceComp, {
|
||||
value: root.thing.states.getState(stateType.id).value
|
||||
})
|
||||
|
||||
}
|
||||
@ -231,6 +223,16 @@ Page {
|
||||
property: "unit"
|
||||
value: stateDelegate.stateType.unit
|
||||
}
|
||||
Binding {
|
||||
target: stateDelegateLoader.item.hasOwnProperty("writable") ? stateDelegateLoader.item : null
|
||||
property: "writable"
|
||||
value: false
|
||||
}
|
||||
Binding {
|
||||
target: stateDelegateLoader.item.hasOwnProperty("stateType") ? stateDelegateLoader.item : null
|
||||
property: "stateType"
|
||||
value: stateDelegate.stateType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ NymeaSwipeDelegate {
|
||||
operatorString = " = ";
|
||||
break;
|
||||
case ParamDescriptor.ValueOperatorNotEquals:
|
||||
operatorString = " != ";
|
||||
operatorString = " !== ";
|
||||
break;
|
||||
case ParamDescriptor.ValueOperatorGreater:
|
||||
operatorString = " > ";
|
||||
|
||||
@ -84,7 +84,7 @@ DashboardDelegateBase {
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
visible: root.stateType && root.stateType.type.toLowerCase() != "bool"
|
||||
visible: root.stateType && root.stateType.type.toLowerCase() !== "bool"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font: Style.largeFont
|
||||
elide: Text.ElideRight
|
||||
|
||||
@ -26,6 +26,7 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Nymea
|
||||
|
||||
import "../components"
|
||||
@ -48,12 +49,12 @@ Page {
|
||||
|
||||
Connections {
|
||||
target: engine.systemController
|
||||
onEnableRepositoryFinished: {
|
||||
if (!success) {
|
||||
var popup = errorDialogComponent.createObject(app, {errorCode: qsTr("Failure adding repository.") })
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
onEnableRepositoryFinished: (id, success) => {
|
||||
if (!success) {
|
||||
var popup = errorDialogComponent.createObject(app, {errorCode: qsTr("Failure adding repository.") })
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PackagesFilterModel {
|
||||
@ -62,151 +63,350 @@ Page {
|
||||
updatesOnly: true
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
Loader {
|
||||
id: updateTypeLoader
|
||||
anchors.fill: parent
|
||||
sourceComponent: engine.systemController.updateManagementType === SystemController.UpdateTypeSystem ?
|
||||
updateSystemComponent :
|
||||
updatePackageManagerComponent
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
visible: updatesModel.count === 0
|
||||
}
|
||||
|
||||
Component {
|
||||
id: updateSystemComponent
|
||||
|
||||
ColumnLayout {
|
||||
id: systemUpdateContentColumn
|
||||
|
||||
property Package systemPackage: engine.systemController.packages.get(0)
|
||||
property bool changelogAvailable: systemUpdateContentColumn.systemPackage.changelog.length !== 0
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
visible: updatesModel.count === 0
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
spacing: app.margins * 2
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: Style.iconSize * 4
|
||||
Layout.preferredWidth: height
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
name: "qrc:/icons/system-update.svg"
|
||||
color: Style.accentColor
|
||||
RotationAnimation on rotation {
|
||||
from: 0; to: 360
|
||||
duration: 2000
|
||||
running: engine.systemController.updateManagementBusy
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Style.margins
|
||||
Layout.rightMargin: Style.margins
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: engine.systemController.updateManagementBusy ? qsTr("Checking for updates...") : qsTr("Your system is up to date.")
|
||||
}
|
||||
|
||||
Label {
|
||||
id: versionLabel
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Version:") + " " + systemUpdateContentColumn.systemPackage.installedVersion
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
PlatformHelper.toClipBoard(versionLabel.text);
|
||||
ToolTip.show(qsTr("Version copied to clipboard"), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Style.margins
|
||||
Layout.rightMargin: Style.margins
|
||||
|
||||
text: qsTr("Check for updates")
|
||||
enabled: !engine.systemController.updateManagementBusy
|
||||
onClicked: {
|
||||
engine.systemController.checkForUpdates()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
spacing: app.margins * 2
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
visible: updatesModel.count > 0
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: Style.iconSize * 4
|
||||
Layout.preferredWidth: height
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
name: "qrc:/icons/system-update.svg"
|
||||
color: Style.accentColor
|
||||
RotationAnimation on rotation {
|
||||
from: 0; to: 360
|
||||
duration: 2000
|
||||
running: engine.systemController.updateManagementBusy
|
||||
loops: Animation.Infinite
|
||||
RowLayout {
|
||||
Layout.margins: app.margins
|
||||
spacing: app.margins
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: Style.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
name: "qrc:/icons/system-update.svg"
|
||||
color: Style.accentColor
|
||||
RotationAnimation on rotation {
|
||||
from: 0; to: 360
|
||||
duration: 2000
|
||||
running: engine.systemController.updateManagementBusy
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.systemController.updateManagementBusy ? qsTr("Checking for updates...") : qsTr("%1 update available").arg(Configuration.systemName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Check again")
|
||||
enabled: !engine.systemController.updateManagementBusy
|
||||
onClicked: engine.systemController.checkForUpdates()
|
||||
}
|
||||
|
||||
NymeaSwipeDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Installed version:")
|
||||
subText: systemUpdateContentColumn.systemPackage.installedVersion
|
||||
progressive: false
|
||||
}
|
||||
|
||||
NymeaSwipeDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Candidate version:")
|
||||
subText: systemUpdateContentColumn.systemPackage.candidateVersion
|
||||
visible: systemUpdateContentColumn.systemPackage.updateAvailable || systemUpdateContentColumn.systemPackage.installedVersion.length === 0
|
||||
progressive: false
|
||||
}
|
||||
|
||||
ThinDivider {
|
||||
visible: changelogAvailable
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.leftMargin: Style.margins
|
||||
Layout.rightMargin: Style.margins
|
||||
|
||||
visible: changelogAvailable
|
||||
text: qsTr("Update changelog:")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Style.margins
|
||||
Layout.rightMargin: Style.margins
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
text: engine.systemController.updateManagementBusy ? qsTr("Checking for updates...") : qsTr("Your system is up to date.")
|
||||
visible: changelogAvailable
|
||||
text: systemPackage.changelog
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Check for updates")
|
||||
Layout.fillWidth: true
|
||||
//Layout.leftMargin: Style.margins
|
||||
//Layout.rightMargin: Style.margins
|
||||
|
||||
text: qsTr("Update")
|
||||
visible: updatesModel.count > 0
|
||||
enabled: !engine.systemController.updateManagementBusy
|
||||
onClicked: {
|
||||
engine.systemController.checkForUpdates()
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/NymeaDialog.qml"));
|
||||
var text = qsTr("This will start a system update. Note that the update might take several minutes and your %1 system might not be functioning properly during this time and restart during the process.\nDo you want to proceed?").arg(Configuration.systemName)
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "qrc:/icons/system-update.svg",
|
||||
title: qsTr("System update"),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
engine.systemController.updatePackages()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: updatePackageManagerComponent
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
visible: updatesModel.count > 0
|
||||
id: contentColumn
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
Layout.margins: app.margins
|
||||
spacing: app.margins
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Style.margins
|
||||
Layout.rightMargin: Style.margins
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: Style.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
name: "qrc:/icons/system-update.svg"
|
||||
color: Style.accentColor
|
||||
RotationAnimation on rotation {
|
||||
from: 0; to: 360
|
||||
duration: 2000
|
||||
running: engine.systemController.updateManagementBusy
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
}
|
||||
visible: updatesModel.count === 0
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
spacing: app.margins * 2
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: Style.iconSize * 4
|
||||
Layout.preferredWidth: height
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
name: "qrc:/icons/system-update.svg"
|
||||
color: Style.accentColor
|
||||
RotationAnimation on rotation {
|
||||
from: 0; to: 360
|
||||
duration: 2000
|
||||
running: engine.systemController.updateManagementBusy
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: engine.systemController.updateManagementBusy ? qsTr("Checking for updates...") : qsTr("%n update(s) available", "", updatesModel.count)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
text: engine.systemController.updateManagementBusy ? qsTr("Checking for updates...") : qsTr("Your system is up to date.")
|
||||
}
|
||||
GridLayout {
|
||||
columns: width > 250 ? 2 : 1
|
||||
Button {
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Check for updates")
|
||||
enabled: !engine.systemController.updateManagementBusy
|
||||
onClicked: engine.systemController.checkForUpdates()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
visible: updatesModel.count > 0
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.margins
|
||||
Layout.leftMargin: Style.margins
|
||||
Layout.rightMargin: Style.margins
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: Style.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
name: "qrc:/icons/system-update.svg"
|
||||
color: Style.accentColor
|
||||
RotationAnimation on rotation {
|
||||
from: 0; to: 360
|
||||
duration: 2000
|
||||
running: engine.systemController.updateManagementBusy
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Check again")
|
||||
enabled: !engine.systemController.updateManagementBusy
|
||||
onClicked: {
|
||||
engine.systemController.checkForUpdates()
|
||||
text: engine.systemController.updateManagementBusy ? qsTr("Checking for updates...") : qsTr("%n update(s) available", "", updatesModel.count)
|
||||
}
|
||||
GridLayout {
|
||||
columns: width > 250 ? 2 : 1
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Check again")
|
||||
enabled: !engine.systemController.updateManagementBusy
|
||||
onClicked: {
|
||||
engine.systemController.checkForUpdates()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Update all")
|
||||
visible: updatesModel.count > 0
|
||||
enabled: !engine.systemController.updateManagementBusy
|
||||
onClicked: {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/NymeaDialog.qml"));
|
||||
var text = qsTr("This will start a system update. Note that the update might take several minutes and your %1 system might not be functioning properly during this time and restart during the process.\nDo you want to proceed?").arg(Configuration.systemName)
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "qrc:/icons/system-update.svg",
|
||||
title: qsTr("System update"),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
engine.systemController.updatePackages()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Update all")
|
||||
visible: updatesModel.count > 0
|
||||
enabled: !engine.systemController.updateManagementBusy
|
||||
onClicked: {
|
||||
var dialog = Qt.createComponent(Qt.resolvedUrl("../components/NymeaDialog.qml"));
|
||||
var text = qsTr("This will start a system update. Note that the update might take several minutes and your %1 system might not be functioning properly during this time and restart during the process.\nDo you want to proceed?").arg(Configuration.systemName)
|
||||
var popup = dialog.createObject(app,
|
||||
{
|
||||
headerIcon: "qrc:/icons/system-update.svg",
|
||||
title: qsTr("System update"),
|
||||
text: text,
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
});
|
||||
popup.open();
|
||||
popup.accepted.connect(function() {
|
||||
engine.systemController.updatePackages()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: count > 0
|
||||
model: updatesModel
|
||||
clip: true
|
||||
delegate: NymeaSwipeDelegate {
|
||||
width: parent.width
|
||||
text: model.displayName
|
||||
subText: model.candidateVersion
|
||||
prominentSubText: false
|
||||
iconName: model.updateAvailable
|
||||
? Qt.resolvedUrl("qrc:/icons/system-update.svg")
|
||||
: Qt.resolvedUrl("qrc:/icons/view-" + (model.installedVersion.length > 0 ? "expand" : "collapse") + ".svg")
|
||||
iconColor: model.updateAvailable
|
||||
? "green"
|
||||
: model.installedVersion.length > 0 ? "blue" : Style.iconColor
|
||||
onClicked: {
|
||||
pageStack.push(Qt.resolvedUrl("PackageDetailsPage.qml"), {pkg: updatesModel.get(index)})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
ListView {
|
||||
NymeaSwipeDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: count > 0
|
||||
model: updatesModel
|
||||
clip: true
|
||||
delegate: NymeaSwipeDelegate {
|
||||
width: parent.width
|
||||
text: model.displayName
|
||||
subText: model.candidateVersion
|
||||
prominentSubText: false
|
||||
iconName: model.updateAvailable
|
||||
? Qt.resolvedUrl("qrc:/icons/system-update.svg")
|
||||
: Qt.resolvedUrl("qrc:/icons/view-" + (model.installedVersion.length > 0 ? "expand" : "collapse") + ".svg")
|
||||
iconColor: model.updateAvailable
|
||||
? "green"
|
||||
: model.installedVersion.length > 0 ? "blue" : Style.iconColor
|
||||
onClicked: {
|
||||
pageStack.push(Qt.resolvedUrl("PackageDetailsPage.qml"), {pkg: updatesModel.get(index)})
|
||||
}
|
||||
text: qsTr("Install or remove software")
|
||||
onClicked: {
|
||||
pageStack.push("PackageListPage.qml")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
NymeaSwipeDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Install or remove software")
|
||||
onClicked: {
|
||||
pageStack.push("PackageListPage.qml")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
@ -253,8 +453,7 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
UpdateRunningOverlay {
|
||||
}
|
||||
UpdateRunningOverlay { }
|
||||
|
||||
Component {
|
||||
id: errorDialogComponent
|
||||
|
||||
@ -59,20 +59,27 @@ SettingsPageBase {
|
||||
width: implicitWidth + app.margins
|
||||
x: parent.width - width
|
||||
|
||||
Component.onCompleted: {
|
||||
deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Rename"), iconSource: "qrc:/icons/edit.svg", functionName: "renameThing"}))
|
||||
// FIXME: This isn't entirely correct... we should have a way to know if a particular thing is in fact autocreated
|
||||
// This check might be wrong for thingClasses with multiple create methods...
|
||||
if (!root.thing.isChild || root.thing.thingClass.createMethods.indexOf("CreateMethodAuto") < 0) {
|
||||
deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Delete"), iconSource: "qrc:/icons/delete.svg", functionName: "deleteThing"}))
|
||||
}
|
||||
// FIXME: This isn't entirely correct... we should have a way to know if a particular thing is in fact autocreated
|
||||
// This check might be wrong for thingClasses with multiple create methods...
|
||||
if (!root.thing.isChild || root.thingClass.createMethods.indexOf("CreateMethodAuto") < 0) {
|
||||
deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Reconfigure"), iconSource: "qrc:/icons/configure.svg", functionName: "reconfigureThing"}))
|
||||
function addMenuEntry(properties) {
|
||||
var item = menuEntryComponent.createObject(null, properties)
|
||||
if (!item) {
|
||||
console.warn("Failed to create menu entry", properties && properties.text)
|
||||
return
|
||||
}
|
||||
|
||||
deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Details"), iconSource: "qrc:/icons/info.svg", functionName: "thingDetails"}))
|
||||
deviceMenu.addItem(item)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
addMenuEntry({text: qsTr("Rename"), iconSource: "qrc:/icons/edit.svg", functionName: "renameThing"})
|
||||
|
||||
// FIXME: This isn't entirely correct... we should have a way to know if a particular thing is in fact autocreated
|
||||
// This check might be wrong for thingClasses with multiple create methods...
|
||||
if (!root.thing.isChild && !root.thing.thingClass.isAutoCreated()) {
|
||||
addMenuEntry({text: qsTr("Delete"), iconSource: "qrc:/icons/delete.svg", functionName: "deleteThing"})
|
||||
addMenuEntry({text: qsTr("Reconfigure"), iconSource: "qrc:/icons/configure.svg", functionName: "reconfigureThing"})
|
||||
}
|
||||
|
||||
addMenuEntry({text: qsTr("Details"), iconSource: "qrc:/icons/info.svg", functionName: "thingDetails"})
|
||||
}
|
||||
|
||||
function renameThing() {
|
||||
@ -106,8 +113,8 @@ SettingsPageBase {
|
||||
|
||||
Connections {
|
||||
target: engine.thingManager
|
||||
onRemoveThingReply: {
|
||||
if (d.pendingCommand != commandId) {
|
||||
function onRemoveThingReply(commandId, thingError, ruleIds) {
|
||||
if (d.pendingCommand !== commandId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -140,6 +147,7 @@ SettingsPageBase {
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
}
|
||||
|
||||
NymeaItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: root.thing.thingClass.displayName
|
||||
@ -191,6 +199,7 @@ SettingsPageBase {
|
||||
|
||||
StateTypesProxy {
|
||||
id: ioModel
|
||||
|
||||
stateTypes: root.thing.thingClass.stateTypes
|
||||
digitalInputs: true
|
||||
digitalOutputs: true
|
||||
@ -200,13 +209,14 @@ SettingsPageBase {
|
||||
|
||||
Repeater {
|
||||
model: ioModel
|
||||
|
||||
delegate: NymeaSwipeDelegate {
|
||||
Layout.fillWidth: true
|
||||
|
||||
iconName: "qrc:/icons/io-connections.svg"
|
||||
text: model.displayName
|
||||
subText: {
|
||||
if (ioStateType.ioType == Types.IOTypeDigitalInput || ioStateType.ioType == Types.IOTypeAnalogInput) {
|
||||
if (ioStateType.ioType === Types.IOTypeDigitalInput || ioStateType.ioType === Types.IOTypeAnalogInput) {
|
||||
if (inputConnectionWatcher.ioConnection) {
|
||||
return "%1: %2".arg(inputConnectionWatcher.outputThing.name).arg(inputConnectionWatcher.outputStateType.displayName)
|
||||
}
|
||||
@ -223,14 +233,17 @@ SettingsPageBase {
|
||||
|
||||
IOInputConnectionWatcher {
|
||||
id: inputConnectionWatcher
|
||||
|
||||
ioConnections: engine.thingManager.ioConnections
|
||||
inputThingId: root.thing.id
|
||||
inputStateTypeId: ioStateType.id
|
||||
property Thing outputThing: ioConnection ? engine.thingManager.things.getThing(ioConnection.outputThingId) : null
|
||||
property StateType outputStateType: ioConnection ? outputThing.thingClass.stateTypes.getStateType(ioConnection.outputStateTypeId) : null
|
||||
}
|
||||
|
||||
IOOutputConnectionWatcher {
|
||||
id: outputConnectionWatcher
|
||||
|
||||
ioConnections: engine.thingManager.ioConnections
|
||||
outputThingId: root.thing.id
|
||||
outputStateTypeId: ioStateType.id
|
||||
@ -253,6 +266,7 @@ SettingsPageBase {
|
||||
|
||||
Repeater {
|
||||
id: settingsRepeater
|
||||
|
||||
model: root.thing.settings
|
||||
delegate: ParamDelegate {
|
||||
Layout.fillWidth: true
|
||||
@ -360,7 +374,7 @@ SettingsPageBase {
|
||||
property IOInputConnectionWatcher inputWatcher: null
|
||||
property IOOutputConnectionWatcher outputWatcher: null
|
||||
|
||||
readonly property bool isInput: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput
|
||||
readonly property bool isInput: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
@ -377,16 +391,16 @@ SettingsPageBase {
|
||||
model: ThingsProxy {
|
||||
id: connectableIODevices
|
||||
engine: _engine
|
||||
showDigitalInputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalOutput
|
||||
showDigitalOutputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput
|
||||
showAnalogInputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogOutput
|
||||
showAnalogOutputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput
|
||||
showDigitalInputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalOutput
|
||||
showDigitalOutputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput
|
||||
showAnalogInputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogOutput
|
||||
showAnalogOutputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput
|
||||
}
|
||||
textRole: "name"
|
||||
Layout.fillWidth: true
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < connectableIODevices.count; i++) {
|
||||
if (ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput) {
|
||||
if (ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput) {
|
||||
if (connectableIODevices.get(i).id === ioConnectionDialog.inputWatcher.ioConnection.outputThingId) {
|
||||
ioThingComboBox.currentIndex = i;
|
||||
break;
|
||||
@ -407,10 +421,10 @@ SettingsPageBase {
|
||||
model: StateTypesProxy {
|
||||
id: connectableStateTypes
|
||||
stateTypes: connectableIODevices.get(ioThingComboBox.currentIndex).thingClass.stateTypes
|
||||
digitalInputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalOutput
|
||||
digitalOutputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput
|
||||
analogInputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogOutput
|
||||
analogOutputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput
|
||||
digitalInputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalOutput
|
||||
digitalOutputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput
|
||||
analogInputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogOutput
|
||||
analogOutputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput
|
||||
}
|
||||
textRole: "displayName"
|
||||
Layout.fillWidth: true
|
||||
@ -418,7 +432,7 @@ SettingsPageBase {
|
||||
// print("loading for:", ioConnectionDialog.inputWatcher.ioConnection.outputStateTypeId)
|
||||
for (var i = 0; i < connectableStateTypes.count; i++) {
|
||||
print("checking:", connectableStateTypes.get(i).id)
|
||||
if (ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput) {
|
||||
if (ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput) {
|
||||
if (connectableStateTypes.get(i).id === ioConnectionDialog.inputWatcher.ioConnection.outputStateTypeId) {
|
||||
ioStateComboBox.currentIndex = i;
|
||||
break;
|
||||
@ -442,6 +456,7 @@ SettingsPageBase {
|
||||
|
||||
CheckBox {
|
||||
id: invertCheckBox
|
||||
|
||||
checked: ioConnectionDialog.isInput ? ioConnectionDialog.inputWatcher.ioConnection.inverted : ioConnectionDialog.outputWatcher.ioConnection.inverted
|
||||
}
|
||||
}
|
||||
@ -453,7 +468,7 @@ SettingsPageBase {
|
||||
|
||||
columns: width > (cancelButton.implicitWidth + disconnectButton.implicitWidth + connectButton.implicitWidth)
|
||||
? 4 : 1
|
||||
layoutDirection: columns == 1 ? Qt.RightToLeft : Qt.LeftToRight
|
||||
layoutDirection: columns === 1 ? Qt.RightToLeft : Qt.LeftToRight
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
@ -473,8 +488,8 @@ SettingsPageBase {
|
||||
Layout.fillWidth: buttonGrid.columns === 1
|
||||
|
||||
onClicked: {
|
||||
if (ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput
|
||||
|| ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput) {
|
||||
if (ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput
|
||||
|| ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput) {
|
||||
engine.thingManager.disconnectIO(ioConnectionDialog.inputWatcher.ioConnection.id);
|
||||
} else {
|
||||
engine.thingManager.disconnectIO(ioConnectionDialog.outputWatcher.ioConnection.id);
|
||||
@ -495,8 +510,8 @@ SettingsPageBase {
|
||||
var inputStateTypeId;
|
||||
var outputThingId;
|
||||
var outputStateTypeId;
|
||||
if (ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput
|
||||
|| ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput) {
|
||||
if (ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput
|
||||
|| ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput) {
|
||||
inputThingId = root.thing.id;
|
||||
inputStateTypeId = ioConnectionDialog.ioStateType.id;
|
||||
outputThingId = connectableIODevices.get(ioThingComboBox.currentIndex).id;
|
||||
|
||||
@ -156,13 +156,12 @@ Page {
|
||||
|
||||
Connections {
|
||||
target: engine.thingManager
|
||||
onPairThingReply: {
|
||||
function onPairThingReply(commandId, thingError, pairingTransactionId, setupMethod, displayMessage, oAuthUrl) {
|
||||
busyOverlay.shown = false
|
||||
if (thingError !== Thing.ThingErrorNoError) {
|
||||
busyOverlay.shown = false;
|
||||
internalPageStack.push(resultsPage, {thingError: thingError, message: displayMessage});
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
d.pairingTransactionId = pairingTransactionId;
|
||||
@ -179,17 +178,21 @@ Page {
|
||||
break;
|
||||
default:
|
||||
print("Setup method reply not handled:", setupMethod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
onConfirmPairingReply: {
|
||||
|
||||
function onConfirmPairingReply(commandId, thingError, thingId, displayMessage) {
|
||||
busyOverlay.shown = false
|
||||
internalPageStack.push(resultsPage, {thingError: thingError, thingId: thingId, message: displayMessage})
|
||||
}
|
||||
onAddThingReply: {
|
||||
|
||||
function onAddThingReply(commandId, thingError, thingId, displayMessage) {
|
||||
busyOverlay.shown = false;
|
||||
internalPageStack.push(resultsPage, {thingError: thingError, thingId: thingId, message: displayMessage})
|
||||
}
|
||||
onReconfigureThingReply: {
|
||||
|
||||
function onReconfigureThingReply(commandId, thingError, displayMessage) {
|
||||
busyOverlay.shown = false;
|
||||
internalPageStack.push(resultsPage, {thingError: thingError, thingId: root.thing.id, message: displayMessage})
|
||||
}
|
||||
@ -372,7 +375,7 @@ Page {
|
||||
Component.onCompleted: {
|
||||
if (root.thingClass.id.toString().match(/\{?f0dd4c03-0aca-42cc-8f34-9902457b05de\}?/)) {
|
||||
console.warn("checking Notification permission!")
|
||||
if (PlatformPermissions.notificationsPermission != PlatformPermissions.PermissionStatusGranted) {
|
||||
if (PlatformPermissions.notificationsPermission !== PlatformPermissions.PermissionStatusGranted) {
|
||||
console.warn("Notification permission missing!")
|
||||
PlatformPermissions.requestPermission(PlatformPermissions.PermissionNotifications)
|
||||
}
|
||||
@ -460,6 +463,11 @@ Page {
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.margins
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
Layout.fillWidth: true
|
||||
@ -468,6 +476,11 @@ Page {
|
||||
visible: pairingPage.setupMethod === "SetupMethodUserAndPassword"
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.margins
|
||||
}
|
||||
|
||||
PasswordTextField {
|
||||
id: pinTextField
|
||||
Layout.fillWidth: true
|
||||
@ -476,7 +489,6 @@ Page {
|
||||
signup: false
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
@ -549,8 +561,9 @@ Page {
|
||||
|
||||
WebView {
|
||||
id: oAuthWebView
|
||||
anchors.fill: parent
|
||||
|
||||
url: oAuthPage.oAuthUrl
|
||||
anchors.fill: parent
|
||||
|
||||
function finishProcess(url) {
|
||||
print("Confirm pairing")
|
||||
@ -559,7 +572,7 @@ Page {
|
||||
oAuthWebView.visible = false
|
||||
}
|
||||
|
||||
onUrlChanged: {
|
||||
onUrlChanged: (url) => {
|
||||
print("OAUTH URL changed", url)
|
||||
if (url.toString().indexOf("https://127.0.0.1") == 0) {
|
||||
print("Redirect URL detected!")
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
1.11.3
|
||||
700
|
||||
1.11.5
|
||||
709
|
||||
|
||||
Reference in New Issue
Block a user