nymea-app/libnymea-app/system/systemcontroller.cpp

446 lines
18 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 <https://www.gnu.org/licenses/>.
*
* 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 <QTimeZone>
#include <QJsonDocument>
#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 &params)
{
qCDebug(dcSystemController) << "Remove result" << commandId << params;
}
void SystemController::enableRepositoryResponse(int commandId, const QVariantMap &params)
{
qCDebug(dcSystemController) << "Enable repo response" << params;
emit enableRepositoryFinished(commandId, params.value("success").toBool());
}
void SystemController::getServerTimeResponse(int commandId, const QVariantMap &params)
{
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 &params)
{
qCDebug(dcSystemController) << "set time response" << commandId << params;
}
void SystemController::restartResponse(int commandId, const QVariantMap &params)
{
bool success = params.value("success").toBool();
emit restartReply(commandId, success);
}
void SystemController::rebootResponse(int commandId, const QVariantMap &params)
{
bool success = params.value("success").toBool();
emit rebootReply(commandId, success);
}
void SystemController::shutdownResponse(int commandId, const QVariantMap &params)
{
bool success = params.value("success").toBool();
emit shutdownReply(commandId, success);
}
void SystemController::getSystemInfoResponse(int commandId, const QVariantMap &params)
{
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();
}