From 9b07216768082bd1c2681f027eb5fc81cdf74a1f Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 10 Dec 2019 23:40:51 +0100 Subject: [PATCH] Add system api to configure the system time This gets rid of the locally kept time zone which caused issues in plugins and the ScriptEngine. --- .../devices/devicemanagerimplementation.cpp | 6 - .../devices/devicemanagerimplementation.h | 3 - .../jsonrpc/configurationhandler.cpp | 39 +++--- libnymea-core/jsonrpc/jsonvalidator.cpp | 6 +- libnymea-core/jsonrpc/systemhandler.cpp | 112 +++++++++++++++++- libnymea-core/jsonrpc/systemhandler.h | 7 ++ libnymea-core/nymeacore.cpp | 11 +- libnymea-core/time/timemanager.cpp | 78 ++---------- libnymea-core/time/timemanager.h | 25 ++-- .../platform/platformsystemcontroller.cpp | 38 ++++++ libnymea/platform/platformsystemcontroller.h | 12 ++ tests/auto/timemanager/testtimemanager.cpp | 44 +------ 12 files changed, 230 insertions(+), 151 deletions(-) diff --git a/libnymea-core/devices/devicemanagerimplementation.cpp b/libnymea-core/devices/devicemanagerimplementation.cpp index 48c365e5..cdf9a477 100644 --- a/libnymea-core/devices/devicemanagerimplementation.cpp +++ b/libnymea-core/devices/devicemanagerimplementation.cpp @@ -1026,12 +1026,6 @@ DeviceActionInfo *DeviceManagerImplementation::executeAction(const Action &actio return info; } -/*! Centralized time tick for the NymeaTimer resource. Ticks every second. */ -void DeviceManagerImplementation::timeTick() -{ - -} - void DeviceManagerImplementation::loadPlugins() { foreach (const QString &path, pluginSearchDirs()) { diff --git a/libnymea-core/devices/devicemanagerimplementation.h b/libnymea-core/devices/devicemanagerimplementation.h index b3e7918e..25853ae2 100644 --- a/libnymea-core/devices/devicemanagerimplementation.h +++ b/libnymea-core/devices/devicemanagerimplementation.h @@ -114,9 +114,6 @@ public: signals: void loaded(); -public slots: - void timeTick(); - private slots: void loadPlugins(); void loadPlugin(DevicePlugin *pluginIface, const PluginMetadata &metaData); diff --git a/libnymea-core/jsonrpc/configurationhandler.cpp b/libnymea-core/jsonrpc/configurationhandler.cpp index faf2ebff..e67593d5 100644 --- a/libnymea-core/jsonrpc/configurationhandler.cpp +++ b/libnymea-core/jsonrpc/configurationhandler.cpp @@ -61,6 +61,8 @@ #include "configurationhandler.h" #include "nymeacore.h" #include "nymeaconfiguration.h" +#include "platform/platform.h" +#include "platform/platformsystemcontroller.h" namespace nymeaserver { @@ -80,21 +82,21 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): QString description; QVariantMap params; QVariantMap returns; description = "Get the list of available timezones."; returns.insert("timeZones", QVariantList() << enumValueName(String)); - registerMethod("GetTimeZones", description, params, returns); + registerMethod("GetTimeZones", description, params, returns, "Use System.GetTimeZones instead."); params.clear(); returns.clear(); - description = "DEPRECATED - Use the locale property in the Handshake message instead - Returns a list of locale codes available for the server. i.e. en_US, de_AT"; + description = "Returns a list of locale codes available for the server. i.e. en_US, de_AT"; returns.insert("languages", QVariantList() << enumValueName(String)); - registerMethod("GetAvailableLanguages", description, params, returns); + registerMethod("GetAvailableLanguages", description, params, returns, "Use the locale property in the Handshake message instead."); params.clear(); returns.clear(); description = "Get all configuration parameters of the server."; QVariantMap basicConfiguration; basicConfiguration.insert("serverName", enumValueName(String)); basicConfiguration.insert("serverUuid", enumValueName(Uuid)); - basicConfiguration.insert("serverTime", enumValueName(Uint)); - basicConfiguration.insert("timeZone", enumValueName(String)); - basicConfiguration.insert("language", enumValueName(String)); + basicConfiguration.insert("d:serverTime", enumValueName(Uint)); + basicConfiguration.insert("d:timeZone", enumValueName(String)); + basicConfiguration.insert("d:language", enumValueName(String)); basicConfiguration.insert("debugServerEnabled", enumValueName(Bool)); returns.insert("basicConfiguration", basicConfiguration); QVariantList tcpServerConfigurations; @@ -123,13 +125,13 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): description = "Set the time zone of the server. See also: \"GetTimeZones\""; params.insert("timeZone", enumValueName(String)); returns.insert("configurationError", enumRef()); - registerMethod("SetTimeZone", description, params, returns); + registerMethod("SetTimeZone", description, params, returns, "Use System.SetTimeZone instead."); params.clear(); returns.clear(); - description = "DEPRECATED - Use the locale property in the Handshake message instead - Sets the server language to the given language. See also: \"GetAvailableLanguages\""; + description = "Sets the server language to the given language. See also: \"GetAvailableLanguages\""; params.insert("language", enumValueName(String)); returns.insert("configurationError", enumRef()); - registerMethod("SetLanguage", description, params, returns); + registerMethod("SetLanguage", description, params, returns, "Use the locale property in the Handshake message instead."); params.clear(); returns.clear(); description = "Enable or disable the debug server."; @@ -339,7 +341,7 @@ JsonReply *ConfigurationHandler::GetTimeZones(const QVariantMap ¶ms) const { Q_UNUSED(params) QVariantList timeZones; - foreach (const QByteArray &timeZoneId, NymeaCore::instance()->timeManager()->availableTimeZones()) { + foreach (const QByteArray &timeZoneId, QTimeZone::availableTimeZoneIds()) { timeZones.append(QString::fromUtf8(timeZoneId)); } @@ -371,11 +373,18 @@ JsonReply *ConfigurationHandler::SetTimeZone(const QVariantMap ¶ms) const { qCDebug(dcJsonRpc()) << "Setting time zone to" << params.value("timeZone").toString(); - QByteArray timeZone = params.value("timeZone").toString().toUtf8(); - if (!NymeaCore::instance()->timeManager()->setTimeZone(timeZone)) - return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidTimeZone)); + QByteArray timeZoneName = params.value("timeZone").toString().toUtf8(); + + QTimeZone timeZone(timeZoneName); + if (!timeZone.isValid()) { + return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidTimeZone)); + } + + bool success = NymeaCore::instance()->platform()->systemController()->setTimeZone(timeZone); + if (!success) { + return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidTimeZone)); + } - NymeaCore::instance()->configuration()->setTimeZone(timeZone); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError)); } @@ -658,7 +667,7 @@ QVariantMap ConfigurationHandler::packBasicConfiguration() basicConfiguration.insert("serverName", NymeaCore::instance()->configuration()->serverName()); basicConfiguration.insert("serverUuid", NymeaCore::instance()->configuration()->serverUuid().toString()); basicConfiguration.insert("serverTime", NymeaCore::instance()->timeManager()->currentDateTime().toTime_t()); - basicConfiguration.insert("timeZone", QString::fromUtf8(NymeaCore::instance()->timeManager()->timeZone())); + basicConfiguration.insert("timeZone", QTimeZone::systemTimeZoneId()); basicConfiguration.insert("language", NymeaCore::instance()->configuration()->locale().name()); basicConfiguration.insert("debugServerEnabled", NymeaCore::instance()->configuration()->debugServerEnabled()); return basicConfiguration; diff --git a/libnymea-core/jsonrpc/jsonvalidator.cpp b/libnymea-core/jsonrpc/jsonvalidator.cpp index 9846e834..601ab04c 100644 --- a/libnymea-core/jsonrpc/jsonvalidator.cpp +++ b/libnymea-core/jsonrpc/jsonvalidator.cpp @@ -112,7 +112,7 @@ JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const Q continue; } QString trimmedKey = key; - trimmedKey.remove(QRegExp("^(o:|r:)")); + trimmedKey.remove(QRegExp("^(o:|r:|d:)")); if (!map.contains(trimmedKey)) { return Result(false, "Missing required key: " + key, key); } @@ -123,7 +123,7 @@ JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const Q // Is the key allowed in here? QVariant expectedValue = definition.value(key); foreach (const QString &definitionKey, definition.keys()) { - QRegExp regExp = QRegExp("(o:|r:)*" + key); + QRegExp regExp = QRegExp("(o:|r:|d:)*" + key); if (regExp.exactMatch(definitionKey)) { expectedValue = definition.value(definitionKey); } @@ -132,7 +132,7 @@ JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const Q expectedValue = definition.value("o:" + key); } if (!expectedValue.isValid()) { - expectedValue = definition.value("o:" + key); + expectedValue = definition.value("d:" + key); } if (!expectedValue.isValid()) { return Result(false, "Invalid key: " + key); diff --git a/libnymea-core/jsonrpc/systemhandler.cpp b/libnymea-core/jsonrpc/systemhandler.cpp index c433f97b..b990ec1d 100644 --- a/libnymea-core/jsonrpc/systemhandler.cpp +++ b/libnymea-core/jsonrpc/systemhandler.cpp @@ -38,9 +38,14 @@ SystemHandler::SystemHandler(Platform *platform, QObject *parent): // Methods QString description; QVariantMap params; QVariantMap returns; - description = "Get the list of capabilites on this system. This allows reading whether things like rebooting or shutting down the system running nymea:core is supported on this host."; + description = "Get the list of capabilites on this system. The property \"powerManagement\" indicates whether " + "rebooting or shutting down the system running nymea:core is supported on this host. The property " + "\"updateManagement indicates whether system update features are available in this API. The " + "property \"timeManagement\" indicates whether the system time can be configured on this system. " + "Note that GetTime will be available in any case."; returns.insert("powerManagement", enumValueName(Bool)); returns.insert("updateManagement", enumValueName(Bool)); + returns.insert("timeManagement", enumValueName(Bool)); registerMethod("GetCapabilities", description, params, returns); params.clear(); returns.clear(); @@ -116,6 +121,39 @@ SystemHandler::SystemHandler(Platform *platform, QObject *parent): returns.insert("success", enumValueName(Bool)); registerMethod("EnableRepository", description, params, returns); + params.clear(); returns.clear(); + description = "Get the system time and configuraton. The \"serverTime\" and \"serverTimezone\" properties " + "give the current server time and time zone. \"automaticTimeAvailable\" indicates whether " + "this system supports automatically setting the clock (e.g. using NTP). \"automaticTime\" will " + "be true if the system is configured to automatically update the clock."; + returns.insert("time", enumValueName(Uint)); + returns.insert("timeZone", enumValueName(String)); + returns.insert("automaticTimeAvailable", enumValueName(Bool)); + returns.insert("automaticTime", enumValueName(Bool)); + registerMethod("GetTime", description, params, returns); + + params.clear(); returns.clear(); + description = "Set the system time configuraton. The system can be configured to update the time automatically " + "by setting \"automaticTime\" to true. This will only work if the \"timeManagement\" capability is " + "available on this system and \"GetTime\" indicates the availability of automatic time settngs. If " + "any of those requirements are not met, this method will return \"false\" in the \"success\" property. " + "In order to manually configure the time, \"automaticTime\" should be set to false and \"time\" should " + "be set. Note that if \"automaticTime\" is set to true and a manual \"time\" is still passed, the system " + "will attempt to configure automatic time updates and only set the manual time if automatic mode fails. " + "A time zone can always be passed optionally to change the system time zone and should be a IANA time zone " + "id."; + params.insert("o:automaticTime", enumValueName(Bool)); + params.insert("o:time", enumValueName(Uint)); + params.insert("o:timeZone", enumValueName(String)); + returns.insert("success", enumValueName(Bool)); + registerMethod("SetTime", description, params, returns); + + params.clear(); returns.clear(); + description = "Returns the list of IANA specified time zone IDs which can be used to select a time zone. It is not " + "required to use this method of the client toolkit already provides means to obtain a list of IANA time " + "zone ids."; + returns.insert("timeZones", enumValueName(StringList)); + registerMethod("GetTimeZones", description, params, returns); // Notifications params.clear(); @@ -160,6 +198,13 @@ SystemHandler::SystemHandler(Platform *platform, QObject *parent): params.insert("repositoryId", enumValueName(String)); registerNotification("RepositoryRemoved", description, params); + params.clear(); + description = "Emitted whenever the time configuration is changed"; + params.insert("time", enumValueName(Uint)); + params.insert("timeZone", enumValueName(String)); + params.insert("automaticTimeAvailable", enumValueName(Bool)); + params.insert("automaticTime", enumValueName(Bool)); + registerNotification("TimeConfigurationChanged", description, params); connect(m_platform->systemController(), &PlatformSystemController::availableChanged, this, &SystemHandler::onCapabilitiesChanged); connect(m_platform->updateController(), &PlatformUpdateController::availableChanged, this, &SystemHandler::onCapabilitiesChanged); @@ -205,6 +250,14 @@ SystemHandler::SystemHandler(Platform *platform, QObject *parent): params.insert("repositoryId", repositoryId); emit RepositoryRemoved(params); }); + connect(m_platform->systemController(), &PlatformSystemController::timeConfigurationChanged, this, [this](){ + QVariantMap params; + params.insert("time", QDateTime::currentDateTime().toSecsSinceEpoch()); + params.insert("timeZone", QTimeZone::systemTimeZoneId()); + params.insert("automaticTimeAvailable", m_platform->systemController()->automaticTimeAvailable()); + params.insert("automaticTime", m_platform->systemController()->automaticTime()); + emit TimeConfigurationChanged(params); + }, Qt::QueuedConnection); // Queued to give QDateTime a chance to sync itself to the system } QString SystemHandler::name() const @@ -218,6 +271,7 @@ JsonReply *SystemHandler::GetCapabilities(const QVariantMap ¶ms) QVariantMap data; data.insert("powerManagement", m_platform->systemController()->powerManagementAvailable()); data.insert("updateManagement", m_platform->updateController()->updateManagementAvailable()); + data.insert("timeManagement", m_platform->systemController()->timeManagementAvailable()); return createReply(data); } @@ -314,6 +368,62 @@ JsonReply *SystemHandler::EnableRepository(const QVariantMap ¶ms) const return createReply(returns); } +JsonReply *SystemHandler::GetTime(const QVariantMap ¶ms) const +{ + Q_UNUSED(params) + QVariantMap returns; + returns.insert("automaticTimeAvailable", m_platform->systemController()->automaticTimeAvailable()); + returns.insert("automaticTime", m_platform->systemController()->automaticTime()); + returns.insert("time", QDateTime::currentDateTime().toSecsSinceEpoch()); + returns.insert("timeZone", QTimeZone::systemTimeZoneId()); + return createReply(returns); +} + +JsonReply *SystemHandler::SetTime(const QVariantMap ¶ms) const +{ + QVariantMap returns; + bool handled = false; + bool automaticTime = params.value("automaticTime", false).toBool(); + if (params.contains("automaticTime") && m_platform->systemController()->automaticTimeAvailable()) { + if (!m_platform->systemController()->setAutomaticTime(automaticTime)) { + returns.insert("success", false); + return createReply(returns); + } + handled = true; + } + if (!automaticTime && params.contains("time")) { + QDateTime time = QDateTime::fromSecsSinceEpoch(params.value("time").toUInt()); + if (!m_platform->systemController()->setTime(time)) { + returns.insert("success", false); + return createReply(returns); + } + handled = true; + } + if (params.contains("timeZone")) { + QTimeZone timeZone(params.value("timeZone").toByteArray()); + if (!m_platform->systemController()->setTimeZone(timeZone)) { + returns.insert("success", false); + return createReply(returns); + } + handled = true; + } + returns.insert("success", handled); + return createReply(returns); +} + +JsonReply *SystemHandler::GetTimeZones(const QVariantMap ¶ms) const +{ + Q_UNUSED(params) + QVariantList timeZones; + foreach (const QByteArray &timeZoneId, QTimeZone::availableTimeZoneIds()) { + timeZones.append(QString::fromUtf8(timeZoneId)); + } + + QVariantMap returns; + returns.insert("timeZones", timeZones); + return createReply(returns); +} + void SystemHandler::onCapabilitiesChanged() { QVariantMap caps; diff --git a/libnymea-core/jsonrpc/systemhandler.h b/libnymea-core/jsonrpc/systemhandler.h index 4c43a4e8..b54f058a 100644 --- a/libnymea-core/jsonrpc/systemhandler.h +++ b/libnymea-core/jsonrpc/systemhandler.h @@ -53,8 +53,13 @@ public: Q_INVOKABLE JsonReply *GetRepositories(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *EnableRepository(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply *GetTime(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply *SetTime(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply *GetTimeZones(const QVariantMap ¶ms) const; + signals: void CapabilitiesChanged(const QVariantMap ¶ms); + void UpdateStatusChanged(const QVariantMap ¶ms); void PackageAdded(const QVariantMap ¶ms); void PackageChanged(const QVariantMap ¶ms); @@ -63,6 +68,8 @@ signals: void RepositoryChanged(const QVariantMap ¶ms); void RepositoryRemoved(const QVariantMap ¶ms); + void TimeConfigurationChanged(const QVariantMap ¶ms); + private slots: void onCapabilitiesChanged(); diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index 6c98bf9c..b774b82c 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -98,6 +98,7 @@ #include "tagging/tagsstorage.h" #include "platform/platform.h" #include "experiences/experiencemanager.h" +#include "platform/platformsystemcontroller.h" #include "devices/devicemanagerimplementation.h" #include "devices/device.h" @@ -140,7 +141,14 @@ void NymeaCore::init() { m_configuration = new NymeaConfiguration(this); qCDebug(dcApplication()) << "Creating Time Manager"; - m_timeManager = new TimeManager(m_configuration->timeZone(), this); + // Migration path: nymea < 0.18 doesn't use system time zone but stores its own time zone in the config + // For migration, let's set the system's time zone to the config now to upgrade to the system time zone based nymea >= 0.18 + if (QTimeZone(m_configuration->timeZone()).isValid()) { + if (m_platform->systemController()->setTimeZone(QTimeZone(m_configuration->timeZone()))) { + m_configuration->setTimeZone(""); + } + } + m_timeManager = new TimeManager(this); qCDebug(dcApplication) << "Creating Log Engine"; m_logger = new LogEngine(m_configuration->logDBDriver(), m_configuration->logDBName(), m_configuration->logDBHost(), m_configuration->logDBUser(), m_configuration->logDBPassword(), m_configuration->logDBMaxEntries(), this); @@ -199,7 +207,6 @@ void NymeaCore::init() { connect(m_ruleEngine, &RuleEngine::ruleConfigurationChanged, this, &NymeaCore::ruleConfigurationChanged); connect(m_timeManager, &TimeManager::dateTimeChanged, this, &NymeaCore::onDateTimeChanged); - connect(m_timeManager, &TimeManager::tick, m_deviceManager, &DeviceManagerImplementation::timeTick); m_logger->logSystemEvent(m_timeManager->currentDateTime(), true); } diff --git a/libnymea-core/time/timemanager.cpp b/libnymea-core/time/timemanager.cpp index 14876caf..ccc200c5 100644 --- a/libnymea-core/time/timemanager.cpp +++ b/libnymea-core/time/timemanager.cpp @@ -40,71 +40,16 @@ namespace nymeaserver { /*! Constructs a new \l{TimeManager} with the given \a timeZone and \a parent. */ -TimeManager::TimeManager(const QByteArray &timeZone, QObject *parent) : +TimeManager::TimeManager(QObject *parent) : QObject(parent) { - m_dateTime = QDateTime::currentDateTimeUtc(); - m_dateTime.setTimeSpec(Qt::UTC); - - setTimeZone(timeZone); - - m_nymeaTimer = new QTimer(this); - m_nymeaTimer->setInterval(1000); - m_nymeaTimer->setSingleShot(false); - - connect(m_nymeaTimer, &QTimer::timeout, this, &TimeManager::nymeaTimeout); - - m_nymeaTimer->start(); -} - -/*! Returns the time zone of this \l{TimeManager}. */ -QByteArray TimeManager::timeZone() const -{ - return m_timeZone.id(); -} - -/*! Sets the \a timeZone of this \l{TimeManager}. Allowed values according to the \l{http://www.iana.org/time-zones}{IANA database}. - * Returns false if the given timezone is not valid. */ -bool TimeManager::setTimeZone(const QByteArray &timeZone) -{ - if (!QTimeZone(timeZone).isValid()) { - qCWarning(dcTimeManager()) << "Invalid time zone" << timeZone; - qCWarning(dcTimeManager()) << "Using system time zone" << QTimeZone::systemTimeZoneId(); - m_timeZone = QTimeZone(QTimeZone::systemTimeZoneId()); - emit dateTimeChanged(currentDateTime()); - return false; - } - - qCDebug(dcTimeManager()) << "Set time zone" << timeZone; - m_timeZone = QTimeZone(timeZone); - qCDebug(dcTimeManager()) << "UTC" << m_dateTime.toString("dd.MM.yyyy hh:mm:ss"); - qCDebug(dcTimeManager) << "Zone time" << currentDateTime().toString("dd.MM.yyyy hh:mm:ss"); - emit dateTimeChanged(currentDateTime()); - return true; + m_timerId = startTimer(1000, Qt::VeryCoarseTimer); } /*! Returns the current dateTime of this \l{TimeManager}. */ QDateTime TimeManager::currentDateTime() const { - return QDateTime::currentDateTimeUtc().toTimeZone(m_timeZone); -} - -/*! Returns the current time of this \l{TimeManager}. */ -QTime TimeManager::currentTime() const -{ - return QDateTime::currentDateTimeUtc().toTimeZone(m_timeZone).time(); -} - -/*! Returns the current date of this \l{TimeManager}. */ -QDate TimeManager::currentDate() const -{ - return QDateTime::currentDateTimeUtc().toTimeZone(m_timeZone).date(); -} - -/*! Returns a list of available time zones on this system. */ -QList TimeManager::availableTimeZones() const -{ - return QTimeZone::availableTimeZoneIds(); + return QDateTime::currentDateTime().addSecs(m_overrideDifference); } /*! Stop the time. @@ -115,7 +60,7 @@ void TimeManager::stopTimer() { qCWarning(dcTimeManager()) << "TimeManager timer stopped. You should only see this in tests."; // Stop clock (used for testing) - m_nymeaTimer->stop(); + killTimer(m_timerId); } /*! Set the current time of this TimeManager to the given \a dateTime. @@ -125,21 +70,22 @@ void TimeManager::stopTimer() void TimeManager::setTime(const QDateTime &dateTime) { qCWarning(dcTimeManager()) << "TimeManager time changed" << dateTime.toString("dd.MM.yyyy hh:mm:ss") << "You should only see this in tests."; + m_overrideDifference = QDateTime::currentDateTime().secsTo(dateTime); // This method will only be called for testing to set the internal time - emit tick(); emit dateTimeChanged(dateTime); } -void TimeManager::nymeaTimeout() +void TimeManager::timerEvent(QTimerEvent *event) { - // tick for deviceManager + Q_UNUSED(event) + emit tick(); // Minute based nymea time - QDateTime utcDateTime = QDateTime::currentDateTimeUtc(); - if (m_dateTime.time().minute() != utcDateTime.toTimeZone(m_timeZone).time().minute()) { - m_dateTime = utcDateTime; - emit dateTimeChanged(currentDateTime()); + QDateTime now = QDateTime::currentDateTime(); + if (m_lastEvent.time().minute() != now.time().minute()) { + m_lastEvent = now; + emit dateTimeChanged(now.addSecs(m_overrideDifference)); } } diff --git a/libnymea-core/time/timemanager.h b/libnymea-core/time/timemanager.h index 24bd7614..e49d0eb4 100644 --- a/libnymea-core/time/timemanager.h +++ b/libnymea-core/time/timemanager.h @@ -32,32 +32,27 @@ class TimeManager : public QObject { Q_OBJECT public: - explicit TimeManager(const QByteArray &timeZone, QObject *parent = 0); - - QByteArray timeZone() const; - bool setTimeZone(const QByteArray &timeZone = QTimeZone::systemTimeZoneId()); + explicit TimeManager(QObject *parent = nullptr); QDateTime currentDateTime() const; - QTime currentTime() const; - QDate currentDate() const; - - QList availableTimeZones() const; + // For testability only void stopTimer(); void setTime(const QDateTime &dateTime); -private: - QTimeZone m_timeZone; - QDateTime m_dateTime; - QTimer *m_nymeaTimer; - signals: void tick(); void dateTimeChanged(const QDateTime &dateTime); -private slots: - void nymeaTimeout(); +protected: + void timerEvent(QTimerEvent *event) override; +private: + int m_timerId = 0; + QDateTime m_lastEvent; + + // For testability + qint64 m_overrideDifference = 0; }; } diff --git a/libnymea/platform/platformsystemcontroller.cpp b/libnymea/platform/platformsystemcontroller.cpp index dda77859..a2380fe3 100644 --- a/libnymea/platform/platformsystemcontroller.cpp +++ b/libnymea/platform/platformsystemcontroller.cpp @@ -22,6 +22,8 @@ #include "platformsystemcontroller.h" +#include "loggingcategories.h" + PlatformSystemController::PlatformSystemController(QObject *parent) : QObject(parent) { @@ -41,3 +43,39 @@ bool PlatformSystemController::shutdown() { return false; } + +bool PlatformSystemController::timeManagementAvailable() const +{ + return false; +} + +bool PlatformSystemController::automaticTimeAvailable() const +{ + return false; +} + +bool PlatformSystemController::automaticTime() const +{ + return false; +} + +bool PlatformSystemController::setTime(const QDateTime &time) +{ + Q_UNUSED(time) + qCWarning(dcPlatform()) << "setTime not implemented in platform plugin"; + return false; +} + +bool PlatformSystemController::setAutomaticTime(bool automaticTime) +{ + Q_UNUSED(automaticTime) + qCWarning(dcPlatform()) << "setAutomaticTime not implemented in platform plugin"; + return false; +} + +bool PlatformSystemController::setTimeZone(const QTimeZone &timeZone) +{ + Q_UNUSED(timeZone) + qCWarning(dcPlatform()) << "setTimeZone not implemented in platform plugin"; + return false; +} diff --git a/libnymea/platform/platformsystemcontroller.h b/libnymea/platform/platformsystemcontroller.h index a1de5fa9..cced1db9 100644 --- a/libnymea/platform/platformsystemcontroller.h +++ b/libnymea/platform/platformsystemcontroller.h @@ -24,6 +24,7 @@ #define PLATFORMSYSTEMCONTROLLER_H #include +#include class PlatformSystemController : public QObject { @@ -36,8 +37,19 @@ public: virtual bool reboot(); virtual bool shutdown(); + virtual bool timeManagementAvailable() const; + virtual bool automaticTimeAvailable() const; + virtual bool automaticTime() const; + virtual bool setTime(const QDateTime &time); + virtual bool setAutomaticTime(bool automaticTime); + virtual bool setTimeZone(const QTimeZone &timeZone); + + signals: void availableChanged(); + void timeZoneManagementAvailableChanged(); + + void timeConfigurationChanged(); }; Q_DECLARE_INTERFACE(PlatformSystemController, "io.nymea.PlatformSystemController") diff --git a/tests/auto/timemanager/testtimemanager.cpp b/tests/auto/timemanager/testtimemanager.cpp index e15336f4..55c5fcf9 100644 --- a/tests/auto/timemanager/testtimemanager.cpp +++ b/tests/auto/timemanager/testtimemanager.cpp @@ -23,6 +23,9 @@ #include "nymeacore.h" #include "servers/mocktcpserver.h" +#include "platform/platform.h" +#include "platform/platformsystemcontroller.h" + using namespace nymeaserver; class TestTimeManager: public NymeaTestBase @@ -37,9 +40,6 @@ private: private slots: void initTestCase(); - void changeTimeZone_data(); - void changeTimeZone(); - void loadSaveTimeDescriptor_data(); void loadSaveTimeDescriptor(); @@ -142,41 +142,6 @@ void TestTimeManager::initTestCase() "TimeManager.debug=true"); } -void TestTimeManager::changeTimeZone_data() -{ - QTest::addColumn("timeZoneId"); - QTest::addColumn("valid"); - - QTest::newRow("valid timezone: Asia/Tokyo") << QByteArray("Asia/Tokyo") << true; - QTest::newRow("valid timezone: America/Lima") << QByteArray("America/Lima") << true; - QTest::newRow("valid timezone: Africa/Harare") << QByteArray("Africa/Harare") << true; - QTest::newRow("invalid timezone: Mars/Diacria") << QByteArray("Mars/Diacria") << false; - QTest::newRow("invalid timezone: Moon/Kepler") << QByteArray("Moon/Kepler") << false; -} - -void TestTimeManager::changeTimeZone() -{ - QFETCH(QByteArray, timeZoneId); - QFETCH(bool, valid); - - QTimeZone currentTimeZone(NymeaCore::instance()->timeManager()->timeZone()); - QTimeZone newTimeZone(timeZoneId); - - - QDateTime currentDateTime = NymeaCore::instance()->timeManager()->currentDateTime(); - - NymeaCore::instance()->timeManager()->setTimeZone(timeZoneId); - - QDateTime newDateTime = NymeaCore::instance()->timeManager()->currentDateTime(); - - int offsetOriginal = currentTimeZone.offsetFromUtc(currentDateTime); - int offsetNew = newTimeZone.offsetFromUtc(newDateTime); - - if (valid) - QVERIFY(offsetOriginal != offsetNew); - -} - void TestTimeManager::loadSaveTimeDescriptor_data() { // Repeating options @@ -2056,8 +2021,7 @@ void TestTimeManager::initTimeManager() removeAllRules(); enableNotifications({"Rules", "Devices", "Events"}); NymeaCore::instance()->timeManager()->stopTimer(); - qDebug() << NymeaCore::instance()->timeManager()->currentTime().toString(); - qDebug() << NymeaCore::instance()->timeManager()->currentDate().toString(); + qDebug() << NymeaCore::instance()->timeManager()->currentDateTime().toString(); } void TestTimeManager::verifyRuleExecuted(const ActionTypeId &actionTypeId)