From 2916086b5849428837b84d00d96774dcdbe24cf4 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Tue, 22 Dec 2020 09:36:15 +0100 Subject: [PATCH] fixed memory leak on redisovery --- doorbird/doorbird.cpp | 69 ++++---------- doorbird/doorbird.h | 4 - doorbird/integrationplugindoorbird.cpp | 118 ++++++++++-------------- doorbird/integrationplugindoorbird.h | 9 +- doorbird/integrationplugindoorbird.json | 3 +- 5 files changed, 76 insertions(+), 127 deletions(-) diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 74225d16..7d1a768c 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -71,8 +71,8 @@ QUuid Doorbird::getSession(const QString &username, const QString &password) QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error DoorBird thing:" << reply->errorString(); @@ -104,8 +104,8 @@ QUuid Doorbird::openDoor(int value) qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error opening DoorBird " << reply->error() << reply->errorString(); @@ -124,8 +124,8 @@ QUuid Doorbird::lightOn() qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error light on" << reply->error() << reply->errorString(); @@ -144,8 +144,8 @@ QUuid Doorbird::liveVideoRequest() qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error live video request" << reply->error() << reply->errorString(); @@ -164,8 +164,8 @@ QUuid Doorbird::liveImageRequest() qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error live image request" << reply->error() << reply->errorString(); @@ -193,8 +193,8 @@ QUuid Doorbird::historyImageRequest(int index) qCDebug(dcDoorBird) << "Sending request:" << url; QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(url)); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error history image request" << reply->error() << reply->errorString(); @@ -213,8 +213,8 @@ QUuid Doorbird::liveAudioReceive() qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error live audio receive"; @@ -238,8 +238,8 @@ QUuid Doorbird::infoRequest() qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error DoorBird" << reply->error() << reply->errorString(); @@ -258,8 +258,8 @@ QUuid Doorbird::listFavorites() qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString(); @@ -288,8 +288,8 @@ QUuid Doorbird::addFavorite(FavoriteType type, const QString &name, const QUrl & QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(url)); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString(); @@ -307,8 +307,8 @@ QUuid Doorbird::listSchedules() qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString(); @@ -326,8 +326,8 @@ QUuid Doorbird::restart() qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ - reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error restarting DoorBird device" << reply; @@ -421,54 +421,19 @@ void Doorbird::connectToEventMonitor() qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; } } + qCDebug(dcDoorBird()) << "Event read buffer size" << m_readBuffer.size(); }); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, this, [this, reply]() { - reply->deleteLater(); emit deviceConnected(false); + m_readBuffer.clear(); qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); - + qCDebug(dcDoorBird) << " - Trying to reconnect in 5 seconds"; QTimer::singleShot(2000, this, [this] { + qCDebug(dcDoorBird) << " - Reconnecting now"; connectToEventMonitor(); }); }); } - -void Doorbird::onUdpBroadcast(const QByteArray &data) -{ - Q_UNUSED(data); - //TODO decryption -} - - -/*NotifyBroadcastCiphertext decryptBroadcastNotification(const NotifyBroadcast* notification, const - StretchedPassword* password) { - NotifyBroadcastCiphertext decrypted = {{0},{0},0}; - if(crypto_aead_chacha20poly1305_decrypt((unsigned char*)&decrypted, NULL, NULL, notification->ciphertext, - sizeof(notification->ciphertext), NULL, 0, notification->nonce, password->key)!=0){ - LOGGING("crypto_aead_chacha20poly1305_decrypt() failed"); - } - return decrypted; -} - -unsigned char* stretchPasswordArgon(const char *password, unsigned char *salt, unsigned* oplimit, unsigned* memlimit) { - if (sodium_is_zero(salt, CRYPTO_SALT_BYTES) && random_bytes(salt, CRYPTO_SALT_BYTES)) { - return NULL; - } - unsigned char* key = malloc(CRYPTO_ARGON_OUT_SIZE); - if (!*oplimit) { - *oplimit = crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE; - } - if (!*memlimit) { - *memlimit = crypto_pwhash_MEMLIMIT_MIN; - } - if (crypto_pwhash(key, CRYPTO_ARGON_OUT_SIZE, password, str_len(password), salt, *oplimit, *memlimit, crypto_pwhash_ALG_ARGON2I13)) { - LOGGING("Argon2 Failed"); - *oplimit = 0; - *memlimit = 0; - CLEAN(key); - return NULL; - } - return key; -}*/ diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index a09bb788..e3842ab9 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -92,9 +92,7 @@ private: QNetworkAccessManager *m_networkAccessManager; QByteArray m_readBuffer; - QList m_networkRequests; - QList m_pendingAuthentications; QString m_username; @@ -110,8 +108,6 @@ signals: void sessionIdReceived(const QString &sessionId); void liveImageReceived(QImage image); -public slots: - void onUdpBroadcast(const QByteArray &data); }; #endif // DOORBIRD_H diff --git a/doorbird/integrationplugindoorbird.cpp b/doorbird/integrationplugindoorbird.cpp index ddce2f09..911dbb72 100644 --- a/doorbird/integrationplugindoorbird.cpp +++ b/doorbird/integrationplugindoorbird.cpp @@ -32,7 +32,6 @@ #include "plugininfo.h" #include "platform/platformzeroconfcontroller.h" -#include "network/zeroconf/zeroconfservicebrowser.h" #include "network/zeroconf/zeroconfserviceentry.h" #include @@ -44,15 +43,17 @@ IntegrationPluginDoorbird::IntegrationPluginDoorbird() { } +void IntegrationPluginDoorbird::init() +{ + m_serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_axis-video._tcp"); +} void IntegrationPluginDoorbird::discoverThings(ThingDiscoveryInfo *info) { if (info->thingClassId() == doorBirdThingClassId) { - ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_axis-video._tcp"); - connect(info, &QObject::destroyed, serviceBrowser, &QObject::deleteLater); - QTimer::singleShot(5000, this, [this, info, serviceBrowser](){ - foreach (const ZeroConfServiceEntry serviceEntry, serviceBrowser->serviceEntries()) { + QTimer::singleShot(5000, info, [this, info](){ + foreach (const ZeroConfServiceEntry serviceEntry, m_serviceBrowser->serviceEntries()) { if (serviceEntry.hostName().startsWith("bha-")) { qCDebug(dcDoorBird) << "Found DoorBird Thing, name: " << serviceEntry.name() << "\n host address:" << serviceEntry.hostAddress().toString() << "\n text:" << serviceEntry.txt() << serviceEntry.protocol() << serviceEntry.serviceType(); ThingDescriptor ThingDescriptor(doorBirdThingClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); @@ -79,7 +80,6 @@ void IntegrationPluginDoorbird::discoverThings(ThingDiscoveryInfo *info) info->addThingDescriptor(ThingDescriptor); } } - serviceBrowser->deleteLater(); info->finish(Thing::ThingErrorNoError); }); return; @@ -92,6 +92,7 @@ void IntegrationPluginDoorbird::discoverThings(ThingDiscoveryInfo *info) void IntegrationPluginDoorbird::startPairing(ThingPairingInfo *info) { + qCDebug(dcDoorBird()) << "Start pairing"; if (info->thingClassId() == doorBirdThingClassId) { info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter username and password for the DoorBird Thing")); return; @@ -104,25 +105,18 @@ void IntegrationPluginDoorbird::startPairing(ThingPairingInfo *info) void IntegrationPluginDoorbird::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &password) { + qCDebug(dcDoorBird()) << "Confirm pairing"; if (info->thingClassId() == doorBirdThingClassId) { QHostAddress address = QHostAddress(info->params().paramValue(doorBirdThingAddressParamTypeId).toString()); Doorbird *doorbird = new Doorbird(address, this); - connect(doorbird, &Doorbird::deviceConnected, this, &IntegrationPluginDoorbird::onDoorBirdConnected); - connect(doorbird, &Doorbird::eventReveiced, this, &IntegrationPluginDoorbird::onDoorBirdEvent); - connect(doorbird, &Doorbird::requestSent, this, &IntegrationPluginDoorbird::onDoorBirdRequestSent); - connect(doorbird, &Doorbird::sessionIdReceived, this, &IntegrationPluginDoorbird::onSessionIdReceived); - m_doorbirdConnections.insert(info->thingId(), doorbird); - m_pendingPairings.insert(doorbird, info); doorbird->getSession(username, password); - connect(info, &ThingPairingInfo::aborted, this, [this, info]{ - if (m_pendingPairings.values().contains(info)) { - Doorbird *doorbird = m_pendingPairings.key(info); - m_pendingPairings.remove(doorbird); - doorbird->deleteLater(); - } - m_doorbirdConnections.remove(info->thingId()); + connect(doorbird, &Doorbird::sessionIdReceived, info, [info, doorbird, this] { + qCDebug(dcDoorBird()) << "Session id received, pairing successfull"; + m_pairingConnections.insert(info->thingId(), doorbird); + info->finish(Thing::ThingErrorNoError); }); + connect(info, &ThingPairingInfo::aborted, doorbird, &Doorbird::deleteLater); pluginStorage()->beginGroup(info->thingId().toString()); pluginStorage()->setValue("username", username); @@ -137,36 +131,46 @@ void IntegrationPluginDoorbird::confirmPairing(ThingPairingInfo *info, const QSt void IntegrationPluginDoorbird::setupThing(ThingSetupInfo *info) { + qCDebug(dcDoorBird()) << "Setup thing" << info->thing()->name(); Thing *thing = info->thing(); if (thing->thingClassId() == doorBirdThingClassId) { - QHostAddress address = QHostAddress(thing->paramValue(doorBirdThingAddressParamTypeId).toString()); if (m_doorbirdConnections.contains(thing->id())) { + qCDebug(dcDoorBird()) << "Reconfigure, cleaning up"; + Doorbird *doorbird = m_doorbirdConnections.take(thing->id()); + if (doorbird) + doorbird->deleteLater(); + } + + Doorbird *doorbird; + if (m_pairingConnections.contains(thing->id())) { + qCDebug(dcDoorBird()) << "Setup after pairing process, not creating new connection"; + doorbird = m_pairingConnections.value(thing->id()); + m_doorbirdConnections.insert(thing->id(), doorbird); info->finish(Thing::ThingErrorNoError); } else { + QHostAddress address = QHostAddress(thing->paramValue(doorBirdThingAddressParamTypeId).toString()); + pluginStorage()->beginGroup(thing->id().toString()); QString username = pluginStorage()->value("username").toString(); QString password = pluginStorage()->value("password").toString(); pluginStorage()->endGroup(); - qCDebug(dcDoorBird()) << "Thing setup" << thing->name() << username << password; - Doorbird *doorbird = new Doorbird(address, this); - connect(doorbird, &Doorbird::deviceConnected, this, &IntegrationPluginDoorbird::onDoorBirdConnected); - connect(doorbird, &Doorbird::eventReveiced, this, &IntegrationPluginDoorbird::onDoorBirdEvent); - connect(doorbird, &Doorbird::requestSent, this, &IntegrationPluginDoorbird::onDoorBirdRequestSent); - connect(doorbird, &Doorbird::sessionIdReceived, this, &IntegrationPluginDoorbird::onSessionIdReceived); - m_doorbirdConnections.insert(thing->id(), doorbird); - m_pendingThingSetups.insert(doorbird, info); - doorbird->getSession(username, password); - connect(info, &ThingSetupInfo::aborted, this, [thing, doorbird, this] { - if (!doorbird) { - doorbird->deleteLater(); - } - m_doorbirdConnections.remove(thing->id()); - m_pendingPairings.remove(doorbird); + qCDebug(dcDoorBird()) << "Creating new Doorbird conection" << username << password.left(4)+"******"; + doorbird = new Doorbird(address, this); + + connect(doorbird, &Doorbird::sessionIdReceived, info, [info, thing, doorbird, this] { + qCDebug(dcDoorBird()) << "Session id received, thing setup successfull"; + m_doorbirdConnections.insert(thing->id(), doorbird); + info->finish(Thing::ThingErrorNoError); }); + doorbird->getSession(username, password); + connect(info, &ThingSetupInfo::aborted, doorbird, &Doorbird::deleteLater); } + connect(doorbird, &Doorbird::deviceConnected, this, &IntegrationPluginDoorbird::onDoorBirdConnected); + connect(doorbird, &Doorbird::eventReveiced, this, &IntegrationPluginDoorbird::onDoorBirdEvent); + connect(doorbird, &Doorbird::requestSent, this, &IntegrationPluginDoorbird::onDoorBirdRequestSent); } else { qCWarning(dcDoorBird()) << "Unhandled Thing class" << info->thing()->thingClass(); info->finish(Thing::ThingErrorThingClassNotFound); @@ -176,8 +180,9 @@ void IntegrationPluginDoorbird::setupThing(ThingSetupInfo *info) void IntegrationPluginDoorbird::postSetupThing(Thing *thing) { + qCDebug(dcDoorBird()) << "Post setup thing" << thing->name(); if (thing->thingClassId() == doorBirdThingClassId) { - thing->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection in the ThingSetup + thing->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection inside ThingSetup Doorbird *doorbird = m_doorbirdConnections.value(thing->id()); doorbird->connectToEventMonitor(); doorbird->infoRequest(); @@ -195,7 +200,7 @@ void IntegrationPluginDoorbird::executeAction(ThingActionInfo *info) if (thing->thingClassId() == doorBirdThingClassId) { Doorbird *doorbird = m_doorbirdConnections.value(thing->id()); if (!doorbird) { - qCWarning(dcDoorBird()) << "Doorbird object not found" << thing->name(); + qCWarning(dcDoorBird()) << "Doorbird connection not found" << thing->name(); info->finish(Thing::ThingErrorHardwareFailure); return; } @@ -227,6 +232,7 @@ void IntegrationPluginDoorbird::executeAction(ThingActionInfo *info) void IntegrationPluginDoorbird::thingRemoved(Thing *thing) { + qCDebug(dcDoorBird()) << "Removing thing" << thing->name(); if (thing->thingClassId() == doorBirdThingClassId) { Doorbird *doorbirdConnection = m_doorbirdConnections.take(thing->id()); doorbirdConnection->deleteLater(); @@ -237,8 +243,10 @@ void IntegrationPluginDoorbird::onDoorBirdConnected(bool status) { Doorbird *doorbird = static_cast(sender()); Thing *thing = myThings().findById(m_doorbirdConnections.key(doorbird)); - if (!thing) + if (!thing) { + qCWarning(dcDoorBird()) << "Doorbird connection status changed, associated thing not found"; return; + } thing->setStateValue(doorBirdConnectedStateTypeId, status); } @@ -247,13 +255,17 @@ void IntegrationPluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, b { Doorbird *doorbird = static_cast(sender()); Thing *thing = myThings().findById(m_doorbirdConnections.key(doorbird)); - if (!thing) + if (!thing) { + qCWarning(dcDoorBird()) << "Doorbird event received, associated thing not found"; return; + } switch (eventType) { case Doorbird::EventType::Rfid: + qCDebug(dcDoorBird()) << "RFID event received, this is not yet handled"; break; case Doorbird::EventType::Input: + qCDebug(dcDoorBird()) << "Input event received, this is not yet handled"; break; case Doorbird::EventType::Motion: thing->setStateValue(doorBirdIsPresentStateTypeId, status); @@ -271,36 +283,8 @@ void IntegrationPluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, b void IntegrationPluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) { - Doorbird *doorbird = static_cast(sender()); - if (m_asyncActions.contains(requestId)) { ThingActionInfo* actionInfo = m_asyncActions.take(requestId); actionInfo->finish(success ? Thing::ThingErrorNoError : Thing::ThingErrorInvalidParameter); } - - if (m_pendingPairings.contains(doorbird) && !success) { - ThingPairingInfo *info = m_pendingPairings.take(doorbird); - info->finish(Thing::ThingErrorAuthenticationFailure, tr("Wrong username or password")); - } - - if (m_pendingThingSetups.contains(doorbird) && !success) { - ThingSetupInfo *info = m_pendingThingSetups.take(doorbird); - info->finish(Thing::ThingErrorAuthenticationFailure, tr("Wrong username or password")); - } -} - -void IntegrationPluginDoorbird::onSessionIdReceived(const QString &sessionId) -{ - Q_UNUSED(sessionId); - Doorbird *doorbird = static_cast(sender()); - - if (m_pendingPairings.contains(doorbird)) { - ThingPairingInfo *info = m_pendingPairings.take(doorbird); - info->finish(Thing::ThingErrorNoError); - } - - if (m_pendingThingSetups.contains(doorbird)) { - ThingSetupInfo *info = m_pendingThingSetups.take(doorbird); - info->finish(Thing::ThingErrorNoError); - } } diff --git a/doorbird/integrationplugindoorbird.h b/doorbird/integrationplugindoorbird.h index 3168785d..db82fa78 100644 --- a/doorbird/integrationplugindoorbird.h +++ b/doorbird/integrationplugindoorbird.h @@ -34,6 +34,7 @@ #include #include "integrations/integrationplugin.h" +#include "network/zeroconf/zeroconfservicebrowser.h" #include "doorbird.h" class QNetworkAccessManager; @@ -49,6 +50,7 @@ class IntegrationPluginDoorbird: public IntegrationPlugin public: explicit IntegrationPluginDoorbird(); + void init() override; void discoverThings(ThingDiscoveryInfo *info) override; void setupThing(ThingSetupInfo *info) override; void postSetupThing(Thing *thing) override; @@ -60,9 +62,11 @@ public: void thingRemoved(Thing *thing)override; private: + + ZeroConfServiceBrowser *m_serviceBrowser = nullptr; + + QHash m_pairingConnections; QHash m_doorbirdConnections; - QHash m_pendingPairings; - QHash m_pendingThingSetups; QHash m_asyncActions; @@ -70,7 +74,6 @@ private slots: void onDoorBirdConnected(bool status); void onDoorBirdEvent(Doorbird::EventType eventType, bool status); void onDoorBirdRequestSent(QUuid requestId, bool success); - void onSessionIdReceived(const QString &sessionId); }; #endif // INTEGRATIONPLUGINDOORBIRD_H diff --git a/doorbird/integrationplugindoorbird.json b/doorbird/integrationplugindoorbird.json index 9c9ba049..5840f45c 100644 --- a/doorbird/integrationplugindoorbird.json +++ b/doorbird/integrationplugindoorbird.json @@ -26,7 +26,8 @@ "id": "67ea5534-330a-4291-93b5-0237034e15fa", "name": "serialnumber", "displayName": "Serial number", - "type": "QString" + "type": "QString", + "readOnly": true } ], "actionTypes": [