From 9ba22035ebc0216581b6f00de2f2ac38bbc5888a Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Fri, 18 Jun 2021 16:32:45 +0200 Subject: [PATCH] Add interface based thing discovery capabilities --- libnymea-app/thingdiscovery.cpp | 83 +++++++++++++++++++++++++-------- libnymea-app/thingdiscovery.h | 10 +++- 2 files changed, 72 insertions(+), 21 deletions(-) diff --git a/libnymea-app/thingdiscovery.cpp b/libnymea-app/thingdiscovery.cpp index 858180e4..25918df0 100644 --- a/libnymea-app/thingdiscovery.cpp +++ b/libnymea-app/thingdiscovery.cpp @@ -32,6 +32,10 @@ #include "engine.h" +#include + +Q_DECLARE_LOGGING_CATEGORY(dcThingManager) + ThingDiscovery::ThingDiscovery(QObject *parent) : QAbstractListModel(parent) { @@ -71,31 +75,49 @@ QHash ThingDiscovery::roleNames() const void ThingDiscovery::discoverThings(const QUuid &thingClassId, const QVariantList &discoveryParams) { - if (m_busy) { - qWarning() << "Busy... not restarting discovery"; - return; - } beginResetModel(); m_foundThings.clear(); endResetModel(); emit countChanged(); if (!m_engine) { - qWarning() << "Cannot discover things. No Engine set"; + qCWarning(dcThingManager()) << "Cannot discover things. No Engine set"; return; } if (!m_engine->jsonRpcClient()->connected()) { - qWarning() << "Cannot discover things. Not connected."; + qCWarning(dcThingManager()) << "Cannot discover things. Not connected."; return; } - QVariantMap params; - params.insert("thingClassId", thingClassId.toString()); - if (!discoveryParams.isEmpty()) { - params.insert("discoveryParams", discoveryParams); + discoverThingsInternal(thingClassId, discoveryParams); + m_displayMessage.clear(); + emit busyChanged(); +} + +void ThingDiscovery::discoverThingsByInterface(const QString &interfaceName) +{ + beginResetModel(); + m_foundThings.clear(); + endResetModel(); + emit countChanged(); + + if (!m_engine) { + qCWarning(dcThingManager()) << "Cannot discover things. No Engine set"; + return; } - m_engine->jsonRpcClient()->sendCommand("Integrations.DiscoverThings", params, this, "discoverThingsResponse"); - m_busy = true; + if (!m_engine->jsonRpcClient()->connected()) { + qCWarning(dcThingManager()) << "Cannot discover things. Not connected."; + return; + } + + for (int i = 0; i < m_engine->thingManager()->thingClasses()->rowCount(); i++) { + ThingClass *thingClass = m_engine->thingManager()->thingClasses()->get(i); + if (!thingClass->interfaces().contains(interfaceName)) { + continue; + } + discoverThingsInternal(thingClass->id()); + } + m_displayMessage.clear(); emit busyChanged(); } @@ -123,7 +145,7 @@ void ThingDiscovery::setEngine(Engine *engine) bool ThingDiscovery::busy() const { - return m_busy; + return !m_pendingRequests.isEmpty(); } QString ThingDiscovery::displayMessage() const @@ -131,14 +153,27 @@ QString ThingDiscovery::displayMessage() const return m_displayMessage; } -void ThingDiscovery::discoverThingsResponse(int /*commandId*/, const QVariantMap ¶ms) +void ThingDiscovery::discoverThingsInternal(const QUuid &thingClassId, const QVariantList &discoveryParams) { - qDebug() << "Discovery response received" << params; + qCDebug(dcThingManager()) << "Starting thing discovery for thing class" << m_engine->thingManager()->thingClasses()->getThingClass(thingClassId)->name() << thingClassId; + QVariantMap params; + params.insert("thingClassId", thingClassId.toString()); + if (!discoveryParams.isEmpty()) { + params.insert("discoveryParams", discoveryParams); + } + int commandId = m_engine->jsonRpcClient()->sendCommand("Integrations.DiscoverThings", params, this, "discoverThingsResponse"); + m_pendingRequests.append(commandId); +} + +void ThingDiscovery::discoverThingsResponse(int commandId, const QVariantMap ¶ms) +{ + qCDebug(dcThingManager) << "Discovery response received" << params; QVariantList descriptors = params.value("thingDescriptors").toList(); foreach (const QVariant &descriptorVariant, descriptors) { if (!contains(descriptorVariant.toMap().value("id").toUuid())) { beginInsertRows(QModelIndex(), m_foundThings.count(), m_foundThings.count()); ThingDescriptor *descriptor = new ThingDescriptor(descriptorVariant.toMap().value("id").toUuid(), + descriptorVariant.toMap().value("thingClassId").toUuid(), // Note: This will only be provided as of nymea 0.28! descriptorVariant.toMap().value("thingId").toString(), descriptorVariant.toMap().value("title").toString(), descriptorVariant.toMap().value("description").toString()); @@ -154,16 +189,20 @@ void ThingDiscovery::discoverThingsResponse(int /*commandId*/, const QVariantMap Param* p = new Param(paramVariant.toMap().value("paramTypeId").toString(), paramVariant.toMap().value("value")); descriptor->params()->addParam(p); } - qDebug() << "Found thing. Descriptor:" << descriptor->name() << descriptor->id(); + qCDebug(dcThingManager()) << "Found thing. Descriptor:" << descriptor->name() << descriptor->id(); m_foundThings.append(descriptor); endInsertRows(); emit countChanged(); } } + // Note: in case of multiple discoveries we'll just overwrite the message... Not ideal but multiple error messages from different plugins + // wouldn't be of much use to the user anyways. m_displayMessage = params.value("displayMessage").toString(); - m_busy = false; - emit busyChanged(); + m_pendingRequests.removeAll(commandId); + if (m_pendingRequests.isEmpty()) { + emit busyChanged(); + } } bool ThingDiscovery::contains(const QUuid &deviceDescriptorId) const @@ -176,9 +215,10 @@ bool ThingDiscovery::contains(const QUuid &deviceDescriptorId) const return false; } -ThingDescriptor::ThingDescriptor(const QUuid &id, const QUuid &thingId, const QString &name, const QString &description, QObject *parent): +ThingDescriptor::ThingDescriptor(const QUuid &id, const QUuid &thingClassId, const QUuid &thingId, const QString &name, const QString &description, QObject *parent): QObject(parent), m_id(id), + m_thingClassId(thingClassId), m_thingId(thingId), m_name(name), m_description(description), @@ -192,6 +232,11 @@ QUuid ThingDescriptor::id() const return m_id; } +QUuid ThingDescriptor::thingClassId() const +{ + return m_thingClassId; +} + QUuid ThingDescriptor::thingId() const { return m_thingId; diff --git a/libnymea-app/thingdiscovery.h b/libnymea-app/thingdiscovery.h index d64d63f9..fa918fea 100644 --- a/libnymea-app/thingdiscovery.h +++ b/libnymea-app/thingdiscovery.h @@ -39,14 +39,16 @@ class ThingDescriptor: public QObject { Q_OBJECT Q_PROPERTY(QUuid id READ id CONSTANT) + Q_PROPERTY(QUuid thingClassId READ thingClassId CONSTANT) Q_PROPERTY(QUuid thingId READ thingId CONSTANT) Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString description READ description CONSTANT) Q_PROPERTY(Params* params READ params CONSTANT) public: - ThingDescriptor(const QUuid &id, const QUuid &thingId, const QString &name, const QString &description, QObject *parent = nullptr); + ThingDescriptor(const QUuid &id, const QUuid &thingClassId, const QUuid &thingId, const QString &name, const QString &description, QObject *parent = nullptr); QUuid id() const; + QUuid thingClassId() const; QUuid thingId() const; QString name() const; QString description() const; @@ -54,6 +56,7 @@ public: private: QUuid m_id; + QUuid m_thingClassId; QUuid m_thingId; QString m_name; QString m_description; @@ -83,6 +86,7 @@ public: Q_INVOKABLE void discoverThings(const QUuid &thingClassId, const QVariantList &discoveryParams = {}); + Q_INVOKABLE void discoverThingsByInterface(const QString &interfaceName); Q_INVOKABLE ThingDescriptor* get(int index) const; @@ -93,6 +97,8 @@ public: QString displayMessage() const; private slots: + void discoverThingsInternal(const QUuid &thingClassId, const QVariantList &discoveryParams = {}); + void discoverThingsResponse(int commandId, const QVariantMap ¶ms); signals: @@ -102,8 +108,8 @@ signals: private: Engine *m_engine = nullptr; - bool m_busy = false; QString m_displayMessage; + QList m_pendingRequests; bool contains(const QUuid &deviceDescriptorId) const; QList m_foundThings;