Add Qt6 support
Add Qt6 debian packaging
This commit is contained in:
parent
5f4f2a9b78
commit
f5c4bd16ac
@ -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.
|
||||
|
||||
|
||||
@ -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
1
debian-qt6/changelog
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/changelog
|
||||
1
debian-qt6/compat
Normal file
1
debian-qt6/compat
Normal file
@ -0,0 +1 @@
|
||||
13
|
||||
221
debian-qt6/control
Normal file
221
debian-qt6/control
Normal 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
1
debian-qt6/copyright
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/copyright
|
||||
1
debian-qt6/libnymea-core-dev.install.in
Symbolic link
1
debian-qt6/libnymea-core-dev.install.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/libnymea-core-dev.install.in
|
||||
1
debian-qt6/libnymea-core.install.in
Symbolic link
1
debian-qt6/libnymea-core.install.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/libnymea-core.install.in
|
||||
1
debian-qt6/libnymea-dev.install.in
Symbolic link
1
debian-qt6/libnymea-dev.install.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/libnymea-dev.install.in
|
||||
1
debian-qt6/libnymea-tests-dev.install.in
Symbolic link
1
debian-qt6/libnymea-tests-dev.install.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/libnymea-tests-dev.install.in
|
||||
1
debian-qt6/libnymea-tests.install.in
Symbolic link
1
debian-qt6/libnymea-tests.install.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/libnymea-tests.install.in
|
||||
1
debian-qt6/libnymea1-dev.dirs.in
Symbolic link
1
debian-qt6/libnymea1-dev.dirs.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/libnymea1-dev.dirs.in
|
||||
1
debian-qt6/libnymea1-dev.manpages
Symbolic link
1
debian-qt6/libnymea1-dev.manpages
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/libnymea1-dev.manpages
|
||||
1
debian-qt6/libnymea1.dirs.in
Symbolic link
1
debian-qt6/libnymea1.dirs.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/libnymea1.dirs.in
|
||||
1
debian-qt6/libnymea1.install.in
Symbolic link
1
debian-qt6/libnymea1.install.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/libnymea1.install.in
|
||||
1
debian-qt6/man
Symbolic link
1
debian-qt6/man
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/man/
|
||||
1
debian-qt6/nymea-data.install
Symbolic link
1
debian-qt6/nymea-data.install
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymea-data.install
|
||||
1
debian-qt6/nymea-dev-tools.install.in
Symbolic link
1
debian-qt6/nymea-dev-tools.install.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymea-dev-tools.install.in
|
||||
1
debian-qt6/nymea-doc.dirs
Symbolic link
1
debian-qt6/nymea-doc.dirs
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymea-doc.dirs
|
||||
1
debian-qt6/nymea-doc.install
Symbolic link
1
debian-qt6/nymea-doc.install
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymea-doc.install
|
||||
1
debian-qt6/nymea-tests.dirs.in
Symbolic link
1
debian-qt6/nymea-tests.dirs.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymea-tests.dirs.in
|
||||
1
debian-qt6/nymea-tests.install.in
Symbolic link
1
debian-qt6/nymea-tests.install.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymea-tests.install.in
|
||||
1
debian-qt6/nymea-translations.dirs
Symbolic link
1
debian-qt6/nymea-translations.dirs
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymea-translations.dirs
|
||||
1
debian-qt6/nymea-translations.install
Symbolic link
1
debian-qt6/nymea-translations.install
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymea-translations.install
|
||||
1
debian-qt6/nymead.dirs
Symbolic link
1
debian-qt6/nymead.dirs
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymead.dirs
|
||||
1
debian-qt6/nymead.install.in
Symbolic link
1
debian-qt6/nymead.install.in
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymead.install.in
|
||||
1
debian-qt6/nymead.manpages
Symbolic link
1
debian-qt6/nymead.manpages
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymead.manpages
|
||||
1
debian-qt6/nymead.postinst
Symbolic link
1
debian-qt6/nymead.postinst
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymead.postinst
|
||||
1
debian-qt6/nymead.prerm
Symbolic link
1
debian-qt6/nymead.prerm
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/nymead.prerm
|
||||
66
debian-qt6/rules
Executable file
66
debian-qt6/rules
Executable 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
1
debian-qt6/source
Symbolic link
@ -0,0 +1 @@
|
||||
../debian-qt5/source/
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -5,8 +5,7 @@
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcBluetooth)
|
||||
|
||||
namespace nymeaserver
|
||||
{
|
||||
namespace nymeaserver {
|
||||
|
||||
NymeaBluetoothAgentAdapter::NymeaBluetoothAgentAdapter(NymeaBluetoothAgent *agent, QObject *parent):
|
||||
QObject{parent},
|
||||
|
||||
@ -6,8 +6,9 @@
|
||||
#include <QDBusMessage>
|
||||
#include <QBluetoothAddress>
|
||||
|
||||
namespace nymeaserver
|
||||
{
|
||||
#include <hardware/bluetoothlowenergy/nymeabluetoothagent.h>
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
class NymeaBluetoothAgent;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -94,7 +94,7 @@ private slots:
|
||||
void sendAliveMessage();
|
||||
void discoverTimeout();
|
||||
|
||||
void networkConfigurationChanged(const QNetworkConfiguration &config);
|
||||
//void networkConfigurationChanged(const QNetworkConfiguration &config);
|
||||
|
||||
public slots:
|
||||
bool enable();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
#include <QHostAddress>
|
||||
#include <QTimer>
|
||||
|
||||
#include "libnymea.h"
|
||||
#include <libnymea.h>
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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++) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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")) {
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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 ¬ificationName, 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)")));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 ¶mTypeId)
|
||||
QMetaType::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId ¶mTypeId)
|
||||
{
|
||||
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 ¶mTypeId)
|
||||
QMetaType::Type RuleEngine::getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId ¶mTypeId)
|
||||
{
|
||||
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;
|
||||
|
||||
@ -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 ¶mTypeId);
|
||||
QVariant::Type getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId ¶mTypeId);
|
||||
QMetaType::Type getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId ¶mTypeId);
|
||||
QMetaType::Type getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId ¶mTypeId);
|
||||
|
||||
void appendRule(const Rule &rule);
|
||||
void saveRule(const Rule &rule);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 ¶m, 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());
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 ¶m, 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());
|
||||
}
|
||||
|
||||
@ -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 ¶m, 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
Reference in New Issue
Block a user