/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. * This project including source code and documentation is protected by * copyright law, and remains the property of nymea GmbH. All rights, including * reproduction, publication, editing and translation, are reserved. The use of * this project is subject to the terms of a license agreement to be concluded * with nymea GmbH in accordance with the terms of use of nymea GmbH, available * under https://nymea.io/license * * GNU General Public License Usage * Alternatively, this project may be redistributed and/or modified under the * terms of the GNU General Public License as published by the Free Software * Foundation, GNU version 3. This project is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with * this project. If not, see . * * For any further details and any questions please contact us under * contact@nymea.io or see our FAQ/Licensing Information on * https://nymea.io/license/faq * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "systemcontroller.h" #include "types/package.h" #include "types/repository.h" #include "types/packages.h" #include "types/repositories.h" #include #include #include "logging.h" NYMEA_LOGGING_CATEGORY(dcSystemController, "SystemController") 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); startTimer(1000, Qt::VeryCoarseTimer); } void SystemController::init() { m_packages->clear(); m_repositories->clear(); if (m_jsonRpcClient->ensureServerVersion("2.0")) { m_jsonRpcClient->sendCommand("System.GetCapabilities", this, "getCapabilitiesResponse"); } else { m_powerManagementAvailable = false; m_updateManagementAvailable = false; m_timeManagementAvailable = false; } if (m_jsonRpcClient->ensureServerVersion("5.5")) { m_jsonRpcClient->sendCommand("System.GetSystemInfo", this, "getSystemInfoResponse"); } else { m_deviceSerialNumber.clear(); } } bool SystemController::powerManagementAvailable() const { return m_powerManagementAvailable; } bool SystemController::updateManagementAvailable() const { return m_updateManagementAvailable; } int SystemController::restart() { return m_jsonRpcClient->sendCommand("System.Restart", this, "restartResponse"); } int SystemController::reboot() { return m_jsonRpcClient->sendCommand("System.Reboot", this, "rebootResponse"); } int SystemController::shutdown() { return m_jsonRpcClient->sendCommand("System.Shutdown", this, "shutdownResponse"); } bool SystemController::updateManagementBusy() const { return m_updateManagementBusy; } bool SystemController::updateRunning() const { return m_updateRunning; } void SystemController::checkForUpdates() { m_jsonRpcClient->sendCommand("System.CheckForUpdates"); } Packages *SystemController::packages() const { return m_packages; } void SystemController::updatePackages(const QString packageId) { QVariantMap params; if (!packageId.isEmpty()) { params.insert("packageIds", QStringList() << packageId); } m_jsonRpcClient->sendCommand("System.UpdatePackages", params); } void SystemController::removePackages(const QString packageId) { QVariantMap params; if (!packageId.isEmpty()) { params.insert("packageIds", QStringList() << packageId); } m_jsonRpcClient->sendCommand("System.RemovePackages", params, this, "removePackageResponse"); } Repositories *SystemController::repositories() const { return m_repositories; } int SystemController::enableRepository(const QString &id, bool enabled) { QVariantMap params; params.insert("repositoryId", id); params.insert("enabled", enabled); return m_jsonRpcClient->sendCommand("System.EnableRepository", params, this, "enableRepositoryResponse"); } bool SystemController::timeManagementAvailable() const { return m_timeManagementAvailable; } QDateTime SystemController::serverTime() const { return m_serverTime; } int SystemController::setServerTime(const QDateTime &serverTime) { QVariantMap params; params.insert("automaticTime", false); params.insert("time", serverTime.toSecsSinceEpoch()); params.insert("timeZone", serverTime.timeZone().id()); return m_jsonRpcClient->sendCommand("System.SetTime", params, this, "setTimeResponse"); } QStringList SystemController::timeZones() const { QStringList ret; foreach (const QByteArray &tzId, QTimeZone::availableTimeZoneIds()) { ret << tzId; } return ret; } 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_serverTimeZone; } int SystemController::setServerTimeZone(const QString &serverTimeZone) { QVariantMap params; params.insert("timeZone", serverTimeZone); return m_jsonRpcClient->sendCommand("System.SetTime", params, this, "setTimeResponse"); } bool SystemController::automaticTimeAvailable() const { return m_automaticTimeAvailable; } bool SystemController::automaticTime() const { return m_automaticTime; } int SystemController::setAutomaticTime(bool automaticTime) { QVariantMap params; params.insert("automaticTime", automaticTime); return m_jsonRpcClient->sendCommand("System.SetTime", params, this, "setTimeResponse"); } QString SystemController::deviceSerialNumber() const { return m_deviceSerialNumber; } void SystemController::getCapabilitiesResponse(int /*commandId*/, const QVariantMap &data) { m_powerManagementAvailable = data.value("powerManagement").toBool(); emit powerManagementAvailableChanged(); m_updateManagementAvailable = data.value("updateManagement").toBool(); emit updateManagementAvailableChanged(); m_timeManagementAvailable = data.value("timeManagement").toBool(); emit timeManagementAvailableChanged(); if (m_updateManagementAvailable) { m_jsonRpcClient->sendCommand("System.GetUpdateStatus", this, "getUpdateStatusResponse"); m_jsonRpcClient->sendCommand("System.GetPackages", this, "getPackagesResponse"); m_jsonRpcClient->sendCommand("System.GetRepositories", this, "getRepositoriesResponse"); } if (m_jsonRpcClient->ensureServerVersion("4.1")) { m_jsonRpcClient->sendCommand("System.GetTime", this, "getServerTimeResponse"); } qCDebug(dcSystemController) << "nymea:core capabilities: Power management:" << m_powerManagementAvailable << "Update management:" << m_updateManagementAvailable << "Time management:" << m_timeManagementAvailable; } void SystemController::getUpdateStatusResponse(int /*commandId*/, const QVariantMap &data) { qCDebug(dcSystemController()) << "Update status:" << qUtf8Printable(QJsonDocument::fromVariant(data).toJson(QJsonDocument::Indented)); m_updateManagementBusy = data.value("busy").toBool(); m_updateRunning = data.value("updateRunning").toBool(); emit updateRunningChanged(); } void SystemController::getPackagesResponse(int commandId, const QVariantMap &data) { Q_UNUSED(commandId) 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(); Package *p = new Package(id, displayName); p->setSummary(packageVariant.toMap().value("summary").toString()); p->setInstalledVersion(packageVariant.toMap().value("installedVersion").toString()); p->setCandidateVersion(packageVariant.toMap().value("candidateVersion").toString()); p->setChangelog(packageVariant.toMap().value("changelog").toString()); p->setUpdateAvailable(packageVariant.toMap().value("updateAvailable").toBool()); p->setRollbackAvailable(packageVariant.toMap().value("rollbackAvailable").toBool()); p->setCanRemove(packageVariant.toMap().value("canRemove").toBool()); m_packages->addPackage(p); } } void SystemController::getRepositoriesResponse(int /*commandId*/, const QVariantMap &data) { foreach (const QVariant &repoVariant, data.value("repositories").toList()) { QString id = repoVariant.toMap().value("id").toString(); QString displayName = repoVariant.toMap().value("displayName").toString(); Repository *repo = new Repository(id, displayName); repo->setEnabled(repoVariant.toMap().value("enabled").toBool()); m_repositories->addRepository(repo); } } void SystemController::removePackageResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcSystemController) << "Remove result" << commandId << params; } void SystemController::enableRepositoryResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcSystemController) << "Enable repo response" << params; emit enableRepositoryFinished(commandId, params.value("success").toBool()); } void SystemController::getServerTimeResponse(int commandId, const QVariantMap ¶ms) { Q_UNUSED(commandId) // 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 // m_serverTime.setTimeZone(QTimeZone(params.value("timeZone").toString().toUtf8())); m_serverTimeZone = params.value("timeZone").toString(); emit serverTimeZoneChanged(); // Additionally there is the issue that QML Date().toString() will always show LocalTime but we want to display // the time in the servers time zone. So we'll be setting the time zone to the client one. This is effectively a // wrong/broken DateTime object and should not be used to calculate anything. m_serverTime = QDateTime::fromSecsSinceEpoch(params.value("time").toUInt()); m_serverTime = m_serverTime.toTimeZone(QTimeZone(m_serverTimeZone.toUtf8())); m_serverTime.setTimeZone(QDateTime::currentDateTime().timeZone()); emit serverTimeChanged(); m_automaticTimeAvailable = params.value("automaticTimeAvailable").toBool(); 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; } void SystemController::setTimeResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcSystemController) << "set time response" << commandId << params; } void SystemController::restartResponse(int commandId, const QVariantMap ¶ms) { bool success = params.value("success").toBool(); emit restartReply(commandId, success); } void SystemController::rebootResponse(int commandId, const QVariantMap ¶ms) { bool success = params.value("success").toBool(); emit rebootReply(commandId, success); } void SystemController::shutdownResponse(int commandId, const QVariantMap ¶ms) { bool success = params.value("success").toBool(); emit shutdownReply(commandId, success); } void SystemController::getSystemInfoResponse(int commandId, const QVariantMap ¶ms) { Q_UNUSED(commandId) m_deviceSerialNumber = params.value("deviceSerialNumber").toString(); emit deviceSerialNumberChanged(); } void SystemController::notificationReceived(const QVariantMap &data) { QString notification = data.value("notification").toString(); 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(); emit updateManagementBusyChanged(); } if (m_updateRunning != data.value("params").toMap().value("updateRunning").toBool()) { m_updateRunning = data.value("params").toMap().value("updateRunning").toBool(); emit updateRunningChanged(); } } else if (notification == "System.PackageAdded") { QVariantMap packageMap = data.value("params").toMap().value("package").toMap(); QString id = packageMap.value("id").toString(); QString displayName = packageMap.value("displayName").toString(); Package *p = new Package(id, displayName); p->setSummary(packageMap.value("summary").toString()); p->setInstalledVersion(packageMap.value("installedVersion").toString()); p->setCandidateVersion(packageMap.value("candidateVersion").toString()); p->setChangelog(packageMap.value("changelog").toString()); p->setUpdateAvailable(packageMap.value("updateAvailable").toBool()); p->setRollbackAvailable(packageMap.value("rollbackAvailable").toBool()); p->setCanRemove(packageMap.value("canRemove").toBool()); m_packages->addPackage(p); } else if (notification == "System.PackageChanged") { QVariantMap packageMap = data.value("params").toMap().value("package").toMap(); QString id = packageMap.value("id").toString(); Package *p = m_packages->getPackage(id); if (!p) { qWarning() << "Received a package update notification for a package we don't know"; return; } p->setSummary(packageMap.value("summary").toString()); p->setInstalledVersion(packageMap.value("installedVersion").toString()); p->setCandidateVersion(packageMap.value("candidateVersion").toString()); p->setChangelog(packageMap.value("changelog").toString()); p->setUpdateAvailable(packageMap.value("updateAvailable").toBool()); 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(); m_packages->removePackage(packageId); } else if (notification == "System.RepositoryAdded") { QVariantMap repoMap = data.value("params").toMap().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(); QString id = repoMap.value("id").toString(); Repository *repo = m_repositories->getRepository(id); if (!repo) { qWarning() << "Received a repository update notification for a repository we don't know"; return; } repo->setEnabled(repoMap.value("enabled").toBool()); } else if (notification == "System.RepositoryRemoved") { QString repositoryId = data.value("params").toMap().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; emit powerManagementAvailableChanged(); emit updateManagementAvailableChanged(); } else if (notification == "System.TimeConfigurationChanged") { 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 // https://bugreports.qt.io/browse/QTBUG-83438 // m_serverTime.setTimeZone(QTimeZone(data.value("params").toMap().value("timeZone").toByteArray())); m_serverTimeZone = data.value("params").toMap().value("timeZone").toString(); emit serverTimeZoneChanged(); // Additionally there is the issue that QML Date().toString() will always show LocalTime but we want to display // the time in the servers time zone. So we'll be setting the time zone to the client one. This is effectively a // wrong/broken DateTime object and should not be used to calculate anything. m_serverTime = QDateTime::fromSecsSinceEpoch(data.value("params").toMap().value("time").toUInt()); m_serverTime = m_serverTime.toTimeZone(QTimeZone(m_serverTimeZone.toUtf8())); m_serverTime.setTimeZone(QDateTime::currentDateTime().timeZone()); emit serverTimeChanged(); m_automaticTimeAvailable = data.value("params").toMap().value("automaticTimeAvailable").toBool(); emit automaticTimeAvailableChanged(); m_automaticTime = data.value("params").toMap().value("automaticTime").toBool(); emit automaticTimeChanged(); } else { qWarning() << "Unhandled System Notification" << data.value("notification"); } } void SystemController::timerEvent(QTimerEvent *event) { Q_UNUSED(event) m_serverTime = m_serverTime.addSecs(1); emit serverTimeChanged(); }