Add Qt6 support

Add Qt6 debian packaging
This commit is contained in:
Simon Stürz 2024-01-21 21:18:02 +01:00
parent 5f4f2a9b78
commit f5c4bd16ac
184 changed files with 2527 additions and 1045 deletions

1
debian Symbolic link
View File

@ -0,0 +1 @@
debian-qt5

View File

@ -66,8 +66,8 @@ Recommends: nymea-cli,
nymea-zwave-plugin-impl,
influxdb,
Description: An open source IoT server - daemon
The nymea daemon is a plugin based IoT (Internet of Things) server.
The server works like a translator for devices, things and services
The nymea daemon is a plugin based IoT (Internet of Things) server.
The server works like a translator for devices, things and services
and allows them to interact.
@ -120,7 +120,7 @@ Depends: ${shlibs:Depends},
libnymea-tests (= ${binary:Version}),
libnymea-core-dev (= ${binary:Version})
Description: nymea-tests library - development files
Provides the nymea-tests development files for building
Provides the nymea-tests development files for building
automated tests.

View File

@ -14,7 +14,7 @@ $(info Building with DEB_BUILD_OPTIONS: [${DEB_BUILD_OPTIONS}])
MAKE_TARGETS=all lrelease
ifeq (,$(filter nodoc,$(DEB_BUILD_OPTIONS)))
MAKE_TARGETS += doc
endif
endif
QMAKE_ADDITIONAL_ARGS = CONFIG+=norpath
ifneq (,$(filter coverage,$(DEB_BUILD_OPTIONS)))

1
debian-qt6/changelog Symbolic link
View File

@ -0,0 +1 @@
../debian-qt5/changelog

1
debian-qt6/compat Normal file
View File

@ -0,0 +1 @@
13

221
debian-qt6/control Normal file
View File

@ -0,0 +1,221 @@
Source: nymea
Section: utils
Priority: optional
Maintainer: nymea GmbH <developer@nymea.io>
Standards-Version: 4.7.2
Homepage: https://nymea.io
Vcs-Git: https://github.com/nymea/nymea.git
Build-Depends: debhelper,
dbus-test-runner,
dpkg-dev,
libnymea-mqtt-dev (>= 0.1.2),
libnymea-networkmanager-dev (>= 1.5.0~),
libnymea-remoteproxyclient-dev (>= 0.1.13),
libnymea-zigbee-dev (>= 0.1.0),
libnymea-gpio-dev,
libpython3-dev,
qt6-base-dev,
qt6-base-dev-tools,
qt6-websockets-dev,
qt6-connectivity-dev,
qt6-declarative-dev,
qt6-serialport-dev,
qt6-serialbus-dev,
qml6-module-qtquick,
qtchooser,
libqt6bluetooth6,
libqt6sql6-sqlite,
libqt6dbus6 | libqt6dbus6t64,
libssl-dev,
rsync,
influxdb:native,
libsystemd-dev,
Package: nymea
Architecture: any
Section: metapackages
Multi-Arch: same
Depends: nymead (= ${binary:Version}),
${misc:Depends}
Recommends: qml-module-qtquick2
Suggests: nymea-doc
Description: An open source IoT server - meta package
The nymea daemon is a plugin based IoT (Internet of Things) server.
The server works like a translator for devices, things and services
and allows them to interact.
Package: nymead
Architecture: any
Section: net
Multi-Arch: same
Depends: ${shlibs:Depends},
${misc:Depends},
nymea-translations,
libnymea-core (= ${binary:Version}),
libnymea1 (= ${binary:Version}),
Recommends: nymea-cli,
nymea-data,
network-manager,
nymea-update-plugin-impl,
nymea-system-plugin-impl,
nymea-zeroconf-plugin-impl,
nymea-apikeysprovider-plugin-impl,
nymea-zwave-plugin-impl,
influxdb,
Description: An open source IoT server - daemon
The nymea daemon is a plugin based IoT (Internet of Things) server.
The server works like a translator for devices, things and services
and allows them to interact.
Package: nymea-data
Section: misc
Architecture: all
Depends: ${misc:Depends}
Recommends: nymead
Description: Optional data for extending functionality in nymea daemon - data
Provides application data used by nymea:core
Package: nymea-doc
Section: doc
Architecture: all
Depends: ${misc:Depends}
Recommends: nymea
Build-Profiles: <!nodoc>
Description: Documentation for the nymea library - documentation
This package contains documentation for developing nymea plugins.
Package: nymea-translations
Section: misc
Architecture: all
Depends: ${misc:Depends}
Recommends: nymea
Description: Translation files for nymead - translations
Translation files for the nymea daemon.
Package: libnymea-tests
Section: libs
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends},
${misc:Depends},
libnymea-core (= ${binary:Version})
Description: nymea-tests library - library
Shared resources for nymea-tests.
This package provides also the mock plugin for the tests.
Package: libnymea-tests-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends},
${misc:Depends},
libnymea-tests (= ${binary:Version}),
libnymea-core-dev (= ${binary:Version})
Description: nymea-tests library - development files
Provides the nymea-tests development files for building
automated tests.
Package: nymea-tests
Section: devel
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends},
${misc:Depends},
nymea (= ${binary:Version}),
libnymea-tests (= ${binary:Version}),
influxdb,
Description: nymea automated tests - tests
Automated tests for the nymea daemon.
Package: libnymea1
Section: libs
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends},
${misc:Depends},
Provides: nymea-update-plugin-api-1,
nymea-zeroconf-plugin-api-1,
nymea-system-plugin-api-3
Description: nymea shared resources - library
Shared resources for nymea plugins.
Package: libnymea-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends},
${misc:Depends},
libnymea1 (= ${binary:Version}),
pkg-config,
qt6-base-dev,
Replaces: libnymea1-dev
Breaks: libnymea1-dev
Provides: libnymea1-dev
Description: nymea plugin development API library - development files
Shared resources for developing nymea plugins.
Package: libnymea-core
Section: libs
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends},
${misc:Depends},
logrotate,
bluez,
tar,
iputils-tracepath,
iputils-ping,
qml6-module-qtquick,
libqt6sql6-sqlite,
libcap2-bin,
dnsutils,
Description: nymea:core internal - library
Internal library for nymea:core.
Package: libnymea-core-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends},
${misc:Depends},
libnymea-core (= ${binary:Version}),
libnymea-dev (= ${binary:Version})
Description: nymea:core internal library - development files
Development files for the internal nymea:core library.
Package: nymea-dev-tools
Section: libdevel
Architecture: any
Multi-Arch: no
Depends: ${shlibs:Depends},
${misc:Depends},
qt6-base-dev-tools,
rsync,
Description: Tools for developing nymea plugins.
This package will install the nymea-plugininfocompiler.
Package: nymea-sdk
Section: devel
Architecture: all
Multi-Arch: no
Depends: nymea-dev-tools (= ${binary:Version}),
libnymea-dev (= ${binary:Version}),
Recommends: nymea-qtcreator-wizards,
crossbuilder,
Suggests: libnymea-core-dev (= ${binary:Version})
Description: The nymea SDK.
Meta-Package for everything required to develop nymea integration plugins.

1
debian-qt6/copyright Symbolic link
View File

@ -0,0 +1 @@
../debian-qt5/copyright

View File

@ -0,0 +1 @@
../debian-qt5/libnymea-core-dev.install.in

View File

@ -0,0 +1 @@
../debian-qt5/libnymea-core.install.in

View File

@ -0,0 +1 @@
../debian-qt5/libnymea-dev.install.in

View File

@ -0,0 +1 @@
../debian-qt5/libnymea-tests-dev.install.in

View File

@ -0,0 +1 @@
../debian-qt5/libnymea-tests.install.in

View File

@ -0,0 +1 @@
../debian-qt5/libnymea1-dev.dirs.in

View File

@ -0,0 +1 @@
../debian-qt5/libnymea1-dev.manpages

View File

@ -0,0 +1 @@
../debian-qt5/libnymea1.dirs.in

View File

@ -0,0 +1 @@
../debian-qt5/libnymea1.install.in

1
debian-qt6/man Symbolic link
View File

@ -0,0 +1 @@
../debian-qt5/man/

View File

@ -0,0 +1 @@
../debian-qt5/nymea-data.install

View File

@ -0,0 +1 @@
../debian-qt5/nymea-dev-tools.install.in

1
debian-qt6/nymea-doc.dirs Symbolic link
View File

@ -0,0 +1 @@
../debian-qt5/nymea-doc.dirs

View File

@ -0,0 +1 @@
../debian-qt5/nymea-doc.install

View File

@ -0,0 +1 @@
../debian-qt5/nymea-tests.dirs.in

View File

@ -0,0 +1 @@
../debian-qt5/nymea-tests.install.in

View File

@ -0,0 +1 @@
../debian-qt5/nymea-translations.dirs

View File

@ -0,0 +1 @@
../debian-qt5/nymea-translations.install

1
debian-qt6/nymead.dirs Symbolic link
View File

@ -0,0 +1 @@
../debian-qt5/nymead.dirs

View File

@ -0,0 +1 @@
../debian-qt5/nymead.install.in

1
debian-qt6/nymead.manpages Symbolic link
View File

@ -0,0 +1 @@
../debian-qt5/nymead.manpages

1
debian-qt6/nymead.postinst Symbolic link
View File

@ -0,0 +1 @@
../debian-qt5/nymead.postinst

1
debian-qt6/nymead.prerm Symbolic link
View File

@ -0,0 +1 @@
../debian-qt5/nymead.prerm

66
debian-qt6/rules Executable file
View File

@ -0,0 +1,66 @@
#!/usr/bin/make -f
# -*- makefile -*-
export DH_VERBOSE=1
export QT_QPA_PLATFORM=minimal
ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
DEB_PARALLEL_JOBS ?= $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
else
DEB_PARALLEL_JOBS += $(shell getconf _NPROCESSORS_ONLN)
endif
$(info Building with DEB_BUILD_OPTIONS: [${DEB_BUILD_OPTIONS}])
MAKE_TARGETS=all lrelease
ifeq (,$(filter nodoc,$(DEB_BUILD_OPTIONS)))
MAKE_TARGETS += doc
endif
QMAKE_ADDITIONAL_ARGS = CONFIG+=norpath
ifneq (,$(filter coverage,$(DEB_BUILD_OPTIONS)))
QMAKE_ADDITIONAL_ARGS += CONFIG+=coverage CONFIG+=debug
else
QMAKE_ADDITIONAL_ARGS += CONFIG+=release
endif
DPKG_EXPORT_BUILDFLAGS = 1
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
NYMEA_DPKG_VERSION := $(shell dpkg-parsechangelog -SVersion)
QMAKE_ADDITIONAL_ARGS += NYMEA_VERSION=$(NYMEA_DPKG_VERSION)
include /usr/share/dpkg/buildflags.mk
PREPROCESS_FILES := $(wildcard debian/*.in)
$(PREPROCESS_FILES:.in=): %: %.in
sed 's,/@DEB_HOST_MULTIARCH@,$(DEB_HOST_MULTIARCH:%=/%),g' $< > $@
override_dh_auto_configure:
dh_auto_configure -- $(QMAKE_ADDITIONAL_ARGS)
override_dh_auto_build:
make -j$(DEB_PARALLEL_JOBS) $(MAKE_TARGETS)
override_dh_install: $(PREPROCESS_FILES:.in=)
# Make sure the docs directory exists, even if we didn't build them
mkdir -p $(CURDIR)/doc/html
dh_install
# Note that this is the default with compat level 12.
override_dh_missing:
dh_missing --list-missing
override_dh_auto_test:
dh_auto_test -- -k TESTARGS="-m 600 -p -o -p -,txt -p -o -p test-results.xml,xunitxml" TESTRUNNER="dbus-test-runner --bus-type=both --task"
override_dh_installsystemd:
dh_installsystemd --restart-after-upgrade
override_dh_auto_clean:
dh_auto_clean
find . -name *.qm -exec rm {} \;
rm -rf $(PREPROCESS_FILES:.in=)
%:
dh $@ --buildsystem=qmake6 --parallel

1
debian-qt6/source Symbolic link
View File

@ -0,0 +1 @@
../debian-qt5/source/

View File

@ -32,7 +32,7 @@
#include "openssl/ssl.h"
#include <QRegExp>
#include <QRegularExpression>
#include <QFileInfo>
#include <QSaveFile>
#include <QDir>
@ -87,8 +87,8 @@ void CertificateGenerator::generate(const QString &certificateFilename, const QS
q_check_ptr(x509);
// Randomize serial number in case a previous one is stuck in a browser (Chromium
// completely rejects reused serial numbers and doesn't even allow to bypass it by an exception)
qsrand(QUuid::createUuid().toString().remove(QRegExp("[a-zA-Z{}-]")).left(5).toInt());
ASN1_INTEGER_set(X509_get_serialNumber(x509), qrand());
std::srand(QUuid::createUuid().toString().remove(QRegularExpression("[a-zA-Z{}-]")).left(5).toInt());
ASN1_INTEGER_set(X509_get_serialNumber(x509), std::rand());
X509_gmtime_adj(X509_get_notBefore(x509), 0); // not before current time
X509_gmtime_adj(X509_get_notAfter(x509), 31536000L*10); // not after 10 years from this point
X509_set_pubkey(x509, pkey);

View File

@ -167,28 +167,28 @@ void DebugReportGenerator::saveSystemInformation()
qCDebug(dcDebugServer()) << "Write system information file" << outputFile.fileName();
QTextStream stream(&outputFile);
stream << "Server name: " << NymeaCore::instance()->configuration()->serverName() << endl;
stream << "Server version: " << NYMEA_VERSION_STRING << endl;
stream << "JSON-RPC version: " << JSON_PROTOCOL_VERSION << endl;
stream << "Language: " << NymeaCore::instance()->configuration()->locale().name() << " (" << NymeaCore::instance()->configuration()->locale().nativeCountryName() << " - " << NymeaCore::instance()->configuration()->locale().nativeLanguageName() << ")" << endl;
stream << "Timezone: " << QString::fromUtf8(NymeaCore::instance()->configuration()->timeZone()) << endl;
stream << "Server UUID: " << NymeaCore::instance()->configuration()->serverUuid().toString() << endl;
stream << "Settings path: " << NymeaSettings::settingsPath() << endl;
stream << "Translations path: " << NymeaSettings(NymeaSettings::SettingsRoleGlobal).translationsPath() << endl;
stream << "User: " << qgetenv("USER") << endl;
stream << "Command: " << QCoreApplication::arguments().join(' ') << endl;
stream << "Qt runtime version: " << qVersion() << endl;
stream << "" << endl;
stream << "Server name: " << NymeaCore::instance()->configuration()->serverName() << '\n';
stream << "Server version: " << NYMEA_VERSION_STRING << '\n';
stream << "JSON-RPC version: " << JSON_PROTOCOL_VERSION << '\n';
stream << "Language: " << NymeaCore::instance()->configuration()->locale().name() << " (" << NymeaCore::instance()->configuration()->locale().nativeCountryName() << " - " << NymeaCore::instance()->configuration()->locale().nativeLanguageName() << ")" << '\n';
stream << "Timezone: " << QString::fromUtf8(NymeaCore::instance()->configuration()->timeZone()) << '\n';
stream << "Server UUID: " << NymeaCore::instance()->configuration()->serverUuid().toString() << '\n';
stream << "Settings path: " << NymeaSettings::settingsPath() << '\n';
stream << "Translations path: " << NymeaSettings(NymeaSettings::SettingsRoleGlobal).translationsPath() << '\n';
stream << "User: " << qgetenv("USER") << '\n';
stream << "Command: " << QCoreApplication::arguments().join(' ') << '\n';
stream << "Qt runtime version: " << qVersion() << '\n';
stream << "" << '\n';
#if (QT_VERSION < QT_VERSION_CHECK(5, 6, 0))
stream << "Hostname: " << QHostInfo::localHostName() << endl;
stream << "Hostname: " << QHostInfo::localHostName() << '\n';
#else
stream << "Hostname: " << QSysInfo::machineHostName() << endl;
stream << "Hostname: " << QSysInfo::machineHostName() << '\n';
#endif
stream << "Architecture: " << QSysInfo::currentCpuArchitecture() << endl;
stream << "Kernel type: " << QSysInfo::kernelType() << endl;
stream << "Kernel version: " << QSysInfo::kernelVersion() << endl;
stream << "Product type: " << QSysInfo::productType() << endl;
stream << "Product version: " << QSysInfo::productVersion() << endl;
stream << "Architecture: " << QSysInfo::currentCpuArchitecture() << '\n';
stream << "Kernel type: " << QSysInfo::kernelType() << '\n';
stream << "Kernel version: " << QSysInfo::kernelVersion() << '\n';
stream << "Product type: " << QSysInfo::productType() << '\n';
stream << "Product version: " << QSysInfo::productVersion() << '\n';
outputFile.close();
}

View File

@ -673,7 +673,7 @@ void DebugServerHandler::onPingProcessFinished(int exitCode, QProcess::ExitStatu
{
qCDebug(dcDebugServer()) << "Ping process finished" << exitCode << exitStatus;
QByteArray processOutput = m_pingProcess->readAll();
qCDebug(dcDebugServer()) << "Ping output:" << endl << qUtf8Printable(processOutput);
qCDebug(dcDebugServer()) << "Ping output:" << '\n' << qUtf8Printable(processOutput);
if (m_pingReply) {
m_pingReply->setPayload(processOutput);
@ -690,7 +690,7 @@ void DebugServerHandler::onDigProcessFinished(int exitCode, QProcess::ExitStatus
{
qCDebug(dcDebugServer()) << "Dig process finished" << exitCode << exitStatus;
QByteArray processOutput = m_digProcess->readAll();
qCDebug(dcDebugServer()) << "Dig output:" << endl << qUtf8Printable(processOutput);
qCDebug(dcDebugServer()) << "Dig output:" << '\n' << qUtf8Printable(processOutput);
if (m_digReply) {
m_digReply->setPayload(processOutput);
@ -707,7 +707,7 @@ void DebugServerHandler::onTracePathProcessFinished(int exitCode, QProcess::Exit
{
qCDebug(dcDebugServer()) << "Tracepath process finished" << exitCode << exitStatus;
QByteArray processOutput = m_tracePathProcess->readAll();
qCDebug(dcDebugServer()) << "Tracepath output:" << endl << qUtf8Printable(processOutput);
qCDebug(dcDebugServer()) << "Tracepath output:" << '\n' << qUtf8Printable(processOutput);
if (m_tracePathReply) {
m_tracePathReply->setPayload(processOutput);

View File

@ -70,7 +70,7 @@ void BluetoothDiscoveryReplyImplementation::setDiscoveredDevices(const QList<QPa
void BluetoothDiscoveryReplyImplementation::addDiscoveredDevice(const QBluetoothDeviceInfo &info, const QBluetoothHostInfo &hostInfo)
{
m_discoveredDevices.append(qMakePair<QBluetoothDeviceInfo, QBluetoothHostInfo>(info, hostInfo));
m_discoveredDevices.append(QPair<QBluetoothDeviceInfo, QBluetoothHostInfo>(info, hostInfo));
}
void BluetoothDiscoveryReplyImplementation::setFinished()

View File

@ -37,7 +37,11 @@ BluetoothLowEnergyDeviceImplementation::BluetoothLowEnergyDeviceImplementation(c
BluetoothLowEnergyDevice(parent),
m_deviceInfo(deviceInfo)
{
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
m_controller = QLowEnergyController::createCentral(deviceInfo, this);
#else
m_controller = new QLowEnergyController(address(), this);
#endif
m_controller->setRemoteAddressType(addressType);
connect(m_controller, &QLowEnergyController::connected, this, &BluetoothLowEnergyDeviceImplementation::onConnected);

View File

@ -138,11 +138,19 @@ BluetoothPairingJob *BluetoothLowEnergyManagerImplementation::pairDevice(const Q
return job;
}
localDevice->requestPairing(device, QBluetoothLocalDevice::AuthorizedPaired);
#if QT_VERSION >= QT_VERSION_CHECK(6,2,0)
connect(localDevice, &QBluetoothLocalDevice::errorOccurred, job, [=](QBluetoothLocalDevice::Error error){
qCDebug(dcBluetooth()) << "Pairing error" << error;
job->finish(false);
localDevice->deleteLater();
});
#else
connect(localDevice, &QBluetoothLocalDevice::error, job, [=](QBluetoothLocalDevice::Error error){
qCDebug(dcBluetooth()) << "Pairing error" << error;
job->finish(false);
localDevice->deleteLater();
});
#endif
connect(localDevice, &QBluetoothLocalDevice::pairingFinished, job, [=](const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing){
qCDebug(dcBluetooth()) << "Pairing finished" << address.toString() << pairing;
job->finish(true);

View File

@ -1,11 +1,10 @@
#ifndef BLUETOOTHPAIRINGJOBIMPLEMENTATION_H
#define BLUETOOTHPAIRINGJOBIMPLEMENTATION_H
#include "hardware/bluetoothlowenergy/bluetoothlowenergymanager.h"
#include <QObject>
#include <hardware/bluetoothlowenergy/bluetoothlowenergymanager.h>
namespace nymeaserver
{
namespace nymeaserver {
class NymeaBluetoothAgent;
class BluetoothLowEnergyManagerImplementation;
@ -35,4 +34,5 @@ private:
};
}
#endif // BLUETOOTHPAIRINGJOBIMPLEMENTATION_H

View File

@ -5,8 +5,7 @@
Q_DECLARE_LOGGING_CATEGORY(dcBluetooth)
namespace nymeaserver
{
namespace nymeaserver {
NymeaBluetoothAgentAdapter::NymeaBluetoothAgentAdapter(NymeaBluetoothAgent *agent, QObject *parent):
QObject{parent},

View File

@ -6,8 +6,9 @@
#include <QDBusMessage>
#include <QBluetoothAddress>
namespace nymeaserver
{
#include <hardware/bluetoothlowenergy/nymeabluetoothagent.h>
namespace nymeaserver {
class NymeaBluetoothAgent;

View File

@ -47,7 +47,7 @@ MacAddressDatabase::MacAddressDatabase(QObject *parent) : QObject(parent)
{
// Find database in system data locations
QString databaseFileName;
foreach (const QString &dataLocation, QStandardPaths::standardLocations(QStandardPaths::DataLocation)) {
foreach (const QString &dataLocation, QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) {
QFileInfo databaseFileInfo(dataLocation + QDir::separator() + "mac-addresses.db");
if (!databaseFileInfo.exists()) {
continue;
@ -58,7 +58,7 @@ MacAddressDatabase::MacAddressDatabase(QObject *parent) : QObject(parent)
}
if (databaseFileName.isEmpty()) {
qCWarning(dcMacAddressDatabase()) << "Could not find the mac address database in any system data location paths" << QStandardPaths::standardLocations(QStandardPaths::DataLocation);
qCWarning(dcMacAddressDatabase()) << "Could not find the mac address database in any system data location paths" << QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
qCWarning(dcMacAddressDatabase()) << "The mac address database lookup feature will not be available.";
return;
}
@ -156,7 +156,11 @@ void MacAddressDatabase::runNextLookup()
m_currentReply = m_pendingReplies.dequeue();
m_currentReply->m_startTimestamp = QDateTime::currentMSecsSinceEpoch();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QFuture<QString> future = QtConcurrent::run(&MacAddressDatabase::lookupMacAddressVendorInternal, this, m_currentReply->macAddress());
#else
QFuture<QString> future = QtConcurrent::run(this, &MacAddressDatabase::lookupMacAddressVendorInternal, m_currentReply->macAddress());
#endif
m_futureWatcher->setFuture(future);
}
@ -185,7 +189,12 @@ QString MacAddressDatabase::lookupMacAddressVendorInternal(const QString &macAdd
QString searchString = fullMacAddressString.left(length);
QString queryString = QString("SELECT COUNT(oui) FROM oui WHERE oui LIKE \'%1%\';").arg(searchString);
qCDebug(dcMacAddressDatabase()) << "Query:" << queryString;
QSqlQuery countQuery = m_db.exec(queryString);
QSqlQuery countQuery(m_db);
if (!countQuery.exec(queryString)) {
qCWarning(dcMacAddressDatabase()) << "Unable to execute SQL query" << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
break;
}
if (countQuery.lastError().isValid()) {
qCWarning(dcMacAddressDatabase()) << "Query finished with error" << countQuery.lastError().text();
break;
@ -201,11 +210,16 @@ QString MacAddressDatabase::lookupMacAddressVendorInternal(const QString &macAdd
// Query the name
queryString = QString("SELECT companyName from companyNames WHERE rowid IS (SELECT companyNameIndex FROM oui WHERE oui=\'%1\');").arg(searchString);
qCDebug(dcMacAddressDatabase()) << "Query:" << queryString;
countQuery = m_db.exec(queryString);
if (!countQuery.next())
QSqlQuery rowQuery(m_db);
if (!rowQuery.exec(queryString)) {
qCWarning(dcMacAddressDatabase()) << "Unable to execute SQL query" << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
break;
}
if (!rowQuery.next())
break;
manufacturer = countQuery.value(0).toString();
manufacturer = rowQuery.value(0).toString();
break;
}

View File

@ -32,9 +32,10 @@
#include "mqttchannelimplementation.h"
#include "loggingcategories.h"
#include <QtDebug>
#include <QUuid>
#include <QtDebug>
#include <QNetworkInterface>
#include <QRegularExpression>
namespace nymeaserver {
@ -52,7 +53,7 @@ MqttChannel *MqttProviderImplementation::createChannel(const QHostAddress &clien
QString clientId;
// Generate a clientId that hasn't been used yet.
do {
clientId = QUuid::createUuid().toString().remove(QRegExp("[{}-]")).left(16);
clientId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")).left(16);
} while (m_createdChannels.contains(clientId));
return createChannel(clientId, clientAddress, topicPrefixList);
@ -60,8 +61,8 @@ MqttChannel *MqttProviderImplementation::createChannel(const QHostAddress &clien
MqttChannel *MqttProviderImplementation::createChannel(const QString &clientId, const QHostAddress &clientAddress, const QStringList &topicPrefixList)
{
QString username = QUuid::createUuid().toString().remove(QRegExp("[{}-]")).left(16);
QString password = QUuid::createUuid().toString().remove(QRegExp("[{}-]")).left(16);
QString username = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")).left(16);
QString password = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")).left(16);
return createChannel(clientId, username, password, clientAddress, topicPrefixList);
}
@ -160,8 +161,8 @@ MqttClient *MqttProviderImplementation::createInternalClient(const QString &clie
MqttPolicy policy;
policy.clientId = clientId;
policy.username = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
policy.password = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
policy.username = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
policy.password = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
policy.allowedPublishTopicFilters.append("#");
policy.allowedSubscribeTopicFilters.append("#");
m_broker->updatePolicy(policy);

View File

@ -137,13 +137,15 @@ void NetworkAccessManagerImpl::setEnabled(bool enabled)
return;
}
if (enabled) {
m_manager->setNetworkAccessible(QNetworkAccessManager::Accessible);
qCDebug(dcNetworkManager()) << "Network Manager enabled";
} else {
m_manager->setNetworkAccessible(QNetworkAccessManager::NotAccessible);
qCDebug(dcNetworkManager()) << "Network Manager disabled";
}
// FIXME Qt6: disabeling the networkmanager should be updated with the new hardware resource methods
// if (enabled) {
// m_manager->setNetworkAccessible(QNetworkAccessManager::Accessible);
// qCDebug(dcNetworkManager()) << "Network Manager enabled";
// } else {
// m_manager->setNetworkAccessible(QNetworkAccessManager::NotAccessible);
// qCDebug(dcNetworkManager()) << "Network Manager disabled";
// }
m_enabled = enabled;
}

View File

@ -55,8 +55,8 @@
#include <QNetworkInterface>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QNetworkConfiguration>
#include <QNetworkConfigurationManager>
#include <QRegularExpression>
#include <QStringView>
namespace nymeaserver {
@ -70,10 +70,13 @@ UpnpDiscoveryImplementation::UpnpDiscoveryImplementation(QNetworkAccessManager *
m_notificationTimer->setSingleShot(false);
connect(m_notificationTimer, &QTimer::timeout, this, &UpnpDiscoveryImplementation::notificationTimeout);
QNetworkConfigurationManager *configManager = new QNetworkConfigurationManager(this);
connect(configManager, &QNetworkConfigurationManager::configurationAdded, this, &UpnpDiscoveryImplementation::networkConfigurationChanged);
connect(configManager, &QNetworkConfigurationManager::configurationRemoved, this, &UpnpDiscoveryImplementation::networkConfigurationChanged);
connect(configManager, &QNetworkConfigurationManager::configurationChanged, this, &UpnpDiscoveryImplementation::networkConfigurationChanged);
// FIXME Qt6: maybe we can use udev in order to detect network configuration changes, centralized,
// also available for integration plugins since there are some examples
// QNetworkConfigurationManager *configManager = new QNetworkConfigurationManager(this);
// connect(configManager, &QNetworkConfigurationManager::configurationAdded, this, &UpnpDiscoveryImplementation::networkConfigurationChanged);
// connect(configManager, &QNetworkConfigurationManager::configurationRemoved, this, &UpnpDiscoveryImplementation::networkConfigurationChanged);
// connect(configManager, &QNetworkConfigurationManager::configurationChanged, this, &UpnpDiscoveryImplementation::networkConfigurationChanged);
m_available = true;
@ -132,7 +135,7 @@ void UpnpDiscoveryImplementation::respondToSearchRequest(QHostAddress host, int
// TODO: Once DeviceManager (and with that this can be moved into the server, use NymeaCore's configuration manager instead of parsing the config here...
NymeaSettings globalSettings(NymeaSettings::SettingsRoleGlobal);
globalSettings.beginGroup("nymead");
QByteArray uuid = globalSettings.value("uuid", QUuid()).toString().remove(QRegExp("[{}]")).toUtf8();
QByteArray uuid = globalSettings.value("uuid", QUuid()).toString().remove(QRegularExpression("[{}]")).toUtf8();
globalSettings.endGroup();
globalSettings.beginGroup("WebServer");
@ -184,12 +187,17 @@ void UpnpDiscoveryImplementation::respondToSearchRequest(QHostAddress host, int
// http://upnp.org/specs/basic/UPnP-basic-Basic-v1-Device.pdf
QByteArray rootdeviceResponseMessage = QByteArray("HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=1900\r\n"
"DATE: " + QDateTime::currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8() + " GMT\r\n"
"DATE: " +
QDateTime::currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8() +
" GMT\r\n"
"EXT:\r\n"
"LOCATION: " + locationString.toUtf8() + "\r\n"
"SERVER: nymea/" + QByteArray(NYMEA_VERSION_STRING) + " UPnP/1.1 \r\n"
"LOCATION: " + locationString.toUtf8() +
"\r\n"
"SERVER: nymea/" + QByteArray(NYMEA_VERSION_STRING) +
" UPnP/1.1 \r\n"
"ST: upnp:rootdevice\r\n"
"USN: uuid:" + uuid + "::urn:schemas-upnp-org:device:Basic:1\r\n"
"USN: uuid:" + uuid +
"::urn:schemas-upnp-org:device:Basic:1\r\n"
"\r\n");
qCDebug(dcUpnp()) << QString("Sending response to %1:%2").arg(host.toString()).arg(port);
@ -311,42 +319,81 @@ void UpnpDiscoveryImplementation::replyFinished()
if (xml.isStartElement()) {
if (xml.name().toString() == "device") {
while (!xml.atEnd()) {
if (xml.name() == "deviceType" && xml.isStartElement()) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (xml.name() == QLatin1StringView("deviceType") && xml.isStartElement()) {
upnpDeviceDescriptor.setDeviceType(xml.readElementText());
}
if (xml.name() == "friendlyName" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("friendlyName") && xml.isStartElement()) {
upnpDeviceDescriptor.setFriendlyName(xml.readElementText());
}
if (xml.name() == "manufacturer" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("manufacturer") && xml.isStartElement()) {
upnpDeviceDescriptor.setManufacturer(xml.readElementText());
}
if (xml.name() == "manufacturerURL" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("manufacturerURL") && xml.isStartElement()) {
upnpDeviceDescriptor.setManufacturerURL(QUrl(xml.readElementText()));
}
if (xml.name() == "modelDescription" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("modelDescription") && xml.isStartElement()) {
upnpDeviceDescriptor.setModelDescription(xml.readElementText());
}
if (xml.name() == "modelName" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("modelName") && xml.isStartElement()) {
upnpDeviceDescriptor.setModelName(xml.readElementText());
}
if (xml.name() == "modelNumber" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("modelNumber") && xml.isStartElement()) {
upnpDeviceDescriptor.setModelNumber(xml.readElementText());
}
if (xml.name() == "modelURL" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("modelURL") && xml.isStartElement()) {
upnpDeviceDescriptor.setModelURL(QUrl(xml.readElementText()));
}
if (xml.name() == "serialNumber" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("serialNumber") && xml.isStartElement()) {
upnpDeviceDescriptor.setSerialNumber(xml.readElementText());
}
if (xml.name() == "UDN" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("UDN") && xml.isStartElement()) {
upnpDeviceDescriptor.setUuid(xml.readElementText());
}
if (xml.name() == "uuid" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("uuid") && xml.isStartElement()) {
upnpDeviceDescriptor.setUuid(xml.readElementText());
}
if (xml.name() == "UPC" && xml.isStartElement()) {
if (xml.name() == QLatin1StringView("UPC") && xml.isStartElement()) {
upnpDeviceDescriptor.setUpc(xml.readElementText());
}
#else
if (xml.name() == QLatin1String("deviceType") && xml.isStartElement()) {
upnpDeviceDescriptor.setDeviceType(xml.readElementText());
}
if (xml.name() == QLatin1String("friendlyName") && xml.isStartElement()) {
upnpDeviceDescriptor.setFriendlyName(xml.readElementText());
}
if (xml.name() == QLatin1String("manufacturer") && xml.isStartElement()) {
upnpDeviceDescriptor.setManufacturer(xml.readElementText());
}
if (xml.name() == QLatin1String("manufacturerURL") && xml.isStartElement()) {
upnpDeviceDescriptor.setManufacturerURL(QUrl(xml.readElementText()));
}
if (xml.name() == QLatin1String("modelDescription") && xml.isStartElement()) {
upnpDeviceDescriptor.setModelDescription(xml.readElementText());
}
if (xml.name() == QLatin1String("modelName") && xml.isStartElement()) {
upnpDeviceDescriptor.setModelName(xml.readElementText());
}
if (xml.name() == QLatin1String("modelNumber") && xml.isStartElement()) {
upnpDeviceDescriptor.setModelNumber(xml.readElementText());
}
if (xml.name() == QLatin1String("modelURL") && xml.isStartElement()) {
upnpDeviceDescriptor.setModelURL(QUrl(xml.readElementText()));
}
if (xml.name() == QLatin1String("serialNumber") && xml.isStartElement()) {
upnpDeviceDescriptor.setSerialNumber(xml.readElementText());
}
if (xml.name() == QLatin1String("UDN") && xml.isStartElement()) {
upnpDeviceDescriptor.setUuid(xml.readElementText());
}
if (xml.name() == QLatin1String("uuid") && xml.isStartElement()) {
upnpDeviceDescriptor.setUuid(xml.readElementText());
}
if (xml.name() == QLatin1String("UPC") && xml.isStartElement()) {
upnpDeviceDescriptor.setUpc(xml.readElementText());
}
#endif
xml.readNext();
}
xml.readNext();
@ -403,14 +450,14 @@ void UpnpDiscoveryImplementation::sendByeByeMessage()
// http://upnp.org/specs/basic/UPnP-basic-Basic-v1-Device.pdf
QByteArray byebyeMessage = QByteArray("NOTIFY * HTTP/1.1\r\n"
"HOST:239.255.255.250:1900\r\n"
"CACHE-CONTROL: max-age=1900\r\n"
"LOCATION: " + locationString.toUtf8() + "\r\n"
"NT:urn:schemas-upnp-org:device:Basic:1\r\n"
"USN:uuid:" + uuid + "::urn:schemas-upnp-org:device:Basic:1\r\n"
"NTS: ssdp:byebye\r\n"
"SERVER: nymea/" + QByteArray(NYMEA_VERSION_STRING) + " UPnP/1.1 \r\n"
"\r\n");
"HOST:239.255.255.250:1900\r\n"
"CACHE-CONTROL: max-age=1900\r\n"
"LOCATION: " + locationString.toUtf8() + "\r\n"
"NT:urn:schemas-upnp-org:device:Basic:1\r\n"
"USN:uuid:" + uuid + "::urn:schemas-upnp-org:device:Basic:1\r\n"
"NTS: ssdp:byebye\r\n"
"SERVER: nymea/" + QByteArray(NYMEA_VERSION_STRING) + " UPnP/1.1 \r\n"
"\r\n");
sendToMulticast(byebyeMessage);
}
@ -449,14 +496,14 @@ void UpnpDiscoveryImplementation::sendAliveMessage()
// http://upnp.org/specs/basic/UPnP-basic-Basic-v1-Device.pdf
QByteArray aliveMessage = QByteArray("NOTIFY * HTTP/1.1\r\n"
"HOST:239.255.255.250:1900\r\n"
"CACHE-CONTROL: max-age=1900\r\n"
"LOCATION: " + locationString.toUtf8() + "\r\n"
"NT:urn:schemas-upnp-org:device:Basic:1\r\n"
"USN:uuid:" + uuid + "::urn:schemas-upnp-org:device:Basic:1\r\n"
"NTS: ssdp:alive\r\n"
"SERVER: nymea/" + QByteArray(NYMEA_VERSION_STRING) + " UPnP/1.1 \r\n"
"\r\n");
"HOST:239.255.255.250:1900\r\n"
"CACHE-CONTROL: max-age=1900\r\n"
"LOCATION: " + locationString.toUtf8() + "\r\n"
"NT:urn:schemas-upnp-org:device:Basic:1\r\n"
"USN:uuid:" + uuid + "::urn:schemas-upnp-org:device:Basic:1\r\n"
"NTS: ssdp:alive\r\n"
"SERVER: nymea/" + QByteArray(NYMEA_VERSION_STRING) + " UPnP/1.1 \r\n"
"\r\n");
sendToMulticast(aliveMessage);
}
@ -485,14 +532,14 @@ void UpnpDiscoveryImplementation::discoverTimeout()
delete discoveryRequest;
}
void UpnpDiscoveryImplementation::networkConfigurationChanged(const QNetworkConfiguration &config)
{
Q_UNUSED(config)
if (m_enabled) {
disable();
enable();
}
}
// void UpnpDiscoveryImplementation::networkConfigurationChanged(const QNetworkConfiguration &config)
// {
// Q_UNUSED(config)
// if (m_enabled) {
// disable();
// enable();
// }
// }
bool UpnpDiscoveryImplementation::enable()
{
@ -531,7 +578,11 @@ bool UpnpDiscoveryImplementation::enable()
m_available = true;
emit availableChanged(true);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(m_socket, &QUdpSocket::errorOccurred, this, &UpnpDiscoveryImplementation::error);
#else
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
#endif
connect(m_socket, &QUdpSocket::readyRead, this, &UpnpDiscoveryImplementation::readData);
m_notificationTimer->start();

View File

@ -94,7 +94,7 @@ private slots:
void sendAliveMessage();
void discoverTimeout();
void networkConfigurationChanged(const QNetworkConfiguration &config);
//void networkConfigurationChanged(const QNetworkConfiguration &config);
public slots:
bool enable();

View File

@ -41,7 +41,11 @@ Radio433BrennenstuhlGateway::Radio433BrennenstuhlGateway(QObject *parent) :
// UDP socket to sending data to gateway
m_gateway = new QUdpSocket(this);
connect(m_gateway, &QUdpSocket::readyRead, this, &Radio433BrennenstuhlGateway::readData);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(m_gateway, &QUdpSocket::errorOccurred, this, &Radio433BrennenstuhlGateway::gatewayError);
#else
connect(m_gateway, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(gatewayError(QAbstractSocket::SocketError)));
#endif
// Timer for discovery of the Gateway
m_discoverTimer = new QTimer(this);
@ -62,7 +66,7 @@ bool Radio433BrennenstuhlGateway::sendData(int delay, QList<int> rawData, int re
// bring rawData list to a single ByteArray, values separated by ','
foreach (int value, rawData) {
data.append(QString::number(value) + ",");
data.append(QString("%1,").arg(value).toUtf8());
}
/* Protocol:
@ -79,7 +83,7 @@ bool Radio433BrennenstuhlGateway::sendData(int delay, QList<int> rawData, int re
* ; | end of command
*/
message.append("TXP:0,0," + QString::number(repetitions) + ",0," + QString::number(delay) + "," + QString::number(rawData.count()/2) + "," + data + ";");
message.append(QString("TXP:0,0,%1,0,%2,%3,").arg(repetitions).arg(delay).arg(rawData.count() / 2).toUtf8() + data + QByteArray(";"));
if (m_gateway->writeDatagram(message, m_gatewayAddress, m_port) > 0) {
m_available = true;

View File

@ -36,7 +36,7 @@
#include <QHostAddress>
#include <QTimer>
#include "libnymea.h"
#include <libnymea.h>
namespace nymeaserver {

View File

@ -35,6 +35,7 @@
#include <QDir>
#include <QJsonDocument>
#include <QJsonObject>
#include <QRegularExpression>
#include "nymeasettings.h"
#include "loggingcategories.h"
@ -46,7 +47,7 @@ PluginInfoCache::PluginInfoCache()
void PluginInfoCache::cachePluginInfo(const QJsonObject &metaData)
{
QString fileName = metaData.value("id").toString().remove(QRegExp("[{}]")) + ".cache";
QString fileName = metaData.value("id").toString().remove(QRegularExpression("[{}]")) + ".cache";
QDir path = NymeaSettings::cachePath() + "/plugininfo/";
if (!path.exists()) {
if (!path.mkpath(path.absolutePath())) {
@ -65,7 +66,7 @@ void PluginInfoCache::cachePluginInfo(const QJsonObject &metaData)
QJsonObject PluginInfoCache::loadPluginInfo(const PluginId &pluginId)
{
QString fileName = pluginId.toString().remove(QRegExp("[{}]")) + ".cache";
QString fileName = pluginId.toString().remove(QRegularExpression("[{}]")) + ".cache";
QDir path = NymeaSettings::cachePath() + "/plugininfo/";
QFile file(path.absoluteFilePath(fileName));
if (!file.open(QFile::ReadOnly)) {

View File

@ -15,28 +15,28 @@ PyObject *QVariantToPyObject(const QVariant &value)
{
PyObject *pyValue = nullptr;
switch (value.type()) {
case QVariant::Bool:
switch (static_cast<int>(value.type())) {
case QMetaType::Bool:
pyValue = PyBool_FromLong(value.toBool());
break;
case QVariant::Int:
case QVariant::UInt:
case QVariant::LongLong:
case QVariant::ULongLong:
case QMetaType::Int:
case QMetaType::UInt:
case QMetaType::LongLong:
case QMetaType::ULongLong:
pyValue = PyLong_FromLongLong(value.toLongLong());
break;
case QVariant::String:
case QVariant::ByteArray:
case QMetaType::QString:
case QMetaType::QByteArray:
pyValue = PyUnicode_FromString(value.toString().toUtf8());
break;
case QVariant::Double:
case QMetaType::Double:
pyValue = PyFloat_FromDouble(value.toDouble());
break;
case QVariant::Invalid:
case QMetaType::UnknownType:
pyValue = Py_None;
Py_INCREF(pyValue);
break;
case QVariant::DateTime: {
case QMetaType::QDateTime: {
PyObject *longObj = PyLong_FromLongLong(value.toDateTime().toMSecsSinceEpoch() / 1000);
PyObject *timeTuple = Py_BuildValue("(O)", longObj);
pyValue = PyDateTime_FromTimestamp(timeTuple);
@ -44,7 +44,7 @@ PyObject *QVariantToPyObject(const QVariant &value)
Py_DECREF(timeTuple);
break;
}
case QVariant::StringList: {
case QMetaType::QStringList: {
QStringList stringList = value.toStringList();
pyValue = PyTuple_New(stringList.length());
for (int i = 0; i < stringList.count(); i++) {

View File

@ -76,8 +76,8 @@ bool ScriptIntegrationPlugin::loadScript(const QString &fileName)
spacer += ' ';
}
QDebug dbg = qWarning(dcThingManager()).nospace().noquote();
dbg << metaDataFileName << ":" << lineIndex << ":" << errorOffset + 2 << ": error: JSON parsing failed: " << error.errorString() << ": " << data.trimmed() << endl;
dbg << data << endl;
dbg << metaDataFileName << ":" << lineIndex << ":" << errorOffset + 2 << ": error: JSON parsing failed: " << error.errorString() << ": " << data.trimmed() << '\n';
dbg << data << '\n';
dbg << spacer << "^";
return false;
}

View File

@ -71,6 +71,7 @@
#include <QDir>
#include <QJsonDocument>
#include <QMetaEnum>
#include <QRegularExpression>
ThingManagerImplementation::ThingManagerImplementation(HardwareManager *hardwareManager, LogEngine *logEngine, const QLocale &locale, QObject *parent) :
ThingManager(parent),
@ -1962,7 +1963,8 @@ void ThingManagerImplementation::onLoaded()
void ThingManagerImplementation::cleanupThingStateCache()
{
QDir dir(NymeaSettings::cachePath() + "/thingstates/");
foreach (const QFileInfo &entry, dir.entryList()) {
foreach (const QString &entryString, dir.entryList()) {
QFileInfo entry(entryString);
ThingId thingId(entry.baseName());
if (!m_configuredThings.contains(thingId)) {
qCDebug(dcThingManager()) << "Thing ID" << thingId.toString() << "not found in configured things. Cleaning up stale thing state cache.";
@ -2298,7 +2300,7 @@ void ThingManagerImplementation::postSetupThing(Thing *thing)
QString ThingManagerImplementation::statesCacheFile(const ThingId &thingId)
{
return NymeaSettings::cachePath() + "/thingstates/" + thingId.toString().remove(QRegExp("[{}]")) + ".cache";
return NymeaSettings::cachePath() + "/thingstates/" + thingId.toString().remove(QRegularExpression("[{}]")) + ".cache";
}
void ThingManagerImplementation::loadThingStates(Thing *thing)
@ -2331,8 +2333,17 @@ void ThingManagerImplementation::loadThingStates(Thing *thing)
value = settings->value(stateType.id().toString());
}
value.convert(stateType.type());
minValue.convert(stateType.type());
maxValue.convert(stateType.type());
// Note: the convert method has changed in Qt6. I will return a valid
// QVariant and therefore set min max values for the state even tough they
// are still invalid on purpose
if (minValue.isValid())
minValue.convert(stateType.type());
if (maxValue.isValid())
maxValue.convert(stateType.type());
QVariantList convertedPossibleValues;
foreach (QVariant possibleValue, possibleValues) {
possibleValue.convert(stateType.type());
@ -2409,12 +2420,12 @@ void ThingManagerImplementation::registerStateLogger(Thing *thing, const StateTy
{
StateType stateType = thing->thingClass().getStateType(stateTypeId);
QString name = thing->id().toString() + "-" + stateType.name();
QList<QVariant::Type> sampledTypes {
QVariant::Int,
QVariant::UInt,
QVariant::LongLong,
QVariant::ULongLong,
QVariant::Double
QList<QMetaType::Type> sampledTypes {
QMetaType::Int,
QMetaType::UInt,
QMetaType::LongLong,
QMetaType::ULongLong,
QMetaType::Double
};
Types::LoggingType loggingType = sampledTypes.contains(stateType.type()) ? Types::LoggingTypeSampled : Types::LoggingTypeDiscrete;
Logger *logger = m_logEngine->registerLogSource("state-" + name, {}, loggingType, stateType.name());

View File

@ -29,13 +29,15 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "translator.h"
#include "nymeasettings.h"
#include "thingmanagerimplementation.h"
#include "loggingcategories.h"
#include "integrations/integrationplugin.h"
#include <QCoreApplication>
#include <nymeasettings.h>
#include <loggingcategories.h>
#include <integrations/integrationplugin.h>
#include <QDir>
#include <QCoreApplication>
#include <QRegularExpression>
Translator::Translator(ThingManagerImplementation *thingManager):
m_thingManager(thingManager)
@ -99,7 +101,7 @@ void Translator::loadTranslator(IntegrationPlugin *plugin, const QLocale &locale
loaded = true;
}
} else {
QString pluginId = plugin->pluginId().toString().remove(QRegExp("[{}]"));
QString pluginId = plugin->pluginId().toString().remove(QRegularExpression("[{}]"));
foreach (const QString &pluginPath, m_thingManager->pluginSearchDirs()) {
if (translator->load(locale, pluginId, "-", QDir(pluginPath + "/translations/").absolutePath(), ".qm")) {

View File

@ -753,7 +753,7 @@ QVariantMap ConfigurationHandler::packBasicConfiguration()
QVariantMap basicConfiguration;
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("serverTime", NymeaCore::instance()->timeManager()->currentDateTime().toSecsSinceEpoch());
basicConfiguration.insert("timeZone", QTimeZone::systemTimeZoneId());
basicConfiguration.insert("language", NymeaCore::instance()->configuration()->locale().name());
basicConfiguration.insert("location", QVariantMap{

View File

@ -76,6 +76,7 @@
#include <QJsonDocument>
#include <QStringList>
#include <QSslConfiguration>
#include <QRegularExpression>
namespace nymeaserver {
@ -698,7 +699,7 @@ void JsonRPCServerImplementation::processJsonPacket(TransportInterface *interfac
qCDebug(dcJsonRpc()) << "Invoking method" << targetNamespace + '.' + method << "from client" << clientId;
JsonReply *reply;
JsonReply *reply = nullptr;
if (handler->metaObject()->indexOfMethod(method.toUtf8() + "(QVariantMap,JsonContext)") >= 0) {
QMetaObject::invokeMethod(handler, method.toUtf8().data(), Q_RETURN_ARG(JsonReply*, reply), Q_ARG(QVariantMap, params), Q_ARG(JsonContext, callContext));
} else {
@ -861,12 +862,18 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
// Sanity checks on API:
// * Make sure all $ref: entries are valid.
// * A handler must not register a type name that is already registered by a previously loaded handler with different content.
qCDebug(dcJsonRpc()).nospace() << "Registering JSON RPC handler: " << handler->name() << ". Performing sanity check";
QVariantMap enums = m_api.value("enums").toMap();
QVariantMap flags = m_api.value("flags").toMap();
QVariantMap types = m_api.value("types").toMap();
QVariantMap methods = m_api.value("methods").toMap();
QVariantMap notifications = m_api.value("notifications").toMap();
QVariantMap apiIncludingThis = m_api;
// Verify enums name clash
QVariantMap enums = m_api.value("enums").toMap();
foreach (const QString &enumName, handler->jsonEnums().keys()) {
QVariantList list = handler->jsonEnums().value(enumName).toList();
if (enums.contains(enumName) && enums.value(enumName) != list) {
@ -877,7 +884,6 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
}
apiIncludingThis["enums"] = enums;
QVariantMap flags = m_api.value("flags").toMap();
foreach (const QString &flagName, handler->jsonFlags().keys()) {
QVariant flagDescription = handler->jsonFlags().value(flagName);
if (enums.contains(flagName)) {
@ -893,17 +899,21 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
apiIncludingThis["flags"] = flags;
// Verify objects
QVariantMap existingTypes = m_api.value("types").toMap();
QVariantMap typesIncludingThis = existingTypes;
typesIncludingThis.unite(handler->jsonObjects());
QVariantMap typesIncludingThis = types;
foreach (const QString &objectName, handler->jsonObjects().keys()) {
if (types.contains(objectName) && types.value(objectName) != handler->jsonObjects().value(objectName)) {
qCWarning(dcJsonRpc()) << "Object type" << objectName << "is already registered and not the same. Not registering handler" << handler->name();
qCWarning(dcJsonRpc()) << types.value(objectName);
qCWarning(dcJsonRpc()) << handler->jsonObjects().value(objectName);
return false;
} else {
typesIncludingThis.insert(objectName, handler->jsonObjects().value(objectName));
}
}
apiIncludingThis["types"] = typesIncludingThis;
foreach (const QString &objectName, handler->jsonObjects().keys()) {
QVariantMap object = handler->jsonObjects().value(objectName).toMap();
// Check for name clashes
if (existingTypes.contains(objectName) && existingTypes.value(objectName) != handler->jsonObjects().value(objectName)) {
qCWarning(dcJsonRpc()) << "Object type" << objectName << "is already registered. Not registering handler" << handler->name();
return false;
}
// Check for invalid $ref: entries
if (!JsonValidator::checkRefs(object, apiIncludingThis)) {
qCWarning(dcJsonRpc()).nospace() << "Invalid reference in object type " << objectName << ". Not registering handler " << handler->name();
@ -917,7 +927,7 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
QVariantMap method = handler->jsonMethods().value(methodName).toMap();
if (handler->metaObject()->indexOfMethod(methodName.toUtf8() + "(QVariantMap)") < 0
&& handler->metaObject()->indexOfMethod(methodName.toUtf8() + "(QVariantMap,JsonContext)") < 0) {
&& handler->metaObject()->indexOfMethod(methodName.toUtf8() + "(QVariantMap,JsonContext)") < 0) {
qCWarning(dcJsonRpc()).nospace().noquote() << "Invalid method \"" << methodName << "\". Method \"JsonReply* " + methodName + "(QVariantMap,JsonContext)\" does not exist. Not registering handler " << handler->name();
return false;
}
@ -931,7 +941,16 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
}
newMethods.insert(handler->name() + '.' + methodName, method);
}
methods.unite(newMethods);
foreach (const QString &methodName, newMethods.keys()) {
if (methods.contains(methodName)) {
qCWarning(dcJsonRpc()).nospace() << "Method " << methodName << " has already been registered. Not registering handler " << handler->name();
return false;
}
methods.insert(methodName, newMethods.value(methodName));
}
apiIncludingThis["methods"] = methods;
// Verify notifications
@ -944,7 +963,17 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
}
newNotifications.insert(handler->name() + '.' + notificationName, notification);
}
notifications.unite(newNotifications);
foreach (const QString &notificationName, newNotifications.keys()) {
if (notifications.contains(notificationName)) {
qCWarning(dcJsonRpc()).nospace() << "Notification " << notificationName << " has already been registered. Not registering handler " << handler->name();
return false;
}
notifications.insert(notificationName, newNotifications.value(notificationName));
}
apiIncludingThis["notifications"] = notifications;
// Checks completed. Store new API
@ -954,10 +983,10 @@ bool JsonRPCServerImplementation::registerHandler(JsonHandler *handler)
m_handlers.insert(handler->name(), handler);
for (int i = 0; i < handler->metaObject()->methodCount(); ++i) {
QMetaMethod method = handler->metaObject()->method(i);
if (method.methodType() == QMetaMethod::Signal && QString(method.name()).contains(QRegExp("^[A-Z]"))) {
if (method.parameterCount() == 1 && method.parameterType(0) == QVariant::Map) {
if (method.methodType() == QMetaMethod::Signal && QString(method.name()).contains(QRegularExpression("^[A-Z]"))) {
if (method.parameterCount() == 1 && method.parameterType(0) == QMetaType::QVariantMap) {
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendNotification(QVariantMap)")));
} else if (method.parameterCount() == 2 && method.parameterType(0) == QVariant::Uuid && method.parameterType(1) == QVariant::Map) {
} else if (method.parameterCount() == 2 && method.parameterType(0) == QMetaType::QUuid && method.parameterType(1) == QMetaType::QVariantMap) {
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendClientNotification(QUuid,QVariantMap)")));
}
}

View File

@ -104,19 +104,19 @@ private slots:
private:
QVariantMap m_api;
QHash<JsonHandler*, QString> m_experiences;
QHash<JsonHandler *, QString> m_experiences;
QHash<QString, JsonHandler *> m_handlers;
QHash<JsonReply *, TransportInterface *> m_asyncReplies;
QHash<QUuid, TransportInterface*> m_clientTransports;
QHash<QUuid, TransportInterface *> m_clientTransports;
QHash<QUuid, QByteArray> m_clientBuffers;
QHash<QUuid, QStringList> m_clientNotifications;
QHash<QUuid, QLocale> m_clientLocales;
QHash<QUuid, QByteArray> m_clientTokens;
QHash<int, QUuid> m_pushButtonTransactions;
QHash<QUuid, QTimer*> m_newConnectionWaitTimers;
QHash<QUuid, QTimer *> m_newConnectionWaitTimers;
QHash<QString, JsonReply*> m_pairingRequests;
QHash<QString, JsonReply *> m_pairingRequests;
int m_notificationId;

View File

@ -36,6 +36,7 @@
#include <QJsonDocument>
#include <QColor>
#include <QDateTime>
#include <QRegularExpression>
namespace nymeaserver {
@ -48,26 +49,26 @@ bool JsonValidator::checkRefs(const QVariantMap &map, const QVariantMap &api)
if (map.value(key).toString().startsWith("$ref:")) {
QString refName = map.value(key).toString().remove("$ref:");
if (!enums.contains(refName) && !flags.contains(refName) && !types.contains(refName)) {
qCWarning(dcJsonRpc()) << "Invalid reference to" << refName;
qCWarning(dcJsonRpc()) << "Invalid reference to" << refName << "in" << key << map;
return false;
}
}
if (map.value(key).type() == QVariant::Map) {
if (map.value(key).userType() == QMetaType::QVariantMap) {
bool ret = checkRefs(map.value(key).toMap(), api);
if (!ret) {
return false;
}
}
if (map.value(key).type() == QVariant::List) {
if (map.value(key).userType() == QMetaType::QVariantList) {
foreach (const QVariant &entry, map.value(key).toList()) {
if (entry.toString().startsWith("$ref:")) {
QString refName = entry.toString().remove("$ref:");
if (!enums.contains(refName) && !flags.contains(refName) && !types.contains(refName)) {
qCWarning(dcJsonRpc()) << "Invalid reference to" << refName;
qCWarning(dcJsonRpc()) << "Invalid reference to" << refName << "in" << key << map;
return false;
}
}
if (entry.type() == QVariant::Map) {
if (entry.userType() == QMetaType::QVariantMap) {
bool ret = checkRefs(map.value(key).toMap(), api);
if (!ret) {
return false;
@ -113,16 +114,16 @@ JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const Q
{
// Make sure all required values are available
foreach (const QString &key, definition.keys()) {
QRegExp isOptional = QRegExp("^([a-z]:)*o:.*");
if (isOptional.exactMatch(key)) {
QRegularExpression isOptional = QRegularExpression("^([a-z]:)*o:.*");
if (isOptional.match(key).hasMatch()) {
continue;
}
QRegExp isReadOnly = QRegExp("^([a-z]:)*r:.*");
if (isReadOnly.exactMatch(key) && openMode.testFlag(QIODevice::WriteOnly)) {
QRegularExpression isReadOnly = QRegularExpression("^([a-z]:)*r:.*");
if (isReadOnly.match(key).hasMatch() && openMode.testFlag(QIODevice::WriteOnly)) {
continue;
}
QString trimmedKey = key;
trimmedKey.remove(QRegExp("^(o:|r:|d:)*"));
trimmedKey.remove(QRegularExpression("^(o:|r:|d:)*"));
if (!map.contains(trimmedKey)) {
return Result(false, "Missing required key: " + key, key);
}
@ -133,9 +134,10 @@ 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:|d:)*" + key);
if (regExp.exactMatch(definitionKey)) {
QRegularExpression regExp = QRegularExpression("(o:|r:|d:)" + key);
if (regExp.match(definitionKey).hasMatch()) {
expectedValue = definition.value(definitionKey);
break;
}
}
if (!expectedValue.isValid()) {
@ -158,14 +160,14 @@ JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const Q
return result;
}
}
}
return Result(true);
}
JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const QVariant &definition, const QVariantMap &api, QIODevice::OpenMode openMode)
{
if (definition.type() == QVariant::String) {
if (definition.userType() == QMetaType::QString) {
QString expectedTypeName = definition.toString();
if (expectedTypeName.startsWith("$ref:")) {
@ -178,16 +180,31 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const
QVariant refDefinition = enums.value(refName);
QVariantList enumList = refDefinition.toList();
if (!enumList.contains(value.toString())) {
return Result(false, "Expected enum value for" + refName + " but got " + value.toString());
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (value.metaType().id() == QMetaType::QStringList) {
foreach (const QString &valueString, value.toStringList()) {
if (!enumList.contains(valueString)) {
return Result(false, "Expected enum value for " + refName + " but got " + value.toString());
}
}
} else {
if (!enumList.contains(value.toString())) {
return Result(false, "Expected enum value for " + refName + " but got " + value.toString());
}
}
#else
if (!enumList.contains(value.toString())) {
return Result(false, "Expected enum value for " + refName + " but got " + value.toString());
}
#endif
return Result(true);
}
// Or flags
QVariantMap flags = api.value("flags").toMap();
if (flags.contains(refName)) {
QVariant refDefinition = flags.value(refName);
if (value.type() != QVariant::List && value.type() != QVariant::StringList) {
if (value.userType() != QMetaType::QVariantList && value.userType() != QMetaType::QStringList) {
return Result(false, "Expected flags " + refName + " but got " + value.toString());
}
QString flagEnum = refDefinition.toList().first().toString();
@ -206,7 +223,23 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const
}
JsonHandler::BasicType expectedBasicType = JsonHandler::enumNameToValue<JsonHandler::BasicType>(expectedTypeName);
QVariant::Type expectedVariantType = JsonHandler::basicTypeToVariantType(expectedBasicType);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
// Any string converts fine to Uuid, but the resulting uuid might be null
if (expectedBasicType == JsonHandler::Uuid && value.toUuid().isNull()) {
QString typeName(value.typeName());
//qCCritical(dcJsonRpc()) << value << value.userType() << value.typeId() << value.typeName() << value.toString() << value.toUuid() << value.canConvert(QMetaType::QUuid);
// Verify if this is one of our own uuid types
if (typeName == "ThingId" || typeName == "EventTypeId" || typeName == "StateTypeId" || typeName == "ActionTypeId") {
return Result(true);
}/* else {
return Result(false, "Invalid Uuid: " + value.toString());
}*/
}
#else
QMetaType::Type expectedVariantType = JsonHandler::basicTypeToMetaType(expectedBasicType);
// Verify basic compatiblity
if (expectedBasicType != JsonHandler::Variant && !value.canConvert(expectedVariantType)) {
@ -217,6 +250,8 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const
if (expectedBasicType == JsonHandler::Uuid && value.toUuid().isNull()) {
return Result(false, "Invalid Uuid: " + value.toString());
}
#endif
// Make sure ints are valid
if (expectedBasicType == JsonHandler::Int) {
bool ok;
@ -260,18 +295,27 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const
return Result(true);
}
if (definition.type() == QVariant::Map) {
if (value.type() != QVariant::Map) {
if (definition.userType() == QMetaType::QVariantMap) {
if (value.userType() != QMetaType::QVariantMap) {
return Result(false, "Invalid value. Expected a map but received: " + value.toString());
}
return validateMap(value.toMap(), definition.toMap(), api, openMode);
}
if (definition.type() == QVariant::List) {
if (definition.userType() == QMetaType::QVariantList) {
QVariantList list = definition.toList();
QVariant entryDefinition = list.first();
if (value.type() != QVariant::List && value.type() != QVariant::StringList) {
return Result(false, "Expected list of " + entryDefinition.toString() + " but got value of type " + value.typeName() + "\n" + QJsonDocument::fromVariant(value).toJson());
if (value.userType() != QMetaType::QVariantList && value.userType() != QMetaType::QStringList) {
QString elementType = QString(value.typeName()).remove("QList<").remove(">");
if (elementType == "ThingId" || elementType == "EventTypeId" || elementType == "StateTypeId" || elementType == "ActionTypeId") {
elementType = "Uuid";
} /*else if (elementType == "ParamList") {
elementType = "Param";
} else {
return Result(false, "Expected list of " + entryDefinition.toString() + " but got value of type " + value.typeName() + "\n" + QJsonDocument::fromVariant(value).toJson());
}*/
}
foreach (const QVariant &entry, value.toList()) {
Result result = validateEntry(entry, entryDefinition, api, openMode);

View File

@ -672,9 +672,11 @@ QVariantMap ZigbeeHandler::packNetwork(ZigbeeNetwork *network)
case Zigbee::ZigbeeBackendTypeNxp:
networkMap.insert("backend", ZigbeeAdapter::backendNames().value(ZigbeeAdapter::ZigbeeBackendTypeNxp));
break;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
case Zigbee::ZigbeeBackendTypeTi:
networkMap.insert("backend", ZigbeeAdapter::backendNames().value(ZigbeeAdapter::ZigbeeBackendTypeTi));
break;
#endif
}
switch (network->state()) {

View File

@ -3,38 +3,13 @@ TARGET = nymea-core
include(../nymea.pri)
QT += bluetooth dbus qml sql websockets serialport
QT += core bluetooth dbus qml sql websockets serialport
INCLUDEPATH += $$top_srcdir/libnymea $$top_builddir
LIBS += -L$$top_builddir/libnymea/ -lnymea -lssl -lcrypto
CONFIG += link_pkgconfig
PKGCONFIG += nymea-mqtt nymea-networkmanager nymea-zigbee nymea-remoteproxyclient nymea-gpio
CONFIG(withoutpython) {
message("Building without python support.")
CONFIG -= python
} else:packagesExist(python3-embed) {
# As of Ubuntu focal, there's a commonly named python3-embed pointing to the distro version of python
# For everything below python 3.8 we need to manually select one
PKGCONFIG += python3-embed
CONFIG += python
} else:packagesExist(python-3.5) {
# xenial, stretch
PKGCONFIG += python-3.5
CONFIG += python
} else:packagesExist(python-3.6) {
# bionic
PKGCONFIG += python-3.6
CONFIG += python
} else:packagesExist(python-3.7) {
# buster, eoan
PKGCONFIG += python-3.7
CONFIG += python
} else {
message("Python development package not found. Building without python support.")
CONFIG -= python
}
packagesExist(systemd) {
message(Building with systemd support)
PKGCONFIG += systemd
@ -43,12 +18,25 @@ packagesExist(systemd) {
message(Building without systemd support)
}
packagesExist(Qt5SerialBus) {
message("Building with QtSerialBus support.")
PKGCONFIG += Qt5SerialBus
DEFINES += WITH_QTSERIALBUS
greaterThan(QT_MAJOR_VERSION, 5) {
qtHaveModule(serialbus) {
message("Building with QtSerialBus support.")
QT *= serialbus
DEFINES += WITH_QTSERIALBUS
} else {
message("QtSerialBus package not found. Building without QtSerialBus support.")
}
# Separate module in Qt6
QT *= concurrent
} else {
message("Qt5SerialBus package not found. Building without QtSerialBus support.")
packagesExist(Qt5SerialBus) {
message("Building with QtSerialBus support.")
PKGCONFIG += Qt5SerialBus
DEFINES += WITH_QTSERIALBUS
} else {
message("Qt5SerialBus package not found. Building without QtSerialBus support.")
}
}
# Note: udev is not available on all platforms

View File

@ -34,6 +34,7 @@
#include <QUrlQuery>
#include <QJsonDocument>
#include <QCoreApplication>
#include <QRegularExpression>
LogEngineInfluxDB::LogEngineInfluxDB(const QString &host, const QString &dbName, const QString &username, const QString &password, QObject *parent)
: LogEngine{parent},
@ -185,18 +186,18 @@ void LogEngineInfluxDB::logEvent(Logger *logger, const QStringList &tags, const
foreach (const QString &name, values.keys()) {
QVariant value = values.value(name);
switch (value.type()) {
case QVariant::String:
case QVariant::ByteArray:
switch (value.userType()) {
case QMetaType::QString:
case QMetaType::QByteArray:
fieldsList.append(QString("%1=\"%2\"").arg(name).arg(QString(value.toByteArray().toPercentEncoding())));
break;
case QVariant::Uuid:
case QMetaType::QUuid:
fieldsList.append(QString("%1=\"%2\"").arg(name).arg(value.toString()));
break;
case QVariant::Int:
case QVariant::UInt:
case QVariant::LongLong:
case QVariant::ULongLong:
case QMetaType::Int:
case QMetaType::UInt:
case QMetaType::LongLong:
case QMetaType::ULongLong:
fieldsList.append(QString("%1=%2i").arg(name).arg(value.toString()));
break;
default:
@ -490,10 +491,10 @@ LogFetchJob *LogEngineInfluxDB::fetchLogEntries(const QStringList &sources, cons
for (int i = 1; i < columns.count(); i++) {
QString column = columns.at(i);
if (sampleRate != Types::SampleRateAny) {
column.remove(QRegExp("^mean_"));
column.remove(QRegularExpression("^mean_"));
}
QVariant value = values.at(i);
if (value.type() == QVariant::String || value.type() == QVariant::ByteArray) {
if (value.userType() == QMetaType::QString || value.userType() == QMetaType::QByteArray) {
valuesMap.insert(column, QByteArray::fromPercentEncoding(value.toByteArray()));
} else {
valuesMap.insert(column, values.at(i));
@ -838,3 +839,4 @@ void QueryJob::finish(QNetworkReply::NetworkError status, const QVariantList &re
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, status), Q_ARG(QVariantList, results));
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
}

View File

@ -31,7 +31,6 @@
#ifndef LOGENGINEINFLUXDB_H
#define LOGENGINEINFLUXDB_H
#include "logging/logengine.h"
#include <QObject>
#include <QTimer>
#include <QQueue>
@ -40,9 +39,15 @@
#include <QNetworkReply>
#include <QNetworkAccessManager>
class QueryJob: public QObject {
#include "logging/logentry.h"
#include "logging/logengine.h"
class QueryJob: public QObject
{
Q_OBJECT
public:
explicit QueryJob(const QNetworkRequest &request, bool post, bool isInit, QObject *parent = nullptr);
~QueryJob() = default;
signals:
void finished(QNetworkReply::NetworkError status, const QVariantList &response);
@ -118,12 +123,13 @@ private:
QHash<QString, Logger*> m_loggers;
QQueue<QueryJob*> m_initQueryQueue;
QQueue<QueryJob *> m_initQueryQueue;
QueryJob *m_currentInitQuery = nullptr;
QQueue<QueryJob*> m_queryQueue;
QQueue<QueryJob *> m_queryQueue;
QueryJob *m_currentQuery = nullptr;
QQueue<QueueEntry> m_writeQueue;
QNetworkReply* m_currentWriteReply = nullptr;
QNetworkReply *m_currentWriteReply = nullptr;
};
#endif // LOGENGINEINFLUXDB_H

View File

@ -320,7 +320,7 @@ QLocale NymeaConfiguration::locale() const
{
NymeaSettings settings(NymeaSettings::SettingsRoleGlobal);
settings.beginGroup("nymead");
return settings.value("language", "en_US").toString();
return QLocale(settings.value("language", "en_US").toString());
}
void NymeaConfiguration::setLocale(const QLocale &locale)

View File

@ -243,11 +243,11 @@ void Rule::setActive(const bool &active)
QDebug operator<<(QDebug dbg, const Rule &rule)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << endl << "=== Rule begin ===" << endl;
dbg.nospace() << "ID:" << rule.id().toString() << endl;
dbg.nospace() << "Name:" << rule.name() << endl;
dbg.nospace() << "Enabled:" << rule.enabled() << endl;
dbg.nospace() << "Active:" << rule.active() << endl;
dbg.nospace() << '\n' << "=== Rule begin ===" << '\n';
dbg.nospace() << "ID:" << rule.id().toString() << '\n';
dbg.nospace() << "Name:" << rule.name() << '\n';
dbg.nospace() << "Enabled:" << rule.enabled() << '\n';
dbg.nospace() << "Active:" << rule.active() << '\n';
dbg.nospace() << rule.eventDescriptors();
dbg.nospace() << rule.timeDescriptor();
dbg.nospace() << rule.stateEvaluator();

View File

@ -211,9 +211,9 @@ void RuleAction::operator=(const RuleAction &other)
QDebug operator<<(QDebug dbg, const RuleAction &ruleAction)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "RuleAction(ActionTypeId:" << ruleAction.actionTypeId().toString() << ", ThingId:" << ruleAction.thingId().toString() << ", Interface:" << ruleAction.interface() << ", InterfaceAction:" << ruleAction.interfaceAction() << ", BrowserItemId:" << ruleAction.browserItemId() << ")" << endl;
dbg.nospace() << "RuleAction(ActionTypeId:" << ruleAction.actionTypeId().toString() << ", ThingId:" << ruleAction.thingId().toString() << ", Interface:" << ruleAction.interface() << ", InterfaceAction:" << ruleAction.interfaceAction() << ", BrowserItemId:" << ruleAction.browserItemId() << ")" << '\n';
for (int i = 0; i < ruleAction.ruleActionParams().count(); i++) {
dbg.nospace() << " " << i << ": " << ruleAction.ruleActionParams().at(i) << endl;
dbg.nospace() << " " << i << ": " << ruleAction.ruleActionParams().at(i) << '\n';
}
return dbg;
}
@ -221,7 +221,7 @@ QDebug operator<<(QDebug dbg, const RuleAction &ruleAction)
QDebug operator<<(QDebug dbg, const QList<RuleAction> &ruleActionList)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "RuleActionList (count:" << ruleActionList.count() << "):" << endl;
dbg.nospace() << "RuleActionList (count:" << ruleActionList.count() << "):" << '\n';
for (int i = 0; i < ruleActionList.count(); i++ ) {
dbg.nospace() << " " << i << ": " << ruleActionList.at(i);
}

View File

@ -235,9 +235,9 @@ RuleActionParams RuleActionParams::operator<<(const RuleActionParam &ruleActionP
QDebug operator<<(QDebug dbg, const RuleActionParams &ruleActionParams)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "RuleActionParamList (count:" << ruleActionParams.count() << ")" << endl;
dbg.nospace() << "RuleActionParamList (count:" << ruleActionParams.count() << ")" << '\n';
for (int i = 0; i < ruleActionParams.count(); i++ ) {
dbg.nospace() << " " << i << ": " << ruleActionParams.at(i) << endl;
dbg.nospace() << " " << i << ": " << ruleActionParams.at(i) << '\n';
}
return dbg;

View File

@ -128,6 +128,8 @@
#include <QStandardPaths>
#include <QCoreApplication>
#include <QMetaEnum>
#include <QVariant>
#include <QRegularExpression>
NYMEA_LOGGING_CATEGORY(dcRuleEngine, "RuleEngine")
NYMEA_LOGGING_CATEGORY(dcRuleEngineDebug, "RuleEngineDebug")
@ -185,12 +187,20 @@ QList<Rule> RuleEngine::evaluateEvent(const Event &event)
}
ThingClass thingClass = thing->thingClass();
EventType eventType = thingClass.eventTypes().findById(event.eventTypeId());
StateType stateType = thingClass.stateTypes().findById(event.eventTypeId());
if (event.params().count() == 0) {
qCDebug(dcRuleEngineDebug).nospace().noquote() << "Evaluate event: " << thing->name() << " - " << eventType.name() << " (ThingId:" << thing->id().toString() << ", EventTypeId:" << eventType.id().toString() << ")";
} else {
qCDebug(dcRuleEngineDebug).nospace().noquote() << "Evaluate event: " << thing->name() << " - " << eventType.name() << " (ThingId:" << thing->id().toString() << ", EventTypeId:" << eventType.id().toString() << ")" << endl << " " << event.params();
if (eventType.isValid()) {
if (event.params().count() == 0) {
qCDebug(dcRuleEngineDebug).nospace().noquote() << "Evaluate event: " << thing->name() << " - " << eventType.name() << " (ThingId:" << thing->id().toString() << ", EventTypeId:" << eventType.id().toString() << ")";
} else {
qCDebug(dcRuleEngineDebug).nospace().noquote() << "Evaluate event: " << thing->name() << " - " << eventType.name() << " (ThingId:" << thing->id().toString() << ", EventTypeId:" << eventType.id().toString() << ")" << '\n' << " " << event.params();
}
} else if (stateType.isValid()) {
if (event.params().count() == 0) {
qCDebug(dcRuleEngineDebug).nospace().noquote() << "Evaluate event (state changed): " << thing->name() << " - " << stateType.name() << " (ThingId:" << thing->id().toString() << ", EventTypeId:" << stateType.id().toString() << ")";
} else {
qCDebug(dcRuleEngineDebug).nospace().noquote() << "Evaluate event (state changed): " << thing->name() << " - " << stateType.name() << " (ThingId:" << thing->id().toString() << ", EventTypeId:" << stateType.id().toString() << ")" << '\n' << " " << event.params();
}
}
QList<Rule> rules;
@ -342,7 +352,7 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
// Check IDs in each EventDescriptor
foreach (const EventDescriptor &eventDescriptor, rule.eventDescriptors()) {
if (!eventDescriptor.isValid()) {
qWarning(dcRuleEngine()) << "EventDescriptor is incomplete. It must have either eventTypeId and thingId, or interface and interfaceEvent";
qCWarning(dcRuleEngine()) << "EventDescriptor is incomplete. It must have either eventTypeId and thingId, or interface and interfaceEvent";
return RuleErrorEventTypeNotFound;
}
if (eventDescriptor.type() == EventDescriptor::TypeThing) {
@ -375,11 +385,11 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
// Interface based event
Interface iface = m_thingManager->supportedInterfaces().findByName(eventDescriptor.interface());
if (!iface.isValid()) {
qWarning(dcRuleEngine()) << "No such interface:" << eventDescriptor.interface();
qCWarning(dcRuleEngine()) << "No such interface:" << eventDescriptor.interface();
return RuleErrorInterfaceNotFound;
}
if (iface.eventTypes().findByName(eventDescriptor.interfaceEvent()).name().isEmpty() && iface.stateTypes().findByName(eventDescriptor.interfaceEvent()).name().isEmpty()) {
qWarning(dcRuleEngine()) << "Interface" << iface.name() << "has no such event:" << eventDescriptor.interfaceEvent();
qCWarning(dcRuleEngine()) << "Interface" << iface.name() << "has no such event:" << eventDescriptor.interfaceEvent();
return RuleErrorEventTypeNotFound;
}
}
@ -662,7 +672,7 @@ RuleEngine::RuleError RuleEngine::executeExitActions(const RuleId &ruleId)
qCDebug(dcRuleEngine) << "Executing rule exit actions of rule" << rule.name() << rule.id().toString();
m_logger->log({rule.id().toString(), "executed"}, {{"name", rule.name()}});
// m_logEngine->logRuleExitActionsExecuted(rule);
// m_logEngine->logRuleExitActionsExecuted(rule);
executeRuleActions(rule.id(), rule.exitActions());
return RuleErrorNoError;
}
@ -892,7 +902,7 @@ bool RuleEngine::containsEvent(const Rule &rule, const Event &event, const Thing
paramValue = event.param(paramDescriptor.paramTypeId()).value();
} else {
if (paramDescriptor.paramName().isEmpty()) {
qWarning(dcRuleEngine()) << "ParamDescriptor invalid. Either paramTypeId or paramName are required";
qCWarning(dcRuleEngine()) << "ParamDescriptor invalid. Either paramTypeId or paramName are required";
allOK = false;
continue;
}
@ -907,6 +917,47 @@ bool RuleEngine::containsEvent(const Rule &rule, const Event &event, const Thing
}
}
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
QVariant descriptorValue = paramDescriptor.value();
bool res = descriptorValue.convert(paramValue.metaType());
if (!res) {
qCWarning(dcRuleEngine()) << "Cannot convert the params:" << paramValue << paramDescriptor.value();
allOK = false;
} else {
// Converted successfully to the same type, now compare the values according to the param descriptor
QPartialOrdering ordering = QVariant::compare(paramValue, descriptorValue);
if (ordering == QPartialOrdering::Unordered) {
qCWarning(dcRuleEngine()) << "Cannot compair the params:" << paramValue << paramDescriptor.value();
allOK = false;
} else {
bool result = false;
switch (paramDescriptor.operatorType()) {
case Types::ValueOperatorEquals:
result = (ordering == QPartialOrdering::equivalent);
break;
case Types::ValueOperatorGreater:
result = (ordering == QPartialOrdering::greater);
break;
case Types::ValueOperatorGreaterOrEqual:
result = (ordering == QPartialOrdering::greater || ordering == QPartialOrdering::equivalent);
break;
case Types::ValueOperatorLess:
result = (ordering == QPartialOrdering::less);
break;
case Types::ValueOperatorLessOrEqual:
result = (ordering == QPartialOrdering::less || ordering == QPartialOrdering::equivalent);
break;
case Types::ValueOperatorNotEquals:
result = (ordering != QPartialOrdering::equivalent);
break;
}
qCDebug(dcRuleEngineDebug()) << "Comparing param value" << paramValue << "to descriptor value" << descriptorValue << "with operator" << paramDescriptor.operatorType() << "-->" << result;
allOK = result;
}
}
#else
switch (paramDescriptor.operatorType()) {
case Types::ValueOperatorEquals:
if (paramValue != paramDescriptor.value()) {
@ -939,6 +990,7 @@ bool RuleEngine::containsEvent(const Rule &rule, const Event &event, const Thing
}
break;
}
#endif
}
// All matching!
if (allOK) {
@ -981,7 +1033,7 @@ bool RuleEngine::containsState(const StateEvaluator &stateEvaluator, const Event
RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction, const Rule &rule)
{
if (!ruleAction.isValid()) {
qWarning(dcRuleEngine()) << "Action is incomplete. It must have either thingId and actionTypeId/browserItemId, or interface and interfaceAction:" << ruleAction;
qCWarning(dcRuleEngine()) << "Action is incomplete. It must have either thingId and actionTypeId/browserItemId, or interface and interfaceAction:" << ruleAction;
return RuleErrorActionTypeNotFound;
}
@ -1041,7 +1093,7 @@ RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction,
bool found = !paramType.defaultValue().isNull();
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
if (ruleActionParam.paramTypeId() == paramType.id()
|| ruleActionParam.paramName() == paramType.name()) {
|| ruleActionParam.paramName() == paramType.name()) {
found = true;
break;
}
@ -1081,8 +1133,13 @@ RuleEngine::RuleError RuleEngine::checkRuleActionParam(const RuleActionParam &ru
}
// check if the param type of the event and the action match
QVariant::Type eventParamType = getEventParamType(ruleActionParam.eventTypeId(), ruleActionParam.eventParamTypeId());
QVariant v(eventParamType);
QMetaType::Type eventParamType = getEventParamType(ruleActionParam.eventTypeId(), ruleActionParam.eventParamTypeId());
QVariant v;
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
v = QVariant::fromMetaType(QMetaType(eventParamType));
#else
v = QVariant(static_cast<QVariant::Type>(eventParamType));
#endif
if (eventParamType != paramType.type() && !v.canConvert(static_cast<int>(paramType.type()))) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleActionParam" << ruleActionParam.paramTypeId().toString() << " and given event param " << ruleActionParam.eventParamTypeId().toString() << "have not the same type:";
qCWarning(dcRuleEngine) << " -> actionParamType:" << paramType.type();
@ -1097,8 +1154,13 @@ RuleEngine::RuleError RuleEngine::checkRuleActionParam(const RuleActionParam &ru
}
ThingClass stateThingClass = m_thingManager->findThingClass(d->thingClassId());
StateType stateType = stateThingClass.stateTypes().findById(ruleActionParam.stateTypeId());
QVariant::Type actionParamType = getActionParamType(actionType.id(), ruleActionParam.paramTypeId());
QVariant v(stateType.type());
QMetaType::Type actionParamType = getActionParamType(actionType.id(), ruleActionParam.paramTypeId());
QVariant v;
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
v = QVariant::fromMetaType(QMetaType(stateType.type()));
#else
v = QVariant(static_cast<QVariant::Type>(stateType.type()));
#endif
if (actionParamType != stateType.type() && !v.canConvert(static_cast<int>(actionParamType))) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleActionParam" << ruleActionParam.paramTypeId().toString() << " and given state based param " << ruleActionParam.stateTypeId().toString() << "have not the same type:";
qCWarning(dcRuleEngine) << " -> actionParamType:" << actionParamType;
@ -1110,7 +1172,7 @@ RuleEngine::RuleError RuleEngine::checkRuleActionParam(const RuleActionParam &ru
qCDebug(dcRuleEngine()) << "Cannot create rule. No param value given for action:" << ruleActionParam.paramTypeId().toString();
return RuleErrorInvalidRuleActionParameter;
}
if (paramType.type() != ruleActionParam.value().type() && !ruleActionParam.value().canConvert(static_cast<int>(paramType.type()))) {
if (paramType.type() != ruleActionParam.value().userType() && !ruleActionParam.value().canConvert(static_cast<int>(paramType.type()))) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleActionParam" << ruleActionParam.paramTypeId().toString() << " and given state based param " << ruleActionParam.stateTypeId().toString() << "have not the same type:";
qCWarning(dcRuleEngine) << " -> actionParamType:" << paramType.type();
qCWarning(dcRuleEngine) << " -> stateType:" << ruleActionParam.value().type();
@ -1121,7 +1183,7 @@ RuleEngine::RuleError RuleEngine::checkRuleActionParam(const RuleActionParam &ru
return RuleErrorNoError;
}
QVariant::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId &paramTypeId)
QMetaType::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId &paramTypeId)
{
foreach (const ThingClass &thingClass, m_thingManager->supportedThings()) {
foreach (const ActionType &actionType, thingClass.actionTypes()) {
@ -1135,10 +1197,10 @@ QVariant::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId,
}
}
return QVariant::Invalid;
return QMetaType::UnknownType;
}
QVariant::Type RuleEngine::getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId &paramTypeId)
QMetaType::Type RuleEngine::getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId &paramTypeId)
{
foreach (const ThingClass &thingClass, m_thingManager->supportedThings()) {
foreach (const EventType &eventType, thingClass.eventTypes()) {
@ -1157,7 +1219,7 @@ QVariant::Type RuleEngine::getEventParamType(const EventTypeId &eventTypeId, con
}
}
return QVariant::Invalid;
return QMetaType::UnknownType;
}
void RuleEngine::appendRule(const Rule &rule)
@ -1187,7 +1249,7 @@ void RuleEngine::saveRule(const Rule &rule)
CalendarItem calendarItem = rule.timeDescriptor().calendarItems().at(i);
if (calendarItem.dateTime().isValid())
settings.setValue("dateTime", calendarItem.dateTime().toTime_t());
settings.setValue("dateTime", calendarItem.dateTime().toSecsSinceEpoch());
if (calendarItem.startTime().isValid())
settings.setValue("startTime", calendarItem.startTime().toString("hh:mm"));
@ -1221,7 +1283,7 @@ void RuleEngine::saveRule(const Rule &rule)
TimeEventItem timeEventItem = rule.timeDescriptor().timeEventItems().at(i);
if (timeEventItem.dateTime().isValid())
settings.setValue("dateTime", timeEventItem.dateTime().toTime_t());
settings.setValue("dateTime", timeEventItem.dateTime().toSecsSinceEpoch());
if (timeEventItem.time().isValid())
settings.setValue("time", timeEventItem.time().toString("hh:mm"));
@ -1339,7 +1401,7 @@ QList<RuleAction> RuleEngine::loadRuleActions(NymeaSettings *settings)
foreach (QString paramTypeIdString, settings->childGroups()) {
if (paramTypeIdString.startsWith("RuleActionParam-")) {
settings->beginGroup(paramTypeIdString);
QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-"));
QString strippedParamTypeIdString = paramTypeIdString.remove(QRegularExpression("^RuleActionParam-"));
EventTypeId eventTypeId = EventTypeId(settings->value("eventTypeId", EventTypeId()).toString());
ParamTypeId eventParamTypeId = ParamTypeId(settings->value("eventParamTypeId", ParamTypeId()).toString());
ThingId stateThingId = ThingId(settings->value("stateThingId", ThingId()).toString());
@ -1349,9 +1411,9 @@ QList<RuleAction> RuleEngine::loadRuleActions(NymeaSettings *settings)
StateTypeId stateTypeId = StateTypeId(settings->value("stateTypeId", StateTypeId()).toString());
QVariant value = settings->value("value");
if (settings->contains("valueType")) {
QVariant::Type valueType = static_cast<QVariant::Type>(settings->value("valueType").toInt());
QMetaType::Type valueType = static_cast<QMetaType::Type>(settings->value("valueType").toInt());
// Note: only warn, and continue with the QVariant guessed type
if (valueType == QVariant::Invalid) {
if (valueType == QMetaType::UnknownType) {
qCWarning(dcRuleEngine()) << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant.";
} else if (!value.canConvert(static_cast<int>(valueType))) {
qCWarning(dcRuleEngine()) << "Error loading rule action. Could not convert the rule action param value" << value << "to the stored type" << valueType;
@ -1510,11 +1572,11 @@ void RuleEngine::executeRuleActions(const RuleId &ruleId, const QList<RuleAction
}
ActionType actionType = m_thingManager->findConfiguredThing(action.thingId())->thingClass().actionTypes().findById(action.actionTypeId());
m_logger->log({ruleId.toString(), "executed"}, {
{"name", m_rules.value(ruleId).name()},
{"status", QMetaEnum::fromType<Thing::ThingError>().valueToKey(info->status())},
{"thingId", info->action().thingId()},
{"action", actionType.name()}
});
{"name", m_rules.value(ruleId).name()},
{"status", QMetaEnum::fromType<Thing::ThingError>().valueToKey(info->status())},
{"thingId", info->action().thingId()},
{"action", actionType.name()}
});
});
}
@ -1524,11 +1586,11 @@ void RuleEngine::executeRuleActions(const RuleId &ruleId, const QList<RuleAction
if (info->status() != Thing::ThingErrorNoError) {
qCWarning(dcRuleEngine) << "Error executing browser action:" << info->status();
m_logger->log({ruleId.toString(), "executed"}, {
{"name", m_rules.value(ruleId).name()},
{"status", QMetaEnum::fromType<Thing::ThingError>().valueToKey(info->status())},
{"thingId", info->browserAction().thingId()},
{"browserItem", info->browserAction().itemId()}
});
{"name", m_rules.value(ruleId).name()},
{"status", QMetaEnum::fromType<Thing::ThingError>().valueToKey(info->status())},
{"thingId", info->browserAction().thingId()},
{"browserItem", info->browserAction().itemId()}
});
}
});
}
@ -1546,9 +1608,9 @@ void RuleEngine::onEventTriggered(const Event &event)
// Event based
if (!rule.eventDescriptors().isEmpty()) {
m_logger->log({rule.id().toString()}, {
{"name", rule.name()},
{"state", "triggered"}
});
{"name", rule.name()},
{"state", "triggered"}
});
QList<RuleAction> tmp;
if (rule.statesActive() && rule.timeActive()) {
@ -1587,9 +1649,9 @@ void RuleEngine::onEventTriggered(const Event &event)
} else {
// State based rule
m_logger->log({rule.id().toString()}, {
{"name", rule.name()},
{"state", rule.active() ? "active" : "inactive"}
});
{"name", rule.name()},
{"state", rule.active() ? "active" : "inactive"}
});
emit ruleActiveChanged(rule);
if (rule.active()) {
executeRuleActions(rule.id(), rule.actions());
@ -1668,7 +1730,7 @@ void RuleEngine::init()
settings.beginGroup(childGroup);
CalendarItem calendarItem;
calendarItem.setDateTime(QDateTime::fromTime_t(settings.value("dateTime", 0).toUInt()));
calendarItem.setDateTime(QDateTime::fromSecsSinceEpoch(settings.value("dateTime", 0).toUInt()));
calendarItem.setStartTime(QTime::fromString(settings.value("startTime").toString()));
calendarItem.setDuration(settings.value("duration", 0).toUInt());
@ -1706,7 +1768,7 @@ void RuleEngine::init()
settings.beginGroup(childGroup);
TimeEventItem timeEventItem;
timeEventItem.setDateTime(QDateTime::fromTime_t(settings.value("dateTime", 0).toUInt()));
timeEventItem.setDateTime(QDateTime::fromSecsSinceEpoch(settings.value("dateTime", 0).toUInt()));
timeEventItem.setTime(QTime::fromString(settings.value("time").toString()));
QList<int> weekDays;
@ -1758,13 +1820,13 @@ void RuleEngine::init()
foreach (QString groupName, settings.childGroups()) {
if (groupName.startsWith("ParamDescriptor-")) {
settings.beginGroup(groupName);
QString strippedGroupName = groupName.remove(QRegExp("^ParamDescriptor-"));
QString strippedGroupName = groupName.remove(QRegularExpression("^ParamDescriptor-"));
QVariant value = settings.value("value");
if (settings.contains("valueType")) {
QVariant::Type valueType = static_cast<QVariant::Type>(settings.value("valueType").toInt());
QMetaType::Type valueType = static_cast<QMetaType::Type>(settings.value("valueType").toInt());
// Note: only warn, and continue with the QVariant guessed type
if (valueType == QVariant::Invalid) {
if (valueType == QMetaType::UnknownType) {
qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the param descriptor" << strippedGroupName << ". The value type will be guessed by QVariant.";
} else if (!value.canConvert(static_cast<int>(valueType))) {
qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the param descriptor value" << value << "to the stored type" << valueType;

View File

@ -127,8 +127,8 @@ private:
RuleError checkRuleAction(const RuleAction &ruleAction, const Rule &rule);
RuleError checkRuleActionParam(const RuleActionParam &ruleActionParam, const ActionType &actionType, const Rule &rule);
QVariant::Type getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId &paramTypeId);
QVariant::Type getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId &paramTypeId);
QMetaType::Type getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId &paramTypeId);
QMetaType::Type getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId &paramTypeId);
void appendRule(const Rule &rule);
void saveRule(const Rule &rule);

View File

@ -35,6 +35,8 @@
#include "loggingcategories.h"
#include "nymeasettings.h"
#include <QColor>
namespace nymeaserver {
StateEvaluator::StateEvaluator(const StateDescriptor &stateDescriptor):
@ -199,9 +201,9 @@ StateEvaluator StateEvaluator::loadFromSettings(NymeaSettings &settings, const Q
}
QVariant stateValue = settings.value("value");
if (settings.contains("valueType")) {
QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt();
QMetaType::Type valueType = (QMetaType::Type)settings.value("valueType").toInt();
// Note: only warn, and continue with the QVariant guessed type
if (valueType == QVariant::Invalid) {
if (valueType == QMetaType::UnknownType) {
qCWarning(dcRuleEngine()) << "Could not load the value type of the state evaluator. The value type will be guessed by QVariant" << stateValue;
} else if (!stateValue.canConvert(valueType)) {
qCWarning(dcRuleEngine()) << "Could not convert the state evaluator value" << stateValue << "to the stored type" << valueType << ". The value type will be guessed by QVariant.";
@ -266,6 +268,19 @@ bool StateEvaluator::isValid() const
qCWarning(dcRuleEngine) << "Could not convert value of state descriptor" << m_stateDescriptor.stateTypeId() << " to:" << QVariant::typeToName(stateType.type()) << " Got:" << m_stateDescriptor.stateValue();
return false;
}
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
QPartialOrdering ordering = QPartialOrdering::Unordered;
ordering = QVariant::compare(stateValue, stateType.maxValue());
if (stateType.maxValue().isValid() && ordering == QPartialOrdering::Greater) {
qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Max:" << stateType.maxValue();
return false;
}
ordering = QVariant::compare(stateValue, stateType.minValue());
if (stateType.minValue().isValid() && ordering == QPartialOrdering::Less) {
qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Min:" << stateType.minValue();
return false;
}
#else
if (stateType.maxValue().isValid() && stateValue > stateType.maxValue()) {
qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Max:" << stateType.maxValue();
return false;
@ -275,7 +290,7 @@ bool StateEvaluator::isValid() const
qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Min:" << stateType.minValue();
return false;
}
#endif
if (!stateType.possibleValues().isEmpty() && !stateType.possibleValues().contains(stateValue)) {
QStringList possibleValues;
foreach (const QVariant &value, stateType.possibleValues()) {
@ -286,7 +301,7 @@ bool StateEvaluator::isValid() const
return false;
}
// if comparing to another state
// if comparing to another state
} else if (!m_stateDescriptor.valueThingId().isNull() && !m_stateDescriptor.valueStateTypeId().isNull()) {
Thing *valueThing = NymeaCore::instance()->thingManager()->findConfiguredThing(m_stateDescriptor.valueThingId());
if (!valueThing) {
@ -307,11 +322,11 @@ bool StateEvaluator::isValid() const
} else { // TypeInterface
Interface iface = NymeaCore::instance()->thingManager()->supportedInterfaces().findByName(m_stateDescriptor.interface());
if (!iface.isValid()) {
qWarning(dcRuleEngine()) << "No such interface:" << m_stateDescriptor.interface();
qCWarning(dcRuleEngine()) << "No such interface:" << m_stateDescriptor.interface();
return false;
}
if (iface.stateTypes().findByName(m_stateDescriptor.interfaceState()).name().isEmpty()) {
qWarning(dcRuleEngine()) << "Interface" << iface.name() << "has no such state:" << m_stateDescriptor.interfaceState();
qCWarning(dcRuleEngine()) << "Interface" << iface.name() << "has no such state:" << m_stateDescriptor.interfaceState();
return false;
}
}
@ -356,11 +371,64 @@ bool StateEvaluator::evaluateDescriptor(const StateDescriptor &descriptor) const
}
if (!descriptor.stateValue().isNull()) {
QVariant convertedValue = descriptor.stateValue();
QVariant stateValue = state.value();
QVariant descriptorValue = descriptor.stateValue();
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
bool res = descriptorValue.convert(state.value().metaType());
if (!res) {
return false;
}
// FIXME: not sure if this is a bug in Qt, but compairing two equivalient QVariant containing QColor
// with QPartialOrdering::equivalent results in false. Until this has been fixed or the error has been found,
// we convert colors to strings and compare them
// Comparing QVariant(QColor, QColor(ARGB 1, 0, 0, 0)) to QVariant(QColor, QColor(ARGB 1, 0, 1, 0)) with operator Types::ValueOperatorEquals --> false
if (state.value().typeId() == QMetaType::QColor) {
QColor stateColor = stateValue.value<QColor>();
QColor desciptorColor = descriptorValue.value<QColor>();
QString sateColorName = stateColor.name();
QString desciptorColorName = desciptorColor.name();
stateValue = QVariant(sateColorName);
descriptorValue = QVariant(desciptorColorName);
}
QPartialOrdering ordering = QVariant::compare(stateValue, descriptorValue);
bool result = false;
switch (descriptor.operatorType()) {
case Types::ValueOperatorEquals:
result = (ordering == QPartialOrdering::equivalent);
break;
case Types::ValueOperatorGreater:
result = (ordering == QPartialOrdering::greater);
break;
case Types::ValueOperatorGreaterOrEqual:
result = (ordering == QPartialOrdering::greater || ordering == QPartialOrdering::equivalent);
break;
case Types::ValueOperatorLess:
result = (ordering == QPartialOrdering::less);
break;
case Types::ValueOperatorLessOrEqual:
result = (ordering == QPartialOrdering::less || ordering == QPartialOrdering::equivalent);
break;
case Types::ValueOperatorNotEquals:
result = (ordering != QPartialOrdering::equivalent);
break;
}
qCDebug(dcRuleEngineDebug()) << "Comparing" << stateValue << "to" << descriptorValue << "with operator" << descriptor.operatorType() << "-->" << result;
return result;
#else
bool res = convertedValue.convert(state.value().type());
if (!res) {
return false;
}
switch (descriptor.operatorType()) {
case Types::ValueOperatorEquals:
return state.value() == convertedValue;
@ -375,7 +443,7 @@ bool StateEvaluator::evaluateDescriptor(const StateDescriptor &descriptor) const
case Types::ValueOperatorNotEquals:
return state.value() != convertedValue;
}
#endif
} else if (!descriptor.valueThingId().isNull() && !descriptor.valueStateTypeId().isNull()) {
Thing *valueThing = NymeaCore::instance()->thingManager()->findConfiguredThing(descriptor.valueThingId());
if (!valueThing) {
@ -389,6 +457,23 @@ bool StateEvaluator::evaluateDescriptor(const StateDescriptor &descriptor) const
}
qCDebug(dcRuleEngineDebug()) << "Comparing" << state.value() << "to" << valueState.value() << "with operator" << descriptor.operatorType();
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
QPartialOrdering ordering = QVariant::compare(state.value(), valueState.value());
switch (descriptor.operatorType()) {
case Types::ValueOperatorEquals:
return ordering == QPartialOrdering::Equivalent;
case Types::ValueOperatorGreater:
return ordering == QPartialOrdering::Greater;
case Types::ValueOperatorGreaterOrEqual:
return ordering == QPartialOrdering::Greater || ordering == QPartialOrdering::Equivalent;
case Types::ValueOperatorLess:
return ordering == QPartialOrdering::Less;
case Types::ValueOperatorLessOrEqual:
return ordering == QPartialOrdering::Less || ordering == QPartialOrdering::Equivalent;
case Types::ValueOperatorNotEquals:
return ordering != QPartialOrdering::Equivalent;
}
#else
switch (descriptor.operatorType()) {
case Types::ValueOperatorEquals:
return state.value() == valueState.value();
@ -403,6 +488,7 @@ bool StateEvaluator::evaluateDescriptor(const StateDescriptor &descriptor) const
case Types::ValueOperatorNotEquals:
return state.value() != valueState.value();
}
#endif
}
} else { // Interface based
@ -430,7 +516,7 @@ bool StateEvaluator::evaluateDescriptor(const StateDescriptor &descriptor) const
QDebug operator<<(QDebug dbg, const StateEvaluator &stateEvaluator)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "StateEvaluator: Operator:" << stateEvaluator.operatorType() << endl << " " << stateEvaluator.stateDescriptor() << endl;
dbg.nospace() << "StateEvaluator: Operator:" << stateEvaluator.operatorType() << '\n' << " " << stateEvaluator.stateDescriptor() << '\n';
for (int i = 0; i < stateEvaluator.childEvaluators().count(); i++) {
dbg.nospace() << " " << i << ": " << stateEvaluator.childEvaluators().at(i);
}

View File

@ -41,6 +41,8 @@
#include <QMessageLogger>
#include <QLoggingCategory>
#include <QRegularExpression>
Q_DECLARE_LOGGING_CATEGORY(dcScriptEngine)
namespace nymeaserver {
@ -79,7 +81,7 @@ void ScriptAction::classBegin()
QVariantMap params;
foreach (const Param &param, action.params()) {
params.insert(param.paramTypeId().toString().remove(QRegExp("[{}]")), param.value().toByteArray());
params.insert(param.paramTypeId().toString().remove(QRegularExpression("[{}]")), param.value().toByteArray());
QString paramName = thing->thingClass().actionTypes().findById(action.actionTypeId()).paramTypes().findById(param.paramTypeId()).name();
params.insert(paramName, param.value().toByteArray());
}

View File

@ -45,16 +45,16 @@
#include "nymeasettings.h"
#include "logging/logengine.h"
#include <QDir>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QJsonParseError>
#include <QJsonDocument>
#include <QRegularExpression>
#include "loggingcategories.h"
#include <QDir>
NYMEA_LOGGING_CATEGORY(dcScriptEngine, "ScriptEngine")
namespace nymeaserver {
@ -367,7 +367,7 @@ void ScriptEngine::loadScripts()
}
Script *script = new Script();
script->setId(jsonFileInfo.baseName());
script->setId(QUuid(jsonFileInfo.baseName()));
script->setName(jsonDoc.toVariant().toMap().value("name").toString());
bool loaded = loadScript(script);
@ -449,14 +449,14 @@ void ScriptEngine::unloadScript(Script *script)
QString ScriptEngine::baseName(const QUuid &id)
{
QString path = NymeaSettings::storagePath() + "/scripts/";
QString basename = id.toString().remove(QRegExp("[{}]"));
QString basename = id.toString().remove(QRegularExpression("[{}]"));
return path + basename;
}
void ScriptEngine::onScriptMessage(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
QFileInfo fi(context.file);
QUuid scriptId = fi.baseName();
QUuid scriptId(fi.baseName());
if (!m_scripts.contains(scriptId)) {
return;
}

View File

@ -33,6 +33,7 @@
#include <qqml.h>
#include <QQmlEngine>
#include <QJsonDocument>
#include <QRegularExpression>
namespace nymeaserver {
namespace scriptengine {
@ -97,7 +98,7 @@ void ScriptEvent::onEventTriggered(const Event &event)
return;
}
if (!m_eventTypeId.isEmpty() && event.eventTypeId() != m_eventTypeId) {
if (!m_eventTypeId.isEmpty() && event.eventTypeId() != EventTypeId(m_eventTypeId)) {
return;
}
@ -108,7 +109,7 @@ void ScriptEvent::onEventTriggered(const Event &event)
QVariantMap params;
foreach (const Param &param, event.params()) {
params.insert(param.paramTypeId().toString().remove(QRegExp("[{}]")), param.value().toByteArray());
params.insert(param.paramTypeId().toString().remove(QRegularExpression("[{}]")), param.value().toByteArray());
QString paramName = thing->thingClass().eventTypes().findById(event.eventTypeId()).paramTypes().findById(param.paramTypeId()).name();
params.insert(paramName, param.value().toByteArray());
}

View File

@ -33,6 +33,7 @@
#include <qqml.h>
#include <QQmlEngine>
#include <QJsonDocument>
#include <QRegularExpression>
namespace nymeaserver {
namespace scriptengine {
@ -91,13 +92,13 @@ void ScriptInterfaceEvent::onEventTriggered(const Event &event)
QVariantMap params;
foreach (const Param &param, event.params()) {
params.insert(param.paramTypeId().toString().remove(QRegExp("[{}]")), param.value().toByteArray());
params.insert(param.paramTypeId().toString().remove(QRegularExpression("[{}]")), param.value().toByteArray());
QString paramName = thing->thingClass().eventTypes().findById(event.eventTypeId()).paramTypes().findById(param.paramTypeId()).name();
params.insert(paramName, param.value().toByteArray());
}
// Note: Explicitly convert the params to a Json document because auto-casting from QVariantMap to the JS engine might drop some values.
emit triggered(event.thingId().toString().remove(QRegExp("[{}]")), QJsonDocument::fromVariant(params).toVariant().toMap());
emit triggered(event.thingId().toString().remove(QRegularExpression("[{}]")), QJsonDocument::fromVariant(params).toVariant().toMap());
}
}

Some files were not shown because too many files have changed in this diff Show More