From d62e130cb12dd8e0751b5b1a6863512f9871e81a Mon Sep 17 00:00:00 2001 From: nymea Date: Thu, 29 Aug 2019 18:38:35 +0200 Subject: [PATCH 01/35] moved files from doorbird branch --- doorbird/deviceplugindoorbird.cpp | 216 ++++++++++++++++++ doorbird/deviceplugindoorbird.h | 56 +++++ doorbird/deviceplugindoorbird.json | 70 ++++++ doorbird/doorbird.pro | 11 + ...1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts | 4 + ...68429-e312-4c82-9eab-e1cd996e43d6-en_US.ts | 4 + nymea-plugins.pro | 1 + 7 files changed, 362 insertions(+) create mode 100644 doorbird/deviceplugindoorbird.cpp create mode 100644 doorbird/deviceplugindoorbird.h create mode 100644 doorbird/deviceplugindoorbird.json create mode 100644 doorbird/doorbird.pro create mode 100644 doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts create mode 100644 doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp new file mode 100644 index 00000000..e62e4cf7 --- /dev/null +++ b/doorbird/deviceplugindoorbird.cpp @@ -0,0 +1,216 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * nymea is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * nymea is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with nymea. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "deviceplugindoorbird.h" +#include "plugininfo.h" + +#include "platform/platformzeroconfcontroller.h" +#include "network/zeroconf/zeroconfservicebrowser.h" +#include "network/zeroconf/zeroconfserviceentry.h" + +#include +#include +#include +#include + +DevicePluginDoorbird::DevicePluginDoorbird() +{ + m_nam = new QNetworkAccessManager(this); + connect(m_nam, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { + Device *dev = m_networkRequests.value(reply); + if (!myDevices().contains(dev)) { + qCWarning(dcDoorBird) << "Credentials requested for a device which doesn't exist any more"; + return; + } + qCDebug(dcDoorBird) << "Credentials requested for device:" << dev->name(); + authenticator->setUser(dev->paramValue(doorBirdDeviceUsernameParamTypeId).toString()); + authenticator->setPassword(dev->paramValue(doorBirdDevicePasswordParamTypeId).toString()); + }); +} + +DevicePluginDoorbird::~DevicePluginDoorbird() +{ + +} + +Device::DeviceError DevicePluginDoorbird::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) +{ + Q_UNUSED(deviceClassId) + Q_UNUSED(params) + + // NOTE: Discovery is currently disabled in json file because we don't support discovery & login as parameters in combination + // and there isn't any setupMethod which would allow us to enter user & password. + + ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_xbmc-jsonrpc._tcp"); + QTimer::singleShot(5000, this, [this, serviceBrowser](){ + QList deviceDescriptors; + foreach (const ZeroConfServiceEntry serviceEntry, serviceBrowser->serviceEntries()) { + if (serviceEntry.serviceType() == "_axis-video._tcp" && serviceEntry.hostName().startsWith("bha-")) { + qCDebug(dcDoorBird) << "Found DoorBird device"; + DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); + ParamList params; + //TODO add rediscovery + params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); + deviceDescriptor.setParams(params); + deviceDescriptors.append(deviceDescriptor); + } + } + emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors); + serviceBrowser->deleteLater(); + }); + return Device::DeviceErrorAsync; +} + +Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) +{ + connectToEventMonitor(device); + return Device::DeviceSetupStatusSuccess; +} + +Device::DeviceError DevicePluginDoorbird::executeAction(Device *device, const Action &action) +{ + if (action.actionTypeId() == doorBirdUnlatchActionTypeId) { + QNetworkRequest request(QString("http://%1/bha-api/open-door.cgi?r=1").arg(device->paramValue(doorBirdDeviceAddressParamTypeId).toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_nam->get(request); + m_networkRequests.insert(reply, device); + connect(reply, &QNetworkReply::finished, this, [this, reply, device, action](){ + reply->deleteLater(); + m_networkRequests.remove(reply); + if (!myDevices().contains(device)) { + // Device must have been removed in the meantime + return; + } + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << device->name(); + emit actionExecutionFinished(action.id(), Device::DeviceErrorHardwareFailure); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit actionExecutionFinished(action.id(), Device::DeviceErrorNoError); + }); + } + return Device::DeviceErrorDeviceClassNotFound; +} + +void DevicePluginDoorbird::connectToEventMonitor(Device *device) +{ + qCDebug(dcDoorBird) << "Starting monitoring" << device->name(); + + QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(device->paramValue(doorBirdDeviceAddressParamTypeId).toString())); + QNetworkReply *reply = m_nam->get(request); + m_networkRequests.insert(reply, device); + connect(reply, &QNetworkReply::downloadProgress, this, [this, device, reply](qint64 bytesReceived, qint64 bytesTotal){ + Q_UNUSED(bytesReceived) + Q_UNUSED(bytesTotal); + if (!myDevices().contains(device)) { + qCWarning(dcDoorBird) << "Device disappeared for monitor stream."; + reply->abort(); + return; + } + device->setStateValue(doorBirdConnectedStateTypeId, true); + m_readBuffers[device].append(reply->readAll()); + // qCDebug(dcDoorBird) << "Monitor data for" << device->name(); + // qCDebug(dcDoorBird) << m_readBuffers[device]; + + // Input data looks like: + // "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n" + + while (!m_readBuffers[device].isEmpty()) { + // find next --ioboundary + QString boundary = QStringLiteral("--ioboundary"); + int startIndex = m_readBuffers[device].indexOf(boundary); + if (startIndex == -1) { + qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers[device]; + if (m_readBuffers[device].size() > 1024) { + qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer..."; + m_readBuffers[device].clear(); + } + // Assuming we don't have enough data yet... + return; + } + + QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain"); + int contentTypeIndex = m_readBuffers[device].indexOf(contentType); + if (contentTypeIndex == -1) { + qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffers[device]; + if (m_readBuffers[device].size() > startIndex + 50) { + qCWarning(dcDoorBird) << boundary << "found but unexpected data follows. Skipping this element..."; + m_readBuffers[device].remove(0, startIndex + boundary.length()); + continue; + } + // Assuming we don't have enough data yet... + return; + } + + // At this point we have the boundary and Content-Type. Remove all of that and take the entire string to either end or next boundary + m_readBuffers[device].remove(0, contentTypeIndex + contentType.length()); + int nextStartIndex = m_readBuffers[device].indexOf(boundary); + QByteArray data; + if (nextStartIndex == -1) { + data = m_readBuffers[device]; + m_readBuffers[device].clear(); + } else { + data = m_readBuffers[device].left(nextStartIndex); + m_readBuffers[device].remove(0, nextStartIndex); + } + + QString message = data.trimmed(); + QStringList parts = message.split(":"); + if (parts.count() != 2) { + qCWarning(dcDoorBird) << "Message has invalid format:" << message << "Expected device:state"; + continue; + } + if (parts.first() == "doorbell") { + if (parts.at(1) == "H") { + qCDebug(dcDoorBird) << "Doorbell ringing!"; + emitEvent(Event(doorBirdTriggeredEventTypeId, device->id())); + } + } else if (parts.first() == "motionsensor") { + if (parts.at(1) == "H") { + qCDebug(dcDoorBird) << "Motion sensor detected a person"; + emitEvent(Event(doorBirdMotionDetectedEventTypeId, device->id())); + } + } else { + qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; + } + } + }); + connect(reply, &QNetworkReply::finished, this, [this, device, reply](){ + reply->deleteLater(); + m_networkRequests.remove(reply); + + if (!myDevices().contains(device)) { + qCWarning(dcDoorBird) << "Device has disappeared. Exiting monitor."; + return; + } + + device->setStateValue(doorBirdConnectedStateTypeId, false); + qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); + + QTimer::singleShot(2000, this, [this, device] { + if (!myDevices().contains(device)) { + return; + } + connectToEventMonitor(device); + }); + }); +} diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h new file mode 100644 index 00000000..89edfb46 --- /dev/null +++ b/doorbird/deviceplugindoorbird.h @@ -0,0 +1,56 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * nymea is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * nymea is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with nymea. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINDOORBIRD_H +#define DEVICEPLUGINDOORBIRD_H + +#include "devices/deviceplugin.h" +#include "devices/devicemanager.h" + +class QNetworkAccessManager; +class QNetworkReply; + +class DevicePluginDoorbird: public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindoorbird.json") + Q_INTERFACES(DevicePlugin) + + +public: + explicit DevicePluginDoorbird(); + ~DevicePluginDoorbird() override; + + Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + + Device::DeviceSetupStatus setupDevice(Device *device) override; + Device::DeviceError executeAction(Device *device, const Action &action) override; + + void connectToEventMonitor(Device *device); +private: + + QNetworkAccessManager *m_nam = nullptr; + + QHash m_networkRequests; + QHash m_readBuffers; +}; + +#endif // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json new file mode 100644 index 00000000..974da11a --- /dev/null +++ b/doorbird/deviceplugindoorbird.json @@ -0,0 +1,70 @@ +{ + "name": "doorBird", + "displayName": "DoorBird", + "id": "6fe1614a-fc47-4eb2-a47c-13c50f1798ee", + "vendors": [ + { + "name": "doorBird", + "displayName": "DoorBird", + "id": "2da07435-571e-4956-a387-6caa51d6e845", + "deviceClasses": [ + { + "id": "0485eb61-2a22-42ba-9dd2-a5961485bf08", + "name": "doorBird", + "displayName": "DoorBird", + "createMethods": ["discovery", "user" ], + "interfaces": [ "inputtrigger", "connectable" ], + "paramTypes": [ + { + "id": "8873b17d-526e-408d-95d8-6439b501f489", + "name": "address", + "displayName": "IP address", + "type": "QString" + }, + { + "id": "7ccd8f3a-2a5f-4b90-8042-92899d0ee32a", + "name": "username", + "displayName": "Username", + "type": "QString" + }, + { + "id": "ea285a57-47c5-43f1-b0d6-e0a4d6230f3c", + "name": "password", + "displayName": "Password", + "type": "QString" + } + ], + "actionTypes": [ + { + "id": "b6c3377b-91de-411a-9d48-8b509c39d67c", + "name": "unlatch", + "displayName": "Unlatch the door" + } + ], + "stateTypes": [ + { + "id": "186c270b-923c-46e4-a7da-33e45427cdbb", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false + } + ], + "eventTypes": [ + { + "id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b", + "name": "triggered", + "displayName": "Doorbell pressed" + }, + { + "id": "e9bb229b-8776-4110-a813-9c0dc67375db", + "name": "motionDetected", + "displayName": "Motion detected" + } + ] + } + ] + } + ] +} diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro new file mode 100644 index 00000000..c10ea83a --- /dev/null +++ b/doorbird/doorbird.pro @@ -0,0 +1,11 @@ +include(../plugins.pri) + +QT += network + +TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) + +SOURCES += \ + deviceplugindoorbird.cpp \ + +HEADERS += \ + deviceplugindoorbird.h \ diff --git a/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts new file mode 100644 index 00000000..f7f66d85 --- /dev/null +++ b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts b/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts new file mode 100644 index 00000000..f7f66d85 --- /dev/null +++ b/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 23d78cdf..f20cccb2 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -12,6 +12,7 @@ PLUGIN_DIRS = \ datetime \ daylightsensor \ denon \ + doorbird \ dweetio \ elgato \ elro \ From 653caa938e6f2d16877b25f28667496571638f59 Mon Sep 17 00:00:00 2001 From: nymea Date: Sun, 22 Sep 2019 22:56:56 +0200 Subject: [PATCH 02/35] refactored code --- doorbird/README.md | 0 doorbird/deviceplugindoorbird.cpp | 248 +++++++++---------------- doorbird/deviceplugindoorbird.h | 12 +- doorbird/deviceplugindoorbird.json | 56 ++++-- doorbird/doorbird.cpp | 282 +++++++++++++++++++++++++++++ doorbird/doorbird.h | 76 ++++++++ doorbird/doorbird.pro | 2 + 7 files changed, 489 insertions(+), 187 deletions(-) create mode 100644 doorbird/README.md create mode 100644 doorbird/doorbird.cpp create mode 100644 doorbird/doorbird.h diff --git a/doorbird/README.md b/doorbird/README.md new file mode 100644 index 00000000..e69de29b diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index e62e4cf7..09123d25 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -28,22 +28,11 @@ #include #include -#include +#include #include DevicePluginDoorbird::DevicePluginDoorbird() { - m_nam = new QNetworkAccessManager(this); - connect(m_nam, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { - Device *dev = m_networkRequests.value(reply); - if (!myDevices().contains(dev)) { - qCWarning(dcDoorBird) << "Credentials requested for a device which doesn't exist any more"; - return; - } - qCDebug(dcDoorBird) << "Credentials requested for device:" << dev->name(); - authenticator->setUser(dev->paramValue(doorBirdDeviceUsernameParamTypeId).toString()); - authenticator->setPassword(dev->paramValue(doorBirdDevicePasswordParamTypeId).toString()); - }); } DevicePluginDoorbird::~DevicePluginDoorbird() @@ -53,164 +42,97 @@ DevicePluginDoorbird::~DevicePluginDoorbird() Device::DeviceError DevicePluginDoorbird::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) { - Q_UNUSED(deviceClassId) Q_UNUSED(params) - - // NOTE: Discovery is currently disabled in json file because we don't support discovery & login as parameters in combination - // and there isn't any setupMethod which would allow us to enter user & password. - - ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_xbmc-jsonrpc._tcp"); - QTimer::singleShot(5000, this, [this, serviceBrowser](){ - QList deviceDescriptors; - foreach (const ZeroConfServiceEntry serviceEntry, serviceBrowser->serviceEntries()) { - if (serviceEntry.serviceType() == "_axis-video._tcp" && serviceEntry.hostName().startsWith("bha-")) { - qCDebug(dcDoorBird) << "Found DoorBird device"; - DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); - ParamList params; - //TODO add rediscovery - params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); - deviceDescriptor.setParams(params); - deviceDescriptors.append(deviceDescriptor); + if (deviceClassId == doorBirdDeviceClassId) { + ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser(); + QTimer::singleShot(5000, this, [this, serviceBrowser](){ + QList deviceDescriptors; + foreach (const ZeroConfServiceEntry serviceEntry, serviceBrowser->serviceEntries()) { + if (serviceEntry.hostName().startsWith("bha-")) { + qCDebug(dcDoorBird) << "Found DoorBird device"; + DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); + ParamList params; + //TODO add rediscovery + params.append(Param(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName())); + params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); + deviceDescriptor.setParams(params); + deviceDescriptors.append(deviceDescriptor); + } } - } - emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors); - serviceBrowser->deleteLater(); - }); - return Device::DeviceErrorAsync; -} - -Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) -{ - connectToEventMonitor(device); - return Device::DeviceSetupStatusSuccess; -} - -Device::DeviceError DevicePluginDoorbird::executeAction(Device *device, const Action &action) -{ - if (action.actionTypeId() == doorBirdUnlatchActionTypeId) { - QNetworkRequest request(QString("http://%1/bha-api/open-door.cgi?r=1").arg(device->paramValue(doorBirdDeviceAddressParamTypeId).toString())); - qCDebug(dcDoorBird) << "Sending request:" << request.url(); - QNetworkReply *reply = m_nam->get(request); - m_networkRequests.insert(reply, device); - connect(reply, &QNetworkReply::finished, this, [this, reply, device, action](){ - reply->deleteLater(); - m_networkRequests.remove(reply); - if (!myDevices().contains(device)) { - // Device must have been removed in the meantime - return; - } - if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << device->name(); - emit actionExecutionFinished(action.id(), Device::DeviceErrorHardwareFailure); - return; - } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); - emit actionExecutionFinished(action.id(), Device::DeviceErrorNoError); + emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors); + serviceBrowser->deleteLater(); }); + return Device::DeviceErrorAsync; } return Device::DeviceErrorDeviceClassNotFound; } -void DevicePluginDoorbird::connectToEventMonitor(Device *device) +DevicePairingInfo DevicePluginDoorbird::pairDevice(DevicePairingInfo &devicePairingInfo) { - qCDebug(dcDoorBird) << "Starting monitoring" << device->name(); + qCDebug(dcDoorBird()) << "PairDevice:" << devicePairingInfo.deviceClassId(); - QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(device->paramValue(doorBirdDeviceAddressParamTypeId).toString())); - QNetworkReply *reply = m_nam->get(request); - m_networkRequests.insert(reply, device); - connect(reply, &QNetworkReply::downloadProgress, this, [this, device, reply](qint64 bytesReceived, qint64 bytesTotal){ - Q_UNUSED(bytesReceived) - Q_UNUSED(bytesTotal); - if (!myDevices().contains(device)) { - qCWarning(dcDoorBird) << "Device disappeared for monitor stream."; - reply->abort(); - return; - } - device->setStateValue(doorBirdConnectedStateTypeId, true); - m_readBuffers[device].append(reply->readAll()); - // qCDebug(dcDoorBird) << "Monitor data for" << device->name(); - // qCDebug(dcDoorBird) << m_readBuffers[device]; - - // Input data looks like: - // "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n" - - while (!m_readBuffers[device].isEmpty()) { - // find next --ioboundary - QString boundary = QStringLiteral("--ioboundary"); - int startIndex = m_readBuffers[device].indexOf(boundary); - if (startIndex == -1) { - qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers[device]; - if (m_readBuffers[device].size() > 1024) { - qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer..."; - m_readBuffers[device].clear(); - } - // Assuming we don't have enough data yet... - return; - } - - QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain"); - int contentTypeIndex = m_readBuffers[device].indexOf(contentType); - if (contentTypeIndex == -1) { - qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffers[device]; - if (m_readBuffers[device].size() > startIndex + 50) { - qCWarning(dcDoorBird) << boundary << "found but unexpected data follows. Skipping this element..."; - m_readBuffers[device].remove(0, startIndex + boundary.length()); - continue; - } - // Assuming we don't have enough data yet... - return; - } - - // At this point we have the boundary and Content-Type. Remove all of that and take the entire string to either end or next boundary - m_readBuffers[device].remove(0, contentTypeIndex + contentType.length()); - int nextStartIndex = m_readBuffers[device].indexOf(boundary); - QByteArray data; - if (nextStartIndex == -1) { - data = m_readBuffers[device]; - m_readBuffers[device].clear(); - } else { - data = m_readBuffers[device].left(nextStartIndex); - m_readBuffers[device].remove(0, nextStartIndex); - } - - QString message = data.trimmed(); - QStringList parts = message.split(":"); - if (parts.count() != 2) { - qCWarning(dcDoorBird) << "Message has invalid format:" << message << "Expected device:state"; - continue; - } - if (parts.first() == "doorbell") { - if (parts.at(1) == "H") { - qCDebug(dcDoorBird) << "Doorbell ringing!"; - emitEvent(Event(doorBirdTriggeredEventTypeId, device->id())); - } - } else if (parts.first() == "motionsensor") { - if (parts.at(1) == "H") { - qCDebug(dcDoorBird) << "Motion sensor detected a person"; - emitEvent(Event(doorBirdMotionDetectedEventTypeId, device->id())); - } - } else { - qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; - } - } - }); - connect(reply, &QNetworkReply::finished, this, [this, device, reply](){ - reply->deleteLater(); - m_networkRequests.remove(reply); - - if (!myDevices().contains(device)) { - qCWarning(dcDoorBird) << "Device has disappeared. Exiting monitor."; - return; - } - - device->setStateValue(doorBirdConnectedStateTypeId, false); - qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); - - QTimer::singleShot(2000, this, [this, device] { - if (!myDevices().contains(device)) { - return; - } - connectToEventMonitor(device); - }); - }); + devicePairingInfo.setStatus(Device::DeviceErrorNoError); + devicePairingInfo.setMessage(tr("Please enter username and password for your Doorbird device.")); + return devicePairingInfo; +} + +DevicePairingInfo DevicePluginDoorbird::confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) +{ + qCDebug(dcDoorBird()) << "confirm pairing called"; + + pluginStorage()->beginGroup(devicePairingInfo.deviceId().toString()); + pluginStorage()->setValue("username", username); + pluginStorage()->setValue("password", secret); + pluginStorage()->endGroup(); + + devicePairingInfo.setStatus(Device::DeviceErrorNoError); + + return devicePairingInfo; +} + + +Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) +{ + if (device->deviceClassId() == doorBirdDeviceClassId) { + QHostAddress address = QHostAddress(device->paramValue(doorBirdDeviceAddressParamTypeId).toString()); + + pluginStorage()->beginGroup(device->id().toString()); + QString username = pluginStorage()->value("username").toString(); + QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); + + Doorbird *doorbird = new Doorbird(address, username, password, this); + doorbird->connectToEventMonitor(); + m_doorbirdConnections.insert(device, doorbird); + return Device::DeviceSetupStatusSuccess; + } + return Device::DeviceSetupStatusFailure; +} + +Device::DeviceError DevicePluginDoorbird::executeAction(Device *device, const Action &action) +{ + if (device->deviceClassId() == doorBirdDeviceClassId) { + Doorbird *doorbird = m_doorbirdConnections.value(device); + if (!doorbird) + return Device::DeviceErrorDeviceNotFound; + + if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { + //Todo get action param + doorbird->openDoor(1); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == doorBirdLightOnActionTypeId) { + doorbird->lightOn(); + return Device::DeviceErrorNoError; + } + } + return Device::DeviceErrorDeviceClassNotFound; +} + +void DevicePluginDoorbird::deviceRemoved(Device *device) +{ + if (device->deviceClassId() == doorBirdDeviceClassId) { + Doorbird *doorbirdConnection = m_doorbirdConnections.take(device); + doorbirdConnection->deleteLater(); + } } diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h index 89edfb46..e18b250e 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/deviceplugindoorbird.h @@ -23,6 +23,7 @@ #include "devices/deviceplugin.h" #include "devices/devicemanager.h" +#include "doorbird.h" class QNetworkAccessManager; class QNetworkReply; @@ -44,13 +45,12 @@ public: Device::DeviceSetupStatus setupDevice(Device *device) override; Device::DeviceError executeAction(Device *device, const Action &action) override; - void connectToEventMonitor(Device *device); + DevicePairingInfo confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) override; + DevicePairingInfo pairDevice(DevicePairingInfo &devicePairingInfo) override; + void deviceRemoved(Device *device)override; + private: - - QNetworkAccessManager *m_nam = nullptr; - - QHash m_networkRequests; - QHash m_readBuffers; + QHash m_doorbirdConnections; }; #endif // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index 974da11a..309c918d 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -13,7 +13,8 @@ "name": "doorBird", "displayName": "DoorBird", "createMethods": ["discovery", "user" ], - "interfaces": [ "inputtrigger", "connectable" ], + "interfaces": [ "doorbell", "presencesensor", "connectable" ], + "setupMethod": "userandpassword", "paramTypes": [ { "id": "8873b17d-526e-408d-95d8-6439b501f489", @@ -22,23 +23,30 @@ "type": "QString" }, { - "id": "7ccd8f3a-2a5f-4b90-8042-92899d0ee32a", - "name": "username", - "displayName": "Username", - "type": "QString" - }, - { - "id": "ea285a57-47c5-43f1-b0d6-e0a4d6230f3c", - "name": "password", - "displayName": "Password", + "id": "67ea5534-330a-4291-93b5-0237034e15fa", + "name": "serialnumber", + "displayName": "Serial number", "type": "QString" } ], "actionTypes": [ { "id": "b6c3377b-91de-411a-9d48-8b509c39d67c", - "name": "unlatch", - "displayName": "Unlatch the door" + "name": "openDoor", + "displayName": "Open door", + "paramTypes": [ + { + "id": "6dcce056-e4d4-49c4-8b68-d3f478c00a52", + "name": "number", + "displayName": "Number", + "type": "int" + } + ] + }, + { + "id": "3a6cfc5d-804c-4d21-91b5-999913d4f1a5", + "name": "lightOn", + "displayName": "Light on" } ], "stateTypes": [ @@ -49,18 +57,30 @@ "displayNameEvent": "Connected changed", "type": "bool", "defaultValue": false + }, + { + "id": "0f5eb200-6c0d-45c5-9156-3060fd66d332", + "name": "isPresent", + "displayName": "Motion sensor presence", + "displayNameEvent": "Motion sensor presence detected", + "type": "bool", + "defaultValue": false + }, + { + "id": "295c9700-b598-4681-898f-d63e2889cedf", + "name": "lastSeenTime", + "displayName": "Motion sensor last seen time", + "displayNameEvent": "Motion sensor last seen time changedd", + "type": "int", + "unit": "UnixTime", + "defaultValue": 0 } ], "eventTypes": [ { "id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b", - "name": "triggered", + "name": "doorbellPressed", "displayName": "Doorbell pressed" - }, - { - "id": "e9bb229b-8776-4110-a813-9c0dc67375db", - "name": "motionDetected", - "displayName": "Motion detected" } ] } diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp new file mode 100644 index 00000000..46174ed4 --- /dev/null +++ b/doorbird/doorbird.cpp @@ -0,0 +1,282 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * nymea is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * nymea is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with nymea. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "doorbird.h" +#include "extern-plugininfo.h" + +#include +#include +#include +#include +#include +#include +#include + +Doorbird::Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent) : + QObject(parent), + m_address(address), + m_username(username), + m_password(password) +{ + m_networkAccessManager = new QNetworkAccessManager(this); + connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { + qCDebug(dcDoorBird) << "Credentials requested:"; + authenticator->setUser(username); + authenticator->setPassword(password); + }); +} + +QUuid Doorbird::getSession() +{ + QNetworkRequest request(QString("http://%1/bha-api/getsession.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + emit requestSent(requestId, false); + return; + } + emit requestSent(requestId, true); + + QByteArray data = reply->readAll(); + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcDoorBird()) << "Error parsing json:" << data; + return; + } + }); + return requestId; +} + +QUuid Doorbird::openDoor(int value) +{ + QNetworkRequest request(QString("http://%1/bha-api/open-door.cgi?r=%2").arg(m_address.toString()).arg(QString::number(value))); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error opening DoorBird " << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:"; + emit requestSent(requestId, true); + }); + return requestId; +} + +QUuid Doorbird::lightOn() +{ + QNetworkRequest request(QString("http://%1/bha-api/light-on.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird light on:"; + emit requestSent(requestId, true); + }); + return requestId; +} + +QUuid Doorbird::liveVideoRequest() +{ + +} + +QUuid Doorbird::liveImageRequest() +{ + QNetworkRequest request(QString("http://%1/bha-api/light-on.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird light on:"; + emit requestSent(requestId, true); + + QImage* image = new QImage(); + image->loadFromData(reply->readAll()); + }); + return requestId; +} + +QUuid Doorbird::historyImageRequest(int index) +{ + +} + +QUuid Doorbird::infoRequest() +{ + QNetworkRequest request(QString("http://%1/bha-api/info.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + + connect(reply, &QNetworkReply::finished, this, [this, reply, action](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + }); +} + +QUuid Doorbird::listFavorites() +{ + +} + +QUuid Doorbird::listSchedules() +{ + +} + +QUuid Doorbird::restart() +{ + QNetworkRequest request(QString("http://%1/bha-api/restart.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + + connect(reply, &QNetworkReply::finished, this, [this, reply, action](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error restarting DoorBird device" << reply; + return; + } + qCDebug(dcDoorBird) << "DoorBird restarting"; + }); +} + +void Doorbird::connectToEventMonitor() +{ + qCDebug(dcDoorBird) << "Starting monitoring"; + + QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(m_address.toString()); + QNetworkReply *reply = m_networkAccessManager->get(request); + + connect(reply, &QNetworkReply::downloadProgress, this, [this, reply](qint64 bytesReceived, qint64 bytesTotal){ + Q_UNUSED(bytesReceived) + Q_UNUSED(bytesTotal); + + //TODO emit signal connected + m_readBuffers.append(reply->readAll()); + // qCDebug(dcDoorBird) << "Monitor data for" << device->name(); + // qCDebug(dcDoorBird) << m_readBuffers[device]; + + // Input data looks like: + // "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n" + + while (!m_readBuffers.isEmpty()) { + // find next --ioboundary + QString boundary = QStringLiteral("--ioboundary"); + int startIndex = m_readBuffers.indexOf(boundary); + if (startIndex == -1) { + qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers; + if (m_readBuffers.size() > 1024) { + qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer..."; + m_readBuffers.clear(); + } + // Assuming we don't have enough data yet... + return; + } + + QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain"); + int contentTypeIndex = m_readBuffers.indexOf(contentType); + if (contentTypeIndex == -1) { + qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffers; + if (m_readBuffers.size() > startIndex + 50) { + qCWarning(dcDoorBird) << boundary << "found but unexpected data follows. Skipping this element..."; + m_readBuffers.remove(0, startIndex + boundary.length()); + continue; + } + // Assuming we don't have enough data yet... + return; + } + + // At this point we have the boundary and Content-Type. Remove all of that and take the entire string to either end or next boundary + m_readBuffers.remove(0, contentTypeIndex + contentType.length()); + int nextStartIndex = m_readBuffers.indexOf(boundary); + QByteArray data; + if (nextStartIndex == -1) { + data = m_readBuffers; + m_readBuffers.clear(); + } else { + data = m_readBuffers.left(nextStartIndex); + m_readBuffers.remove(0, nextStartIndex); + } + + QString message = data.trimmed(); + QStringList parts = message.split(":"); + if (parts.count() != 2) { + qCWarning(dcDoorBird) << "Message has invalid format:" << message << "Expected device:state"; + continue; + } + if (parts.first() == "doorbell") { + if (parts.at(1) == "H") { + qCDebug(dcDoorBird) << "Doorbell ringing!"; + //emitEvent(Event(doorBirdTriggeredEventTypeId, device->id())); + } + } else if (parts.first() == "motionsensor") { + if (parts.at(1) == "H") { + qCDebug(dcDoorBird) << "Motion sensor detected a person"; + //emitEvent(Event(doorBirdMotionDetectedEventTypeId, device->id())); + } + } else { + qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; + } + } + }); + connect(reply, &QNetworkReply::finished, this, [this, reply](){ + reply->deleteLater(); + + //device->setStateValue(doorBirdConnectedStateTypeId, false); + qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); + + QTimer::singleShot(2000, this, [this] { + connectToEventMonitor(device); + }); + }); +} + diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h new file mode 100644 index 00000000..69e05d0b --- /dev/null +++ b/doorbird/doorbird.h @@ -0,0 +1,76 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * nymea is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * nymea is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with nymea. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DOORBIRD_H +#define DOORBIRD_H + +#include +#include +#include +#include + +class Doorbird : public QObject +{ + Q_OBJECT +public: + explicit Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent = nullptr); + + QUuid getSession(); + QUuid openDoor(int value); + QUuid lightOn(); + QUuid liveVideoRequest(); + QUuid liveImageRequest(); + QUuid historyImageRequest(int index); + + QUuid liveAudioReceive(); + QUuid liveAudioTransmit(); + QUuid infoRequest(); + + QUuid listFavorites(); + QUuid addFavorite(); + QUuid deleteFavorite(); + + QUuid listSchedules(); + QUuid daddScheduleEntry(); + QUuid deleteScheduleEntry(); + + QUuid restart(); + + void connectToEventMonitor(); +private: + QNetworkAccessManager *m_networkAccessManager; + QList m_readBuffers; + + QHostAddress m_address; + QList m_networkRequests; + + QString m_username; + QString m_password; + + QByteArray sessionId; + +signals: + void requestSent(QUuid requestId, bool success); + +public slots: +}; + +#endif // DOORBIRD_H diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index c10ea83a..0ea9d1fb 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -6,6 +6,8 @@ TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) SOURCES += \ deviceplugindoorbird.cpp \ + doorbird.cpp HEADERS += \ deviceplugindoorbird.h \ + doorbird.h From 73055830b873afed0ec202b6705fe1403d789dce Mon Sep 17 00:00:00 2001 From: nymea Date: Sun, 22 Sep 2019 23:30:43 +0200 Subject: [PATCH 03/35] added some more API calls --- doorbird/deviceplugindoorbird.json | 10 +-- doorbird/doorbird.cpp | 122 ++++++++++++++++++++++++++--- 2 files changed, 111 insertions(+), 21 deletions(-) diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index 309c918d..8f9ed2ee 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -33,15 +33,7 @@ { "id": "b6c3377b-91de-411a-9d48-8b509c39d67c", "name": "openDoor", - "displayName": "Open door", - "paramTypes": [ - { - "id": "6dcce056-e4d4-49c4-8b68-d3f478c00a52", - "name": "number", - "displayName": "Number", - "type": "int" - } - ] + "displayName": "Open door" }, { "id": "3a6cfc5d-804c-4d21-91b5-999913d4f1a5", diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 46174ed4..a2a6dce9 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include Doorbird::Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent) : QObject(parent), @@ -38,9 +40,10 @@ Doorbird::Doorbird(const QHostAddress &address, const QString &username, const Q { m_networkAccessManager = new QNetworkAccessManager(this); connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { + Q_UNUSED(reply); qCDebug(dcDoorBird) << "Credentials requested:"; - authenticator->setUser(username); - authenticator->setPassword(password); + authenticator->setUser(m_username); + authenticator->setPassword(m_password); }); } @@ -114,7 +117,22 @@ QUuid Doorbird::lightOn() QUuid Doorbird::liveVideoRequest() { + QNetworkRequest request(QString("http://%1/bha-api/info.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit requestSent(requestId, true); + }); + return requestId; } QUuid Doorbird::liveImageRequest() @@ -142,7 +160,52 @@ QUuid Doorbird::liveImageRequest() QUuid Doorbird::historyImageRequest(int index) { + QUrl url(QString("http://%1/bha-api/history.cgi").arg(m_address.toString())); + QUrlQuery query; + query.addQueryItem("index", QString::number(index)); + url.setQuery(query); + qCDebug(dcDoorBird) << "Sending request:" << url; + QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(url)); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit requestSent(requestId, true); + }); + return requestId; +} + +QUuid Doorbird::liveAudioReceive() +{ + QNetworkRequest request(QString("http://%1/bha-api/audio-receive.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error DoorBird device"; + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit requestSent(requestId, true); + }); + return requestId; +} + +QUuid Doorbird::liveAudioTransmit() +{ + + return QUuid::createUuid(); } QUuid Doorbird::infoRequest() @@ -150,26 +213,57 @@ QUuid Doorbird::infoRequest() QNetworkRequest request(QString("http://%1/bha-api/info.cgi").arg(m_address.toString())); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); - - connect(reply, &QNetworkReply::finished, this, [this, reply, action](){ + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + emit requestSent(requestId, false); return; } qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit requestSent(requestId, true); }); + return requestId; } QUuid Doorbird::listFavorites() { + QNetworkRequest request(QString("http://%1/bha-api/favorites.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + emit requestSent(requestId, true); + }); + return requestId; } QUuid Doorbird::listSchedules() { + QNetworkRequest request(QString("http://%1/bha-api/schedule.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + emit requestSent(requestId, true); + }); + return requestId; } QUuid Doorbird::restart() @@ -177,24 +271,27 @@ QUuid Doorbird::restart() QNetworkRequest request(QString("http://%1/bha-api/restart.cgi").arg(m_address.toString())); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); - - connect(reply, &QNetworkReply::finished, this, [this, reply, action](){ + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error restarting DoorBird device" << reply; + emit requestSent(requestId, false); return; } qCDebug(dcDoorBird) << "DoorBird restarting"; + emit requestSent(requestId, true); }); + return requestId; } void Doorbird::connectToEventMonitor() { qCDebug(dcDoorBird) << "Starting monitoring"; - QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(m_address.toString()); - QNetworkReply *reply = m_networkAccessManager->get(request); + QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(m_address.toString())); + QNetworkReply *reply = m_networkAccessManager->get(request); connect(reply, &QNetworkReply::downloadProgress, this, [this, reply](qint64 bytesReceived, qint64 bytesTotal){ Q_UNUSED(bytesReceived) @@ -210,7 +307,7 @@ void Doorbird::connectToEventMonitor() while (!m_readBuffers.isEmpty()) { // find next --ioboundary - QString boundary = QStringLiteral("--ioboundary"); + /*QString boundary = QStringLiteral("--ioboundary"); int startIndex = m_readBuffers.indexOf(boundary); if (startIndex == -1) { qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers; @@ -265,17 +362,18 @@ void Doorbird::connectToEventMonitor() } } else { qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; - } + }*/ } }); - connect(reply, &QNetworkReply::finished, this, [this, reply](){ + + connect(reply, &QNetworkReply::finished, this, [this, reply]() { reply->deleteLater(); //device->setStateValue(doorBirdConnectedStateTypeId, false); qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); QTimer::singleShot(2000, this, [this] { - connectToEventMonitor(device); + connectToEventMonitor(); }); }); } From 9ef3674dba58a1145127ceaaa8d6718b34ad4a16 Mon Sep 17 00:00:00 2001 From: nymea Date: Tue, 24 Sep 2019 17:21:53 +0200 Subject: [PATCH 04/35] added qr code reader --- doorbird/doorbird.pro | 8 +- doorbird/qrcodereader.cpp | 189 ++++++++++++++++++++++++++++++++++++++ doorbird/qrcodereader.h | 88 ++++++++++++++++++ 3 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 doorbird/qrcodereader.cpp create mode 100644 doorbird/qrcodereader.h diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index 0ea9d1fb..c519c376 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -2,12 +2,16 @@ include(../plugins.pri) QT += network +LIBS += -lzbar + TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) SOURCES += \ deviceplugindoorbird.cpp \ - doorbird.cpp + doorbird.cpp \ + qrcodereader.cpp \ HEADERS += \ deviceplugindoorbird.h \ - doorbird.h + doorbird.h \ + qrcodereader.h \ diff --git a/doorbird/qrcodereader.cpp b/doorbird/qrcodereader.cpp new file mode 100644 index 00000000..658a2496 --- /dev/null +++ b/doorbird/qrcodereader.cpp @@ -0,0 +1,189 @@ +/***************************************************************************** + * Copyright: 2013 Michael Zanetti * + * * + * This file is part of ubuntu-authenticator * + * * + * This prject is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This project is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + * * + ****************************************************************************/ + +#include "qrcodereader.h" + +#include +#include +#include +#include + +QRCodeReader::QRCodeReader(QObject *parent) : + QObject(parent) +{ + connect(&m_readerThread, &QThread::started, this, &QRCodeReader::scanningChanged); + connect(&m_readerThread, &QThread::finished, this, &QRCodeReader::scanningChanged); +} + +bool QRCodeReader::valid() const +{ + return !m_type.isEmpty() && !m_text.isEmpty(); +} + +QString QRCodeReader::type() const +{ + return m_type; +} + +QString QRCodeReader::text() const +{ + return m_text; +} + +QImage QRCodeReader::image() const +{ + return m_image; +} + +QRect QRCodeReader::scanRect() const +{ + return m_scanRect; +} + +void QRCodeReader::setScanRect(const QRect &rect) +{ + if (m_scanRect != rect) { + m_scanRect = rect; + emit scanRectChanged(); + } +} + +bool QRCodeReader::scanning() const +{ + return m_readerThread.isRunning(); +} + +void QRCodeReader::grab(QImage image) +{ + m_type.clear(); + m_text.clear(); + emit validChanged(); + + if (m_scanRect.isValid()) { + image = image.copy(m_scanRect); + } + + Reader *reader = new Reader; + reader->moveToThread(&m_readerThread); + connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater())); + connect(reader, SIGNAL(finished()), &m_readerThread, SLOT(quit())); + connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); + m_readerThread.start(); + + QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, img), Q_ARG(bool, false)); +} + +void QRCodeReader::processImage(const QUrl &url) +{ + QImage image; + if (!image.load(url.path())) { + qWarning() << "can't open" << url.path(); + return; + } + + Reader *reader = new Reader; + reader->moveToThread(&m_readerThread); + connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater())); + connect(reader, SIGNAL(finished()), &m_readerThread, SLOT(quit())); + connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); + m_readerThread.start(); + + QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, image), Q_ARG(bool, false)); +} + +void QRCodeReader::handleResults(const QString &type, const QString &text, const QImage &codeImage) +{ + m_type = type; + m_text = text; + m_image = codeImage; + m_imageUuid = QUuid::createUuid(); + emit validChanged(); +} + +void Reader::doWork(const QImage &image, bool invert) +{ + // Prepare image + QImage copy = image; + if (invert) { + copy.invertPixels(); + } + zbar::QZBarImage img(copy.convertToFormat(QImage::Format_RGB32)); + zbar::Image tmp = img.convert(*(long*)"Y800"); + + // create a reader + zbar::ImageScanner scanner; + + // configure the reader + scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1); + scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_POSITION, 1); + scanner.set_config(zbar::ZBAR_PARTIAL, zbar::ZBAR_CFG_ENABLE, 0); + + // scan the image for barcodes + int n = scanner.scan(tmp); +// qDebug() << "scanned. have" << n << "symbols"; + if (!invert && n == 0) { + // Nothing found... try again inverted + doWork(image, true); + return; + } + + img.set_symbols(tmp.get_symbols()); + + // extract results + for(zbar::Image::SymbolIterator symbol = img.symbol_begin(); symbol != img.symbol_end(); ++symbol) { + + QString typeName = QString::fromStdString(symbol->get_type_name()); + QString symbolString = QString::fromStdString(symbol->get_data()); + + int x0 = 999999; + int y0 = 999999; + int x1 = 0; + int y1 = 0; + + for (int i = 0; i < symbol->get_location_size(); ++i) { + int x = symbol->get_location_x(i); + int y = symbol->get_location_y(i); + qDebug() << "got point" << x << y; + if (x < x0) x0 = x; + if (y < y0) y0 = y; + if (x > x1) x1 = x; + if (y > y1) y1 = y; + } + + int width = x1 - x0; + int height = y1 - y0; + + // Workaround for zBar sometimes only giving us the first bar in a barcode. + if (width < 10) width = img.get_width() - x0; + if (height < 10) height = img.get_height() - y0; + + qDebug() << "extracting code image (" << x0 << y0 << ") ("<< x1 << y1 << ")"; + QImage codeImage = image.copy(x0, y0, width, height); + + qDebug() << "Code recognized:" << typeName << ", Text:" << symbolString; + + emit resultReady(typeName, symbolString, codeImage); + } + + tmp.set_data(NULL, 0); + img.set_data(NULL, 0); + + emit finished(); +} diff --git a/doorbird/qrcodereader.h b/doorbird/qrcodereader.h new file mode 100644 index 00000000..f0c22b05 --- /dev/null +++ b/doorbird/qrcodereader.h @@ -0,0 +1,88 @@ +/***************************************************************************** + * Copyright: 2013 Michael Zanetti * + * * + * This file is part of tagger * + * * + * This prject is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This project is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + * * + ****************************************************************************/ + +#ifndef QRCODEREADER_H +#define QRCODEREADER_H + +#include +#include +#include +#include + +class QRCodeReader : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool valid READ valid NOTIFY validChanged) + Q_PROPERTY(QString type READ type NOTIFY validChanged) + Q_PROPERTY(QString text READ text NOTIFY validChanged) + Q_PROPERTY(QImage image READ image NOTIFY validChanged) + Q_PROPERTY(QString imageSource READ imageSource NOTIFY validChanged) + Q_PROPERTY(QRect scanRect READ scanRect WRITE setScanRect NOTIFY scanRectChanged) + Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged) + Q_PROPERTY(HistoryModel* history READ history CONSTANT) + +public: + explicit QRCodeReader(QObject *parent = nullptr); + + bool valid() const; + QString type() const; + QString text() const; + QImage image() const; + QString imageSource() const; + QRect scanRect() const; + void setScanRect(const QRect &rect); + bool scanning() const; + + +public slots: + void grab(QImage image); + void processImage(const QUrl &url); + +signals: + void validChanged(); + void scanRectChanged(); + void scanningChanged(); + +private slots: + void handleResults(const QString &type, const QString &text, const QImage &codeImage); + +private: + QString m_type; + QString m_text; + QImage m_image; + QUuid m_imageUuid; + QRect m_scanRect; + + QThread m_readerThread; +}; + +class Reader : public QObject +{ + Q_OBJECT + +public slots: + void doWork(const QImage &image, bool invert); + +signals: + void resultReady(const QString &type, const QString &text, const QImage &codeImage); + void finished(); +}; + +#endif // QRCODEREADER_H From de456952494c8d39557f5d3918027d8acc50b0d4 Mon Sep 17 00:00:00 2001 From: nymea Date: Tue, 24 Sep 2019 23:05:30 +0200 Subject: [PATCH 05/35] updated to new nymea api --- doorbird/deviceplugindoorbird.cpp | 211 +++++++++++++++++++++-------- doorbird/deviceplugindoorbird.h | 22 ++- doorbird/deviceplugindoorbird.json | 20 ++- doorbird/doorbird.cpp | 122 +++++++++++++---- doorbird/doorbird.h | 31 ++++- doorbird/qrcodereader.cpp | 5 +- doorbird/qrcodereader.h | 4 +- 7 files changed, 315 insertions(+), 100 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 09123d25..03f117eb 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -35,64 +35,74 @@ DevicePluginDoorbird::DevicePluginDoorbird() { } -DevicePluginDoorbird::~DevicePluginDoorbird() -{ -} - -Device::DeviceError DevicePluginDoorbird::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) +void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) { - Q_UNUSED(params) - if (deviceClassId == doorBirdDeviceClassId) { - ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser(); - QTimer::singleShot(5000, this, [this, serviceBrowser](){ - QList deviceDescriptors; + if (info->deviceClassId() == doorBirdDeviceClassId) { + 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()) { if (serviceEntry.hostName().startsWith("bha-")) { - qCDebug(dcDoorBird) << "Found DoorBird device"; + qCDebug(dcDoorBird) << "Found DoorBird device, name: " << serviceEntry.name() << "\n host address:" << serviceEntry.hostAddress().toString() << "\n text:" << serviceEntry.txt() << serviceEntry.protocol() << serviceEntry.serviceType(); DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); ParamList params; - //TODO add rediscovery - params.append(Param(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName())); + QString macAddress = serviceEntry.txt().first(); + if (!myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).isEmpty()) { + Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName()).first(); + deviceDescriptor.setDeviceId(existingDevice->id()); + } + params.append(Param(doorBirdDeviceSerialnumberParamTypeId, macAddress)); params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); deviceDescriptor.setParams(params); - deviceDescriptors.append(deviceDescriptor); + info->addDeviceDescriptor(deviceDescriptor); } } - emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors); serviceBrowser->deleteLater(); + info->finish(Device::DeviceErrorNoError); }); - return Device::DeviceErrorAsync; + return; } - return Device::DeviceErrorDeviceClassNotFound; -} - -DevicePairingInfo DevicePluginDoorbird::pairDevice(DevicePairingInfo &devicePairingInfo) -{ - qCDebug(dcDoorBird()) << "PairDevice:" << devicePairingInfo.deviceClassId(); - - devicePairingInfo.setStatus(Device::DeviceErrorNoError); - devicePairingInfo.setMessage(tr("Please enter username and password for your Doorbird device.")); - return devicePairingInfo; -} - -DevicePairingInfo DevicePluginDoorbird::confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) -{ - qCDebug(dcDoorBird()) << "confirm pairing called"; - - pluginStorage()->beginGroup(devicePairingInfo.deviceId().toString()); - pluginStorage()->setValue("username", username); - pluginStorage()->setValue("password", secret); - pluginStorage()->endGroup(); - - devicePairingInfo.setStatus(Device::DeviceErrorNoError); - - return devicePairingInfo; + qCWarning(dcDoorBird()) << "Cannot discover for deviceClassId" << info->deviceClassId(); + info->finish(Device::DeviceErrorDeviceNotFound); } -Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) +void DevicePluginDoorbird::startPairing(DevicePairingInfo *info) { + + if (info->deviceClassId() == doorBirdDeviceClassId) { + qCDebug(dcDoorBird()) << "User and password. Login is \"user\" and \"password\"."; + info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter the user credentials")); + return; + } + info->finish(Device::DeviceErrorCreationMethodNotSupported); +} + + +void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) +{ + if (info->deviceClassId() == doorBirdDeviceClassId) { + qCDebug(dcDoorBird()) << "confirm pairing called"; + + pluginStorage()->beginGroup(info->deviceId().toString()); + pluginStorage()->setValue("username", username); + pluginStorage()->setValue("password", secret); + pluginStorage()->endGroup(); + + info->finish(Device::DeviceErrorNoError); + return; + } + info->finish(Device::DeviceErrorDeviceClassNotFound); + return; +} + + +void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) +{ + Device *device = info->device(); + if (device->deviceClassId() == doorBirdDeviceClassId) { QHostAddress address = QHostAddress(device->paramValue(doorBirdDeviceAddressParamTypeId).toString()); @@ -101,32 +111,61 @@ Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) QString password = pluginStorage()->value("password").toString(); pluginStorage()->endGroup(); + qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; Doorbird *doorbird = new Doorbird(address, username, password, this); + connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); + connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); + connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(device, doorbird); - return Device::DeviceSetupStatusSuccess; + info->finish(Device::DeviceErrorNoError); + return; } - return Device::DeviceSetupStatusFailure; + qCWarning(dcDoorBird()) << "Unhandled device class" << info->device()->deviceClass(); + info->finish(Device::DeviceErrorDeviceClassNotFound); } -Device::DeviceError DevicePluginDoorbird::executeAction(Device *device, const Action &action) + +void DevicePluginDoorbird::postSetupDevice(Device *device) { if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbird = m_doorbirdConnections.value(device); - if (!doorbird) - return Device::DeviceErrorDeviceNotFound; - - if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { - //Todo get action param - doorbird->openDoor(1); - return Device::DeviceErrorNoError; - } - if (action.actionTypeId() == doorBirdLightOnActionTypeId) { - doorbird->lightOn(); - return Device::DeviceErrorNoError; - } + Doorbird *doorbird = m_doorbirdConnections.value(device); + doorbird->infoRequest(); + doorbird->listFavorites(); + doorbird->listSchedules(); } - return Device::DeviceErrorDeviceClassNotFound; +} + + +void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) +{ + Device *device = info->device(); + Action action = info->action(); + + if (device->deviceClassId() == doorBirdDeviceClassId) { + Doorbird *doorbird = m_doorbirdConnections.value(device); + if (!doorbird) { + info->finish(Device::DeviceErrorHardwareFailure); + return; + } + if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { + int number = action.param(doorBirdOpenDoorActionNumberParamTypeId).value().toInt(); + doorbird->openDoor(number); + info->finish(Device::DeviceErrorNoError); + return; + } else if (action.actionTypeId() == doorBirdLightOnActionTypeId) { + doorbird->lightOn(); + info->finish(Device::DeviceErrorNoError); + return; + } else if (action.actionTypeId() == doorBirdRestartActionTypeId) { + doorbird->restart(); + info->finish(Device::DeviceErrorNoError); + return; + } + info->finish(Device::DeviceErrorActionTypeNotFound); + return; + } + info->finish(Device::DeviceErrorDeviceClassNotFound); } void DevicePluginDoorbird::deviceRemoved(Device *device) @@ -136,3 +175,59 @@ void DevicePluginDoorbird::deviceRemoved(Device *device) doorbirdConnection->deleteLater(); } } + +void DevicePluginDoorbird::onDoorBirdConnected(bool status) +{ + Doorbird *doorbird = static_cast(sender()); + Device *device = m_doorbirdConnections.key(doorbird); + if (!device) + return; + + device->setStateValue(doorBirdConnectedStateTypeId, status); +} + +void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool status) +{ + Doorbird *doorbird = static_cast(sender()); + Device *device = m_doorbirdConnections.key(doorbird); + if (!device) + return; + + switch (eventType) { + case Doorbird::EventType::Rfid: + break; + case Doorbird::EventType::Input: + break; + case Doorbird::EventType::Motion: + device->setStateValue(doorBirdIsPresentStateTypeId, status); + if (status) { + doorbird->liveImageRequest(); + } + break; + case Doorbird::EventType::Doorbell: + if (status) { + emit emitEvent(Event(doorBirdDoorbellPressedEventTypeId ,device->id())); + } + break; + } +} + +void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) +{ + Doorbird *doorbird = static_cast(sender()); + Device *device = m_doorbirdConnections.key(doorbird); + if (!device) + return; + + if (!m_asyncActions.contains(requestId)) + return; + + DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); + actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); +} + +void DevicePluginDoorbird::onImageReceived(QImage image) +{ + Q_UNUSED(image) + //QString code = +} diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h index e18b250e..9be0a4ee 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/deviceplugindoorbird.h @@ -21,6 +21,8 @@ #ifndef DEVICEPLUGINDOORBIRD_H #define DEVICEPLUGINDOORBIRD_H +#include + #include "devices/deviceplugin.h" #include "devices/devicemanager.h" #include "doorbird.h" @@ -35,22 +37,28 @@ class DevicePluginDoorbird: public DevicePlugin Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindoorbird.json") Q_INTERFACES(DevicePlugin) - public: explicit DevicePluginDoorbird(); - ~DevicePluginDoorbird() override; - Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + void discoverDevices(DeviceDiscoveryInfo *info) override; + void setupDevice(DeviceSetupInfo *info) override; + void postSetupDevice(Device *device) override; + void executeAction(DeviceActionInfo *info) override; - Device::DeviceSetupStatus setupDevice(Device *device) override; - Device::DeviceError executeAction(Device *device, const Action &action) override; + void startPairing(DevicePairingInfo *info) override; + void confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) override; - DevicePairingInfo confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) override; - DevicePairingInfo pairDevice(DevicePairingInfo &devicePairingInfo) override; void deviceRemoved(Device *device)override; private: QHash m_doorbirdConnections; + QHash m_asyncActions; + +private slots: + void onDoorBirdConnected(bool status); + void onDoorBirdEvent(Doorbird::EventType eventType, bool status); + void onDoorBirdRequestSent(QUuid requestId, bool success); + void onImageReceived(QImage image); }; #endif // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index 8f9ed2ee..caa78c48 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -33,12 +33,30 @@ { "id": "b6c3377b-91de-411a-9d48-8b509c39d67c", "name": "openDoor", - "displayName": "Open door" + "displayName": "Open door", + "paramTypes": [ + { + "id": "95dd35d7-0bc3-49e1-af96-d8da8ea5244d", + "name": "number", + "displayName": "Relay number", + "type": "QString", + "allowedValues": [ + "1", + "2" + ], + "defaultValue": 1 + } + ] }, { "id": "3a6cfc5d-804c-4d21-91b5-999913d4f1a5", "name": "lightOn", "displayName": "Light on" + }, + { + "id": "e874242e-5acb-4d98-94c7-0a70db65150c", + "name": "restart", + "displayName": "Restart" } ], "stateTypes": [ diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index a2a6dce9..429b4b45 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -218,11 +218,11 @@ QUuid Doorbird::infoRequest() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + qCWarning(dcDoorBird) << "Error DoorBird" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + qCDebug(dcDoorBird) << "DoorBird info:" << reply->readAll() ; emit requestSent(requestId, true); }); return requestId; @@ -247,6 +247,36 @@ QUuid Doorbird::listFavorites() return requestId; } +QUuid Doorbird::addFavorite(FavoriteType type, const QString &name, const QUrl &commandUrl, int id) +{ + QUrl url(QString("http://%1/bha-api/favorites.cgi").arg(m_address.toString())); + QUrlQuery query; + query.addQueryItem("action", "save"); + if (type == FavoriteType::Http) { + query.addQueryItem("type", "http"); + } else { + query.addQueryItem("type", "sip"); + } + query.addQueryItem("title", name); + query.addQueryItem("value", commandUrl.toString()); + query.addQueryItem("id", QString::number(id)); + url.setQuery(query); + + QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(url)); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + emit requestSent(requestId, true); + }); + return requestId; +} + QUuid Doorbird::listSchedules() { QNetworkRequest request(QString("http://%1/bha-api/schedule.cgi").arg(m_address.toString())); @@ -297,35 +327,34 @@ void Doorbird::connectToEventMonitor() Q_UNUSED(bytesReceived) Q_UNUSED(bytesTotal); - //TODO emit signal connected - m_readBuffers.append(reply->readAll()); - // qCDebug(dcDoorBird) << "Monitor data for" << device->name(); - // qCDebug(dcDoorBird) << m_readBuffers[device]; + emit deviceConnected(true); + m_readBuffer.append(reply->readAll()); + qCDebug(dcDoorBird) << "Event received" << m_readBuffer; // Input data looks like: // "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n" - while (!m_readBuffers.isEmpty()) { + while (!m_readBuffer.isEmpty()) { // find next --ioboundary - /*QString boundary = QStringLiteral("--ioboundary"); - int startIndex = m_readBuffers.indexOf(boundary); + QString boundary = QStringLiteral("--ioboundary"); + int startIndex = m_readBuffer.indexOf(boundary); if (startIndex == -1) { - qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers; - if (m_readBuffers.size() > 1024) { + qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffer; + if (m_readBuffer.size() > 1024) { qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer..."; - m_readBuffers.clear(); + m_readBuffer.clear(); } // Assuming we don't have enough data yet... return; } QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain"); - int contentTypeIndex = m_readBuffers.indexOf(contentType); + int contentTypeIndex = m_readBuffer.indexOf(contentType); if (contentTypeIndex == -1) { - qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffers; - if (m_readBuffers.size() > startIndex + 50) { + qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffer; + if (m_readBuffer.size() > startIndex + 50) { qCWarning(dcDoorBird) << boundary << "found but unexpected data follows. Skipping this element..."; - m_readBuffers.remove(0, startIndex + boundary.length()); + m_readBuffer.remove(0, startIndex + boundary.length()); continue; } // Assuming we don't have enough data yet... @@ -333,15 +362,15 @@ void Doorbird::connectToEventMonitor() } // At this point we have the boundary and Content-Type. Remove all of that and take the entire string to either end or next boundary - m_readBuffers.remove(0, contentTypeIndex + contentType.length()); - int nextStartIndex = m_readBuffers.indexOf(boundary); + m_readBuffer.remove(0, contentTypeIndex + contentType.length()); + int nextStartIndex = m_readBuffer.indexOf(boundary); QByteArray data; if (nextStartIndex == -1) { - data = m_readBuffers; - m_readBuffers.clear(); + data = m_readBuffer; + m_readBuffer.clear(); } else { - data = m_readBuffers.left(nextStartIndex); - m_readBuffers.remove(0, nextStartIndex); + data = m_readBuffer.left(nextStartIndex); + m_readBuffer.remove(0, nextStartIndex); } QString message = data.trimmed(); @@ -353,23 +382,27 @@ void Doorbird::connectToEventMonitor() if (parts.first() == "doorbell") { if (parts.at(1) == "H") { qCDebug(dcDoorBird) << "Doorbell ringing!"; - //emitEvent(Event(doorBirdTriggeredEventTypeId, device->id())); + emit eventReveiced(EventType::Doorbell, true); + } else { + emit eventReveiced(EventType::Doorbell, false); } } else if (parts.first() == "motionsensor") { if (parts.at(1) == "H") { qCDebug(dcDoorBird) << "Motion sensor detected a person"; - //emitEvent(Event(doorBirdMotionDetectedEventTypeId, device->id())); + emit eventReveiced(EventType::Motion, true); + } else { + emit eventReveiced(EventType::Motion, false); } } else { qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; - }*/ + } } }); connect(reply, &QNetworkReply::finished, this, [this, reply]() { reply->deleteLater(); - //device->setStateValue(doorBirdConnectedStateTypeId, false); + emit deviceConnected(false); qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); QTimer::singleShot(2000, this, [this] { @@ -378,3 +411,40 @@ void Doorbird::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 69e05d0b..1f376345 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -1,4 +1,4 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2019 Bernhard Trinnes * * Copyright (C) 2019 Michael Zanetti * @@ -33,6 +33,24 @@ class Doorbird : public QObject public: explicit Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent = nullptr); + enum EventType { + Doorbell, + Motion, + Input, + Rfid + }; + enum FavoriteType { + Http, + Sip + }; + + struct FavoriteObject { + FavoriteType type; + QString title; + QUrl value; + int id; + }; + QUuid getSession(); QUuid openDoor(int value); QUuid lightOn(); @@ -45,11 +63,11 @@ public: QUuid infoRequest(); QUuid listFavorites(); - QUuid addFavorite(); + QUuid addFavorite(FavoriteType type, const QString &name, const QUrl &url, int id); QUuid deleteFavorite(); QUuid listSchedules(); - QUuid daddScheduleEntry(); + QUuid addScheduleEntry(EventType event, int favoriteNumber, bool enabled); QUuid deleteScheduleEntry(); QUuid restart(); @@ -57,7 +75,7 @@ public: void connectToEventMonitor(); private: QNetworkAccessManager *m_networkAccessManager; - QList m_readBuffers; + QByteArray m_readBuffer; QHostAddress m_address; QList m_networkRequests; @@ -68,9 +86,14 @@ private: QByteArray sessionId; signals: + void deviceConnected(bool status); void requestSent(QUuid requestId, bool success); + void eventReveiced(EventType eventType, bool status); + void favoritesReceived(QList favourites); + public slots: + void onUdpBroadcast(const QByteArray &data); }; #endif // DOORBIRD_H diff --git a/doorbird/qrcodereader.cpp b/doorbird/qrcodereader.cpp index 658a2496..e16fa363 100644 --- a/doorbird/qrcodereader.cpp +++ b/doorbird/qrcodereader.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . * * * ****************************************************************************/ - +/* #include "qrcodereader.h" #include @@ -87,7 +87,7 @@ void QRCodeReader::grab(QImage image) connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); m_readerThread.start(); - QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, img), Q_ARG(bool, false)); + //QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, img), Q_ARG(bool, false)); } void QRCodeReader::processImage(const QUrl &url) @@ -187,3 +187,4 @@ void Reader::doWork(const QImage &image, bool invert) emit finished(); } +*/ diff --git a/doorbird/qrcodereader.h b/doorbird/qrcodereader.h index f0c22b05..7b2edd15 100644 --- a/doorbird/qrcodereader.h +++ b/doorbird/qrcodereader.h @@ -17,7 +17,7 @@ * along with this program. If not, see . * * * ****************************************************************************/ - +/* #ifndef QRCODEREADER_H #define QRCODEREADER_H @@ -36,7 +36,6 @@ class QRCodeReader : public QObject Q_PROPERTY(QString imageSource READ imageSource NOTIFY validChanged) Q_PROPERTY(QRect scanRect READ scanRect WRITE setScanRect NOTIFY scanRectChanged) Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged) - Q_PROPERTY(HistoryModel* history READ history CONSTANT) public: explicit QRCodeReader(QObject *parent = nullptr); @@ -86,3 +85,4 @@ signals: }; #endif // QRCODEREADER_H +*/ From e94dbeea4c63a63dd477286020f9bc2cdf9dd9ee Mon Sep 17 00:00:00 2001 From: Boernsman Date: Fri, 15 Nov 2019 16:44:27 +0100 Subject: [PATCH 06/35] removed qr code added image request --- doorbird/deviceplugindoorbird.cpp | 14 ++- doorbird/deviceplugindoorbird.json | 13 ++ doorbird/doorbird.cpp | 30 ++--- doorbird/doorbird.h | 3 + doorbird/doorbird.pro | 4 - doorbird/qrcodereader.cpp | 190 ----------------------------- doorbird/qrcodereader.h | 88 ------------- 7 files changed, 44 insertions(+), 298 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 03f117eb..e34eb03a 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -116,6 +116,7 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); + connect(doorbird, &Doorbird::liveImageReceived, this, &DevicePluginDoorbird::onImageReceived); doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(device, doorbird); info->finish(Device::DeviceErrorNoError); @@ -228,6 +229,15 @@ void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) void DevicePluginDoorbird::onImageReceived(QImage image) { - Q_UNUSED(image) - //QString code = + Q_UNUSED(image); + Doorbird *doorbird = static_cast(sender()); + Device *device = m_doorbirdConnections.key(doorbird); + if (!device) + return; + //TODO add QR code detection + Event event; + event.setDeviceId(device->id()); + event.setEventTypeId(doorBirdQrCodeDetectedEventTypeId); + event.setParams(ParamList() << Param(doorBirdQrCodeDetectedEventDataParamTypeId, "image received")); + emit emitEvent(event); } diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index caa78c48..6e0057c7 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -91,6 +91,19 @@ "id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b", "name": "doorbellPressed", "displayName": "Doorbell pressed" + }, + { + "id": "224f1be5-17d3-460d-9bb8-a25fcdf28936", + "name": "qrCodeDetected", + "displayName": "QR code detected", + "paramTypes": [ + { + "id": "c53068b9-604e-4dfc-8d23-26e8bab0233c", + "name": "data", + "displayName": "Data", + "type": "QString" + } + ] } ] } diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 429b4b45..4fb99b79 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -105,7 +105,7 @@ QUuid Doorbird::lightOn() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << reply->error() << reply->errorString(); + qCWarning(dcDoorBird) << "Error light on" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } @@ -117,7 +117,7 @@ QUuid Doorbird::lightOn() QUuid Doorbird::liveVideoRequest() { - QNetworkRequest request(QString("http://%1/bha-api/info.cgi").arg(m_address.toString())); + QNetworkRequest request(QString("http://%1/bha-api/video.cgi").arg(m_address.toString())); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); @@ -125,11 +125,11 @@ QUuid Doorbird::liveVideoRequest() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + qCWarning(dcDoorBird) << "Error live video request" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + qCDebug(dcDoorBird) << "DoorBird live video request" ; emit requestSent(requestId, true); }); return requestId; @@ -137,7 +137,7 @@ QUuid Doorbird::liveVideoRequest() QUuid Doorbird::liveImageRequest() { - QNetworkRequest request(QString("http://%1/bha-api/light-on.cgi").arg(m_address.toString())); + QNetworkRequest request(QString("http://%1/bha-api/image.cgi").arg(m_address.toString())); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); @@ -145,15 +145,17 @@ QUuid Doorbird::liveImageRequest() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << reply->error() << reply->errorString(); + qCWarning(dcDoorBird) << "Error live image request" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird light on:"; - emit requestSent(requestId, true); - QImage* image = new QImage(); - image->loadFromData(reply->readAll()); + QByteArrayList tokens = reply->readAll().split('\n'); + QImage image = QImage::fromData(tokens.last()); + //image.save("testfile"); + emit liveImageReceived(image); + qCDebug(dcDoorBird) << "DoorBird live image request:"; + emit requestSent(requestId, true); }); return requestId; } @@ -172,11 +174,11 @@ QUuid Doorbird::historyImageRequest(int index) reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + qCWarning(dcDoorBird) << "Error history image request" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + qCDebug(dcDoorBird) << "DoorBird history image received:"; emit requestSent(requestId, true); }); return requestId; @@ -192,11 +194,11 @@ QUuid Doorbird::liveAudioReceive() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error DoorBird device"; + qCWarning(dcDoorBird) << "Error live audio receive"; emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + qCDebug(dcDoorBird) << "DoorBird live audio receive"; emit requestSent(requestId, true); }); return requestId; diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 1f376345..75a8bb18 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -26,6 +26,7 @@ #include #include #include +#include class Doorbird : public QObject { @@ -92,6 +93,8 @@ signals: void eventReveiced(EventType eventType, bool status); void favoritesReceived(QList favourites); + void liveImageReceived(QImage image); + public slots: void onUdpBroadcast(const QByteArray &data); }; diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index c519c376..55b1f7e0 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -2,16 +2,12 @@ include(../plugins.pri) QT += network -LIBS += -lzbar - TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) SOURCES += \ deviceplugindoorbird.cpp \ doorbird.cpp \ - qrcodereader.cpp \ HEADERS += \ deviceplugindoorbird.h \ doorbird.h \ - qrcodereader.h \ diff --git a/doorbird/qrcodereader.cpp b/doorbird/qrcodereader.cpp index e16fa363..e69de29b 100644 --- a/doorbird/qrcodereader.cpp +++ b/doorbird/qrcodereader.cpp @@ -1,190 +0,0 @@ -/***************************************************************************** - * Copyright: 2013 Michael Zanetti * - * * - * This file is part of ubuntu-authenticator * - * * - * This prject is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This project is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ****************************************************************************/ -/* -#include "qrcodereader.h" - -#include -#include -#include -#include - -QRCodeReader::QRCodeReader(QObject *parent) : - QObject(parent) -{ - connect(&m_readerThread, &QThread::started, this, &QRCodeReader::scanningChanged); - connect(&m_readerThread, &QThread::finished, this, &QRCodeReader::scanningChanged); -} - -bool QRCodeReader::valid() const -{ - return !m_type.isEmpty() && !m_text.isEmpty(); -} - -QString QRCodeReader::type() const -{ - return m_type; -} - -QString QRCodeReader::text() const -{ - return m_text; -} - -QImage QRCodeReader::image() const -{ - return m_image; -} - -QRect QRCodeReader::scanRect() const -{ - return m_scanRect; -} - -void QRCodeReader::setScanRect(const QRect &rect) -{ - if (m_scanRect != rect) { - m_scanRect = rect; - emit scanRectChanged(); - } -} - -bool QRCodeReader::scanning() const -{ - return m_readerThread.isRunning(); -} - -void QRCodeReader::grab(QImage image) -{ - m_type.clear(); - m_text.clear(); - emit validChanged(); - - if (m_scanRect.isValid()) { - image = image.copy(m_scanRect); - } - - Reader *reader = new Reader; - reader->moveToThread(&m_readerThread); - connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater())); - connect(reader, SIGNAL(finished()), &m_readerThread, SLOT(quit())); - connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); - m_readerThread.start(); - - //QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, img), Q_ARG(bool, false)); -} - -void QRCodeReader::processImage(const QUrl &url) -{ - QImage image; - if (!image.load(url.path())) { - qWarning() << "can't open" << url.path(); - return; - } - - Reader *reader = new Reader; - reader->moveToThread(&m_readerThread); - connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater())); - connect(reader, SIGNAL(finished()), &m_readerThread, SLOT(quit())); - connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); - m_readerThread.start(); - - QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, image), Q_ARG(bool, false)); -} - -void QRCodeReader::handleResults(const QString &type, const QString &text, const QImage &codeImage) -{ - m_type = type; - m_text = text; - m_image = codeImage; - m_imageUuid = QUuid::createUuid(); - emit validChanged(); -} - -void Reader::doWork(const QImage &image, bool invert) -{ - // Prepare image - QImage copy = image; - if (invert) { - copy.invertPixels(); - } - zbar::QZBarImage img(copy.convertToFormat(QImage::Format_RGB32)); - zbar::Image tmp = img.convert(*(long*)"Y800"); - - // create a reader - zbar::ImageScanner scanner; - - // configure the reader - scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1); - scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_POSITION, 1); - scanner.set_config(zbar::ZBAR_PARTIAL, zbar::ZBAR_CFG_ENABLE, 0); - - // scan the image for barcodes - int n = scanner.scan(tmp); -// qDebug() << "scanned. have" << n << "symbols"; - if (!invert && n == 0) { - // Nothing found... try again inverted - doWork(image, true); - return; - } - - img.set_symbols(tmp.get_symbols()); - - // extract results - for(zbar::Image::SymbolIterator symbol = img.symbol_begin(); symbol != img.symbol_end(); ++symbol) { - - QString typeName = QString::fromStdString(symbol->get_type_name()); - QString symbolString = QString::fromStdString(symbol->get_data()); - - int x0 = 999999; - int y0 = 999999; - int x1 = 0; - int y1 = 0; - - for (int i = 0; i < symbol->get_location_size(); ++i) { - int x = symbol->get_location_x(i); - int y = symbol->get_location_y(i); - qDebug() << "got point" << x << y; - if (x < x0) x0 = x; - if (y < y0) y0 = y; - if (x > x1) x1 = x; - if (y > y1) y1 = y; - } - - int width = x1 - x0; - int height = y1 - y0; - - // Workaround for zBar sometimes only giving us the first bar in a barcode. - if (width < 10) width = img.get_width() - x0; - if (height < 10) height = img.get_height() - y0; - - qDebug() << "extracting code image (" << x0 << y0 << ") ("<< x1 << y1 << ")"; - QImage codeImage = image.copy(x0, y0, width, height); - - qDebug() << "Code recognized:" << typeName << ", Text:" << symbolString; - - emit resultReady(typeName, symbolString, codeImage); - } - - tmp.set_data(NULL, 0); - img.set_data(NULL, 0); - - emit finished(); -} -*/ diff --git a/doorbird/qrcodereader.h b/doorbird/qrcodereader.h index 7b2edd15..e69de29b 100644 --- a/doorbird/qrcodereader.h +++ b/doorbird/qrcodereader.h @@ -1,88 +0,0 @@ -/***************************************************************************** - * Copyright: 2013 Michael Zanetti * - * * - * This file is part of tagger * - * * - * This prject is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This project is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ****************************************************************************/ -/* -#ifndef QRCODEREADER_H -#define QRCODEREADER_H - -#include -#include -#include -#include - -class QRCodeReader : public QObject -{ - Q_OBJECT - Q_PROPERTY(bool valid READ valid NOTIFY validChanged) - Q_PROPERTY(QString type READ type NOTIFY validChanged) - Q_PROPERTY(QString text READ text NOTIFY validChanged) - Q_PROPERTY(QImage image READ image NOTIFY validChanged) - Q_PROPERTY(QString imageSource READ imageSource NOTIFY validChanged) - Q_PROPERTY(QRect scanRect READ scanRect WRITE setScanRect NOTIFY scanRectChanged) - Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged) - -public: - explicit QRCodeReader(QObject *parent = nullptr); - - bool valid() const; - QString type() const; - QString text() const; - QImage image() const; - QString imageSource() const; - QRect scanRect() const; - void setScanRect(const QRect &rect); - bool scanning() const; - - -public slots: - void grab(QImage image); - void processImage(const QUrl &url); - -signals: - void validChanged(); - void scanRectChanged(); - void scanningChanged(); - -private slots: - void handleResults(const QString &type, const QString &text, const QImage &codeImage); - -private: - QString m_type; - QString m_text; - QImage m_image; - QUuid m_imageUuid; - QRect m_scanRect; - - QThread m_readerThread; -}; - -class Reader : public QObject -{ - Q_OBJECT - -public slots: - void doWork(const QImage &image, bool invert); - -signals: - void resultReady(const QString &type, const QString &text, const QImage &codeImage); - void finished(); -}; - -#endif // QRCODEREADER_H -*/ From 0bacb2efb8c1d6cf96aaa53f444a02e4563bbd41 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 10 Feb 2020 18:23:38 +0500 Subject: [PATCH 07/35] removed QR code scanner and fixed last seen state --- doorbird/README.md | 8 ++ doorbird/deviceplugindoorbird.cpp | 77 ++++++------ doorbird/deviceplugindoorbird.h | 49 ++++---- doorbird/deviceplugindoorbird.json | 13 -- doorbird/doorbird.cpp | 50 ++++---- doorbird/doorbird.h | 49 ++++---- doorbird/qrcodereader.cpp | 0 doorbird/qrcodereader.h | 0 ...1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts | 112 +++++++++++++++++- ...68429-e312-4c82-9eab-e1cd996e43d6-en_US.ts | 4 - 10 files changed, 243 insertions(+), 119 deletions(-) delete mode 100644 doorbird/qrcodereader.cpp delete mode 100644 doorbird/qrcodereader.h delete mode 100644 doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts diff --git a/doorbird/README.md b/doorbird/README.md index e69de29b..bba30f16 100644 --- a/doorbird/README.md +++ b/doorbird/README.md @@ -0,0 +1,8 @@ +# DoorBird + +This plug-in let'S you capture doorbell pressed and motion events, +you are also able to set the power of the IR and the relays. + +It uses a local connection and will work without Internet. + + diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index e34eb03a..ef85a647 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -1,23 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Bernhard Trinnes * - * Copyright (C) 2019 Michael Zanetti * - * * - * This file is part of nymea. * - * * - * nymea is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, version 2 of the License. * - * * - * nymea is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with nymea. If not, see . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "deviceplugindoorbird.h" #include "plugininfo.h" @@ -48,7 +57,13 @@ void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) qCDebug(dcDoorBird) << "Found DoorBird device, name: " << serviceEntry.name() << "\n host address:" << serviceEntry.hostAddress().toString() << "\n text:" << serviceEntry.txt() << serviceEntry.protocol() << serviceEntry.serviceType(); DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); ParamList params; - QString macAddress = serviceEntry.txt().first(); + QString macAddress; + if (serviceEntry.txt().first().split("=").length() == 2) { + macAddress = serviceEntry.txt().first().split("=").last(); + } else { + qCWarning(dcDoorBird()) << "Could not parse MAC Address" << serviceEntry.txt().first(); + return; + } if (!myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).isEmpty()) { Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName()).first(); deviceDescriptor.setDeviceId(existingDevice->id()); @@ -116,7 +131,6 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); - connect(doorbird, &Doorbird::liveImageReceived, this, &DevicePluginDoorbird::onImageReceived); doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(device, doorbird); info->finish(Device::DeviceErrorNoError); @@ -201,9 +215,7 @@ void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool s break; case Doorbird::EventType::Motion: device->setStateValue(doorBirdIsPresentStateTypeId, status); - if (status) { - doorbird->liveImageRequest(); - } + device->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); break; case Doorbird::EventType::Doorbell: if (status) { @@ -226,18 +238,3 @@ void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); } - -void DevicePluginDoorbird::onImageReceived(QImage image) -{ - Q_UNUSED(image); - Doorbird *doorbird = static_cast(sender()); - Device *device = m_doorbirdConnections.key(doorbird); - if (!device) - return; - //TODO add QR code detection - Event event; - event.setDeviceId(device->id()); - event.setEventTypeId(doorBirdQrCodeDetectedEventTypeId); - event.setParams(ParamList() << Param(doorBirdQrCodeDetectedEventDataParamTypeId, "image received")); - emit emitEvent(event); -} diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h index 9be0a4ee..9c7ebd7f 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/deviceplugindoorbird.h @@ -1,22 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Michael Zanetti * - * * - * This file is part of nymea. * - * * - * nymea is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, version 2 of the License. * - * * - * nymea is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with nymea. If not, see . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef DEVICEPLUGINDOORBIRD_H #define DEVICEPLUGINDOORBIRD_H @@ -58,7 +68,6 @@ private slots: void onDoorBirdConnected(bool status); void onDoorBirdEvent(Doorbird::EventType eventType, bool status); void onDoorBirdRequestSent(QUuid requestId, bool success); - void onImageReceived(QImage image); }; #endif // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index 6e0057c7..caa78c48 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -91,19 +91,6 @@ "id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b", "name": "doorbellPressed", "displayName": "Doorbell pressed" - }, - { - "id": "224f1be5-17d3-460d-9bb8-a25fcdf28936", - "name": "qrCodeDetected", - "displayName": "QR code detected", - "paramTypes": [ - { - "id": "c53068b9-604e-4dfc-8d23-26e8bab0233c", - "name": "data", - "displayName": "Data", - "type": "QString" - } - ] } ] } diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 4fb99b79..0d8b591f 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -1,23 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Bernhard Trinnes * - * Copyright (C) 2019 Michael Zanetti * - * * - * This file is part of nymea. * - * * - * nymea is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, version 2 of the License. * - * * - * nymea is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with nymea. If not, see . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "doorbird.h" #include "extern-plugininfo.h" @@ -206,7 +215,6 @@ QUuid Doorbird::liveAudioReceive() QUuid Doorbird::liveAudioTransmit() { - return QUuid::createUuid(); } diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 75a8bb18..da95ab56 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -1,23 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Bernhard Trinnes * - * Copyright (C) 2019 Michael Zanetti * - * * - * This file is part of nymea. * - * * - * nymea is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, version 2 of the License. * - * * - * nymea is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with nymea. If not, see . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef DOORBIRD_H #define DOORBIRD_H diff --git a/doorbird/qrcodereader.cpp b/doorbird/qrcodereader.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/doorbird/qrcodereader.h b/doorbird/qrcodereader.h deleted file mode 100644 index e69de29b..00000000 diff --git a/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts index f7f66d85..3dbea2ab 100644 --- a/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts +++ b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts @@ -1,4 +1,114 @@ - \ No newline at end of file + + DevicePluginDoorbird + + + Please enter the user credentials + + + + + doorBird + + + + Connected + The name of the ParamType (DeviceClass: doorBird, EventType: connected, ID: {186c270b-923c-46e4-a7da-33e45427cdbb}) +---------- +The name of the StateType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird + + + + + Connected changed + The name of the EventType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird + + + + + + + DoorBird + The name of the DeviceClass ({0485eb61-2a22-42ba-9dd2-a5961485bf08}) +---------- +The name of the vendor ({2da07435-571e-4956-a387-6caa51d6e845}) +---------- +The name of the plugin doorBird ({6fe1614a-fc47-4eb2-a47c-13c50f1798ee}) + + + + + Doorbell pressed + The name of the EventType ({9bc89937-a2ab-4e8e-af0e-a9ba41caa89b}) of DeviceClass doorBird + + + + + IP address + The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {8873b17d-526e-408d-95d8-6439b501f489}) + + + + + Light on + The name of the ActionType ({3a6cfc5d-804c-4d21-91b5-999913d4f1a5}) of DeviceClass doorBird + + + + + + Motion sensor last seen time + The name of the ParamType (DeviceClass: doorBird, EventType: lastSeenTime, ID: {295c9700-b598-4681-898f-d63e2889cedf}) +---------- +The name of the StateType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird + + + + + Motion sensor last seen time changedd + The name of the EventType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird + + + + + + Motion sensor presence + The name of the ParamType (DeviceClass: doorBird, EventType: isPresent, ID: {0f5eb200-6c0d-45c5-9156-3060fd66d332}) +---------- +The name of the StateType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird + + + + + Motion sensor presence detected + The name of the EventType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird + + + + + Open door + The name of the ActionType ({b6c3377b-91de-411a-9d48-8b509c39d67c}) of DeviceClass doorBird + + + + + Relay number + The name of the ParamType (DeviceClass: doorBird, ActionType: openDoor, ID: {95dd35d7-0bc3-49e1-af96-d8da8ea5244d}) + + + + + Restart + The name of the ActionType ({e874242e-5acb-4d98-94c7-0a70db65150c}) of DeviceClass doorBird + + + + + Serial number + The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {67ea5534-330a-4291-93b5-0237034e15fa}) + + + + diff --git a/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts b/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts deleted file mode 100644 index f7f66d85..00000000 --- a/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From e947c1d75c512722c4c820d85a55ec5b7807a481 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 10 Feb 2020 18:28:46 +0500 Subject: [PATCH 08/35] added debian packaging for doorbird --- debian/control | 16 ++++++++++++++++ debian/nymea-plugin-doorbird.install.in | 1 + doorbird/deviceplugindoorbird.json | 1 + 3 files changed, 18 insertions(+) create mode 100644 debian/nymea-plugin-doorbird.install.in diff --git a/debian/control b/debian/control index 6efef76e..d0b530df 100644 --- a/debian/control +++ b/debian/control @@ -200,6 +200,21 @@ Description: nymea.io plugin for denon This package will install the nymea.io plugin for denon +Package: nymea-plugin-doorbird +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-translations, +Description: nymea.io plugin for DoorBird + 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. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install the nymea.io plugin for DoorBird + + Package: nymea-plugin-dweetio Architecture: any Depends: ${shlibs:Depends}, @@ -965,6 +980,7 @@ Depends: nymea-plugin-anel, nymea-plugin-datetime, nymea-plugin-daylightsensor, nymea-plugin-denon, + nymea-plugin-doorbird, nymea-plugin-eq-3, nymea-plugin-flowercare, nymea-plugin-kodi, diff --git a/debian/nymea-plugin-doorbird.install.in b/debian/nymea-plugin-doorbird.install.in new file mode 100644 index 00000000..3a517946 --- /dev/null +++ b/debian/nymea-plugin-doorbird.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_deviceplugindoorbird.so diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index caa78c48..a5e348f7 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -66,6 +66,7 @@ "displayName": "Connected", "displayNameEvent": "Connected changed", "type": "bool", + "cached": false, "defaultValue": false }, { From c57cfc59f0a5d2dcd4db42eaa8182ef208df6679 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 10 Feb 2020 18:38:41 +0500 Subject: [PATCH 09/35] fixed rediscovery --- doorbird/deviceplugindoorbird.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index ef85a647..89fec001 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -58,14 +58,19 @@ void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); ParamList params; QString macAddress; + if (serviceEntry.txt().length() == 0) { + qCWarning(dcDoorBird()) << "Discovery failed, service entry missing"; + continue; + } + if (serviceEntry.txt().first().split("=").length() == 2) { macAddress = serviceEntry.txt().first().split("=").last(); } else { qCWarning(dcDoorBird()) << "Could not parse MAC Address" << serviceEntry.txt().first(); - return; + continue; } if (!myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).isEmpty()) { - Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName()).first(); + Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).first(); deviceDescriptor.setDeviceId(existingDevice->id()); } params.append(Param(doorBirdDeviceSerialnumberParamTypeId, macAddress)); @@ -86,7 +91,6 @@ void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) void DevicePluginDoorbird::startPairing(DevicePairingInfo *info) { - if (info->deviceClassId() == doorBirdDeviceClassId) { qCDebug(dcDoorBird()) << "User and password. Login is \"user\" and \"password\"."; info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter the user credentials")); From 11c978191fe07277f2e5b84a02d94b2e92719795 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 10 Feb 2020 21:57:53 +0500 Subject: [PATCH 10/35] added async device setup --- doorbird/deviceplugindoorbird.cpp | 127 +++++++++++++++++++++--------- doorbird/deviceplugindoorbird.h | 6 +- doorbird/doorbird.cpp | 37 ++++++--- doorbird/doorbird.h | 11 +-- 4 files changed, 128 insertions(+), 53 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 89fec001..aea749c6 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -83,38 +83,57 @@ void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) info->finish(Device::DeviceErrorNoError); }); return; + } else { + qCWarning(dcDoorBird()) << "Cannot discover for deviceClassId" << info->deviceClassId(); + info->finish(Device::DeviceErrorDeviceNotFound); } - qCWarning(dcDoorBird()) << "Cannot discover for deviceClassId" << info->deviceClassId(); - info->finish(Device::DeviceErrorDeviceNotFound); } void DevicePluginDoorbird::startPairing(DevicePairingInfo *info) { if (info->deviceClassId() == doorBirdDeviceClassId) { - qCDebug(dcDoorBird()) << "User and password. Login is \"user\" and \"password\"."; - info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter the user credentials")); + info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter username and password for the DoorBird device")); return; + } else { + qCWarning(dcDoorBird()) << "StartPairing unhandled deviceClassId" << info->deviceClassId(); + info->finish(Device::DeviceErrorCreationMethodNotSupported); } - info->finish(Device::DeviceErrorCreationMethodNotSupported); } -void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) +void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &password) { if (info->deviceClassId() == doorBirdDeviceClassId) { - qCDebug(dcDoorBird()) << "confirm pairing called"; + QHostAddress address = QHostAddress(info->params().paramValue(doorBirdDeviceAddressParamTypeId).toString()); + + Doorbird *doorbird = new Doorbird(address, this); + connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); + connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); + connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); + connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); + doorbird->initConnection(username, password); + doorbird->connectToEventMonitor(); + m_doorbirdConnections.insert(info->deviceId(), doorbird); + m_pendingPairings.insert(doorbird, info); + doorbird->getSession(); + connect(info, &DevicePairingInfo::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->deviceId()); + }); pluginStorage()->beginGroup(info->deviceId().toString()); pluginStorage()->setValue("username", username); - pluginStorage()->setValue("password", secret); + pluginStorage()->setValue("password", password); pluginStorage()->endGroup(); - - info->finish(Device::DeviceErrorNoError); - return; + } else { + qCWarning(dcDoorBird()) << "Confirm pairing DeviceClassNotFound" << info->deviceClassId(); + info->finish(Device::DeviceErrorDeviceClassNotFound); } - info->finish(Device::DeviceErrorDeviceClassNotFound); - return; } @@ -125,30 +144,45 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) if (device->deviceClassId() == doorBirdDeviceClassId) { QHostAddress address = QHostAddress(device->paramValue(doorBirdDeviceAddressParamTypeId).toString()); - pluginStorage()->beginGroup(device->id().toString()); - QString username = pluginStorage()->value("username").toString(); - QString password = pluginStorage()->value("password").toString(); - pluginStorage()->endGroup(); + if (m_doorbirdConnections.contains(device->id())) { + info->finish(Device::DeviceErrorNoError); + } else { + pluginStorage()->beginGroup(device->id().toString()); + QString username = pluginStorage()->value("username").toString(); + QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); - qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; - Doorbird *doorbird = new Doorbird(address, username, password, this); - connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); - connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); - connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); - doorbird->connectToEventMonitor(); - m_doorbirdConnections.insert(device, doorbird); - info->finish(Device::DeviceErrorNoError); - return; + qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; + Doorbird *doorbird = new Doorbird(address, this); + connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); + connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); + connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); + connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); + doorbird->initConnection(username, password); + doorbird->connectToEventMonitor(); + m_doorbirdConnections.insert(device->id(), doorbird); + m_pendingDeviceSetups.insert(doorbird, info); + doorbird->getSession(); + connect(info, &DeviceSetupInfo::aborted, this, [device, doorbird, this] { + if (!doorbird) { + doorbird->deleteLater(); + } + m_doorbirdConnections.remove(device->id()); + m_pendingPairings.remove(doorbird); + }); + } + } else { + qCWarning(dcDoorBird()) << "Unhandled device class" << info->device()->deviceClass(); + info->finish(Device::DeviceErrorDeviceClassNotFound); } - qCWarning(dcDoorBird()) << "Unhandled device class" << info->device()->deviceClass(); - info->finish(Device::DeviceErrorDeviceClassNotFound); } void DevicePluginDoorbird::postSetupDevice(Device *device) { if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbird = m_doorbirdConnections.value(device); + device->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection in the deviceSetup + Doorbird *doorbird = m_doorbirdConnections.value(device->id()); doorbird->infoRequest(); doorbird->listFavorites(); doorbird->listSchedules(); @@ -162,7 +196,7 @@ void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) Action action = info->action(); if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbird = m_doorbirdConnections.value(device); + Doorbird *doorbird = m_doorbirdConnections.value(device->id()); if (!doorbird) { info->finish(Device::DeviceErrorHardwareFailure); return; @@ -180,17 +214,20 @@ void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) doorbird->restart(); info->finish(Device::DeviceErrorNoError); return; + } else { + qCWarning(dcDoorBird()) << "Unhandled ActionTypeId:" << action.actionTypeId(); + info->finish(Device::DeviceErrorActionTypeNotFound); } - info->finish(Device::DeviceErrorActionTypeNotFound); - return; + } else { + qCWarning(dcDoorBird()) << "Execute action, unhandled device class" << device->deviceClass(); + info->finish(Device::DeviceErrorDeviceClassNotFound); } - info->finish(Device::DeviceErrorDeviceClassNotFound); } void DevicePluginDoorbird::deviceRemoved(Device *device) { if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbirdConnection = m_doorbirdConnections.take(device); + Doorbird *doorbirdConnection = m_doorbirdConnections.take(device->id()); doorbirdConnection->deleteLater(); } } @@ -198,7 +235,7 @@ void DevicePluginDoorbird::deviceRemoved(Device *device) void DevicePluginDoorbird::onDoorBirdConnected(bool status) { Doorbird *doorbird = static_cast(sender()); - Device *device = m_doorbirdConnections.key(doorbird); + Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); if (!device) return; @@ -208,7 +245,7 @@ void DevicePluginDoorbird::onDoorBirdConnected(bool status) void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool status) { Doorbird *doorbird = static_cast(sender()); - Device *device = m_doorbirdConnections.key(doorbird); + Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); if (!device) return; @@ -232,7 +269,7 @@ void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool s void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) { Doorbird *doorbird = static_cast(sender()); - Device *device = m_doorbirdConnections.key(doorbird); + Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); if (!device) return; @@ -242,3 +279,19 @@ void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); } + +void DevicePluginDoorbird::onSessionIdReceived(const QString &sessionId) +{ + Q_UNUSED(sessionId); + Doorbird *doorbird = static_cast(sender()); + + if (m_pendingPairings.contains(doorbird)) { + DevicePairingInfo *info = m_pendingPairings.take(doorbird); + info->finish(Device::DeviceErrorNoError); + } + + if (m_pendingDeviceSetups.contains(doorbird)) { + DeviceSetupInfo *info = m_pendingDeviceSetups.take(doorbird); + info->finish(Device::DeviceErrorNoError); + } +} diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h index 9c7ebd7f..594decb0 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/deviceplugindoorbird.h @@ -61,13 +61,17 @@ public: void deviceRemoved(Device *device)override; private: - QHash m_doorbirdConnections; + QHash m_doorbirdConnections; + QHash m_pendingPairings; + QHash m_pendingDeviceSetups; + QHash m_asyncActions; 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 // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 0d8b591f..2db09e10 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -41,18 +41,30 @@ #include #include -Doorbird::Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent) : +Doorbird::Doorbird(const QHostAddress &address, QObject *parent) : QObject(parent), - m_address(address), - m_username(username), - m_password(password) + m_address(address) +{ + +} + +QHostAddress Doorbird::address() +{ + return m_address; +} + +void Doorbird::setAddress(const QHostAddress &address) +{ + m_address = address; +} + +void Doorbird::initConnection(const QString &username, const QString &password) { m_networkAccessManager = new QNetworkAccessManager(this); - connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { + connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [username, password, this](QNetworkReply *reply, QAuthenticator *authenticator) { Q_UNUSED(reply); - qCDebug(dcDoorBird) << "Credentials requested:"; - authenticator->setUser(m_username); - authenticator->setPassword(m_password); + authenticator->setUser(username); + authenticator->setPassword(password); }); } @@ -67,12 +79,11 @@ QUuid Doorbird::getSession() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + qCWarning(dcDoorBird) << "Error DoorBird device"; emit requestSent(requestId, false); return; } emit requestSent(requestId, true); - QByteArray data = reply->readAll(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); @@ -80,6 +91,12 @@ QUuid Doorbird::getSession() qCWarning(dcDoorBird()) << "Error parsing json:" << data; return; } + QVariantMap map = jsonDoc.toVariant().toMap().value("BHA").toMap(); + if (map.contains("SESSIONID")) { + QString sessionId = map.value("SESSIONID").toString(); + qCDebug(dcDoorBird) << "Got sessionId" << sessionId; + emit sessionIdReceived(sessionId); + } }); return requestId; } diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index da95ab56..163e9516 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -41,7 +41,7 @@ class Doorbird : public QObject { Q_OBJECT public: - explicit Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent = nullptr); + explicit Doorbird(const QHostAddress &address, QObject *parent = nullptr); enum EventType { Doorbell, @@ -61,6 +61,9 @@ public: int id; }; + QHostAddress address(); + void setAddress(const QHostAddress &address); + void initConnection(const QString &username, const QString &password); QUuid getSession(); QUuid openDoor(int value); QUuid lightOn(); @@ -90,10 +93,7 @@ private: QHostAddress m_address; QList m_networkRequests; - QString m_username; - QString m_password; - - QByteArray sessionId; + //QByteArray sessionId; signals: void deviceConnected(bool status); @@ -102,6 +102,7 @@ signals: void eventReveiced(EventType eventType, bool status); void favoritesReceived(QList favourites); + void sessionIdReceived(const QString &sessionId); void liveImageReceived(QImage image); public slots: From 33aec78f572b7f62b82e539ff297057a338b41db Mon Sep 17 00:00:00 2001 From: Boernsman Date: Fri, 14 Feb 2020 15:53:30 +0500 Subject: [PATCH 11/35] added authentication failure handling --- doorbird/deviceplugindoorbird.cpp | 23 ++++++++++++++--------- doorbird/doorbird.cpp | 18 +++++++++++++----- doorbird/doorbird.h | 2 ++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index aea749c6..6e8fe3d4 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -113,7 +113,6 @@ void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); doorbird->initConnection(username, password); - doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(info->deviceId(), doorbird); m_pendingPairings.insert(doorbird, info); doorbird->getSession(); @@ -159,7 +158,6 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); doorbird->initConnection(username, password); - doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(device->id(), doorbird); m_pendingDeviceSetups.insert(doorbird, info); doorbird->getSession(); @@ -183,6 +181,7 @@ void DevicePluginDoorbird::postSetupDevice(Device *device) if (device->deviceClassId() == doorBirdDeviceClassId) { device->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection in the deviceSetup Doorbird *doorbird = m_doorbirdConnections.value(device->id()); + doorbird->connectToEventMonitor(); doorbird->infoRequest(); doorbird->listFavorites(); doorbird->listSchedules(); @@ -269,15 +268,21 @@ void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool s void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) { Doorbird *doorbird = static_cast(sender()); - Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); - if (!device) - return; - if (!m_asyncActions.contains(requestId)) - return; + if (m_asyncActions.contains(requestId)) { + DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); + actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); + } - DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); - actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); + if (m_pendingPairings.contains(doorbird) && !success) { + DevicePairingInfo *info = m_pendingPairings.take(doorbird); + info->finish(Device::DeviceErrorAuthenticationFailure, tr("Wrong username or password")); + } + + if (m_pendingDeviceSetups.contains(doorbird) && !success) { + DeviceSetupInfo *info = m_pendingDeviceSetups.take(doorbird); + info->finish(Device::DeviceErrorAuthenticationFailure, tr("Wrong username or password")); + } } void DevicePluginDoorbird::onSessionIdReceived(const QString &sessionId) diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 2db09e10..2b399619 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -61,10 +61,17 @@ void Doorbird::setAddress(const QHostAddress &address) void Doorbird::initConnection(const QString &username, const QString &password) { m_networkAccessManager = new QNetworkAccessManager(this); - connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [username, password, this](QNetworkReply *reply, QAuthenticator *authenticator) { - Q_UNUSED(reply); - authenticator->setUser(username); - authenticator->setPassword(password); + connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [username, password, this] (QNetworkReply *reply, QAuthenticator *authenticator) { + + qCWarning(dcDoorBird()) << "Authenticator" << reply->errorString() << reply->error(); + if (m_pendingAuthentications.contains(reply)) { + m_pendingAuthentications.removeOne(reply); + reply->abort(); + } else { + authenticator->setUser(username); + authenticator->setPassword(password); + m_pendingAuthentications.append(reply); + } }); } @@ -79,7 +86,7 @@ QUuid Doorbird::getSession() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error DoorBird device"; + qCWarning(dcDoorBird) << "Error DoorBird device:" << reply->errorString(); emit requestSent(requestId, false); return; } @@ -98,6 +105,7 @@ QUuid Doorbird::getSession() emit sessionIdReceived(sessionId); } }); + return requestId; } diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 163e9516..0d519412 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -93,6 +93,8 @@ private: QHostAddress m_address; QList m_networkRequests; + QList m_pendingAuthentications; + //QByteArray sessionId; signals: From 1c3b1b736bf5951bc3b2174139d8e05f51e8de39 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Fri, 14 Feb 2020 22:36:00 +0500 Subject: [PATCH 12/35] authentication now with url credentials --- doorbird/deviceplugindoorbird.cpp | 10 ++++------ doorbird/doorbird.cpp | 32 +++++++++++-------------------- doorbird/doorbird.h | 15 +++++++++------ 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 6e8fe3d4..5bccd648 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -107,15 +107,14 @@ void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString if (info->deviceClassId() == doorBirdDeviceClassId) { QHostAddress address = QHostAddress(info->params().paramValue(doorBirdDeviceAddressParamTypeId).toString()); - Doorbird *doorbird = new Doorbird(address, this); + Doorbird *doorbird = new Doorbird(hardwareManager()->networkManager(), address, this); connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); - doorbird->initConnection(username, password); m_doorbirdConnections.insert(info->deviceId(), doorbird); m_pendingPairings.insert(doorbird, info); - doorbird->getSession(); + doorbird->getSession(username, password); connect(info, &DevicePairingInfo::aborted, this, [this, info]{ if (m_pendingPairings.values().contains(info)) { Doorbird *doorbird = m_pendingPairings.key(info); @@ -152,15 +151,14 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) pluginStorage()->endGroup(); qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; - Doorbird *doorbird = new Doorbird(address, this); + Doorbird *doorbird = new Doorbird(hardwareManager()->networkManager(), address, this); connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); - doorbird->initConnection(username, password); m_doorbirdConnections.insert(device->id(), doorbird); m_pendingDeviceSetups.insert(doorbird, info); - doorbird->getSession(); + doorbird->getSession(username, password); connect(info, &DeviceSetupInfo::aborted, this, [device, doorbird, this] { if (!doorbird) { doorbird->deleteLater(); diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 2b399619..a6074acb 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -41,9 +41,10 @@ #include #include -Doorbird::Doorbird(const QHostAddress &address, QObject *parent) : +Doorbird::Doorbird(NetworkAccessManager *networkAccessManager, const QHostAddress &address, QObject *parent) : QObject(parent), - m_address(address) + m_address(address), + m_networkAccessManager(networkAccessManager) { } @@ -58,26 +59,15 @@ void Doorbird::setAddress(const QHostAddress &address) m_address = address; } -void Doorbird::initConnection(const QString &username, const QString &password) +QUuid Doorbird::getSession(const QString &username, const QString &password) { - m_networkAccessManager = new QNetworkAccessManager(this); - connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [username, password, this] (QNetworkReply *reply, QAuthenticator *authenticator) { - - qCWarning(dcDoorBird()) << "Authenticator" << reply->errorString() << reply->error(); - if (m_pendingAuthentications.contains(reply)) { - m_pendingAuthentications.removeOne(reply); - reply->abort(); - } else { - authenticator->setUser(username); - authenticator->setPassword(password); - m_pendingAuthentications.append(reply); - } - }); -} - -QUuid Doorbird::getSession() -{ - QNetworkRequest request(QString("http://%1/bha-api/getsession.cgi").arg(m_address.toString())); + QUrl url; + url.setHost(m_address.toString()); + url.setScheme("http"); + url.setPath("/bha-api/getsession.cgi"); + url.setUserName(username); + url.setPassword(password); + QNetworkRequest request(url); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 0d519412..7f26da9b 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -37,11 +37,13 @@ #include #include +#include "network/networkaccessmanager.h" + class Doorbird : public QObject { Q_OBJECT public: - explicit Doorbird(const QHostAddress &address, QObject *parent = nullptr); + explicit Doorbird(NetworkAccessManager *networkAccessManager, const QHostAddress &address, QObject *parent = nullptr); enum EventType { Doorbell, @@ -63,8 +65,7 @@ public: QHostAddress address(); void setAddress(const QHostAddress &address); - void initConnection(const QString &username, const QString &password); - QUuid getSession(); + QUuid getSession(const QString &username, const QString &password); QUuid openDoor(int value); QUuid lightOn(); QUuid liveVideoRequest(); @@ -87,15 +88,17 @@ public: void connectToEventMonitor(); private: - QNetworkAccessManager *m_networkAccessManager; + QHostAddress m_address; + NetworkAccessManager *m_networkAccessManager; QByteArray m_readBuffer; - QHostAddress m_address; + QList m_networkRequests; QList m_pendingAuthentications; - //QByteArray sessionId; + QString m_username; + QString m_password; signals: void deviceConnected(bool status); From d01a7260bb37c4d3387a7bf0a909aafad52b2ba9 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Fri, 14 Feb 2020 22:48:46 +0500 Subject: [PATCH 13/35] fixed connection time-out --- doorbird/deviceplugindoorbird.cpp | 8 +++++--- doorbird/doorbird.cpp | 7 +++---- doorbird/doorbird.h | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 5bccd648..3668bc5c 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -107,7 +107,7 @@ void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString if (info->deviceClassId() == doorBirdDeviceClassId) { QHostAddress address = QHostAddress(info->params().paramValue(doorBirdDeviceAddressParamTypeId).toString()); - Doorbird *doorbird = new Doorbird(hardwareManager()->networkManager(), address, this); + Doorbird *doorbird = new Doorbird(address, this); connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); @@ -151,7 +151,7 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) pluginStorage()->endGroup(); qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; - Doorbird *doorbird = new Doorbird(hardwareManager()->networkManager(), address, this); + Doorbird *doorbird = new Doorbird(address, this); connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); @@ -253,7 +253,9 @@ void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool s break; case Doorbird::EventType::Motion: device->setStateValue(doorBirdIsPresentStateTypeId, status); - device->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); + if (status) { + device->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); + } break; case Doorbird::EventType::Doorbell: if (status) { diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index a6074acb..a8bda046 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -41,12 +41,11 @@ #include #include -Doorbird::Doorbird(NetworkAccessManager *networkAccessManager, const QHostAddress &address, QObject *parent) : +Doorbird::Doorbird(const QHostAddress &address, QObject *parent) : QObject(parent), - m_address(address), - m_networkAccessManager(networkAccessManager) + m_address(address) { - + m_networkAccessManager = new QNetworkAccessManager(this); } QHostAddress Doorbird::address() diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 7f26da9b..a09bb788 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -43,7 +43,7 @@ class Doorbird : public QObject { Q_OBJECT public: - explicit Doorbird(NetworkAccessManager *networkAccessManager, const QHostAddress &address, QObject *parent = nullptr); + explicit Doorbird(const QHostAddress &address, QObject *parent = nullptr); enum EventType { Doorbell, @@ -89,7 +89,7 @@ public: void connectToEventMonitor(); private: QHostAddress m_address; - NetworkAccessManager *m_networkAccessManager; + QNetworkAccessManager *m_networkAccessManager; QByteArray m_readBuffer; From b6bb9f41f7747e94cbbc5d8e82a774cafef207bd Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 17 Feb 2020 11:04:39 +0500 Subject: [PATCH 14/35] made actions async --- doorbird/README.md | 12 +++++++++--- doorbird/deviceplugindoorbird.cpp | 16 ++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/doorbird/README.md b/doorbird/README.md index bba30f16..ab687dc5 100644 --- a/doorbird/README.md +++ b/doorbird/README.md @@ -1,8 +1,14 @@ # DoorBird -This plug-in let'S you capture doorbell pressed and motion events, -you are also able to set the power of the IR and the relays. +This plugin integrates DoorBird video doorbells into nymea. All the communication between nymea and the DoorBird device happens locally and will work without internet connection. -It uses a local connection and will work without Internet. +Currently supported features are: +* Doorbell presses +* Motion events +* Enable/disable IR light +* Switching door relays + +The user must have the permission to act as DoorBird API-operator. +You can check the permissions in the DoorBird app. diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 3668bc5c..70a7c42a 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -195,21 +195,25 @@ void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) if (device->deviceClassId() == doorBirdDeviceClassId) { Doorbird *doorbird = m_doorbirdConnections.value(device->id()); if (!doorbird) { + qCWarning(dcDoorBird()) << "Doorbird object not found" << device->name(); info->finish(Device::DeviceErrorHardwareFailure); return; } if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { int number = action.param(doorBirdOpenDoorActionNumberParamTypeId).value().toInt(); - doorbird->openDoor(number); - info->finish(Device::DeviceErrorNoError); + QUuid requestId = doorbird->openDoor(number); + m_asyncActions.insert(requestId, info); + connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); return; } else if (action.actionTypeId() == doorBirdLightOnActionTypeId) { - doorbird->lightOn(); - info->finish(Device::DeviceErrorNoError); + QUuid requestId = doorbird->lightOn(); + m_asyncActions.insert(requestId, info); + connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); return; } else if (action.actionTypeId() == doorBirdRestartActionTypeId) { - doorbird->restart(); - info->finish(Device::DeviceErrorNoError); + QUuid requestId = doorbird->restart(); + m_asyncActions.insert(requestId, info); + connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); return; } else { qCWarning(dcDoorBird()) << "Unhandled ActionTypeId:" << action.actionTypeId(); From e9b9166e65269c9a2e6b840036b902253d23cf25 Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Fri, 20 Mar 2020 08:16:10 +0100 Subject: [PATCH 15/35] devices to things --- doorbird/README.md | 26 +- doorbird/deviceplugindoorbird.cpp | 306 ------------------ doorbird/doorbird.cpp | 2 +- doorbird/doorbird.pro | 6 +- doorbird/integrationplugindoorbird.cpp | 306 ++++++++++++++++++ ...doorbird.h => integrationplugindoorbird.h} | 39 ++- ...rd.json => integrationplugindoorbird.json} | 2 +- 7 files changed, 349 insertions(+), 338 deletions(-) delete mode 100644 doorbird/deviceplugindoorbird.cpp create mode 100644 doorbird/integrationplugindoorbird.cpp rename doorbird/{deviceplugindoorbird.h => integrationplugindoorbird.h} (64%) rename doorbird/{deviceplugindoorbird.json => integrationplugindoorbird.json} (99%) diff --git a/doorbird/README.md b/doorbird/README.md index ab687dc5..05134aea 100644 --- a/doorbird/README.md +++ b/doorbird/README.md @@ -2,13 +2,25 @@ This plugin integrates DoorBird video doorbells into nymea. All the communication between nymea and the DoorBird device happens locally and will work without internet connection. -Currently supported features are: +## Supported Things -* Doorbell presses -* Motion events -* Enable/disable IR light -* Switching door relays +* All video doorbells + * Auto discovery + * Doorbell presses + * Motion events + * Enable/disable IR light + * Switching door relays + * No internet connection required -The user must have the permission to act as DoorBird API-operator. -You can check the permissions in the DoorBird app. +## Requirements +* The DoorBird device must be in the same local area network as nymea. +* The router must not block avahi/zeroconf multicast messages. +* TCP Sockets on port 80 must not be blocked by the router. +* The user must have the permission to act as DoorBird API-operator. + * You can check the permissions in the DoorBird app. +* The package "nymea-plugin-doorbird" must be installed. + +## More + +https://www.doorbird.com/ diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp deleted file mode 100644 index 70a7c42a..00000000 --- a/doorbird/deviceplugindoorbird.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "deviceplugindoorbird.h" -#include "plugininfo.h" - -#include "platform/platformzeroconfcontroller.h" -#include "network/zeroconf/zeroconfservicebrowser.h" -#include "network/zeroconf/zeroconfserviceentry.h" - -#include -#include -#include -#include - -DevicePluginDoorbird::DevicePluginDoorbird() -{ -} - - -void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) -{ - if (info->deviceClassId() == doorBirdDeviceClassId) { - 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()) { - if (serviceEntry.hostName().startsWith("bha-")) { - qCDebug(dcDoorBird) << "Found DoorBird device, name: " << serviceEntry.name() << "\n host address:" << serviceEntry.hostAddress().toString() << "\n text:" << serviceEntry.txt() << serviceEntry.protocol() << serviceEntry.serviceType(); - DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); - ParamList params; - QString macAddress; - if (serviceEntry.txt().length() == 0) { - qCWarning(dcDoorBird()) << "Discovery failed, service entry missing"; - continue; - } - - if (serviceEntry.txt().first().split("=").length() == 2) { - macAddress = serviceEntry.txt().first().split("=").last(); - } else { - qCWarning(dcDoorBird()) << "Could not parse MAC Address" << serviceEntry.txt().first(); - continue; - } - if (!myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).isEmpty()) { - Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).first(); - deviceDescriptor.setDeviceId(existingDevice->id()); - } - params.append(Param(doorBirdDeviceSerialnumberParamTypeId, macAddress)); - params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); - deviceDescriptor.setParams(params); - info->addDeviceDescriptor(deviceDescriptor); - } - } - serviceBrowser->deleteLater(); - info->finish(Device::DeviceErrorNoError); - }); - return; - } else { - qCWarning(dcDoorBird()) << "Cannot discover for deviceClassId" << info->deviceClassId(); - info->finish(Device::DeviceErrorDeviceNotFound); - } -} - - -void DevicePluginDoorbird::startPairing(DevicePairingInfo *info) -{ - if (info->deviceClassId() == doorBirdDeviceClassId) { - info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter username and password for the DoorBird device")); - return; - } else { - qCWarning(dcDoorBird()) << "StartPairing unhandled deviceClassId" << info->deviceClassId(); - info->finish(Device::DeviceErrorCreationMethodNotSupported); - } -} - - -void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &password) -{ - if (info->deviceClassId() == doorBirdDeviceClassId) { - QHostAddress address = QHostAddress(info->params().paramValue(doorBirdDeviceAddressParamTypeId).toString()); - - Doorbird *doorbird = new Doorbird(address, this); - connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); - connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); - connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); - connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); - m_doorbirdConnections.insert(info->deviceId(), doorbird); - m_pendingPairings.insert(doorbird, info); - doorbird->getSession(username, password); - connect(info, &DevicePairingInfo::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->deviceId()); - }); - - pluginStorage()->beginGroup(info->deviceId().toString()); - pluginStorage()->setValue("username", username); - pluginStorage()->setValue("password", password); - pluginStorage()->endGroup(); - } else { - qCWarning(dcDoorBird()) << "Confirm pairing DeviceClassNotFound" << info->deviceClassId(); - info->finish(Device::DeviceErrorDeviceClassNotFound); - } -} - - -void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) -{ - Device *device = info->device(); - - if (device->deviceClassId() == doorBirdDeviceClassId) { - QHostAddress address = QHostAddress(device->paramValue(doorBirdDeviceAddressParamTypeId).toString()); - - if (m_doorbirdConnections.contains(device->id())) { - info->finish(Device::DeviceErrorNoError); - } else { - pluginStorage()->beginGroup(device->id().toString()); - QString username = pluginStorage()->value("username").toString(); - QString password = pluginStorage()->value("password").toString(); - pluginStorage()->endGroup(); - - qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; - Doorbird *doorbird = new Doorbird(address, this); - connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); - connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); - connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); - connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); - m_doorbirdConnections.insert(device->id(), doorbird); - m_pendingDeviceSetups.insert(doorbird, info); - doorbird->getSession(username, password); - connect(info, &DeviceSetupInfo::aborted, this, [device, doorbird, this] { - if (!doorbird) { - doorbird->deleteLater(); - } - m_doorbirdConnections.remove(device->id()); - m_pendingPairings.remove(doorbird); - }); - } - } else { - qCWarning(dcDoorBird()) << "Unhandled device class" << info->device()->deviceClass(); - info->finish(Device::DeviceErrorDeviceClassNotFound); - } -} - - -void DevicePluginDoorbird::postSetupDevice(Device *device) -{ - if (device->deviceClassId() == doorBirdDeviceClassId) { - device->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection in the deviceSetup - Doorbird *doorbird = m_doorbirdConnections.value(device->id()); - doorbird->connectToEventMonitor(); - doorbird->infoRequest(); - doorbird->listFavorites(); - doorbird->listSchedules(); - } -} - - -void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) -{ - Device *device = info->device(); - Action action = info->action(); - - if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbird = m_doorbirdConnections.value(device->id()); - if (!doorbird) { - qCWarning(dcDoorBird()) << "Doorbird object not found" << device->name(); - info->finish(Device::DeviceErrorHardwareFailure); - return; - } - if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { - int number = action.param(doorBirdOpenDoorActionNumberParamTypeId).value().toInt(); - QUuid requestId = doorbird->openDoor(number); - m_asyncActions.insert(requestId, info); - connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); - return; - } else if (action.actionTypeId() == doorBirdLightOnActionTypeId) { - QUuid requestId = doorbird->lightOn(); - m_asyncActions.insert(requestId, info); - connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); - return; - } else if (action.actionTypeId() == doorBirdRestartActionTypeId) { - QUuid requestId = doorbird->restart(); - m_asyncActions.insert(requestId, info); - connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); - return; - } else { - qCWarning(dcDoorBird()) << "Unhandled ActionTypeId:" << action.actionTypeId(); - info->finish(Device::DeviceErrorActionTypeNotFound); - } - } else { - qCWarning(dcDoorBird()) << "Execute action, unhandled device class" << device->deviceClass(); - info->finish(Device::DeviceErrorDeviceClassNotFound); - } -} - -void DevicePluginDoorbird::deviceRemoved(Device *device) -{ - if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbirdConnection = m_doorbirdConnections.take(device->id()); - doorbirdConnection->deleteLater(); - } -} - -void DevicePluginDoorbird::onDoorBirdConnected(bool status) -{ - Doorbird *doorbird = static_cast(sender()); - Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); - if (!device) - return; - - device->setStateValue(doorBirdConnectedStateTypeId, status); -} - -void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool status) -{ - Doorbird *doorbird = static_cast(sender()); - Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); - if (!device) - return; - - switch (eventType) { - case Doorbird::EventType::Rfid: - break; - case Doorbird::EventType::Input: - break; - case Doorbird::EventType::Motion: - device->setStateValue(doorBirdIsPresentStateTypeId, status); - if (status) { - device->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); - } - break; - case Doorbird::EventType::Doorbell: - if (status) { - emit emitEvent(Event(doorBirdDoorbellPressedEventTypeId ,device->id())); - } - break; - } -} - -void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) -{ - Doorbird *doorbird = static_cast(sender()); - - if (m_asyncActions.contains(requestId)) { - DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); - actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); - } - - if (m_pendingPairings.contains(doorbird) && !success) { - DevicePairingInfo *info = m_pendingPairings.take(doorbird); - info->finish(Device::DeviceErrorAuthenticationFailure, tr("Wrong username or password")); - } - - if (m_pendingDeviceSetups.contains(doorbird) && !success) { - DeviceSetupInfo *info = m_pendingDeviceSetups.take(doorbird); - info->finish(Device::DeviceErrorAuthenticationFailure, tr("Wrong username or password")); - } -} - -void DevicePluginDoorbird::onSessionIdReceived(const QString &sessionId) -{ - Q_UNUSED(sessionId); - Doorbird *doorbird = static_cast(sender()); - - if (m_pendingPairings.contains(doorbird)) { - DevicePairingInfo *info = m_pendingPairings.take(doorbird); - info->finish(Device::DeviceErrorNoError); - } - - if (m_pendingDeviceSetups.contains(doorbird)) { - DeviceSetupInfo *info = m_pendingDeviceSetups.take(doorbird); - info->finish(Device::DeviceErrorNoError); - } -} diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index a8bda046..74225d16 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -75,7 +75,7 @@ QUuid Doorbird::getSession(const QString &username, const QString &password) reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error DoorBird device:" << reply->errorString(); + qCWarning(dcDoorBird) << "Error DoorBird thing:" << reply->errorString(); emit requestSent(requestId, false); return; } diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index 55b1f7e0..99666af5 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -2,12 +2,12 @@ include(../plugins.pri) QT += network -TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) +TARGET = $$qtLibraryTarget(nymea_integrationplugindoorbird) SOURCES += \ - deviceplugindoorbird.cpp \ + integrationplugindoorbird.cpp \ doorbird.cpp \ HEADERS += \ - deviceplugindoorbird.h \ + integrationplugindoorbird.h \ doorbird.h \ diff --git a/doorbird/integrationplugindoorbird.cpp b/doorbird/integrationplugindoorbird.cpp new file mode 100644 index 00000000..85a5643a --- /dev/null +++ b/doorbird/integrationplugindoorbird.cpp @@ -0,0 +1,306 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "integrationplugindoorbird.h" +#include "plugininfo.h" + +#include "platform/platformzeroconfcontroller.h" +#include "network/zeroconf/zeroconfservicebrowser.h" +#include "network/zeroconf/zeroconfserviceentry.h" + +#include +#include +#include +#include + +IntegrationPluginDoorbird::IntegrationPluginDoorbird() +{ +} + + +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()) { + 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()); + ParamList params; + QString macAddress; + if (serviceEntry.txt().length() == 0) { + qCWarning(dcDoorBird()) << "Discovery failed, service entry missing"; + continue; + } + + if (serviceEntry.txt().first().split("=").length() == 2) { + macAddress = serviceEntry.txt().first().split("=").last(); + } else { + qCWarning(dcDoorBird()) << "Could not parse MAC Address" << serviceEntry.txt().first(); + continue; + } + if (!myThings().filterByParam(doorBirdThingSerialnumberParamTypeId, macAddress).isEmpty()) { + Thing *existingThing = myThings().filterByParam(doorBirdThingSerialnumberParamTypeId, macAddress).first(); + ThingDescriptor.setThingId(existingThing->id()); + } + params.append(Param(doorBirdThingSerialnumberParamTypeId, macAddress)); + params.append(Param(doorBirdThingAddressParamTypeId, serviceEntry.hostAddress().toString())); + ThingDescriptor.setParams(params); + info->addThingDescriptor(ThingDescriptor); + } + } + serviceBrowser->deleteLater(); + info->finish(Thing::ThingErrorNoError); + }); + return; + } else { + qCWarning(dcDoorBird()) << "Cannot discover for ThingClassId" << info->thingClassId(); + info->finish(Thing::ThingErrorThingNotFound); + } +} + + +void IntegrationPluginDoorbird::startPairing(ThingPairingInfo *info) +{ + if (info->thingClassId() == doorBirdThingClassId) { + info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter username and password for the DoorBird Thing")); + return; + } else { + qCWarning(dcDoorBird()) << "StartPairing unhandled ThingClassId" << info->thingClassId(); + info->finish(Thing::ThingErrorCreationMethodNotSupported); + } +} + + +void IntegrationPluginDoorbird::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &password) +{ + 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()); + }); + + pluginStorage()->beginGroup(info->thingId().toString()); + pluginStorage()->setValue("username", username); + pluginStorage()->setValue("password", password); + pluginStorage()->endGroup(); + } else { + qCWarning(dcDoorBird()) << "Confirm pairing ThingClassNotFound" << info->thingClassId(); + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + + +void IntegrationPluginDoorbird::setupThing(ThingSetupInfo *info) +{ + Thing *thing = info->thing(); + + if (thing->thingClassId() == doorBirdThingClassId) { + QHostAddress address = QHostAddress(thing->paramValue(doorBirdThingAddressParamTypeId).toString()); + + if (m_doorbirdConnections.contains(thing->id())) { + info->finish(Thing::ThingErrorNoError); + } else { + 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); + }); + } + } else { + qCWarning(dcDoorBird()) << "Unhandled Thing class" << info->thing()->thingClass(); + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + + +void IntegrationPluginDoorbird::postSetupThing(Thing *thing) +{ + if (thing->thingClassId() == doorBirdThingClassId) { + thing->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection in the ThingSetup + Doorbird *doorbird = m_doorbirdConnections.value(thing->id()); + doorbird->connectToEventMonitor(); + doorbird->infoRequest(); + doorbird->listFavorites(); + doorbird->listSchedules(); + } +} + + +void IntegrationPluginDoorbird::executeAction(ThingActionInfo *info) +{ + Thing *thing = info->thing(); + Action action = info->action(); + + if (thing->thingClassId() == doorBirdThingClassId) { + Doorbird *doorbird = m_doorbirdConnections.value(thing->id()); + if (!doorbird) { + qCWarning(dcDoorBird()) << "Doorbird object not found" << thing->name(); + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { + int number = action.param(doorBirdOpenDoorActionNumberParamTypeId).value().toInt(); + QUuid requestId = doorbird->openDoor(number); + m_asyncActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); + return; + } else if (action.actionTypeId() == doorBirdLightOnActionTypeId) { + QUuid requestId = doorbird->lightOn(); + m_asyncActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); + return; + } else if (action.actionTypeId() == doorBirdRestartActionTypeId) { + QUuid requestId = doorbird->restart(); + m_asyncActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); + return; + } else { + qCWarning(dcDoorBird()) << "Unhandled ActionTypeId:" << action.actionTypeId(); + info->finish(Thing::ThingErrorActionTypeNotFound); + } + } else { + qCWarning(dcDoorBird()) << "Execute action, unhandled Thing class" << thing->thingClass(); + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + +void IntegrationPluginDoorbird::thingRemoved(Thing *thing) +{ + if (thing->thingClassId() == doorBirdThingClassId) { + Doorbird *doorbirdConnection = m_doorbirdConnections.take(thing->id()); + doorbirdConnection->deleteLater(); + } +} + +void IntegrationPluginDoorbird::onDoorBirdConnected(bool status) +{ + Doorbird *doorbird = static_cast(sender()); + Thing *thing = myThings().findById(m_doorbirdConnections.key(doorbird)); + if (!thing) + return; + + thing->setStateValue(doorBirdConnectedStateTypeId, status); +} + +void IntegrationPluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool status) +{ + Doorbird *doorbird = static_cast(sender()); + Thing *thing = myThings().findById(m_doorbirdConnections.key(doorbird)); + if (!thing) + return; + + switch (eventType) { + case Doorbird::EventType::Rfid: + break; + case Doorbird::EventType::Input: + break; + case Doorbird::EventType::Motion: + thing->setStateValue(doorBirdIsPresentStateTypeId, status); + if (status) { + thing->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); + } + break; + case Doorbird::EventType::Doorbell: + if (status) { + emit emitEvent(Event(doorBirdDoorbellPressedEventTypeId ,thing->id())); + } + break; + } +} + +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/deviceplugindoorbird.h b/doorbird/integrationplugindoorbird.h similarity index 64% rename from doorbird/deviceplugindoorbird.h rename to doorbird/integrationplugindoorbird.h index 594decb0..3168785d 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/integrationplugindoorbird.h @@ -28,44 +28,43 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef DEVICEPLUGINDOORBIRD_H -#define DEVICEPLUGINDOORBIRD_H +#ifndef INTEGRATIONPLUGINDOORBIRD_H +#define INTEGRATIONPLUGINDOORBIRD_H #include -#include "devices/deviceplugin.h" -#include "devices/devicemanager.h" +#include "integrations/integrationplugin.h" #include "doorbird.h" class QNetworkAccessManager; class QNetworkReply; -class DevicePluginDoorbird: public DevicePlugin +class IntegrationPluginDoorbird: public IntegrationPlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindoorbird.json") - Q_INTERFACES(DevicePlugin) + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugindoorbird.json") + Q_INTERFACES(IntegrationPlugin) public: - explicit DevicePluginDoorbird(); + explicit IntegrationPluginDoorbird(); - void discoverDevices(DeviceDiscoveryInfo *info) override; - void setupDevice(DeviceSetupInfo *info) override; - void postSetupDevice(Device *device) override; - void executeAction(DeviceActionInfo *info) override; + void discoverThings(ThingDiscoveryInfo *info) override; + void setupThing(ThingSetupInfo *info) override; + void postSetupThing(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; - void startPairing(DevicePairingInfo *info) override; - void confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) override; + void startPairing(ThingPairingInfo *info) override; + void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override; - void deviceRemoved(Device *device)override; + void thingRemoved(Thing *thing)override; private: - QHash m_doorbirdConnections; - QHash m_pendingPairings; - QHash m_pendingDeviceSetups; + QHash m_doorbirdConnections; + QHash m_pendingPairings; + QHash m_pendingThingSetups; - QHash m_asyncActions; + QHash m_asyncActions; private slots: void onDoorBirdConnected(bool status); @@ -74,4 +73,4 @@ private slots: void onSessionIdReceived(const QString &sessionId); }; -#endif // DEVICEPLUGINDOORBIRD_H +#endif // INTEGRATIONPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/integrationplugindoorbird.json similarity index 99% rename from doorbird/deviceplugindoorbird.json rename to doorbird/integrationplugindoorbird.json index a5e348f7..ac31c717 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/integrationplugindoorbird.json @@ -7,7 +7,7 @@ "name": "doorBird", "displayName": "DoorBird", "id": "2da07435-571e-4956-a387-6caa51d6e845", - "deviceClasses": [ + "thingClasses": [ { "id": "0485eb61-2a22-42ba-9dd2-a5961485bf08", "name": "doorBird", From 311774f326fce7cab98b560acc2a72fe74be0b65 Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Mon, 23 Mar 2020 14:36:22 +0100 Subject: [PATCH 16/35] changes requested by reviewer --- doorbird/README.md | 6 ++++-- doorbird/doorbird.pro | 2 -- doorbird/integrationplugindoorbird.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doorbird/README.md b/doorbird/README.md index 05134aea..36130e22 100644 --- a/doorbird/README.md +++ b/doorbird/README.md @@ -12,10 +12,12 @@ This plugin integrates DoorBird video doorbells into nymea. All the communicatio * Switching door relays * No internet connection required +NOTE: This plug-in does not handle any video- or audio stream. + ## Requirements * The DoorBird device must be in the same local area network as nymea. -* The router must not block avahi/zeroconf multicast messages. +* The router must not block ZeroConf/mDNS multicast messages. * TCP Sockets on port 80 must not be blocked by the router. * The user must have the permission to act as DoorBird API-operator. * You can check the permissions in the DoorBird app. @@ -23,4 +25,4 @@ This plugin integrates DoorBird video doorbells into nymea. All the communicatio ## More -https://www.doorbird.com/ +https://www.doorbird.com diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index 99666af5..7efb2342 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -2,8 +2,6 @@ include(../plugins.pri) QT += network -TARGET = $$qtLibraryTarget(nymea_integrationplugindoorbird) - SOURCES += \ integrationplugindoorbird.cpp \ doorbird.cpp \ diff --git a/doorbird/integrationplugindoorbird.cpp b/doorbird/integrationplugindoorbird.cpp index 85a5643a..ddce2f09 100644 --- a/doorbird/integrationplugindoorbird.cpp +++ b/doorbird/integrationplugindoorbird.cpp @@ -85,7 +85,7 @@ void IntegrationPluginDoorbird::discoverThings(ThingDiscoveryInfo *info) return; } else { qCWarning(dcDoorBird()) << "Cannot discover for ThingClassId" << info->thingClassId(); - info->finish(Thing::ThingErrorThingNotFound); + info->finish(Thing::ThingErrorThingClassNotFound); } } @@ -97,7 +97,7 @@ void IntegrationPluginDoorbird::startPairing(ThingPairingInfo *info) return; } else { qCWarning(dcDoorBird()) << "StartPairing unhandled ThingClassId" << info->thingClassId(); - info->finish(Thing::ThingErrorCreationMethodNotSupported); + info->finish(Thing::ThingErrorThingClassNotFound); } } From 20ba477760f89c0dab67bbef246822c2f7c78a2e Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Mon, 23 Mar 2020 14:53:13 +0100 Subject: [PATCH 17/35] fixed debian packaging --- debian/nymea-plugin-doorbird.install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/nymea-plugin-doorbird.install.in b/debian/nymea-plugin-doorbird.install.in index 3a517946..47568ef1 100644 --- a/debian/nymea-plugin-doorbird.install.in +++ b/debian/nymea-plugin-doorbird.install.in @@ -1 +1 @@ -usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_deviceplugindoorbird.so +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationplugindoorbird.so From cc9e225457b3c8e482e3f2f8abf4827397d226e7 Mon Sep 17 00:00:00 2001 From: nymea Date: Thu, 29 Aug 2019 18:38:35 +0200 Subject: [PATCH 18/35] moved files from doorbird branch --- doorbird/deviceplugindoorbird.cpp | 216 ++++++++++++++++++ doorbird/deviceplugindoorbird.h | 56 +++++ doorbird/deviceplugindoorbird.json | 70 ++++++ doorbird/doorbird.pro | 11 + ...1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts | 4 + ...68429-e312-4c82-9eab-e1cd996e43d6-en_US.ts | 4 + nymea-plugins.pro | 1 + 7 files changed, 362 insertions(+) create mode 100644 doorbird/deviceplugindoorbird.cpp create mode 100644 doorbird/deviceplugindoorbird.h create mode 100644 doorbird/deviceplugindoorbird.json create mode 100644 doorbird/doorbird.pro create mode 100644 doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts create mode 100644 doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp new file mode 100644 index 00000000..e62e4cf7 --- /dev/null +++ b/doorbird/deviceplugindoorbird.cpp @@ -0,0 +1,216 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * nymea is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * nymea is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with nymea. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "deviceplugindoorbird.h" +#include "plugininfo.h" + +#include "platform/platformzeroconfcontroller.h" +#include "network/zeroconf/zeroconfservicebrowser.h" +#include "network/zeroconf/zeroconfserviceentry.h" + +#include +#include +#include +#include + +DevicePluginDoorbird::DevicePluginDoorbird() +{ + m_nam = new QNetworkAccessManager(this); + connect(m_nam, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { + Device *dev = m_networkRequests.value(reply); + if (!myDevices().contains(dev)) { + qCWarning(dcDoorBird) << "Credentials requested for a device which doesn't exist any more"; + return; + } + qCDebug(dcDoorBird) << "Credentials requested for device:" << dev->name(); + authenticator->setUser(dev->paramValue(doorBirdDeviceUsernameParamTypeId).toString()); + authenticator->setPassword(dev->paramValue(doorBirdDevicePasswordParamTypeId).toString()); + }); +} + +DevicePluginDoorbird::~DevicePluginDoorbird() +{ + +} + +Device::DeviceError DevicePluginDoorbird::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) +{ + Q_UNUSED(deviceClassId) + Q_UNUSED(params) + + // NOTE: Discovery is currently disabled in json file because we don't support discovery & login as parameters in combination + // and there isn't any setupMethod which would allow us to enter user & password. + + ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_xbmc-jsonrpc._tcp"); + QTimer::singleShot(5000, this, [this, serviceBrowser](){ + QList deviceDescriptors; + foreach (const ZeroConfServiceEntry serviceEntry, serviceBrowser->serviceEntries()) { + if (serviceEntry.serviceType() == "_axis-video._tcp" && serviceEntry.hostName().startsWith("bha-")) { + qCDebug(dcDoorBird) << "Found DoorBird device"; + DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); + ParamList params; + //TODO add rediscovery + params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); + deviceDescriptor.setParams(params); + deviceDescriptors.append(deviceDescriptor); + } + } + emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors); + serviceBrowser->deleteLater(); + }); + return Device::DeviceErrorAsync; +} + +Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) +{ + connectToEventMonitor(device); + return Device::DeviceSetupStatusSuccess; +} + +Device::DeviceError DevicePluginDoorbird::executeAction(Device *device, const Action &action) +{ + if (action.actionTypeId() == doorBirdUnlatchActionTypeId) { + QNetworkRequest request(QString("http://%1/bha-api/open-door.cgi?r=1").arg(device->paramValue(doorBirdDeviceAddressParamTypeId).toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_nam->get(request); + m_networkRequests.insert(reply, device); + connect(reply, &QNetworkReply::finished, this, [this, reply, device, action](){ + reply->deleteLater(); + m_networkRequests.remove(reply); + if (!myDevices().contains(device)) { + // Device must have been removed in the meantime + return; + } + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << device->name(); + emit actionExecutionFinished(action.id(), Device::DeviceErrorHardwareFailure); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit actionExecutionFinished(action.id(), Device::DeviceErrorNoError); + }); + } + return Device::DeviceErrorDeviceClassNotFound; +} + +void DevicePluginDoorbird::connectToEventMonitor(Device *device) +{ + qCDebug(dcDoorBird) << "Starting monitoring" << device->name(); + + QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(device->paramValue(doorBirdDeviceAddressParamTypeId).toString())); + QNetworkReply *reply = m_nam->get(request); + m_networkRequests.insert(reply, device); + connect(reply, &QNetworkReply::downloadProgress, this, [this, device, reply](qint64 bytesReceived, qint64 bytesTotal){ + Q_UNUSED(bytesReceived) + Q_UNUSED(bytesTotal); + if (!myDevices().contains(device)) { + qCWarning(dcDoorBird) << "Device disappeared for monitor stream."; + reply->abort(); + return; + } + device->setStateValue(doorBirdConnectedStateTypeId, true); + m_readBuffers[device].append(reply->readAll()); + // qCDebug(dcDoorBird) << "Monitor data for" << device->name(); + // qCDebug(dcDoorBird) << m_readBuffers[device]; + + // Input data looks like: + // "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n" + + while (!m_readBuffers[device].isEmpty()) { + // find next --ioboundary + QString boundary = QStringLiteral("--ioboundary"); + int startIndex = m_readBuffers[device].indexOf(boundary); + if (startIndex == -1) { + qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers[device]; + if (m_readBuffers[device].size() > 1024) { + qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer..."; + m_readBuffers[device].clear(); + } + // Assuming we don't have enough data yet... + return; + } + + QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain"); + int contentTypeIndex = m_readBuffers[device].indexOf(contentType); + if (contentTypeIndex == -1) { + qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffers[device]; + if (m_readBuffers[device].size() > startIndex + 50) { + qCWarning(dcDoorBird) << boundary << "found but unexpected data follows. Skipping this element..."; + m_readBuffers[device].remove(0, startIndex + boundary.length()); + continue; + } + // Assuming we don't have enough data yet... + return; + } + + // At this point we have the boundary and Content-Type. Remove all of that and take the entire string to either end or next boundary + m_readBuffers[device].remove(0, contentTypeIndex + contentType.length()); + int nextStartIndex = m_readBuffers[device].indexOf(boundary); + QByteArray data; + if (nextStartIndex == -1) { + data = m_readBuffers[device]; + m_readBuffers[device].clear(); + } else { + data = m_readBuffers[device].left(nextStartIndex); + m_readBuffers[device].remove(0, nextStartIndex); + } + + QString message = data.trimmed(); + QStringList parts = message.split(":"); + if (parts.count() != 2) { + qCWarning(dcDoorBird) << "Message has invalid format:" << message << "Expected device:state"; + continue; + } + if (parts.first() == "doorbell") { + if (parts.at(1) == "H") { + qCDebug(dcDoorBird) << "Doorbell ringing!"; + emitEvent(Event(doorBirdTriggeredEventTypeId, device->id())); + } + } else if (parts.first() == "motionsensor") { + if (parts.at(1) == "H") { + qCDebug(dcDoorBird) << "Motion sensor detected a person"; + emitEvent(Event(doorBirdMotionDetectedEventTypeId, device->id())); + } + } else { + qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; + } + } + }); + connect(reply, &QNetworkReply::finished, this, [this, device, reply](){ + reply->deleteLater(); + m_networkRequests.remove(reply); + + if (!myDevices().contains(device)) { + qCWarning(dcDoorBird) << "Device has disappeared. Exiting monitor."; + return; + } + + device->setStateValue(doorBirdConnectedStateTypeId, false); + qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); + + QTimer::singleShot(2000, this, [this, device] { + if (!myDevices().contains(device)) { + return; + } + connectToEventMonitor(device); + }); + }); +} diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h new file mode 100644 index 00000000..89edfb46 --- /dev/null +++ b/doorbird/deviceplugindoorbird.h @@ -0,0 +1,56 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * nymea is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * nymea is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with nymea. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINDOORBIRD_H +#define DEVICEPLUGINDOORBIRD_H + +#include "devices/deviceplugin.h" +#include "devices/devicemanager.h" + +class QNetworkAccessManager; +class QNetworkReply; + +class DevicePluginDoorbird: public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindoorbird.json") + Q_INTERFACES(DevicePlugin) + + +public: + explicit DevicePluginDoorbird(); + ~DevicePluginDoorbird() override; + + Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + + Device::DeviceSetupStatus setupDevice(Device *device) override; + Device::DeviceError executeAction(Device *device, const Action &action) override; + + void connectToEventMonitor(Device *device); +private: + + QNetworkAccessManager *m_nam = nullptr; + + QHash m_networkRequests; + QHash m_readBuffers; +}; + +#endif // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json new file mode 100644 index 00000000..974da11a --- /dev/null +++ b/doorbird/deviceplugindoorbird.json @@ -0,0 +1,70 @@ +{ + "name": "doorBird", + "displayName": "DoorBird", + "id": "6fe1614a-fc47-4eb2-a47c-13c50f1798ee", + "vendors": [ + { + "name": "doorBird", + "displayName": "DoorBird", + "id": "2da07435-571e-4956-a387-6caa51d6e845", + "deviceClasses": [ + { + "id": "0485eb61-2a22-42ba-9dd2-a5961485bf08", + "name": "doorBird", + "displayName": "DoorBird", + "createMethods": ["discovery", "user" ], + "interfaces": [ "inputtrigger", "connectable" ], + "paramTypes": [ + { + "id": "8873b17d-526e-408d-95d8-6439b501f489", + "name": "address", + "displayName": "IP address", + "type": "QString" + }, + { + "id": "7ccd8f3a-2a5f-4b90-8042-92899d0ee32a", + "name": "username", + "displayName": "Username", + "type": "QString" + }, + { + "id": "ea285a57-47c5-43f1-b0d6-e0a4d6230f3c", + "name": "password", + "displayName": "Password", + "type": "QString" + } + ], + "actionTypes": [ + { + "id": "b6c3377b-91de-411a-9d48-8b509c39d67c", + "name": "unlatch", + "displayName": "Unlatch the door" + } + ], + "stateTypes": [ + { + "id": "186c270b-923c-46e4-a7da-33e45427cdbb", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false + } + ], + "eventTypes": [ + { + "id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b", + "name": "triggered", + "displayName": "Doorbell pressed" + }, + { + "id": "e9bb229b-8776-4110-a813-9c0dc67375db", + "name": "motionDetected", + "displayName": "Motion detected" + } + ] + } + ] + } + ] +} diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro new file mode 100644 index 00000000..c10ea83a --- /dev/null +++ b/doorbird/doorbird.pro @@ -0,0 +1,11 @@ +include(../plugins.pri) + +QT += network + +TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) + +SOURCES += \ + deviceplugindoorbird.cpp \ + +HEADERS += \ + deviceplugindoorbird.h \ diff --git a/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts new file mode 100644 index 00000000..f7f66d85 --- /dev/null +++ b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts b/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts new file mode 100644 index 00000000..f7f66d85 --- /dev/null +++ b/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 23d78cdf..f20cccb2 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -12,6 +12,7 @@ PLUGIN_DIRS = \ datetime \ daylightsensor \ denon \ + doorbird \ dweetio \ elgato \ elro \ From bb645fed61359ebad056a7e77f41ef13790eacd0 Mon Sep 17 00:00:00 2001 From: nymea Date: Sun, 22 Sep 2019 22:56:56 +0200 Subject: [PATCH 19/35] refactored code --- doorbird/README.md | 0 doorbird/deviceplugindoorbird.cpp | 248 +++++++++---------------- doorbird/deviceplugindoorbird.h | 12 +- doorbird/deviceplugindoorbird.json | 56 ++++-- doorbird/doorbird.cpp | 282 +++++++++++++++++++++++++++++ doorbird/doorbird.h | 76 ++++++++ doorbird/doorbird.pro | 2 + 7 files changed, 489 insertions(+), 187 deletions(-) create mode 100644 doorbird/README.md create mode 100644 doorbird/doorbird.cpp create mode 100644 doorbird/doorbird.h diff --git a/doorbird/README.md b/doorbird/README.md new file mode 100644 index 00000000..e69de29b diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index e62e4cf7..09123d25 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -28,22 +28,11 @@ #include #include -#include +#include #include DevicePluginDoorbird::DevicePluginDoorbird() { - m_nam = new QNetworkAccessManager(this); - connect(m_nam, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { - Device *dev = m_networkRequests.value(reply); - if (!myDevices().contains(dev)) { - qCWarning(dcDoorBird) << "Credentials requested for a device which doesn't exist any more"; - return; - } - qCDebug(dcDoorBird) << "Credentials requested for device:" << dev->name(); - authenticator->setUser(dev->paramValue(doorBirdDeviceUsernameParamTypeId).toString()); - authenticator->setPassword(dev->paramValue(doorBirdDevicePasswordParamTypeId).toString()); - }); } DevicePluginDoorbird::~DevicePluginDoorbird() @@ -53,164 +42,97 @@ DevicePluginDoorbird::~DevicePluginDoorbird() Device::DeviceError DevicePluginDoorbird::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) { - Q_UNUSED(deviceClassId) Q_UNUSED(params) - - // NOTE: Discovery is currently disabled in json file because we don't support discovery & login as parameters in combination - // and there isn't any setupMethod which would allow us to enter user & password. - - ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_xbmc-jsonrpc._tcp"); - QTimer::singleShot(5000, this, [this, serviceBrowser](){ - QList deviceDescriptors; - foreach (const ZeroConfServiceEntry serviceEntry, serviceBrowser->serviceEntries()) { - if (serviceEntry.serviceType() == "_axis-video._tcp" && serviceEntry.hostName().startsWith("bha-")) { - qCDebug(dcDoorBird) << "Found DoorBird device"; - DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); - ParamList params; - //TODO add rediscovery - params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); - deviceDescriptor.setParams(params); - deviceDescriptors.append(deviceDescriptor); + if (deviceClassId == doorBirdDeviceClassId) { + ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser(); + QTimer::singleShot(5000, this, [this, serviceBrowser](){ + QList deviceDescriptors; + foreach (const ZeroConfServiceEntry serviceEntry, serviceBrowser->serviceEntries()) { + if (serviceEntry.hostName().startsWith("bha-")) { + qCDebug(dcDoorBird) << "Found DoorBird device"; + DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); + ParamList params; + //TODO add rediscovery + params.append(Param(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName())); + params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); + deviceDescriptor.setParams(params); + deviceDescriptors.append(deviceDescriptor); + } } - } - emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors); - serviceBrowser->deleteLater(); - }); - return Device::DeviceErrorAsync; -} - -Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) -{ - connectToEventMonitor(device); - return Device::DeviceSetupStatusSuccess; -} - -Device::DeviceError DevicePluginDoorbird::executeAction(Device *device, const Action &action) -{ - if (action.actionTypeId() == doorBirdUnlatchActionTypeId) { - QNetworkRequest request(QString("http://%1/bha-api/open-door.cgi?r=1").arg(device->paramValue(doorBirdDeviceAddressParamTypeId).toString())); - qCDebug(dcDoorBird) << "Sending request:" << request.url(); - QNetworkReply *reply = m_nam->get(request); - m_networkRequests.insert(reply, device); - connect(reply, &QNetworkReply::finished, this, [this, reply, device, action](){ - reply->deleteLater(); - m_networkRequests.remove(reply); - if (!myDevices().contains(device)) { - // Device must have been removed in the meantime - return; - } - if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << device->name(); - emit actionExecutionFinished(action.id(), Device::DeviceErrorHardwareFailure); - return; - } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); - emit actionExecutionFinished(action.id(), Device::DeviceErrorNoError); + emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors); + serviceBrowser->deleteLater(); }); + return Device::DeviceErrorAsync; } return Device::DeviceErrorDeviceClassNotFound; } -void DevicePluginDoorbird::connectToEventMonitor(Device *device) +DevicePairingInfo DevicePluginDoorbird::pairDevice(DevicePairingInfo &devicePairingInfo) { - qCDebug(dcDoorBird) << "Starting monitoring" << device->name(); + qCDebug(dcDoorBird()) << "PairDevice:" << devicePairingInfo.deviceClassId(); - QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(device->paramValue(doorBirdDeviceAddressParamTypeId).toString())); - QNetworkReply *reply = m_nam->get(request); - m_networkRequests.insert(reply, device); - connect(reply, &QNetworkReply::downloadProgress, this, [this, device, reply](qint64 bytesReceived, qint64 bytesTotal){ - Q_UNUSED(bytesReceived) - Q_UNUSED(bytesTotal); - if (!myDevices().contains(device)) { - qCWarning(dcDoorBird) << "Device disappeared for monitor stream."; - reply->abort(); - return; - } - device->setStateValue(doorBirdConnectedStateTypeId, true); - m_readBuffers[device].append(reply->readAll()); - // qCDebug(dcDoorBird) << "Monitor data for" << device->name(); - // qCDebug(dcDoorBird) << m_readBuffers[device]; - - // Input data looks like: - // "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n" - - while (!m_readBuffers[device].isEmpty()) { - // find next --ioboundary - QString boundary = QStringLiteral("--ioboundary"); - int startIndex = m_readBuffers[device].indexOf(boundary); - if (startIndex == -1) { - qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers[device]; - if (m_readBuffers[device].size() > 1024) { - qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer..."; - m_readBuffers[device].clear(); - } - // Assuming we don't have enough data yet... - return; - } - - QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain"); - int contentTypeIndex = m_readBuffers[device].indexOf(contentType); - if (contentTypeIndex == -1) { - qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffers[device]; - if (m_readBuffers[device].size() > startIndex + 50) { - qCWarning(dcDoorBird) << boundary << "found but unexpected data follows. Skipping this element..."; - m_readBuffers[device].remove(0, startIndex + boundary.length()); - continue; - } - // Assuming we don't have enough data yet... - return; - } - - // At this point we have the boundary and Content-Type. Remove all of that and take the entire string to either end or next boundary - m_readBuffers[device].remove(0, contentTypeIndex + contentType.length()); - int nextStartIndex = m_readBuffers[device].indexOf(boundary); - QByteArray data; - if (nextStartIndex == -1) { - data = m_readBuffers[device]; - m_readBuffers[device].clear(); - } else { - data = m_readBuffers[device].left(nextStartIndex); - m_readBuffers[device].remove(0, nextStartIndex); - } - - QString message = data.trimmed(); - QStringList parts = message.split(":"); - if (parts.count() != 2) { - qCWarning(dcDoorBird) << "Message has invalid format:" << message << "Expected device:state"; - continue; - } - if (parts.first() == "doorbell") { - if (parts.at(1) == "H") { - qCDebug(dcDoorBird) << "Doorbell ringing!"; - emitEvent(Event(doorBirdTriggeredEventTypeId, device->id())); - } - } else if (parts.first() == "motionsensor") { - if (parts.at(1) == "H") { - qCDebug(dcDoorBird) << "Motion sensor detected a person"; - emitEvent(Event(doorBirdMotionDetectedEventTypeId, device->id())); - } - } else { - qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; - } - } - }); - connect(reply, &QNetworkReply::finished, this, [this, device, reply](){ - reply->deleteLater(); - m_networkRequests.remove(reply); - - if (!myDevices().contains(device)) { - qCWarning(dcDoorBird) << "Device has disappeared. Exiting monitor."; - return; - } - - device->setStateValue(doorBirdConnectedStateTypeId, false); - qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); - - QTimer::singleShot(2000, this, [this, device] { - if (!myDevices().contains(device)) { - return; - } - connectToEventMonitor(device); - }); - }); + devicePairingInfo.setStatus(Device::DeviceErrorNoError); + devicePairingInfo.setMessage(tr("Please enter username and password for your Doorbird device.")); + return devicePairingInfo; +} + +DevicePairingInfo DevicePluginDoorbird::confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) +{ + qCDebug(dcDoorBird()) << "confirm pairing called"; + + pluginStorage()->beginGroup(devicePairingInfo.deviceId().toString()); + pluginStorage()->setValue("username", username); + pluginStorage()->setValue("password", secret); + pluginStorage()->endGroup(); + + devicePairingInfo.setStatus(Device::DeviceErrorNoError); + + return devicePairingInfo; +} + + +Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) +{ + if (device->deviceClassId() == doorBirdDeviceClassId) { + QHostAddress address = QHostAddress(device->paramValue(doorBirdDeviceAddressParamTypeId).toString()); + + pluginStorage()->beginGroup(device->id().toString()); + QString username = pluginStorage()->value("username").toString(); + QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); + + Doorbird *doorbird = new Doorbird(address, username, password, this); + doorbird->connectToEventMonitor(); + m_doorbirdConnections.insert(device, doorbird); + return Device::DeviceSetupStatusSuccess; + } + return Device::DeviceSetupStatusFailure; +} + +Device::DeviceError DevicePluginDoorbird::executeAction(Device *device, const Action &action) +{ + if (device->deviceClassId() == doorBirdDeviceClassId) { + Doorbird *doorbird = m_doorbirdConnections.value(device); + if (!doorbird) + return Device::DeviceErrorDeviceNotFound; + + if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { + //Todo get action param + doorbird->openDoor(1); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == doorBirdLightOnActionTypeId) { + doorbird->lightOn(); + return Device::DeviceErrorNoError; + } + } + return Device::DeviceErrorDeviceClassNotFound; +} + +void DevicePluginDoorbird::deviceRemoved(Device *device) +{ + if (device->deviceClassId() == doorBirdDeviceClassId) { + Doorbird *doorbirdConnection = m_doorbirdConnections.take(device); + doorbirdConnection->deleteLater(); + } } diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h index 89edfb46..e18b250e 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/deviceplugindoorbird.h @@ -23,6 +23,7 @@ #include "devices/deviceplugin.h" #include "devices/devicemanager.h" +#include "doorbird.h" class QNetworkAccessManager; class QNetworkReply; @@ -44,13 +45,12 @@ public: Device::DeviceSetupStatus setupDevice(Device *device) override; Device::DeviceError executeAction(Device *device, const Action &action) override; - void connectToEventMonitor(Device *device); + DevicePairingInfo confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) override; + DevicePairingInfo pairDevice(DevicePairingInfo &devicePairingInfo) override; + void deviceRemoved(Device *device)override; + private: - - QNetworkAccessManager *m_nam = nullptr; - - QHash m_networkRequests; - QHash m_readBuffers; + QHash m_doorbirdConnections; }; #endif // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index 974da11a..309c918d 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -13,7 +13,8 @@ "name": "doorBird", "displayName": "DoorBird", "createMethods": ["discovery", "user" ], - "interfaces": [ "inputtrigger", "connectable" ], + "interfaces": [ "doorbell", "presencesensor", "connectable" ], + "setupMethod": "userandpassword", "paramTypes": [ { "id": "8873b17d-526e-408d-95d8-6439b501f489", @@ -22,23 +23,30 @@ "type": "QString" }, { - "id": "7ccd8f3a-2a5f-4b90-8042-92899d0ee32a", - "name": "username", - "displayName": "Username", - "type": "QString" - }, - { - "id": "ea285a57-47c5-43f1-b0d6-e0a4d6230f3c", - "name": "password", - "displayName": "Password", + "id": "67ea5534-330a-4291-93b5-0237034e15fa", + "name": "serialnumber", + "displayName": "Serial number", "type": "QString" } ], "actionTypes": [ { "id": "b6c3377b-91de-411a-9d48-8b509c39d67c", - "name": "unlatch", - "displayName": "Unlatch the door" + "name": "openDoor", + "displayName": "Open door", + "paramTypes": [ + { + "id": "6dcce056-e4d4-49c4-8b68-d3f478c00a52", + "name": "number", + "displayName": "Number", + "type": "int" + } + ] + }, + { + "id": "3a6cfc5d-804c-4d21-91b5-999913d4f1a5", + "name": "lightOn", + "displayName": "Light on" } ], "stateTypes": [ @@ -49,18 +57,30 @@ "displayNameEvent": "Connected changed", "type": "bool", "defaultValue": false + }, + { + "id": "0f5eb200-6c0d-45c5-9156-3060fd66d332", + "name": "isPresent", + "displayName": "Motion sensor presence", + "displayNameEvent": "Motion sensor presence detected", + "type": "bool", + "defaultValue": false + }, + { + "id": "295c9700-b598-4681-898f-d63e2889cedf", + "name": "lastSeenTime", + "displayName": "Motion sensor last seen time", + "displayNameEvent": "Motion sensor last seen time changedd", + "type": "int", + "unit": "UnixTime", + "defaultValue": 0 } ], "eventTypes": [ { "id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b", - "name": "triggered", + "name": "doorbellPressed", "displayName": "Doorbell pressed" - }, - { - "id": "e9bb229b-8776-4110-a813-9c0dc67375db", - "name": "motionDetected", - "displayName": "Motion detected" } ] } diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp new file mode 100644 index 00000000..46174ed4 --- /dev/null +++ b/doorbird/doorbird.cpp @@ -0,0 +1,282 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * nymea is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * nymea is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with nymea. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "doorbird.h" +#include "extern-plugininfo.h" + +#include +#include +#include +#include +#include +#include +#include + +Doorbird::Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent) : + QObject(parent), + m_address(address), + m_username(username), + m_password(password) +{ + m_networkAccessManager = new QNetworkAccessManager(this); + connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { + qCDebug(dcDoorBird) << "Credentials requested:"; + authenticator->setUser(username); + authenticator->setPassword(password); + }); +} + +QUuid Doorbird::getSession() +{ + QNetworkRequest request(QString("http://%1/bha-api/getsession.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + emit requestSent(requestId, false); + return; + } + emit requestSent(requestId, true); + + QByteArray data = reply->readAll(); + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcDoorBird()) << "Error parsing json:" << data; + return; + } + }); + return requestId; +} + +QUuid Doorbird::openDoor(int value) +{ + QNetworkRequest request(QString("http://%1/bha-api/open-door.cgi?r=%2").arg(m_address.toString()).arg(QString::number(value))); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error opening DoorBird " << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:"; + emit requestSent(requestId, true); + }); + return requestId; +} + +QUuid Doorbird::lightOn() +{ + QNetworkRequest request(QString("http://%1/bha-api/light-on.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird light on:"; + emit requestSent(requestId, true); + }); + return requestId; +} + +QUuid Doorbird::liveVideoRequest() +{ + +} + +QUuid Doorbird::liveImageRequest() +{ + QNetworkRequest request(QString("http://%1/bha-api/light-on.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird light on:"; + emit requestSent(requestId, true); + + QImage* image = new QImage(); + image->loadFromData(reply->readAll()); + }); + return requestId; +} + +QUuid Doorbird::historyImageRequest(int index) +{ + +} + +QUuid Doorbird::infoRequest() +{ + QNetworkRequest request(QString("http://%1/bha-api/info.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + + connect(reply, &QNetworkReply::finished, this, [this, reply, action](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + }); +} + +QUuid Doorbird::listFavorites() +{ + +} + +QUuid Doorbird::listSchedules() +{ + +} + +QUuid Doorbird::restart() +{ + QNetworkRequest request(QString("http://%1/bha-api/restart.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + + connect(reply, &QNetworkReply::finished, this, [this, reply, action](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error restarting DoorBird device" << reply; + return; + } + qCDebug(dcDoorBird) << "DoorBird restarting"; + }); +} + +void Doorbird::connectToEventMonitor() +{ + qCDebug(dcDoorBird) << "Starting monitoring"; + + QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(m_address.toString()); + QNetworkReply *reply = m_networkAccessManager->get(request); + + connect(reply, &QNetworkReply::downloadProgress, this, [this, reply](qint64 bytesReceived, qint64 bytesTotal){ + Q_UNUSED(bytesReceived) + Q_UNUSED(bytesTotal); + + //TODO emit signal connected + m_readBuffers.append(reply->readAll()); + // qCDebug(dcDoorBird) << "Monitor data for" << device->name(); + // qCDebug(dcDoorBird) << m_readBuffers[device]; + + // Input data looks like: + // "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n" + + while (!m_readBuffers.isEmpty()) { + // find next --ioboundary + QString boundary = QStringLiteral("--ioboundary"); + int startIndex = m_readBuffers.indexOf(boundary); + if (startIndex == -1) { + qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers; + if (m_readBuffers.size() > 1024) { + qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer..."; + m_readBuffers.clear(); + } + // Assuming we don't have enough data yet... + return; + } + + QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain"); + int contentTypeIndex = m_readBuffers.indexOf(contentType); + if (contentTypeIndex == -1) { + qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffers; + if (m_readBuffers.size() > startIndex + 50) { + qCWarning(dcDoorBird) << boundary << "found but unexpected data follows. Skipping this element..."; + m_readBuffers.remove(0, startIndex + boundary.length()); + continue; + } + // Assuming we don't have enough data yet... + return; + } + + // At this point we have the boundary and Content-Type. Remove all of that and take the entire string to either end or next boundary + m_readBuffers.remove(0, contentTypeIndex + contentType.length()); + int nextStartIndex = m_readBuffers.indexOf(boundary); + QByteArray data; + if (nextStartIndex == -1) { + data = m_readBuffers; + m_readBuffers.clear(); + } else { + data = m_readBuffers.left(nextStartIndex); + m_readBuffers.remove(0, nextStartIndex); + } + + QString message = data.trimmed(); + QStringList parts = message.split(":"); + if (parts.count() != 2) { + qCWarning(dcDoorBird) << "Message has invalid format:" << message << "Expected device:state"; + continue; + } + if (parts.first() == "doorbell") { + if (parts.at(1) == "H") { + qCDebug(dcDoorBird) << "Doorbell ringing!"; + //emitEvent(Event(doorBirdTriggeredEventTypeId, device->id())); + } + } else if (parts.first() == "motionsensor") { + if (parts.at(1) == "H") { + qCDebug(dcDoorBird) << "Motion sensor detected a person"; + //emitEvent(Event(doorBirdMotionDetectedEventTypeId, device->id())); + } + } else { + qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; + } + } + }); + connect(reply, &QNetworkReply::finished, this, [this, reply](){ + reply->deleteLater(); + + //device->setStateValue(doorBirdConnectedStateTypeId, false); + qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); + + QTimer::singleShot(2000, this, [this] { + connectToEventMonitor(device); + }); + }); +} + diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h new file mode 100644 index 00000000..69e05d0b --- /dev/null +++ b/doorbird/doorbird.h @@ -0,0 +1,76 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes * + * Copyright (C) 2019 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * nymea is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * nymea is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with nymea. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DOORBIRD_H +#define DOORBIRD_H + +#include +#include +#include +#include + +class Doorbird : public QObject +{ + Q_OBJECT +public: + explicit Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent = nullptr); + + QUuid getSession(); + QUuid openDoor(int value); + QUuid lightOn(); + QUuid liveVideoRequest(); + QUuid liveImageRequest(); + QUuid historyImageRequest(int index); + + QUuid liveAudioReceive(); + QUuid liveAudioTransmit(); + QUuid infoRequest(); + + QUuid listFavorites(); + QUuid addFavorite(); + QUuid deleteFavorite(); + + QUuid listSchedules(); + QUuid daddScheduleEntry(); + QUuid deleteScheduleEntry(); + + QUuid restart(); + + void connectToEventMonitor(); +private: + QNetworkAccessManager *m_networkAccessManager; + QList m_readBuffers; + + QHostAddress m_address; + QList m_networkRequests; + + QString m_username; + QString m_password; + + QByteArray sessionId; + +signals: + void requestSent(QUuid requestId, bool success); + +public slots: +}; + +#endif // DOORBIRD_H diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index c10ea83a..0ea9d1fb 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -6,6 +6,8 @@ TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) SOURCES += \ deviceplugindoorbird.cpp \ + doorbird.cpp HEADERS += \ deviceplugindoorbird.h \ + doorbird.h From 20aa068d18f6a39bbb4b39ff57992283eb119366 Mon Sep 17 00:00:00 2001 From: nymea Date: Sun, 22 Sep 2019 23:30:43 +0200 Subject: [PATCH 20/35] added some more API calls --- doorbird/deviceplugindoorbird.json | 10 +-- doorbird/doorbird.cpp | 122 ++++++++++++++++++++++++++--- 2 files changed, 111 insertions(+), 21 deletions(-) diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index 309c918d..8f9ed2ee 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -33,15 +33,7 @@ { "id": "b6c3377b-91de-411a-9d48-8b509c39d67c", "name": "openDoor", - "displayName": "Open door", - "paramTypes": [ - { - "id": "6dcce056-e4d4-49c4-8b68-d3f478c00a52", - "name": "number", - "displayName": "Number", - "type": "int" - } - ] + "displayName": "Open door" }, { "id": "3a6cfc5d-804c-4d21-91b5-999913d4f1a5", diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 46174ed4..a2a6dce9 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include Doorbird::Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent) : QObject(parent), @@ -38,9 +40,10 @@ Doorbird::Doorbird(const QHostAddress &address, const QString &username, const Q { m_networkAccessManager = new QNetworkAccessManager(this); connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { + Q_UNUSED(reply); qCDebug(dcDoorBird) << "Credentials requested:"; - authenticator->setUser(username); - authenticator->setPassword(password); + authenticator->setUser(m_username); + authenticator->setPassword(m_password); }); } @@ -114,7 +117,22 @@ QUuid Doorbird::lightOn() QUuid Doorbird::liveVideoRequest() { + QNetworkRequest request(QString("http://%1/bha-api/info.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit requestSent(requestId, true); + }); + return requestId; } QUuid Doorbird::liveImageRequest() @@ -142,7 +160,52 @@ QUuid Doorbird::liveImageRequest() QUuid Doorbird::historyImageRequest(int index) { + QUrl url(QString("http://%1/bha-api/history.cgi").arg(m_address.toString())); + QUrlQuery query; + query.addQueryItem("index", QString::number(index)); + url.setQuery(query); + qCDebug(dcDoorBird) << "Sending request:" << url; + QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(url)); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit requestSent(requestId, true); + }); + return requestId; +} + +QUuid Doorbird::liveAudioReceive() +{ + QNetworkRequest request(QString("http://%1/bha-api/audio-receive.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error DoorBird device"; + emit requestSent(requestId, false); + return; + } + qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit requestSent(requestId, true); + }); + return requestId; +} + +QUuid Doorbird::liveAudioTransmit() +{ + + return QUuid::createUuid(); } QUuid Doorbird::infoRequest() @@ -150,26 +213,57 @@ QUuid Doorbird::infoRequest() QNetworkRequest request(QString("http://%1/bha-api/info.cgi").arg(m_address.toString())); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); - - connect(reply, &QNetworkReply::finished, this, [this, reply, action](){ + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + emit requestSent(requestId, false); return; } qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + emit requestSent(requestId, true); }); + return requestId; } QUuid Doorbird::listFavorites() { + QNetworkRequest request(QString("http://%1/bha-api/favorites.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + emit requestSent(requestId, true); + }); + return requestId; } QUuid Doorbird::listSchedules() { + QNetworkRequest request(QString("http://%1/bha-api/schedule.cgi").arg(m_address.toString())); + qCDebug(dcDoorBird) << "Sending request:" << request.url(); + QNetworkReply *reply = m_networkAccessManager->get(request); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + emit requestSent(requestId, true); + }); + return requestId; } QUuid Doorbird::restart() @@ -177,24 +271,27 @@ QUuid Doorbird::restart() QNetworkRequest request(QString("http://%1/bha-api/restart.cgi").arg(m_address.toString())); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); - - connect(reply, &QNetworkReply::finished, this, [this, reply, action](){ + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(dcDoorBird) << "Error restarting DoorBird device" << reply; + emit requestSent(requestId, false); return; } qCDebug(dcDoorBird) << "DoorBird restarting"; + emit requestSent(requestId, true); }); + return requestId; } void Doorbird::connectToEventMonitor() { qCDebug(dcDoorBird) << "Starting monitoring"; - QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(m_address.toString()); - QNetworkReply *reply = m_networkAccessManager->get(request); + QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(m_address.toString())); + QNetworkReply *reply = m_networkAccessManager->get(request); connect(reply, &QNetworkReply::downloadProgress, this, [this, reply](qint64 bytesReceived, qint64 bytesTotal){ Q_UNUSED(bytesReceived) @@ -210,7 +307,7 @@ void Doorbird::connectToEventMonitor() while (!m_readBuffers.isEmpty()) { // find next --ioboundary - QString boundary = QStringLiteral("--ioboundary"); + /*QString boundary = QStringLiteral("--ioboundary"); int startIndex = m_readBuffers.indexOf(boundary); if (startIndex == -1) { qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers; @@ -265,17 +362,18 @@ void Doorbird::connectToEventMonitor() } } else { qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; - } + }*/ } }); - connect(reply, &QNetworkReply::finished, this, [this, reply](){ + + connect(reply, &QNetworkReply::finished, this, [this, reply]() { reply->deleteLater(); //device->setStateValue(doorBirdConnectedStateTypeId, false); qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); QTimer::singleShot(2000, this, [this] { - connectToEventMonitor(device); + connectToEventMonitor(); }); }); } From 6d0c479d18ebd468b878b8162797471b88245240 Mon Sep 17 00:00:00 2001 From: nymea Date: Tue, 24 Sep 2019 17:21:53 +0200 Subject: [PATCH 21/35] added qr code reader --- doorbird/doorbird.pro | 8 +- doorbird/qrcodereader.cpp | 189 ++++++++++++++++++++++++++++++++++++++ doorbird/qrcodereader.h | 88 ++++++++++++++++++ 3 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 doorbird/qrcodereader.cpp create mode 100644 doorbird/qrcodereader.h diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index 0ea9d1fb..c519c376 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -2,12 +2,16 @@ include(../plugins.pri) QT += network +LIBS += -lzbar + TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) SOURCES += \ deviceplugindoorbird.cpp \ - doorbird.cpp + doorbird.cpp \ + qrcodereader.cpp \ HEADERS += \ deviceplugindoorbird.h \ - doorbird.h + doorbird.h \ + qrcodereader.h \ diff --git a/doorbird/qrcodereader.cpp b/doorbird/qrcodereader.cpp new file mode 100644 index 00000000..658a2496 --- /dev/null +++ b/doorbird/qrcodereader.cpp @@ -0,0 +1,189 @@ +/***************************************************************************** + * Copyright: 2013 Michael Zanetti * + * * + * This file is part of ubuntu-authenticator * + * * + * This prject is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This project is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + * * + ****************************************************************************/ + +#include "qrcodereader.h" + +#include +#include +#include +#include + +QRCodeReader::QRCodeReader(QObject *parent) : + QObject(parent) +{ + connect(&m_readerThread, &QThread::started, this, &QRCodeReader::scanningChanged); + connect(&m_readerThread, &QThread::finished, this, &QRCodeReader::scanningChanged); +} + +bool QRCodeReader::valid() const +{ + return !m_type.isEmpty() && !m_text.isEmpty(); +} + +QString QRCodeReader::type() const +{ + return m_type; +} + +QString QRCodeReader::text() const +{ + return m_text; +} + +QImage QRCodeReader::image() const +{ + return m_image; +} + +QRect QRCodeReader::scanRect() const +{ + return m_scanRect; +} + +void QRCodeReader::setScanRect(const QRect &rect) +{ + if (m_scanRect != rect) { + m_scanRect = rect; + emit scanRectChanged(); + } +} + +bool QRCodeReader::scanning() const +{ + return m_readerThread.isRunning(); +} + +void QRCodeReader::grab(QImage image) +{ + m_type.clear(); + m_text.clear(); + emit validChanged(); + + if (m_scanRect.isValid()) { + image = image.copy(m_scanRect); + } + + Reader *reader = new Reader; + reader->moveToThread(&m_readerThread); + connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater())); + connect(reader, SIGNAL(finished()), &m_readerThread, SLOT(quit())); + connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); + m_readerThread.start(); + + QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, img), Q_ARG(bool, false)); +} + +void QRCodeReader::processImage(const QUrl &url) +{ + QImage image; + if (!image.load(url.path())) { + qWarning() << "can't open" << url.path(); + return; + } + + Reader *reader = new Reader; + reader->moveToThread(&m_readerThread); + connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater())); + connect(reader, SIGNAL(finished()), &m_readerThread, SLOT(quit())); + connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); + m_readerThread.start(); + + QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, image), Q_ARG(bool, false)); +} + +void QRCodeReader::handleResults(const QString &type, const QString &text, const QImage &codeImage) +{ + m_type = type; + m_text = text; + m_image = codeImage; + m_imageUuid = QUuid::createUuid(); + emit validChanged(); +} + +void Reader::doWork(const QImage &image, bool invert) +{ + // Prepare image + QImage copy = image; + if (invert) { + copy.invertPixels(); + } + zbar::QZBarImage img(copy.convertToFormat(QImage::Format_RGB32)); + zbar::Image tmp = img.convert(*(long*)"Y800"); + + // create a reader + zbar::ImageScanner scanner; + + // configure the reader + scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1); + scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_POSITION, 1); + scanner.set_config(zbar::ZBAR_PARTIAL, zbar::ZBAR_CFG_ENABLE, 0); + + // scan the image for barcodes + int n = scanner.scan(tmp); +// qDebug() << "scanned. have" << n << "symbols"; + if (!invert && n == 0) { + // Nothing found... try again inverted + doWork(image, true); + return; + } + + img.set_symbols(tmp.get_symbols()); + + // extract results + for(zbar::Image::SymbolIterator symbol = img.symbol_begin(); symbol != img.symbol_end(); ++symbol) { + + QString typeName = QString::fromStdString(symbol->get_type_name()); + QString symbolString = QString::fromStdString(symbol->get_data()); + + int x0 = 999999; + int y0 = 999999; + int x1 = 0; + int y1 = 0; + + for (int i = 0; i < symbol->get_location_size(); ++i) { + int x = symbol->get_location_x(i); + int y = symbol->get_location_y(i); + qDebug() << "got point" << x << y; + if (x < x0) x0 = x; + if (y < y0) y0 = y; + if (x > x1) x1 = x; + if (y > y1) y1 = y; + } + + int width = x1 - x0; + int height = y1 - y0; + + // Workaround for zBar sometimes only giving us the first bar in a barcode. + if (width < 10) width = img.get_width() - x0; + if (height < 10) height = img.get_height() - y0; + + qDebug() << "extracting code image (" << x0 << y0 << ") ("<< x1 << y1 << ")"; + QImage codeImage = image.copy(x0, y0, width, height); + + qDebug() << "Code recognized:" << typeName << ", Text:" << symbolString; + + emit resultReady(typeName, symbolString, codeImage); + } + + tmp.set_data(NULL, 0); + img.set_data(NULL, 0); + + emit finished(); +} diff --git a/doorbird/qrcodereader.h b/doorbird/qrcodereader.h new file mode 100644 index 00000000..f0c22b05 --- /dev/null +++ b/doorbird/qrcodereader.h @@ -0,0 +1,88 @@ +/***************************************************************************** + * Copyright: 2013 Michael Zanetti * + * * + * This file is part of tagger * + * * + * This prject is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This project is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + * * + ****************************************************************************/ + +#ifndef QRCODEREADER_H +#define QRCODEREADER_H + +#include +#include +#include +#include + +class QRCodeReader : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool valid READ valid NOTIFY validChanged) + Q_PROPERTY(QString type READ type NOTIFY validChanged) + Q_PROPERTY(QString text READ text NOTIFY validChanged) + Q_PROPERTY(QImage image READ image NOTIFY validChanged) + Q_PROPERTY(QString imageSource READ imageSource NOTIFY validChanged) + Q_PROPERTY(QRect scanRect READ scanRect WRITE setScanRect NOTIFY scanRectChanged) + Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged) + Q_PROPERTY(HistoryModel* history READ history CONSTANT) + +public: + explicit QRCodeReader(QObject *parent = nullptr); + + bool valid() const; + QString type() const; + QString text() const; + QImage image() const; + QString imageSource() const; + QRect scanRect() const; + void setScanRect(const QRect &rect); + bool scanning() const; + + +public slots: + void grab(QImage image); + void processImage(const QUrl &url); + +signals: + void validChanged(); + void scanRectChanged(); + void scanningChanged(); + +private slots: + void handleResults(const QString &type, const QString &text, const QImage &codeImage); + +private: + QString m_type; + QString m_text; + QImage m_image; + QUuid m_imageUuid; + QRect m_scanRect; + + QThread m_readerThread; +}; + +class Reader : public QObject +{ + Q_OBJECT + +public slots: + void doWork(const QImage &image, bool invert); + +signals: + void resultReady(const QString &type, const QString &text, const QImage &codeImage); + void finished(); +}; + +#endif // QRCODEREADER_H From 527dc6050801160112ba4fcbf5e473c37b92802b Mon Sep 17 00:00:00 2001 From: nymea Date: Tue, 24 Sep 2019 23:05:30 +0200 Subject: [PATCH 22/35] updated to new nymea api --- doorbird/deviceplugindoorbird.cpp | 211 +++++++++++++++++++++-------- doorbird/deviceplugindoorbird.h | 22 ++- doorbird/deviceplugindoorbird.json | 20 ++- doorbird/doorbird.cpp | 122 +++++++++++++---- doorbird/doorbird.h | 31 ++++- doorbird/qrcodereader.cpp | 5 +- doorbird/qrcodereader.h | 4 +- 7 files changed, 315 insertions(+), 100 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 09123d25..03f117eb 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -35,64 +35,74 @@ DevicePluginDoorbird::DevicePluginDoorbird() { } -DevicePluginDoorbird::~DevicePluginDoorbird() -{ -} - -Device::DeviceError DevicePluginDoorbird::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) +void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) { - Q_UNUSED(params) - if (deviceClassId == doorBirdDeviceClassId) { - ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser(); - QTimer::singleShot(5000, this, [this, serviceBrowser](){ - QList deviceDescriptors; + if (info->deviceClassId() == doorBirdDeviceClassId) { + 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()) { if (serviceEntry.hostName().startsWith("bha-")) { - qCDebug(dcDoorBird) << "Found DoorBird device"; + qCDebug(dcDoorBird) << "Found DoorBird device, name: " << serviceEntry.name() << "\n host address:" << serviceEntry.hostAddress().toString() << "\n text:" << serviceEntry.txt() << serviceEntry.protocol() << serviceEntry.serviceType(); DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); ParamList params; - //TODO add rediscovery - params.append(Param(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName())); + QString macAddress = serviceEntry.txt().first(); + if (!myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).isEmpty()) { + Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName()).first(); + deviceDescriptor.setDeviceId(existingDevice->id()); + } + params.append(Param(doorBirdDeviceSerialnumberParamTypeId, macAddress)); params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); deviceDescriptor.setParams(params); - deviceDescriptors.append(deviceDescriptor); + info->addDeviceDescriptor(deviceDescriptor); } } - emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors); serviceBrowser->deleteLater(); + info->finish(Device::DeviceErrorNoError); }); - return Device::DeviceErrorAsync; + return; } - return Device::DeviceErrorDeviceClassNotFound; -} - -DevicePairingInfo DevicePluginDoorbird::pairDevice(DevicePairingInfo &devicePairingInfo) -{ - qCDebug(dcDoorBird()) << "PairDevice:" << devicePairingInfo.deviceClassId(); - - devicePairingInfo.setStatus(Device::DeviceErrorNoError); - devicePairingInfo.setMessage(tr("Please enter username and password for your Doorbird device.")); - return devicePairingInfo; -} - -DevicePairingInfo DevicePluginDoorbird::confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) -{ - qCDebug(dcDoorBird()) << "confirm pairing called"; - - pluginStorage()->beginGroup(devicePairingInfo.deviceId().toString()); - pluginStorage()->setValue("username", username); - pluginStorage()->setValue("password", secret); - pluginStorage()->endGroup(); - - devicePairingInfo.setStatus(Device::DeviceErrorNoError); - - return devicePairingInfo; + qCWarning(dcDoorBird()) << "Cannot discover for deviceClassId" << info->deviceClassId(); + info->finish(Device::DeviceErrorDeviceNotFound); } -Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) +void DevicePluginDoorbird::startPairing(DevicePairingInfo *info) { + + if (info->deviceClassId() == doorBirdDeviceClassId) { + qCDebug(dcDoorBird()) << "User and password. Login is \"user\" and \"password\"."; + info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter the user credentials")); + return; + } + info->finish(Device::DeviceErrorCreationMethodNotSupported); +} + + +void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) +{ + if (info->deviceClassId() == doorBirdDeviceClassId) { + qCDebug(dcDoorBird()) << "confirm pairing called"; + + pluginStorage()->beginGroup(info->deviceId().toString()); + pluginStorage()->setValue("username", username); + pluginStorage()->setValue("password", secret); + pluginStorage()->endGroup(); + + info->finish(Device::DeviceErrorNoError); + return; + } + info->finish(Device::DeviceErrorDeviceClassNotFound); + return; +} + + +void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) +{ + Device *device = info->device(); + if (device->deviceClassId() == doorBirdDeviceClassId) { QHostAddress address = QHostAddress(device->paramValue(doorBirdDeviceAddressParamTypeId).toString()); @@ -101,32 +111,61 @@ Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device) QString password = pluginStorage()->value("password").toString(); pluginStorage()->endGroup(); + qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; Doorbird *doorbird = new Doorbird(address, username, password, this); + connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); + connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); + connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(device, doorbird); - return Device::DeviceSetupStatusSuccess; + info->finish(Device::DeviceErrorNoError); + return; } - return Device::DeviceSetupStatusFailure; + qCWarning(dcDoorBird()) << "Unhandled device class" << info->device()->deviceClass(); + info->finish(Device::DeviceErrorDeviceClassNotFound); } -Device::DeviceError DevicePluginDoorbird::executeAction(Device *device, const Action &action) + +void DevicePluginDoorbird::postSetupDevice(Device *device) { if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbird = m_doorbirdConnections.value(device); - if (!doorbird) - return Device::DeviceErrorDeviceNotFound; - - if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { - //Todo get action param - doorbird->openDoor(1); - return Device::DeviceErrorNoError; - } - if (action.actionTypeId() == doorBirdLightOnActionTypeId) { - doorbird->lightOn(); - return Device::DeviceErrorNoError; - } + Doorbird *doorbird = m_doorbirdConnections.value(device); + doorbird->infoRequest(); + doorbird->listFavorites(); + doorbird->listSchedules(); } - return Device::DeviceErrorDeviceClassNotFound; +} + + +void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) +{ + Device *device = info->device(); + Action action = info->action(); + + if (device->deviceClassId() == doorBirdDeviceClassId) { + Doorbird *doorbird = m_doorbirdConnections.value(device); + if (!doorbird) { + info->finish(Device::DeviceErrorHardwareFailure); + return; + } + if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { + int number = action.param(doorBirdOpenDoorActionNumberParamTypeId).value().toInt(); + doorbird->openDoor(number); + info->finish(Device::DeviceErrorNoError); + return; + } else if (action.actionTypeId() == doorBirdLightOnActionTypeId) { + doorbird->lightOn(); + info->finish(Device::DeviceErrorNoError); + return; + } else if (action.actionTypeId() == doorBirdRestartActionTypeId) { + doorbird->restart(); + info->finish(Device::DeviceErrorNoError); + return; + } + info->finish(Device::DeviceErrorActionTypeNotFound); + return; + } + info->finish(Device::DeviceErrorDeviceClassNotFound); } void DevicePluginDoorbird::deviceRemoved(Device *device) @@ -136,3 +175,59 @@ void DevicePluginDoorbird::deviceRemoved(Device *device) doorbirdConnection->deleteLater(); } } + +void DevicePluginDoorbird::onDoorBirdConnected(bool status) +{ + Doorbird *doorbird = static_cast(sender()); + Device *device = m_doorbirdConnections.key(doorbird); + if (!device) + return; + + device->setStateValue(doorBirdConnectedStateTypeId, status); +} + +void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool status) +{ + Doorbird *doorbird = static_cast(sender()); + Device *device = m_doorbirdConnections.key(doorbird); + if (!device) + return; + + switch (eventType) { + case Doorbird::EventType::Rfid: + break; + case Doorbird::EventType::Input: + break; + case Doorbird::EventType::Motion: + device->setStateValue(doorBirdIsPresentStateTypeId, status); + if (status) { + doorbird->liveImageRequest(); + } + break; + case Doorbird::EventType::Doorbell: + if (status) { + emit emitEvent(Event(doorBirdDoorbellPressedEventTypeId ,device->id())); + } + break; + } +} + +void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) +{ + Doorbird *doorbird = static_cast(sender()); + Device *device = m_doorbirdConnections.key(doorbird); + if (!device) + return; + + if (!m_asyncActions.contains(requestId)) + return; + + DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); + actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); +} + +void DevicePluginDoorbird::onImageReceived(QImage image) +{ + Q_UNUSED(image) + //QString code = +} diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h index e18b250e..9be0a4ee 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/deviceplugindoorbird.h @@ -21,6 +21,8 @@ #ifndef DEVICEPLUGINDOORBIRD_H #define DEVICEPLUGINDOORBIRD_H +#include + #include "devices/deviceplugin.h" #include "devices/devicemanager.h" #include "doorbird.h" @@ -35,22 +37,28 @@ class DevicePluginDoorbird: public DevicePlugin Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindoorbird.json") Q_INTERFACES(DevicePlugin) - public: explicit DevicePluginDoorbird(); - ~DevicePluginDoorbird() override; - Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + void discoverDevices(DeviceDiscoveryInfo *info) override; + void setupDevice(DeviceSetupInfo *info) override; + void postSetupDevice(Device *device) override; + void executeAction(DeviceActionInfo *info) override; - Device::DeviceSetupStatus setupDevice(Device *device) override; - Device::DeviceError executeAction(Device *device, const Action &action) override; + void startPairing(DevicePairingInfo *info) override; + void confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) override; - DevicePairingInfo confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) override; - DevicePairingInfo pairDevice(DevicePairingInfo &devicePairingInfo) override; void deviceRemoved(Device *device)override; private: QHash m_doorbirdConnections; + QHash m_asyncActions; + +private slots: + void onDoorBirdConnected(bool status); + void onDoorBirdEvent(Doorbird::EventType eventType, bool status); + void onDoorBirdRequestSent(QUuid requestId, bool success); + void onImageReceived(QImage image); }; #endif // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index 8f9ed2ee..caa78c48 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -33,12 +33,30 @@ { "id": "b6c3377b-91de-411a-9d48-8b509c39d67c", "name": "openDoor", - "displayName": "Open door" + "displayName": "Open door", + "paramTypes": [ + { + "id": "95dd35d7-0bc3-49e1-af96-d8da8ea5244d", + "name": "number", + "displayName": "Relay number", + "type": "QString", + "allowedValues": [ + "1", + "2" + ], + "defaultValue": 1 + } + ] }, { "id": "3a6cfc5d-804c-4d21-91b5-999913d4f1a5", "name": "lightOn", "displayName": "Light on" + }, + { + "id": "e874242e-5acb-4d98-94c7-0a70db65150c", + "name": "restart", + "displayName": "Restart" } ], "stateTypes": [ diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index a2a6dce9..429b4b45 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -218,11 +218,11 @@ QUuid Doorbird::infoRequest() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + qCWarning(dcDoorBird) << "Error DoorBird" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + qCDebug(dcDoorBird) << "DoorBird info:" << reply->readAll() ; emit requestSent(requestId, true); }); return requestId; @@ -247,6 +247,36 @@ QUuid Doorbird::listFavorites() return requestId; } +QUuid Doorbird::addFavorite(FavoriteType type, const QString &name, const QUrl &commandUrl, int id) +{ + QUrl url(QString("http://%1/bha-api/favorites.cgi").arg(m_address.toString())); + QUrlQuery query; + query.addQueryItem("action", "save"); + if (type == FavoriteType::Http) { + query.addQueryItem("type", "http"); + } else { + query.addQueryItem("type", "sip"); + } + query.addQueryItem("title", name); + query.addQueryItem("value", commandUrl.toString()); + query.addQueryItem("id", QString::number(id)); + url.setQuery(query); + + QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(url)); + QUuid requestId = QUuid::createUuid(); + connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString(); + emit requestSent(requestId, false); + return; + } + emit requestSent(requestId, true); + }); + return requestId; +} + QUuid Doorbird::listSchedules() { QNetworkRequest request(QString("http://%1/bha-api/schedule.cgi").arg(m_address.toString())); @@ -297,35 +327,34 @@ void Doorbird::connectToEventMonitor() Q_UNUSED(bytesReceived) Q_UNUSED(bytesTotal); - //TODO emit signal connected - m_readBuffers.append(reply->readAll()); - // qCDebug(dcDoorBird) << "Monitor data for" << device->name(); - // qCDebug(dcDoorBird) << m_readBuffers[device]; + emit deviceConnected(true); + m_readBuffer.append(reply->readAll()); + qCDebug(dcDoorBird) << "Event received" << m_readBuffer; // Input data looks like: // "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n" - while (!m_readBuffers.isEmpty()) { + while (!m_readBuffer.isEmpty()) { // find next --ioboundary - /*QString boundary = QStringLiteral("--ioboundary"); - int startIndex = m_readBuffers.indexOf(boundary); + QString boundary = QStringLiteral("--ioboundary"); + int startIndex = m_readBuffer.indexOf(boundary); if (startIndex == -1) { - qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers; - if (m_readBuffers.size() > 1024) { + qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffer; + if (m_readBuffer.size() > 1024) { qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer..."; - m_readBuffers.clear(); + m_readBuffer.clear(); } // Assuming we don't have enough data yet... return; } QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain"); - int contentTypeIndex = m_readBuffers.indexOf(contentType); + int contentTypeIndex = m_readBuffer.indexOf(contentType); if (contentTypeIndex == -1) { - qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffers; - if (m_readBuffers.size() > startIndex + 50) { + qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffer; + if (m_readBuffer.size() > startIndex + 50) { qCWarning(dcDoorBird) << boundary << "found but unexpected data follows. Skipping this element..."; - m_readBuffers.remove(0, startIndex + boundary.length()); + m_readBuffer.remove(0, startIndex + boundary.length()); continue; } // Assuming we don't have enough data yet... @@ -333,15 +362,15 @@ void Doorbird::connectToEventMonitor() } // At this point we have the boundary and Content-Type. Remove all of that and take the entire string to either end or next boundary - m_readBuffers.remove(0, contentTypeIndex + contentType.length()); - int nextStartIndex = m_readBuffers.indexOf(boundary); + m_readBuffer.remove(0, contentTypeIndex + contentType.length()); + int nextStartIndex = m_readBuffer.indexOf(boundary); QByteArray data; if (nextStartIndex == -1) { - data = m_readBuffers; - m_readBuffers.clear(); + data = m_readBuffer; + m_readBuffer.clear(); } else { - data = m_readBuffers.left(nextStartIndex); - m_readBuffers.remove(0, nextStartIndex); + data = m_readBuffer.left(nextStartIndex); + m_readBuffer.remove(0, nextStartIndex); } QString message = data.trimmed(); @@ -353,23 +382,27 @@ void Doorbird::connectToEventMonitor() if (parts.first() == "doorbell") { if (parts.at(1) == "H") { qCDebug(dcDoorBird) << "Doorbell ringing!"; - //emitEvent(Event(doorBirdTriggeredEventTypeId, device->id())); + emit eventReveiced(EventType::Doorbell, true); + } else { + emit eventReveiced(EventType::Doorbell, false); } } else if (parts.first() == "motionsensor") { if (parts.at(1) == "H") { qCDebug(dcDoorBird) << "Motion sensor detected a person"; - //emitEvent(Event(doorBirdMotionDetectedEventTypeId, device->id())); + emit eventReveiced(EventType::Motion, true); + } else { + emit eventReveiced(EventType::Motion, false); } } else { qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message; - }*/ + } } }); connect(reply, &QNetworkReply::finished, this, [this, reply]() { reply->deleteLater(); - //device->setStateValue(doorBirdConnectedStateTypeId, false); + emit deviceConnected(false); qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error(); QTimer::singleShot(2000, this, [this] { @@ -378,3 +411,40 @@ void Doorbird::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 69e05d0b..1f376345 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -1,4 +1,4 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2019 Bernhard Trinnes * * Copyright (C) 2019 Michael Zanetti * @@ -33,6 +33,24 @@ class Doorbird : public QObject public: explicit Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent = nullptr); + enum EventType { + Doorbell, + Motion, + Input, + Rfid + }; + enum FavoriteType { + Http, + Sip + }; + + struct FavoriteObject { + FavoriteType type; + QString title; + QUrl value; + int id; + }; + QUuid getSession(); QUuid openDoor(int value); QUuid lightOn(); @@ -45,11 +63,11 @@ public: QUuid infoRequest(); QUuid listFavorites(); - QUuid addFavorite(); + QUuid addFavorite(FavoriteType type, const QString &name, const QUrl &url, int id); QUuid deleteFavorite(); QUuid listSchedules(); - QUuid daddScheduleEntry(); + QUuid addScheduleEntry(EventType event, int favoriteNumber, bool enabled); QUuid deleteScheduleEntry(); QUuid restart(); @@ -57,7 +75,7 @@ public: void connectToEventMonitor(); private: QNetworkAccessManager *m_networkAccessManager; - QList m_readBuffers; + QByteArray m_readBuffer; QHostAddress m_address; QList m_networkRequests; @@ -68,9 +86,14 @@ private: QByteArray sessionId; signals: + void deviceConnected(bool status); void requestSent(QUuid requestId, bool success); + void eventReveiced(EventType eventType, bool status); + void favoritesReceived(QList favourites); + public slots: + void onUdpBroadcast(const QByteArray &data); }; #endif // DOORBIRD_H diff --git a/doorbird/qrcodereader.cpp b/doorbird/qrcodereader.cpp index 658a2496..e16fa363 100644 --- a/doorbird/qrcodereader.cpp +++ b/doorbird/qrcodereader.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . * * * ****************************************************************************/ - +/* #include "qrcodereader.h" #include @@ -87,7 +87,7 @@ void QRCodeReader::grab(QImage image) connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); m_readerThread.start(); - QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, img), Q_ARG(bool, false)); + //QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, img), Q_ARG(bool, false)); } void QRCodeReader::processImage(const QUrl &url) @@ -187,3 +187,4 @@ void Reader::doWork(const QImage &image, bool invert) emit finished(); } +*/ diff --git a/doorbird/qrcodereader.h b/doorbird/qrcodereader.h index f0c22b05..7b2edd15 100644 --- a/doorbird/qrcodereader.h +++ b/doorbird/qrcodereader.h @@ -17,7 +17,7 @@ * along with this program. If not, see . * * * ****************************************************************************/ - +/* #ifndef QRCODEREADER_H #define QRCODEREADER_H @@ -36,7 +36,6 @@ class QRCodeReader : public QObject Q_PROPERTY(QString imageSource READ imageSource NOTIFY validChanged) Q_PROPERTY(QRect scanRect READ scanRect WRITE setScanRect NOTIFY scanRectChanged) Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged) - Q_PROPERTY(HistoryModel* history READ history CONSTANT) public: explicit QRCodeReader(QObject *parent = nullptr); @@ -86,3 +85,4 @@ signals: }; #endif // QRCODEREADER_H +*/ From 4eec7e271749baf8a0df647331766b3dca9a090c Mon Sep 17 00:00:00 2001 From: Boernsman Date: Fri, 15 Nov 2019 16:44:27 +0100 Subject: [PATCH 23/35] removed qr code added image request --- doorbird/deviceplugindoorbird.cpp | 14 ++- doorbird/deviceplugindoorbird.json | 13 ++ doorbird/doorbird.cpp | 30 ++--- doorbird/doorbird.h | 3 + doorbird/doorbird.pro | 4 - doorbird/qrcodereader.cpp | 190 ----------------------------- doorbird/qrcodereader.h | 88 ------------- 7 files changed, 44 insertions(+), 298 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 03f117eb..e34eb03a 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -116,6 +116,7 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); + connect(doorbird, &Doorbird::liveImageReceived, this, &DevicePluginDoorbird::onImageReceived); doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(device, doorbird); info->finish(Device::DeviceErrorNoError); @@ -228,6 +229,15 @@ void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) void DevicePluginDoorbird::onImageReceived(QImage image) { - Q_UNUSED(image) - //QString code = + Q_UNUSED(image); + Doorbird *doorbird = static_cast(sender()); + Device *device = m_doorbirdConnections.key(doorbird); + if (!device) + return; + //TODO add QR code detection + Event event; + event.setDeviceId(device->id()); + event.setEventTypeId(doorBirdQrCodeDetectedEventTypeId); + event.setParams(ParamList() << Param(doorBirdQrCodeDetectedEventDataParamTypeId, "image received")); + emit emitEvent(event); } diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index caa78c48..6e0057c7 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -91,6 +91,19 @@ "id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b", "name": "doorbellPressed", "displayName": "Doorbell pressed" + }, + { + "id": "224f1be5-17d3-460d-9bb8-a25fcdf28936", + "name": "qrCodeDetected", + "displayName": "QR code detected", + "paramTypes": [ + { + "id": "c53068b9-604e-4dfc-8d23-26e8bab0233c", + "name": "data", + "displayName": "Data", + "type": "QString" + } + ] } ] } diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 429b4b45..4fb99b79 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -105,7 +105,7 @@ QUuid Doorbird::lightOn() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << reply->error() << reply->errorString(); + qCWarning(dcDoorBird) << "Error light on" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } @@ -117,7 +117,7 @@ QUuid Doorbird::lightOn() QUuid Doorbird::liveVideoRequest() { - QNetworkRequest request(QString("http://%1/bha-api/info.cgi").arg(m_address.toString())); + QNetworkRequest request(QString("http://%1/bha-api/video.cgi").arg(m_address.toString())); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); @@ -125,11 +125,11 @@ QUuid Doorbird::liveVideoRequest() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + qCWarning(dcDoorBird) << "Error live video request" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + qCDebug(dcDoorBird) << "DoorBird live video request" ; emit requestSent(requestId, true); }); return requestId; @@ -137,7 +137,7 @@ QUuid Doorbird::liveVideoRequest() QUuid Doorbird::liveImageRequest() { - QNetworkRequest request(QString("http://%1/bha-api/light-on.cgi").arg(m_address.toString())); + QNetworkRequest request(QString("http://%1/bha-api/image.cgi").arg(m_address.toString())); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); @@ -145,15 +145,17 @@ QUuid Doorbird::liveImageRequest() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device" << reply->error() << reply->errorString(); + qCWarning(dcDoorBird) << "Error live image request" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird light on:"; - emit requestSent(requestId, true); - QImage* image = new QImage(); - image->loadFromData(reply->readAll()); + QByteArrayList tokens = reply->readAll().split('\n'); + QImage image = QImage::fromData(tokens.last()); + //image.save("testfile"); + emit liveImageReceived(image); + qCDebug(dcDoorBird) << "DoorBird live image request:"; + emit requestSent(requestId, true); }); return requestId; } @@ -172,11 +174,11 @@ QUuid Doorbird::historyImageRequest(int index) reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + qCWarning(dcDoorBird) << "Error history image request" << reply->error() << reply->errorString(); emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + qCDebug(dcDoorBird) << "DoorBird history image received:"; emit requestSent(requestId, true); }); return requestId; @@ -192,11 +194,11 @@ QUuid Doorbird::liveAudioReceive() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error DoorBird device"; + qCWarning(dcDoorBird) << "Error live audio receive"; emit requestSent(requestId, false); return; } - qCDebug(dcDoorBird) << "DoorBird unlatched:" << reply->error() << reply->errorString(); + qCDebug(dcDoorBird) << "DoorBird live audio receive"; emit requestSent(requestId, true); }); return requestId; diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 1f376345..75a8bb18 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -26,6 +26,7 @@ #include #include #include +#include class Doorbird : public QObject { @@ -92,6 +93,8 @@ signals: void eventReveiced(EventType eventType, bool status); void favoritesReceived(QList favourites); + void liveImageReceived(QImage image); + public slots: void onUdpBroadcast(const QByteArray &data); }; diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index c519c376..55b1f7e0 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -2,16 +2,12 @@ include(../plugins.pri) QT += network -LIBS += -lzbar - TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) SOURCES += \ deviceplugindoorbird.cpp \ doorbird.cpp \ - qrcodereader.cpp \ HEADERS += \ deviceplugindoorbird.h \ doorbird.h \ - qrcodereader.h \ diff --git a/doorbird/qrcodereader.cpp b/doorbird/qrcodereader.cpp index e16fa363..e69de29b 100644 --- a/doorbird/qrcodereader.cpp +++ b/doorbird/qrcodereader.cpp @@ -1,190 +0,0 @@ -/***************************************************************************** - * Copyright: 2013 Michael Zanetti * - * * - * This file is part of ubuntu-authenticator * - * * - * This prject is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This project is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ****************************************************************************/ -/* -#include "qrcodereader.h" - -#include -#include -#include -#include - -QRCodeReader::QRCodeReader(QObject *parent) : - QObject(parent) -{ - connect(&m_readerThread, &QThread::started, this, &QRCodeReader::scanningChanged); - connect(&m_readerThread, &QThread::finished, this, &QRCodeReader::scanningChanged); -} - -bool QRCodeReader::valid() const -{ - return !m_type.isEmpty() && !m_text.isEmpty(); -} - -QString QRCodeReader::type() const -{ - return m_type; -} - -QString QRCodeReader::text() const -{ - return m_text; -} - -QImage QRCodeReader::image() const -{ - return m_image; -} - -QRect QRCodeReader::scanRect() const -{ - return m_scanRect; -} - -void QRCodeReader::setScanRect(const QRect &rect) -{ - if (m_scanRect != rect) { - m_scanRect = rect; - emit scanRectChanged(); - } -} - -bool QRCodeReader::scanning() const -{ - return m_readerThread.isRunning(); -} - -void QRCodeReader::grab(QImage image) -{ - m_type.clear(); - m_text.clear(); - emit validChanged(); - - if (m_scanRect.isValid()) { - image = image.copy(m_scanRect); - } - - Reader *reader = new Reader; - reader->moveToThread(&m_readerThread); - connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater())); - connect(reader, SIGNAL(finished()), &m_readerThread, SLOT(quit())); - connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); - m_readerThread.start(); - - //QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, img), Q_ARG(bool, false)); -} - -void QRCodeReader::processImage(const QUrl &url) -{ - QImage image; - if (!image.load(url.path())) { - qWarning() << "can't open" << url.path(); - return; - } - - Reader *reader = new Reader; - reader->moveToThread(&m_readerThread); - connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater())); - connect(reader, SIGNAL(finished()), &m_readerThread, SLOT(quit())); - connect(reader, SIGNAL(resultReady(QString, QString, QImage)), this, SLOT(handleResults(QString, QString, QImage))); - m_readerThread.start(); - - QMetaObject::invokeMethod(reader, "doWork", Q_ARG(QImage, image), Q_ARG(bool, false)); -} - -void QRCodeReader::handleResults(const QString &type, const QString &text, const QImage &codeImage) -{ - m_type = type; - m_text = text; - m_image = codeImage; - m_imageUuid = QUuid::createUuid(); - emit validChanged(); -} - -void Reader::doWork(const QImage &image, bool invert) -{ - // Prepare image - QImage copy = image; - if (invert) { - copy.invertPixels(); - } - zbar::QZBarImage img(copy.convertToFormat(QImage::Format_RGB32)); - zbar::Image tmp = img.convert(*(long*)"Y800"); - - // create a reader - zbar::ImageScanner scanner; - - // configure the reader - scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1); - scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_POSITION, 1); - scanner.set_config(zbar::ZBAR_PARTIAL, zbar::ZBAR_CFG_ENABLE, 0); - - // scan the image for barcodes - int n = scanner.scan(tmp); -// qDebug() << "scanned. have" << n << "symbols"; - if (!invert && n == 0) { - // Nothing found... try again inverted - doWork(image, true); - return; - } - - img.set_symbols(tmp.get_symbols()); - - // extract results - for(zbar::Image::SymbolIterator symbol = img.symbol_begin(); symbol != img.symbol_end(); ++symbol) { - - QString typeName = QString::fromStdString(symbol->get_type_name()); - QString symbolString = QString::fromStdString(symbol->get_data()); - - int x0 = 999999; - int y0 = 999999; - int x1 = 0; - int y1 = 0; - - for (int i = 0; i < symbol->get_location_size(); ++i) { - int x = symbol->get_location_x(i); - int y = symbol->get_location_y(i); - qDebug() << "got point" << x << y; - if (x < x0) x0 = x; - if (y < y0) y0 = y; - if (x > x1) x1 = x; - if (y > y1) y1 = y; - } - - int width = x1 - x0; - int height = y1 - y0; - - // Workaround for zBar sometimes only giving us the first bar in a barcode. - if (width < 10) width = img.get_width() - x0; - if (height < 10) height = img.get_height() - y0; - - qDebug() << "extracting code image (" << x0 << y0 << ") ("<< x1 << y1 << ")"; - QImage codeImage = image.copy(x0, y0, width, height); - - qDebug() << "Code recognized:" << typeName << ", Text:" << symbolString; - - emit resultReady(typeName, symbolString, codeImage); - } - - tmp.set_data(NULL, 0); - img.set_data(NULL, 0); - - emit finished(); -} -*/ diff --git a/doorbird/qrcodereader.h b/doorbird/qrcodereader.h index 7b2edd15..e69de29b 100644 --- a/doorbird/qrcodereader.h +++ b/doorbird/qrcodereader.h @@ -1,88 +0,0 @@ -/***************************************************************************** - * Copyright: 2013 Michael Zanetti * - * * - * This file is part of tagger * - * * - * This prject is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This project is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ****************************************************************************/ -/* -#ifndef QRCODEREADER_H -#define QRCODEREADER_H - -#include -#include -#include -#include - -class QRCodeReader : public QObject -{ - Q_OBJECT - Q_PROPERTY(bool valid READ valid NOTIFY validChanged) - Q_PROPERTY(QString type READ type NOTIFY validChanged) - Q_PROPERTY(QString text READ text NOTIFY validChanged) - Q_PROPERTY(QImage image READ image NOTIFY validChanged) - Q_PROPERTY(QString imageSource READ imageSource NOTIFY validChanged) - Q_PROPERTY(QRect scanRect READ scanRect WRITE setScanRect NOTIFY scanRectChanged) - Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged) - -public: - explicit QRCodeReader(QObject *parent = nullptr); - - bool valid() const; - QString type() const; - QString text() const; - QImage image() const; - QString imageSource() const; - QRect scanRect() const; - void setScanRect(const QRect &rect); - bool scanning() const; - - -public slots: - void grab(QImage image); - void processImage(const QUrl &url); - -signals: - void validChanged(); - void scanRectChanged(); - void scanningChanged(); - -private slots: - void handleResults(const QString &type, const QString &text, const QImage &codeImage); - -private: - QString m_type; - QString m_text; - QImage m_image; - QUuid m_imageUuid; - QRect m_scanRect; - - QThread m_readerThread; -}; - -class Reader : public QObject -{ - Q_OBJECT - -public slots: - void doWork(const QImage &image, bool invert); - -signals: - void resultReady(const QString &type, const QString &text, const QImage &codeImage); - void finished(); -}; - -#endif // QRCODEREADER_H -*/ From 8dd4067d7095cb4386e636f9fd374221af5ca7e8 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 10 Feb 2020 18:23:38 +0500 Subject: [PATCH 24/35] removed QR code scanner and fixed last seen state --- doorbird/README.md | 8 ++ doorbird/deviceplugindoorbird.cpp | 77 ++++++------ doorbird/deviceplugindoorbird.h | 49 ++++---- doorbird/deviceplugindoorbird.json | 13 -- doorbird/doorbird.cpp | 50 ++++---- doorbird/doorbird.h | 49 ++++---- doorbird/qrcodereader.cpp | 0 doorbird/qrcodereader.h | 0 ...1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts | 112 +++++++++++++++++- ...68429-e312-4c82-9eab-e1cd996e43d6-en_US.ts | 4 - 10 files changed, 243 insertions(+), 119 deletions(-) delete mode 100644 doorbird/qrcodereader.cpp delete mode 100644 doorbird/qrcodereader.h delete mode 100644 doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts diff --git a/doorbird/README.md b/doorbird/README.md index e69de29b..bba30f16 100644 --- a/doorbird/README.md +++ b/doorbird/README.md @@ -0,0 +1,8 @@ +# DoorBird + +This plug-in let'S you capture doorbell pressed and motion events, +you are also able to set the power of the IR and the relays. + +It uses a local connection and will work without Internet. + + diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index e34eb03a..ef85a647 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -1,23 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Bernhard Trinnes * - * Copyright (C) 2019 Michael Zanetti * - * * - * This file is part of nymea. * - * * - * nymea is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, version 2 of the License. * - * * - * nymea is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with nymea. If not, see . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "deviceplugindoorbird.h" #include "plugininfo.h" @@ -48,7 +57,13 @@ void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) qCDebug(dcDoorBird) << "Found DoorBird device, name: " << serviceEntry.name() << "\n host address:" << serviceEntry.hostAddress().toString() << "\n text:" << serviceEntry.txt() << serviceEntry.protocol() << serviceEntry.serviceType(); DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); ParamList params; - QString macAddress = serviceEntry.txt().first(); + QString macAddress; + if (serviceEntry.txt().first().split("=").length() == 2) { + macAddress = serviceEntry.txt().first().split("=").last(); + } else { + qCWarning(dcDoorBird()) << "Could not parse MAC Address" << serviceEntry.txt().first(); + return; + } if (!myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).isEmpty()) { Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName()).first(); deviceDescriptor.setDeviceId(existingDevice->id()); @@ -116,7 +131,6 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); - connect(doorbird, &Doorbird::liveImageReceived, this, &DevicePluginDoorbird::onImageReceived); doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(device, doorbird); info->finish(Device::DeviceErrorNoError); @@ -201,9 +215,7 @@ void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool s break; case Doorbird::EventType::Motion: device->setStateValue(doorBirdIsPresentStateTypeId, status); - if (status) { - doorbird->liveImageRequest(); - } + device->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); break; case Doorbird::EventType::Doorbell: if (status) { @@ -226,18 +238,3 @@ void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); } - -void DevicePluginDoorbird::onImageReceived(QImage image) -{ - Q_UNUSED(image); - Doorbird *doorbird = static_cast(sender()); - Device *device = m_doorbirdConnections.key(doorbird); - if (!device) - return; - //TODO add QR code detection - Event event; - event.setDeviceId(device->id()); - event.setEventTypeId(doorBirdQrCodeDetectedEventTypeId); - event.setParams(ParamList() << Param(doorBirdQrCodeDetectedEventDataParamTypeId, "image received")); - emit emitEvent(event); -} diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h index 9be0a4ee..9c7ebd7f 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/deviceplugindoorbird.h @@ -1,22 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Michael Zanetti * - * * - * This file is part of nymea. * - * * - * nymea is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, version 2 of the License. * - * * - * nymea is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with nymea. If not, see . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef DEVICEPLUGINDOORBIRD_H #define DEVICEPLUGINDOORBIRD_H @@ -58,7 +68,6 @@ private slots: void onDoorBirdConnected(bool status); void onDoorBirdEvent(Doorbird::EventType eventType, bool status); void onDoorBirdRequestSent(QUuid requestId, bool success); - void onImageReceived(QImage image); }; #endif // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index 6e0057c7..caa78c48 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -91,19 +91,6 @@ "id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b", "name": "doorbellPressed", "displayName": "Doorbell pressed" - }, - { - "id": "224f1be5-17d3-460d-9bb8-a25fcdf28936", - "name": "qrCodeDetected", - "displayName": "QR code detected", - "paramTypes": [ - { - "id": "c53068b9-604e-4dfc-8d23-26e8bab0233c", - "name": "data", - "displayName": "Data", - "type": "QString" - } - ] } ] } diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 4fb99b79..0d8b591f 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -1,23 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Bernhard Trinnes * - * Copyright (C) 2019 Michael Zanetti * - * * - * This file is part of nymea. * - * * - * nymea is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, version 2 of the License. * - * * - * nymea is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with nymea. If not, see . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "doorbird.h" #include "extern-plugininfo.h" @@ -206,7 +215,6 @@ QUuid Doorbird::liveAudioReceive() QUuid Doorbird::liveAudioTransmit() { - return QUuid::createUuid(); } diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 75a8bb18..da95ab56 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -1,23 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Bernhard Trinnes * - * Copyright (C) 2019 Michael Zanetti * - * * - * This file is part of nymea. * - * * - * nymea is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, version 2 of the License. * - * * - * nymea is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with nymea. If not, see . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef DOORBIRD_H #define DOORBIRD_H diff --git a/doorbird/qrcodereader.cpp b/doorbird/qrcodereader.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/doorbird/qrcodereader.h b/doorbird/qrcodereader.h deleted file mode 100644 index e69de29b..00000000 diff --git a/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts index f7f66d85..3dbea2ab 100644 --- a/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts +++ b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts @@ -1,4 +1,114 @@ - \ No newline at end of file + + DevicePluginDoorbird + + + Please enter the user credentials + + + + + doorBird + + + + Connected + The name of the ParamType (DeviceClass: doorBird, EventType: connected, ID: {186c270b-923c-46e4-a7da-33e45427cdbb}) +---------- +The name of the StateType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird + + + + + Connected changed + The name of the EventType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird + + + + + + + DoorBird + The name of the DeviceClass ({0485eb61-2a22-42ba-9dd2-a5961485bf08}) +---------- +The name of the vendor ({2da07435-571e-4956-a387-6caa51d6e845}) +---------- +The name of the plugin doorBird ({6fe1614a-fc47-4eb2-a47c-13c50f1798ee}) + + + + + Doorbell pressed + The name of the EventType ({9bc89937-a2ab-4e8e-af0e-a9ba41caa89b}) of DeviceClass doorBird + + + + + IP address + The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {8873b17d-526e-408d-95d8-6439b501f489}) + + + + + Light on + The name of the ActionType ({3a6cfc5d-804c-4d21-91b5-999913d4f1a5}) of DeviceClass doorBird + + + + + + Motion sensor last seen time + The name of the ParamType (DeviceClass: doorBird, EventType: lastSeenTime, ID: {295c9700-b598-4681-898f-d63e2889cedf}) +---------- +The name of the StateType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird + + + + + Motion sensor last seen time changedd + The name of the EventType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird + + + + + + Motion sensor presence + The name of the ParamType (DeviceClass: doorBird, EventType: isPresent, ID: {0f5eb200-6c0d-45c5-9156-3060fd66d332}) +---------- +The name of the StateType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird + + + + + Motion sensor presence detected + The name of the EventType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird + + + + + Open door + The name of the ActionType ({b6c3377b-91de-411a-9d48-8b509c39d67c}) of DeviceClass doorBird + + + + + Relay number + The name of the ParamType (DeviceClass: doorBird, ActionType: openDoor, ID: {95dd35d7-0bc3-49e1-af96-d8da8ea5244d}) + + + + + Restart + The name of the ActionType ({e874242e-5acb-4d98-94c7-0a70db65150c}) of DeviceClass doorBird + + + + + Serial number + The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {67ea5534-330a-4291-93b5-0237034e15fa}) + + + + diff --git a/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts b/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts deleted file mode 100644 index f7f66d85..00000000 --- a/doorbird/translations/b7368429-e312-4c82-9eab-e1cd996e43d6-en_US.ts +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From d19fb44c1415e7ae9ecc3d3fc5a6e713919907c7 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 10 Feb 2020 18:28:46 +0500 Subject: [PATCH 25/35] added debian packaging for doorbird --- debian/control | 16 ++++++++++++++++ debian/nymea-plugin-doorbird.install.in | 1 + doorbird/deviceplugindoorbird.json | 1 + 3 files changed, 18 insertions(+) create mode 100644 debian/nymea-plugin-doorbird.install.in diff --git a/debian/control b/debian/control index 6efef76e..d0b530df 100644 --- a/debian/control +++ b/debian/control @@ -200,6 +200,21 @@ Description: nymea.io plugin for denon This package will install the nymea.io plugin for denon +Package: nymea-plugin-doorbird +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-translations, +Description: nymea.io plugin for DoorBird + 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. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install the nymea.io plugin for DoorBird + + Package: nymea-plugin-dweetio Architecture: any Depends: ${shlibs:Depends}, @@ -965,6 +980,7 @@ Depends: nymea-plugin-anel, nymea-plugin-datetime, nymea-plugin-daylightsensor, nymea-plugin-denon, + nymea-plugin-doorbird, nymea-plugin-eq-3, nymea-plugin-flowercare, nymea-plugin-kodi, diff --git a/debian/nymea-plugin-doorbird.install.in b/debian/nymea-plugin-doorbird.install.in new file mode 100644 index 00000000..3a517946 --- /dev/null +++ b/debian/nymea-plugin-doorbird.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_deviceplugindoorbird.so diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/deviceplugindoorbird.json index caa78c48..a5e348f7 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/deviceplugindoorbird.json @@ -66,6 +66,7 @@ "displayName": "Connected", "displayNameEvent": "Connected changed", "type": "bool", + "cached": false, "defaultValue": false }, { From 2be130884ef2d24e6cc20d3ef773be64d8ec2466 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 10 Feb 2020 18:38:41 +0500 Subject: [PATCH 26/35] fixed rediscovery --- doorbird/deviceplugindoorbird.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index ef85a647..89fec001 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -58,14 +58,19 @@ void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); ParamList params; QString macAddress; + if (serviceEntry.txt().length() == 0) { + qCWarning(dcDoorBird()) << "Discovery failed, service entry missing"; + continue; + } + if (serviceEntry.txt().first().split("=").length() == 2) { macAddress = serviceEntry.txt().first().split("=").last(); } else { qCWarning(dcDoorBird()) << "Could not parse MAC Address" << serviceEntry.txt().first(); - return; + continue; } if (!myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).isEmpty()) { - Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName()).first(); + Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).first(); deviceDescriptor.setDeviceId(existingDevice->id()); } params.append(Param(doorBirdDeviceSerialnumberParamTypeId, macAddress)); @@ -86,7 +91,6 @@ void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) void DevicePluginDoorbird::startPairing(DevicePairingInfo *info) { - if (info->deviceClassId() == doorBirdDeviceClassId) { qCDebug(dcDoorBird()) << "User and password. Login is \"user\" and \"password\"."; info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter the user credentials")); From 64631790b4009d7fb467e2626f693a666218a223 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 10 Feb 2020 21:57:53 +0500 Subject: [PATCH 27/35] added async device setup --- doorbird/deviceplugindoorbird.cpp | 127 +++++++++++++++++++++--------- doorbird/deviceplugindoorbird.h | 6 +- doorbird/doorbird.cpp | 37 ++++++--- doorbird/doorbird.h | 11 +-- 4 files changed, 128 insertions(+), 53 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 89fec001..aea749c6 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -83,38 +83,57 @@ void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) info->finish(Device::DeviceErrorNoError); }); return; + } else { + qCWarning(dcDoorBird()) << "Cannot discover for deviceClassId" << info->deviceClassId(); + info->finish(Device::DeviceErrorDeviceNotFound); } - qCWarning(dcDoorBird()) << "Cannot discover for deviceClassId" << info->deviceClassId(); - info->finish(Device::DeviceErrorDeviceNotFound); } void DevicePluginDoorbird::startPairing(DevicePairingInfo *info) { if (info->deviceClassId() == doorBirdDeviceClassId) { - qCDebug(dcDoorBird()) << "User and password. Login is \"user\" and \"password\"."; - info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter the user credentials")); + info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter username and password for the DoorBird device")); return; + } else { + qCWarning(dcDoorBird()) << "StartPairing unhandled deviceClassId" << info->deviceClassId(); + info->finish(Device::DeviceErrorCreationMethodNotSupported); } - info->finish(Device::DeviceErrorCreationMethodNotSupported); } -void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) +void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &password) { if (info->deviceClassId() == doorBirdDeviceClassId) { - qCDebug(dcDoorBird()) << "confirm pairing called"; + QHostAddress address = QHostAddress(info->params().paramValue(doorBirdDeviceAddressParamTypeId).toString()); + + Doorbird *doorbird = new Doorbird(address, this); + connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); + connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); + connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); + connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); + doorbird->initConnection(username, password); + doorbird->connectToEventMonitor(); + m_doorbirdConnections.insert(info->deviceId(), doorbird); + m_pendingPairings.insert(doorbird, info); + doorbird->getSession(); + connect(info, &DevicePairingInfo::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->deviceId()); + }); pluginStorage()->beginGroup(info->deviceId().toString()); pluginStorage()->setValue("username", username); - pluginStorage()->setValue("password", secret); + pluginStorage()->setValue("password", password); pluginStorage()->endGroup(); - - info->finish(Device::DeviceErrorNoError); - return; + } else { + qCWarning(dcDoorBird()) << "Confirm pairing DeviceClassNotFound" << info->deviceClassId(); + info->finish(Device::DeviceErrorDeviceClassNotFound); } - info->finish(Device::DeviceErrorDeviceClassNotFound); - return; } @@ -125,30 +144,45 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) if (device->deviceClassId() == doorBirdDeviceClassId) { QHostAddress address = QHostAddress(device->paramValue(doorBirdDeviceAddressParamTypeId).toString()); - pluginStorage()->beginGroup(device->id().toString()); - QString username = pluginStorage()->value("username").toString(); - QString password = pluginStorage()->value("password").toString(); - pluginStorage()->endGroup(); + if (m_doorbirdConnections.contains(device->id())) { + info->finish(Device::DeviceErrorNoError); + } else { + pluginStorage()->beginGroup(device->id().toString()); + QString username = pluginStorage()->value("username").toString(); + QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); - qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; - Doorbird *doorbird = new Doorbird(address, username, password, this); - connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); - connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); - connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); - doorbird->connectToEventMonitor(); - m_doorbirdConnections.insert(device, doorbird); - info->finish(Device::DeviceErrorNoError); - return; + qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; + Doorbird *doorbird = new Doorbird(address, this); + connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); + connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); + connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); + connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); + doorbird->initConnection(username, password); + doorbird->connectToEventMonitor(); + m_doorbirdConnections.insert(device->id(), doorbird); + m_pendingDeviceSetups.insert(doorbird, info); + doorbird->getSession(); + connect(info, &DeviceSetupInfo::aborted, this, [device, doorbird, this] { + if (!doorbird) { + doorbird->deleteLater(); + } + m_doorbirdConnections.remove(device->id()); + m_pendingPairings.remove(doorbird); + }); + } + } else { + qCWarning(dcDoorBird()) << "Unhandled device class" << info->device()->deviceClass(); + info->finish(Device::DeviceErrorDeviceClassNotFound); } - qCWarning(dcDoorBird()) << "Unhandled device class" << info->device()->deviceClass(); - info->finish(Device::DeviceErrorDeviceClassNotFound); } void DevicePluginDoorbird::postSetupDevice(Device *device) { if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbird = m_doorbirdConnections.value(device); + device->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection in the deviceSetup + Doorbird *doorbird = m_doorbirdConnections.value(device->id()); doorbird->infoRequest(); doorbird->listFavorites(); doorbird->listSchedules(); @@ -162,7 +196,7 @@ void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) Action action = info->action(); if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbird = m_doorbirdConnections.value(device); + Doorbird *doorbird = m_doorbirdConnections.value(device->id()); if (!doorbird) { info->finish(Device::DeviceErrorHardwareFailure); return; @@ -180,17 +214,20 @@ void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) doorbird->restart(); info->finish(Device::DeviceErrorNoError); return; + } else { + qCWarning(dcDoorBird()) << "Unhandled ActionTypeId:" << action.actionTypeId(); + info->finish(Device::DeviceErrorActionTypeNotFound); } - info->finish(Device::DeviceErrorActionTypeNotFound); - return; + } else { + qCWarning(dcDoorBird()) << "Execute action, unhandled device class" << device->deviceClass(); + info->finish(Device::DeviceErrorDeviceClassNotFound); } - info->finish(Device::DeviceErrorDeviceClassNotFound); } void DevicePluginDoorbird::deviceRemoved(Device *device) { if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbirdConnection = m_doorbirdConnections.take(device); + Doorbird *doorbirdConnection = m_doorbirdConnections.take(device->id()); doorbirdConnection->deleteLater(); } } @@ -198,7 +235,7 @@ void DevicePluginDoorbird::deviceRemoved(Device *device) void DevicePluginDoorbird::onDoorBirdConnected(bool status) { Doorbird *doorbird = static_cast(sender()); - Device *device = m_doorbirdConnections.key(doorbird); + Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); if (!device) return; @@ -208,7 +245,7 @@ void DevicePluginDoorbird::onDoorBirdConnected(bool status) void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool status) { Doorbird *doorbird = static_cast(sender()); - Device *device = m_doorbirdConnections.key(doorbird); + Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); if (!device) return; @@ -232,7 +269,7 @@ void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool s void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) { Doorbird *doorbird = static_cast(sender()); - Device *device = m_doorbirdConnections.key(doorbird); + Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); if (!device) return; @@ -242,3 +279,19 @@ void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); } + +void DevicePluginDoorbird::onSessionIdReceived(const QString &sessionId) +{ + Q_UNUSED(sessionId); + Doorbird *doorbird = static_cast(sender()); + + if (m_pendingPairings.contains(doorbird)) { + DevicePairingInfo *info = m_pendingPairings.take(doorbird); + info->finish(Device::DeviceErrorNoError); + } + + if (m_pendingDeviceSetups.contains(doorbird)) { + DeviceSetupInfo *info = m_pendingDeviceSetups.take(doorbird); + info->finish(Device::DeviceErrorNoError); + } +} diff --git a/doorbird/deviceplugindoorbird.h b/doorbird/deviceplugindoorbird.h index 9c7ebd7f..594decb0 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/deviceplugindoorbird.h @@ -61,13 +61,17 @@ public: void deviceRemoved(Device *device)override; private: - QHash m_doorbirdConnections; + QHash m_doorbirdConnections; + QHash m_pendingPairings; + QHash m_pendingDeviceSetups; + QHash m_asyncActions; 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 // DEVICEPLUGINDOORBIRD_H diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 0d8b591f..2db09e10 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -41,18 +41,30 @@ #include #include -Doorbird::Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent) : +Doorbird::Doorbird(const QHostAddress &address, QObject *parent) : QObject(parent), - m_address(address), - m_username(username), - m_password(password) + m_address(address) +{ + +} + +QHostAddress Doorbird::address() +{ + return m_address; +} + +void Doorbird::setAddress(const QHostAddress &address) +{ + m_address = address; +} + +void Doorbird::initConnection(const QString &username, const QString &password) { m_networkAccessManager = new QNetworkAccessManager(this); - connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *authenticator) { + connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [username, password, this](QNetworkReply *reply, QAuthenticator *authenticator) { Q_UNUSED(reply); - qCDebug(dcDoorBird) << "Credentials requested:"; - authenticator->setUser(m_username); - authenticator->setPassword(m_password); + authenticator->setUser(username); + authenticator->setPassword(password); }); } @@ -67,12 +79,11 @@ QUuid Doorbird::getSession() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error unlatching DoorBird device"; + qCWarning(dcDoorBird) << "Error DoorBird device"; emit requestSent(requestId, false); return; } emit requestSent(requestId, true); - QByteArray data = reply->readAll(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); @@ -80,6 +91,12 @@ QUuid Doorbird::getSession() qCWarning(dcDoorBird()) << "Error parsing json:" << data; return; } + QVariantMap map = jsonDoc.toVariant().toMap().value("BHA").toMap(); + if (map.contains("SESSIONID")) { + QString sessionId = map.value("SESSIONID").toString(); + qCDebug(dcDoorBird) << "Got sessionId" << sessionId; + emit sessionIdReceived(sessionId); + } }); return requestId; } diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index da95ab56..163e9516 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -41,7 +41,7 @@ class Doorbird : public QObject { Q_OBJECT public: - explicit Doorbird(const QHostAddress &address, const QString &username, const QString &password, QObject *parent = nullptr); + explicit Doorbird(const QHostAddress &address, QObject *parent = nullptr); enum EventType { Doorbell, @@ -61,6 +61,9 @@ public: int id; }; + QHostAddress address(); + void setAddress(const QHostAddress &address); + void initConnection(const QString &username, const QString &password); QUuid getSession(); QUuid openDoor(int value); QUuid lightOn(); @@ -90,10 +93,7 @@ private: QHostAddress m_address; QList m_networkRequests; - QString m_username; - QString m_password; - - QByteArray sessionId; + //QByteArray sessionId; signals: void deviceConnected(bool status); @@ -102,6 +102,7 @@ signals: void eventReveiced(EventType eventType, bool status); void favoritesReceived(QList favourites); + void sessionIdReceived(const QString &sessionId); void liveImageReceived(QImage image); public slots: From 48d30baab13fecbbe9deb36017d0d305d9f77531 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Fri, 14 Feb 2020 15:53:30 +0500 Subject: [PATCH 28/35] added authentication failure handling --- doorbird/deviceplugindoorbird.cpp | 23 ++++++++++++++--------- doorbird/doorbird.cpp | 18 +++++++++++++----- doorbird/doorbird.h | 2 ++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index aea749c6..6e8fe3d4 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -113,7 +113,6 @@ void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); doorbird->initConnection(username, password); - doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(info->deviceId(), doorbird); m_pendingPairings.insert(doorbird, info); doorbird->getSession(); @@ -159,7 +158,6 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); doorbird->initConnection(username, password); - doorbird->connectToEventMonitor(); m_doorbirdConnections.insert(device->id(), doorbird); m_pendingDeviceSetups.insert(doorbird, info); doorbird->getSession(); @@ -183,6 +181,7 @@ void DevicePluginDoorbird::postSetupDevice(Device *device) if (device->deviceClassId() == doorBirdDeviceClassId) { device->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection in the deviceSetup Doorbird *doorbird = m_doorbirdConnections.value(device->id()); + doorbird->connectToEventMonitor(); doorbird->infoRequest(); doorbird->listFavorites(); doorbird->listSchedules(); @@ -269,15 +268,21 @@ void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool s void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) { Doorbird *doorbird = static_cast(sender()); - Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); - if (!device) - return; - if (!m_asyncActions.contains(requestId)) - return; + if (m_asyncActions.contains(requestId)) { + DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); + actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); + } - DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); - actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); + if (m_pendingPairings.contains(doorbird) && !success) { + DevicePairingInfo *info = m_pendingPairings.take(doorbird); + info->finish(Device::DeviceErrorAuthenticationFailure, tr("Wrong username or password")); + } + + if (m_pendingDeviceSetups.contains(doorbird) && !success) { + DeviceSetupInfo *info = m_pendingDeviceSetups.take(doorbird); + info->finish(Device::DeviceErrorAuthenticationFailure, tr("Wrong username or password")); + } } void DevicePluginDoorbird::onSessionIdReceived(const QString &sessionId) diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 2db09e10..2b399619 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -61,10 +61,17 @@ void Doorbird::setAddress(const QHostAddress &address) void Doorbird::initConnection(const QString &username, const QString &password) { m_networkAccessManager = new QNetworkAccessManager(this); - connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [username, password, this](QNetworkReply *reply, QAuthenticator *authenticator) { - Q_UNUSED(reply); - authenticator->setUser(username); - authenticator->setPassword(password); + connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [username, password, this] (QNetworkReply *reply, QAuthenticator *authenticator) { + + qCWarning(dcDoorBird()) << "Authenticator" << reply->errorString() << reply->error(); + if (m_pendingAuthentications.contains(reply)) { + m_pendingAuthentications.removeOne(reply); + reply->abort(); + } else { + authenticator->setUser(username); + authenticator->setPassword(password); + m_pendingAuthentications.append(reply); + } }); } @@ -79,7 +86,7 @@ QUuid Doorbird::getSession() reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error DoorBird device"; + qCWarning(dcDoorBird) << "Error DoorBird device:" << reply->errorString(); emit requestSent(requestId, false); return; } @@ -98,6 +105,7 @@ QUuid Doorbird::getSession() emit sessionIdReceived(sessionId); } }); + return requestId; } diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 163e9516..0d519412 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -93,6 +93,8 @@ private: QHostAddress m_address; QList m_networkRequests; + QList m_pendingAuthentications; + //QByteArray sessionId; signals: From 80e08f567584a93b101f9a5531e8f6a13936b07d Mon Sep 17 00:00:00 2001 From: Boernsman Date: Fri, 14 Feb 2020 22:36:00 +0500 Subject: [PATCH 29/35] authentication now with url credentials --- doorbird/deviceplugindoorbird.cpp | 10 ++++------ doorbird/doorbird.cpp | 32 +++++++++++-------------------- doorbird/doorbird.h | 15 +++++++++------ 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 6e8fe3d4..5bccd648 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -107,15 +107,14 @@ void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString if (info->deviceClassId() == doorBirdDeviceClassId) { QHostAddress address = QHostAddress(info->params().paramValue(doorBirdDeviceAddressParamTypeId).toString()); - Doorbird *doorbird = new Doorbird(address, this); + Doorbird *doorbird = new Doorbird(hardwareManager()->networkManager(), address, this); connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); - doorbird->initConnection(username, password); m_doorbirdConnections.insert(info->deviceId(), doorbird); m_pendingPairings.insert(doorbird, info); - doorbird->getSession(); + doorbird->getSession(username, password); connect(info, &DevicePairingInfo::aborted, this, [this, info]{ if (m_pendingPairings.values().contains(info)) { Doorbird *doorbird = m_pendingPairings.key(info); @@ -152,15 +151,14 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) pluginStorage()->endGroup(); qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; - Doorbird *doorbird = new Doorbird(address, this); + Doorbird *doorbird = new Doorbird(hardwareManager()->networkManager(), address, this); connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); - doorbird->initConnection(username, password); m_doorbirdConnections.insert(device->id(), doorbird); m_pendingDeviceSetups.insert(doorbird, info); - doorbird->getSession(); + doorbird->getSession(username, password); connect(info, &DeviceSetupInfo::aborted, this, [device, doorbird, this] { if (!doorbird) { doorbird->deleteLater(); diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index 2b399619..a6074acb 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -41,9 +41,10 @@ #include #include -Doorbird::Doorbird(const QHostAddress &address, QObject *parent) : +Doorbird::Doorbird(NetworkAccessManager *networkAccessManager, const QHostAddress &address, QObject *parent) : QObject(parent), - m_address(address) + m_address(address), + m_networkAccessManager(networkAccessManager) { } @@ -58,26 +59,15 @@ void Doorbird::setAddress(const QHostAddress &address) m_address = address; } -void Doorbird::initConnection(const QString &username, const QString &password) +QUuid Doorbird::getSession(const QString &username, const QString &password) { - m_networkAccessManager = new QNetworkAccessManager(this); - connect(m_networkAccessManager, &QNetworkAccessManager::authenticationRequired, this, [username, password, this] (QNetworkReply *reply, QAuthenticator *authenticator) { - - qCWarning(dcDoorBird()) << "Authenticator" << reply->errorString() << reply->error(); - if (m_pendingAuthentications.contains(reply)) { - m_pendingAuthentications.removeOne(reply); - reply->abort(); - } else { - authenticator->setUser(username); - authenticator->setPassword(password); - m_pendingAuthentications.append(reply); - } - }); -} - -QUuid Doorbird::getSession() -{ - QNetworkRequest request(QString("http://%1/bha-api/getsession.cgi").arg(m_address.toString())); + QUrl url; + url.setHost(m_address.toString()); + url.setScheme("http"); + url.setPath("/bha-api/getsession.cgi"); + url.setUserName(username); + url.setPassword(password); + QNetworkRequest request(url); qCDebug(dcDoorBird) << "Sending request:" << request.url(); QNetworkReply *reply = m_networkAccessManager->get(request); QUuid requestId = QUuid::createUuid(); diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 0d519412..7f26da9b 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -37,11 +37,13 @@ #include #include +#include "network/networkaccessmanager.h" + class Doorbird : public QObject { Q_OBJECT public: - explicit Doorbird(const QHostAddress &address, QObject *parent = nullptr); + explicit Doorbird(NetworkAccessManager *networkAccessManager, const QHostAddress &address, QObject *parent = nullptr); enum EventType { Doorbell, @@ -63,8 +65,7 @@ public: QHostAddress address(); void setAddress(const QHostAddress &address); - void initConnection(const QString &username, const QString &password); - QUuid getSession(); + QUuid getSession(const QString &username, const QString &password); QUuid openDoor(int value); QUuid lightOn(); QUuid liveVideoRequest(); @@ -87,15 +88,17 @@ public: void connectToEventMonitor(); private: - QNetworkAccessManager *m_networkAccessManager; + QHostAddress m_address; + NetworkAccessManager *m_networkAccessManager; QByteArray m_readBuffer; - QHostAddress m_address; + QList m_networkRequests; QList m_pendingAuthentications; - //QByteArray sessionId; + QString m_username; + QString m_password; signals: void deviceConnected(bool status); From 1dd0cdb6e6db742f3d5202bdcd44f8edb49d313b Mon Sep 17 00:00:00 2001 From: Boernsman Date: Fri, 14 Feb 2020 22:48:46 +0500 Subject: [PATCH 30/35] fixed connection time-out --- doorbird/deviceplugindoorbird.cpp | 8 +++++--- doorbird/doorbird.cpp | 7 +++---- doorbird/doorbird.h | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 5bccd648..3668bc5c 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -107,7 +107,7 @@ void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString if (info->deviceClassId() == doorBirdDeviceClassId) { QHostAddress address = QHostAddress(info->params().paramValue(doorBirdDeviceAddressParamTypeId).toString()); - Doorbird *doorbird = new Doorbird(hardwareManager()->networkManager(), address, this); + Doorbird *doorbird = new Doorbird(address, this); connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); @@ -151,7 +151,7 @@ void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) pluginStorage()->endGroup(); qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; - Doorbird *doorbird = new Doorbird(hardwareManager()->networkManager(), address, this); + Doorbird *doorbird = new Doorbird(address, this); connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); @@ -253,7 +253,9 @@ void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool s break; case Doorbird::EventType::Motion: device->setStateValue(doorBirdIsPresentStateTypeId, status); - device->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); + if (status) { + device->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); + } break; case Doorbird::EventType::Doorbell: if (status) { diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index a6074acb..a8bda046 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -41,12 +41,11 @@ #include #include -Doorbird::Doorbird(NetworkAccessManager *networkAccessManager, const QHostAddress &address, QObject *parent) : +Doorbird::Doorbird(const QHostAddress &address, QObject *parent) : QObject(parent), - m_address(address), - m_networkAccessManager(networkAccessManager) + m_address(address) { - + m_networkAccessManager = new QNetworkAccessManager(this); } QHostAddress Doorbird::address() diff --git a/doorbird/doorbird.h b/doorbird/doorbird.h index 7f26da9b..a09bb788 100644 --- a/doorbird/doorbird.h +++ b/doorbird/doorbird.h @@ -43,7 +43,7 @@ class Doorbird : public QObject { Q_OBJECT public: - explicit Doorbird(NetworkAccessManager *networkAccessManager, const QHostAddress &address, QObject *parent = nullptr); + explicit Doorbird(const QHostAddress &address, QObject *parent = nullptr); enum EventType { Doorbell, @@ -89,7 +89,7 @@ public: void connectToEventMonitor(); private: QHostAddress m_address; - NetworkAccessManager *m_networkAccessManager; + QNetworkAccessManager *m_networkAccessManager; QByteArray m_readBuffer; From 0e1aab8a61dd3ac31bfa4d8a719394f6764fa638 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Mon, 17 Feb 2020 11:04:39 +0500 Subject: [PATCH 31/35] made actions async --- doorbird/README.md | 12 +++++++++--- doorbird/deviceplugindoorbird.cpp | 16 ++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/doorbird/README.md b/doorbird/README.md index bba30f16..ab687dc5 100644 --- a/doorbird/README.md +++ b/doorbird/README.md @@ -1,8 +1,14 @@ # DoorBird -This plug-in let'S you capture doorbell pressed and motion events, -you are also able to set the power of the IR and the relays. +This plugin integrates DoorBird video doorbells into nymea. All the communication between nymea and the DoorBird device happens locally and will work without internet connection. -It uses a local connection and will work without Internet. +Currently supported features are: +* Doorbell presses +* Motion events +* Enable/disable IR light +* Switching door relays + +The user must have the permission to act as DoorBird API-operator. +You can check the permissions in the DoorBird app. diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp index 3668bc5c..70a7c42a 100644 --- a/doorbird/deviceplugindoorbird.cpp +++ b/doorbird/deviceplugindoorbird.cpp @@ -195,21 +195,25 @@ void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) if (device->deviceClassId() == doorBirdDeviceClassId) { Doorbird *doorbird = m_doorbirdConnections.value(device->id()); if (!doorbird) { + qCWarning(dcDoorBird()) << "Doorbird object not found" << device->name(); info->finish(Device::DeviceErrorHardwareFailure); return; } if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { int number = action.param(doorBirdOpenDoorActionNumberParamTypeId).value().toInt(); - doorbird->openDoor(number); - info->finish(Device::DeviceErrorNoError); + QUuid requestId = doorbird->openDoor(number); + m_asyncActions.insert(requestId, info); + connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); return; } else if (action.actionTypeId() == doorBirdLightOnActionTypeId) { - doorbird->lightOn(); - info->finish(Device::DeviceErrorNoError); + QUuid requestId = doorbird->lightOn(); + m_asyncActions.insert(requestId, info); + connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); return; } else if (action.actionTypeId() == doorBirdRestartActionTypeId) { - doorbird->restart(); - info->finish(Device::DeviceErrorNoError); + QUuid requestId = doorbird->restart(); + m_asyncActions.insert(requestId, info); + connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); return; } else { qCWarning(dcDoorBird()) << "Unhandled ActionTypeId:" << action.actionTypeId(); From f8da4797841812c7d2609de94340a7b1b6faaf51 Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Fri, 20 Mar 2020 08:16:10 +0100 Subject: [PATCH 32/35] devices to things --- doorbird/README.md | 26 +- doorbird/deviceplugindoorbird.cpp | 306 ------------------ doorbird/doorbird.cpp | 2 +- doorbird/doorbird.pro | 6 +- doorbird/integrationplugindoorbird.cpp | 306 ++++++++++++++++++ ...doorbird.h => integrationplugindoorbird.h} | 39 ++- ...rd.json => integrationplugindoorbird.json} | 2 +- 7 files changed, 349 insertions(+), 338 deletions(-) delete mode 100644 doorbird/deviceplugindoorbird.cpp create mode 100644 doorbird/integrationplugindoorbird.cpp rename doorbird/{deviceplugindoorbird.h => integrationplugindoorbird.h} (64%) rename doorbird/{deviceplugindoorbird.json => integrationplugindoorbird.json} (99%) diff --git a/doorbird/README.md b/doorbird/README.md index ab687dc5..05134aea 100644 --- a/doorbird/README.md +++ b/doorbird/README.md @@ -2,13 +2,25 @@ This plugin integrates DoorBird video doorbells into nymea. All the communication between nymea and the DoorBird device happens locally and will work without internet connection. -Currently supported features are: +## Supported Things -* Doorbell presses -* Motion events -* Enable/disable IR light -* Switching door relays +* All video doorbells + * Auto discovery + * Doorbell presses + * Motion events + * Enable/disable IR light + * Switching door relays + * No internet connection required -The user must have the permission to act as DoorBird API-operator. -You can check the permissions in the DoorBird app. +## Requirements +* The DoorBird device must be in the same local area network as nymea. +* The router must not block avahi/zeroconf multicast messages. +* TCP Sockets on port 80 must not be blocked by the router. +* The user must have the permission to act as DoorBird API-operator. + * You can check the permissions in the DoorBird app. +* The package "nymea-plugin-doorbird" must be installed. + +## More + +https://www.doorbird.com/ diff --git a/doorbird/deviceplugindoorbird.cpp b/doorbird/deviceplugindoorbird.cpp deleted file mode 100644 index 70a7c42a..00000000 --- a/doorbird/deviceplugindoorbird.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "deviceplugindoorbird.h" -#include "plugininfo.h" - -#include "platform/platformzeroconfcontroller.h" -#include "network/zeroconf/zeroconfservicebrowser.h" -#include "network/zeroconf/zeroconfserviceentry.h" - -#include -#include -#include -#include - -DevicePluginDoorbird::DevicePluginDoorbird() -{ -} - - -void DevicePluginDoorbird::discoverDevices(DeviceDiscoveryInfo *info) -{ - if (info->deviceClassId() == doorBirdDeviceClassId) { - 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()) { - if (serviceEntry.hostName().startsWith("bha-")) { - qCDebug(dcDoorBird) << "Found DoorBird device, name: " << serviceEntry.name() << "\n host address:" << serviceEntry.hostAddress().toString() << "\n text:" << serviceEntry.txt() << serviceEntry.protocol() << serviceEntry.serviceType(); - DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); - ParamList params; - QString macAddress; - if (serviceEntry.txt().length() == 0) { - qCWarning(dcDoorBird()) << "Discovery failed, service entry missing"; - continue; - } - - if (serviceEntry.txt().first().split("=").length() == 2) { - macAddress = serviceEntry.txt().first().split("=").last(); - } else { - qCWarning(dcDoorBird()) << "Could not parse MAC Address" << serviceEntry.txt().first(); - continue; - } - if (!myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).isEmpty()) { - Device *existingDevice = myDevices().filterByParam(doorBirdDeviceSerialnumberParamTypeId, macAddress).first(); - deviceDescriptor.setDeviceId(existingDevice->id()); - } - params.append(Param(doorBirdDeviceSerialnumberParamTypeId, macAddress)); - params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); - deviceDescriptor.setParams(params); - info->addDeviceDescriptor(deviceDescriptor); - } - } - serviceBrowser->deleteLater(); - info->finish(Device::DeviceErrorNoError); - }); - return; - } else { - qCWarning(dcDoorBird()) << "Cannot discover for deviceClassId" << info->deviceClassId(); - info->finish(Device::DeviceErrorDeviceNotFound); - } -} - - -void DevicePluginDoorbird::startPairing(DevicePairingInfo *info) -{ - if (info->deviceClassId() == doorBirdDeviceClassId) { - info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter username and password for the DoorBird device")); - return; - } else { - qCWarning(dcDoorBird()) << "StartPairing unhandled deviceClassId" << info->deviceClassId(); - info->finish(Device::DeviceErrorCreationMethodNotSupported); - } -} - - -void DevicePluginDoorbird::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &password) -{ - if (info->deviceClassId() == doorBirdDeviceClassId) { - QHostAddress address = QHostAddress(info->params().paramValue(doorBirdDeviceAddressParamTypeId).toString()); - - Doorbird *doorbird = new Doorbird(address, this); - connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); - connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); - connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); - connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); - m_doorbirdConnections.insert(info->deviceId(), doorbird); - m_pendingPairings.insert(doorbird, info); - doorbird->getSession(username, password); - connect(info, &DevicePairingInfo::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->deviceId()); - }); - - pluginStorage()->beginGroup(info->deviceId().toString()); - pluginStorage()->setValue("username", username); - pluginStorage()->setValue("password", password); - pluginStorage()->endGroup(); - } else { - qCWarning(dcDoorBird()) << "Confirm pairing DeviceClassNotFound" << info->deviceClassId(); - info->finish(Device::DeviceErrorDeviceClassNotFound); - } -} - - -void DevicePluginDoorbird::setupDevice(DeviceSetupInfo *info) -{ - Device *device = info->device(); - - if (device->deviceClassId() == doorBirdDeviceClassId) { - QHostAddress address = QHostAddress(device->paramValue(doorBirdDeviceAddressParamTypeId).toString()); - - if (m_doorbirdConnections.contains(device->id())) { - info->finish(Device::DeviceErrorNoError); - } else { - pluginStorage()->beginGroup(device->id().toString()); - QString username = pluginStorage()->value("username").toString(); - QString password = pluginStorage()->value("password").toString(); - pluginStorage()->endGroup(); - - qCDebug(dcDoorBird()) << "Device setup" << device->name() << username << password; - Doorbird *doorbird = new Doorbird(address, this); - connect(doorbird, &Doorbird::deviceConnected, this, &DevicePluginDoorbird::onDoorBirdConnected); - connect(doorbird, &Doorbird::eventReveiced, this, &DevicePluginDoorbird::onDoorBirdEvent); - connect(doorbird, &Doorbird::requestSent, this, &DevicePluginDoorbird::onDoorBirdRequestSent); - connect(doorbird, &Doorbird::sessionIdReceived, this, &DevicePluginDoorbird::onSessionIdReceived); - m_doorbirdConnections.insert(device->id(), doorbird); - m_pendingDeviceSetups.insert(doorbird, info); - doorbird->getSession(username, password); - connect(info, &DeviceSetupInfo::aborted, this, [device, doorbird, this] { - if (!doorbird) { - doorbird->deleteLater(); - } - m_doorbirdConnections.remove(device->id()); - m_pendingPairings.remove(doorbird); - }); - } - } else { - qCWarning(dcDoorBird()) << "Unhandled device class" << info->device()->deviceClass(); - info->finish(Device::DeviceErrorDeviceClassNotFound); - } -} - - -void DevicePluginDoorbird::postSetupDevice(Device *device) -{ - if (device->deviceClassId() == doorBirdDeviceClassId) { - device->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection in the deviceSetup - Doorbird *doorbird = m_doorbirdConnections.value(device->id()); - doorbird->connectToEventMonitor(); - doorbird->infoRequest(); - doorbird->listFavorites(); - doorbird->listSchedules(); - } -} - - -void DevicePluginDoorbird::executeAction(DeviceActionInfo *info) -{ - Device *device = info->device(); - Action action = info->action(); - - if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbird = m_doorbirdConnections.value(device->id()); - if (!doorbird) { - qCWarning(dcDoorBird()) << "Doorbird object not found" << device->name(); - info->finish(Device::DeviceErrorHardwareFailure); - return; - } - if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { - int number = action.param(doorBirdOpenDoorActionNumberParamTypeId).value().toInt(); - QUuid requestId = doorbird->openDoor(number); - m_asyncActions.insert(requestId, info); - connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); - return; - } else if (action.actionTypeId() == doorBirdLightOnActionTypeId) { - QUuid requestId = doorbird->lightOn(); - m_asyncActions.insert(requestId, info); - connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); - return; - } else if (action.actionTypeId() == doorBirdRestartActionTypeId) { - QUuid requestId = doorbird->restart(); - m_asyncActions.insert(requestId, info); - connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); - return; - } else { - qCWarning(dcDoorBird()) << "Unhandled ActionTypeId:" << action.actionTypeId(); - info->finish(Device::DeviceErrorActionTypeNotFound); - } - } else { - qCWarning(dcDoorBird()) << "Execute action, unhandled device class" << device->deviceClass(); - info->finish(Device::DeviceErrorDeviceClassNotFound); - } -} - -void DevicePluginDoorbird::deviceRemoved(Device *device) -{ - if (device->deviceClassId() == doorBirdDeviceClassId) { - Doorbird *doorbirdConnection = m_doorbirdConnections.take(device->id()); - doorbirdConnection->deleteLater(); - } -} - -void DevicePluginDoorbird::onDoorBirdConnected(bool status) -{ - Doorbird *doorbird = static_cast(sender()); - Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); - if (!device) - return; - - device->setStateValue(doorBirdConnectedStateTypeId, status); -} - -void DevicePluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool status) -{ - Doorbird *doorbird = static_cast(sender()); - Device *device = myDevices().findById(m_doorbirdConnections.key(doorbird)); - if (!device) - return; - - switch (eventType) { - case Doorbird::EventType::Rfid: - break; - case Doorbird::EventType::Input: - break; - case Doorbird::EventType::Motion: - device->setStateValue(doorBirdIsPresentStateTypeId, status); - if (status) { - device->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); - } - break; - case Doorbird::EventType::Doorbell: - if (status) { - emit emitEvent(Event(doorBirdDoorbellPressedEventTypeId ,device->id())); - } - break; - } -} - -void DevicePluginDoorbird::onDoorBirdRequestSent(QUuid requestId, bool success) -{ - Doorbird *doorbird = static_cast(sender()); - - if (m_asyncActions.contains(requestId)) { - DeviceActionInfo* actionInfo = m_asyncActions.take(requestId); - actionInfo->finish(success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); - } - - if (m_pendingPairings.contains(doorbird) && !success) { - DevicePairingInfo *info = m_pendingPairings.take(doorbird); - info->finish(Device::DeviceErrorAuthenticationFailure, tr("Wrong username or password")); - } - - if (m_pendingDeviceSetups.contains(doorbird) && !success) { - DeviceSetupInfo *info = m_pendingDeviceSetups.take(doorbird); - info->finish(Device::DeviceErrorAuthenticationFailure, tr("Wrong username or password")); - } -} - -void DevicePluginDoorbird::onSessionIdReceived(const QString &sessionId) -{ - Q_UNUSED(sessionId); - Doorbird *doorbird = static_cast(sender()); - - if (m_pendingPairings.contains(doorbird)) { - DevicePairingInfo *info = m_pendingPairings.take(doorbird); - info->finish(Device::DeviceErrorNoError); - } - - if (m_pendingDeviceSetups.contains(doorbird)) { - DeviceSetupInfo *info = m_pendingDeviceSetups.take(doorbird); - info->finish(Device::DeviceErrorNoError); - } -} diff --git a/doorbird/doorbird.cpp b/doorbird/doorbird.cpp index a8bda046..74225d16 100644 --- a/doorbird/doorbird.cpp +++ b/doorbird/doorbird.cpp @@ -75,7 +75,7 @@ QUuid Doorbird::getSession(const QString &username, const QString &password) reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - qCWarning(dcDoorBird) << "Error DoorBird device:" << reply->errorString(); + qCWarning(dcDoorBird) << "Error DoorBird thing:" << reply->errorString(); emit requestSent(requestId, false); return; } diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index 55b1f7e0..99666af5 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -2,12 +2,12 @@ include(../plugins.pri) QT += network -TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) +TARGET = $$qtLibraryTarget(nymea_integrationplugindoorbird) SOURCES += \ - deviceplugindoorbird.cpp \ + integrationplugindoorbird.cpp \ doorbird.cpp \ HEADERS += \ - deviceplugindoorbird.h \ + integrationplugindoorbird.h \ doorbird.h \ diff --git a/doorbird/integrationplugindoorbird.cpp b/doorbird/integrationplugindoorbird.cpp new file mode 100644 index 00000000..85a5643a --- /dev/null +++ b/doorbird/integrationplugindoorbird.cpp @@ -0,0 +1,306 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "integrationplugindoorbird.h" +#include "plugininfo.h" + +#include "platform/platformzeroconfcontroller.h" +#include "network/zeroconf/zeroconfservicebrowser.h" +#include "network/zeroconf/zeroconfserviceentry.h" + +#include +#include +#include +#include + +IntegrationPluginDoorbird::IntegrationPluginDoorbird() +{ +} + + +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()) { + 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()); + ParamList params; + QString macAddress; + if (serviceEntry.txt().length() == 0) { + qCWarning(dcDoorBird()) << "Discovery failed, service entry missing"; + continue; + } + + if (serviceEntry.txt().first().split("=").length() == 2) { + macAddress = serviceEntry.txt().first().split("=").last(); + } else { + qCWarning(dcDoorBird()) << "Could not parse MAC Address" << serviceEntry.txt().first(); + continue; + } + if (!myThings().filterByParam(doorBirdThingSerialnumberParamTypeId, macAddress).isEmpty()) { + Thing *existingThing = myThings().filterByParam(doorBirdThingSerialnumberParamTypeId, macAddress).first(); + ThingDescriptor.setThingId(existingThing->id()); + } + params.append(Param(doorBirdThingSerialnumberParamTypeId, macAddress)); + params.append(Param(doorBirdThingAddressParamTypeId, serviceEntry.hostAddress().toString())); + ThingDescriptor.setParams(params); + info->addThingDescriptor(ThingDescriptor); + } + } + serviceBrowser->deleteLater(); + info->finish(Thing::ThingErrorNoError); + }); + return; + } else { + qCWarning(dcDoorBird()) << "Cannot discover for ThingClassId" << info->thingClassId(); + info->finish(Thing::ThingErrorThingNotFound); + } +} + + +void IntegrationPluginDoorbird::startPairing(ThingPairingInfo *info) +{ + if (info->thingClassId() == doorBirdThingClassId) { + info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter username and password for the DoorBird Thing")); + return; + } else { + qCWarning(dcDoorBird()) << "StartPairing unhandled ThingClassId" << info->thingClassId(); + info->finish(Thing::ThingErrorCreationMethodNotSupported); + } +} + + +void IntegrationPluginDoorbird::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &password) +{ + 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()); + }); + + pluginStorage()->beginGroup(info->thingId().toString()); + pluginStorage()->setValue("username", username); + pluginStorage()->setValue("password", password); + pluginStorage()->endGroup(); + } else { + qCWarning(dcDoorBird()) << "Confirm pairing ThingClassNotFound" << info->thingClassId(); + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + + +void IntegrationPluginDoorbird::setupThing(ThingSetupInfo *info) +{ + Thing *thing = info->thing(); + + if (thing->thingClassId() == doorBirdThingClassId) { + QHostAddress address = QHostAddress(thing->paramValue(doorBirdThingAddressParamTypeId).toString()); + + if (m_doorbirdConnections.contains(thing->id())) { + info->finish(Thing::ThingErrorNoError); + } else { + 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); + }); + } + } else { + qCWarning(dcDoorBird()) << "Unhandled Thing class" << info->thing()->thingClass(); + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + + +void IntegrationPluginDoorbird::postSetupThing(Thing *thing) +{ + if (thing->thingClassId() == doorBirdThingClassId) { + thing->setStateValue(doorBirdConnectedStateTypeId, true); //since we checked the connection in the ThingSetup + Doorbird *doorbird = m_doorbirdConnections.value(thing->id()); + doorbird->connectToEventMonitor(); + doorbird->infoRequest(); + doorbird->listFavorites(); + doorbird->listSchedules(); + } +} + + +void IntegrationPluginDoorbird::executeAction(ThingActionInfo *info) +{ + Thing *thing = info->thing(); + Action action = info->action(); + + if (thing->thingClassId() == doorBirdThingClassId) { + Doorbird *doorbird = m_doorbirdConnections.value(thing->id()); + if (!doorbird) { + qCWarning(dcDoorBird()) << "Doorbird object not found" << thing->name(); + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) { + int number = action.param(doorBirdOpenDoorActionNumberParamTypeId).value().toInt(); + QUuid requestId = doorbird->openDoor(number); + m_asyncActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); + return; + } else if (action.actionTypeId() == doorBirdLightOnActionTypeId) { + QUuid requestId = doorbird->lightOn(); + m_asyncActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); + return; + } else if (action.actionTypeId() == doorBirdRestartActionTypeId) { + QUuid requestId = doorbird->restart(); + m_asyncActions.insert(requestId, info); + connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);}); + return; + } else { + qCWarning(dcDoorBird()) << "Unhandled ActionTypeId:" << action.actionTypeId(); + info->finish(Thing::ThingErrorActionTypeNotFound); + } + } else { + qCWarning(dcDoorBird()) << "Execute action, unhandled Thing class" << thing->thingClass(); + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + +void IntegrationPluginDoorbird::thingRemoved(Thing *thing) +{ + if (thing->thingClassId() == doorBirdThingClassId) { + Doorbird *doorbirdConnection = m_doorbirdConnections.take(thing->id()); + doorbirdConnection->deleteLater(); + } +} + +void IntegrationPluginDoorbird::onDoorBirdConnected(bool status) +{ + Doorbird *doorbird = static_cast(sender()); + Thing *thing = myThings().findById(m_doorbirdConnections.key(doorbird)); + if (!thing) + return; + + thing->setStateValue(doorBirdConnectedStateTypeId, status); +} + +void IntegrationPluginDoorbird::onDoorBirdEvent(Doorbird::EventType eventType, bool status) +{ + Doorbird *doorbird = static_cast(sender()); + Thing *thing = myThings().findById(m_doorbirdConnections.key(doorbird)); + if (!thing) + return; + + switch (eventType) { + case Doorbird::EventType::Rfid: + break; + case Doorbird::EventType::Input: + break; + case Doorbird::EventType::Motion: + thing->setStateValue(doorBirdIsPresentStateTypeId, status); + if (status) { + thing->setStateValue(doorBirdLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); + } + break; + case Doorbird::EventType::Doorbell: + if (status) { + emit emitEvent(Event(doorBirdDoorbellPressedEventTypeId ,thing->id())); + } + break; + } +} + +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/deviceplugindoorbird.h b/doorbird/integrationplugindoorbird.h similarity index 64% rename from doorbird/deviceplugindoorbird.h rename to doorbird/integrationplugindoorbird.h index 594decb0..3168785d 100644 --- a/doorbird/deviceplugindoorbird.h +++ b/doorbird/integrationplugindoorbird.h @@ -28,44 +28,43 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef DEVICEPLUGINDOORBIRD_H -#define DEVICEPLUGINDOORBIRD_H +#ifndef INTEGRATIONPLUGINDOORBIRD_H +#define INTEGRATIONPLUGINDOORBIRD_H #include -#include "devices/deviceplugin.h" -#include "devices/devicemanager.h" +#include "integrations/integrationplugin.h" #include "doorbird.h" class QNetworkAccessManager; class QNetworkReply; -class DevicePluginDoorbird: public DevicePlugin +class IntegrationPluginDoorbird: public IntegrationPlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindoorbird.json") - Q_INTERFACES(DevicePlugin) + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugindoorbird.json") + Q_INTERFACES(IntegrationPlugin) public: - explicit DevicePluginDoorbird(); + explicit IntegrationPluginDoorbird(); - void discoverDevices(DeviceDiscoveryInfo *info) override; - void setupDevice(DeviceSetupInfo *info) override; - void postSetupDevice(Device *device) override; - void executeAction(DeviceActionInfo *info) override; + void discoverThings(ThingDiscoveryInfo *info) override; + void setupThing(ThingSetupInfo *info) override; + void postSetupThing(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; - void startPairing(DevicePairingInfo *info) override; - void confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) override; + void startPairing(ThingPairingInfo *info) override; + void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override; - void deviceRemoved(Device *device)override; + void thingRemoved(Thing *thing)override; private: - QHash m_doorbirdConnections; - QHash m_pendingPairings; - QHash m_pendingDeviceSetups; + QHash m_doorbirdConnections; + QHash m_pendingPairings; + QHash m_pendingThingSetups; - QHash m_asyncActions; + QHash m_asyncActions; private slots: void onDoorBirdConnected(bool status); @@ -74,4 +73,4 @@ private slots: void onSessionIdReceived(const QString &sessionId); }; -#endif // DEVICEPLUGINDOORBIRD_H +#endif // INTEGRATIONPLUGINDOORBIRD_H diff --git a/doorbird/deviceplugindoorbird.json b/doorbird/integrationplugindoorbird.json similarity index 99% rename from doorbird/deviceplugindoorbird.json rename to doorbird/integrationplugindoorbird.json index a5e348f7..ac31c717 100644 --- a/doorbird/deviceplugindoorbird.json +++ b/doorbird/integrationplugindoorbird.json @@ -7,7 +7,7 @@ "name": "doorBird", "displayName": "DoorBird", "id": "2da07435-571e-4956-a387-6caa51d6e845", - "deviceClasses": [ + "thingClasses": [ { "id": "0485eb61-2a22-42ba-9dd2-a5961485bf08", "name": "doorBird", From 811fcad6516fc15dadf916e1d6d724cb764196c2 Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Mon, 23 Mar 2020 14:36:22 +0100 Subject: [PATCH 33/35] changes requested by reviewer --- doorbird/README.md | 6 ++++-- doorbird/doorbird.pro | 2 -- doorbird/integrationplugindoorbird.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doorbird/README.md b/doorbird/README.md index 05134aea..36130e22 100644 --- a/doorbird/README.md +++ b/doorbird/README.md @@ -12,10 +12,12 @@ This plugin integrates DoorBird video doorbells into nymea. All the communicatio * Switching door relays * No internet connection required +NOTE: This plug-in does not handle any video- or audio stream. + ## Requirements * The DoorBird device must be in the same local area network as nymea. -* The router must not block avahi/zeroconf multicast messages. +* The router must not block ZeroConf/mDNS multicast messages. * TCP Sockets on port 80 must not be blocked by the router. * The user must have the permission to act as DoorBird API-operator. * You can check the permissions in the DoorBird app. @@ -23,4 +25,4 @@ This plugin integrates DoorBird video doorbells into nymea. All the communicatio ## More -https://www.doorbird.com/ +https://www.doorbird.com diff --git a/doorbird/doorbird.pro b/doorbird/doorbird.pro index 99666af5..7efb2342 100644 --- a/doorbird/doorbird.pro +++ b/doorbird/doorbird.pro @@ -2,8 +2,6 @@ include(../plugins.pri) QT += network -TARGET = $$qtLibraryTarget(nymea_integrationplugindoorbird) - SOURCES += \ integrationplugindoorbird.cpp \ doorbird.cpp \ diff --git a/doorbird/integrationplugindoorbird.cpp b/doorbird/integrationplugindoorbird.cpp index 85a5643a..ddce2f09 100644 --- a/doorbird/integrationplugindoorbird.cpp +++ b/doorbird/integrationplugindoorbird.cpp @@ -85,7 +85,7 @@ void IntegrationPluginDoorbird::discoverThings(ThingDiscoveryInfo *info) return; } else { qCWarning(dcDoorBird()) << "Cannot discover for ThingClassId" << info->thingClassId(); - info->finish(Thing::ThingErrorThingNotFound); + info->finish(Thing::ThingErrorThingClassNotFound); } } @@ -97,7 +97,7 @@ void IntegrationPluginDoorbird::startPairing(ThingPairingInfo *info) return; } else { qCWarning(dcDoorBird()) << "StartPairing unhandled ThingClassId" << info->thingClassId(); - info->finish(Thing::ThingErrorCreationMethodNotSupported); + info->finish(Thing::ThingErrorThingClassNotFound); } } From cf221f1bfd3ec7e92a506534ea5b6c812a67ffad Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Mon, 23 Mar 2020 14:53:13 +0100 Subject: [PATCH 34/35] fixed debian packaging --- debian/nymea-plugin-doorbird.install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/nymea-plugin-doorbird.install.in b/debian/nymea-plugin-doorbird.install.in index 3a517946..47568ef1 100644 --- a/debian/nymea-plugin-doorbird.install.in +++ b/debian/nymea-plugin-doorbird.install.in @@ -1 +1 @@ -usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_deviceplugindoorbird.so +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationplugindoorbird.so From 3d49d0c2ab2530e97dce92850efefeb030d9020e Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Thu, 28 May 2020 11:22:48 +0200 Subject: [PATCH 35/35] added meta file and german translation --- doorbird/doorbird.jpg | Bin 0 -> 10967 bytes doorbird/integrationplugindoorbird.json | 2 +- doorbird/meta.json | 13 ++ ...6fe1614a-fc47-4eb2-a47c-13c50f1798ee-de.ts | 114 ++++++++++++++++++ ...1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts | 48 ++++---- 5 files changed, 155 insertions(+), 22 deletions(-) create mode 100644 doorbird/doorbird.jpg create mode 100644 doorbird/meta.json create mode 100644 doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-de.ts diff --git a/doorbird/doorbird.jpg b/doorbird/doorbird.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6819bf145027b29985614cea99cc219b10a01469 GIT binary patch literal 10967 zcmbt(1yo$gw)Sag+}+(ZxC9CAE(yVcyL+&p!7WH|cL@?8f?IHR4el<%;We3=J9Gc_ z-dg{Ax4Qe(UfWKcU0wTpRr_(~aT!3DlaiGJKp+4BK|a9aDpa5 zU;m@pe=k5XF?BYE3=ke7<}-G1a)Hny5L(II#o>vLh0xf>7KSDeIv+waJ3$5rp+}zh zMt{)jPqg_T^wksXq@gAb08q#fn%w*kn)!(~`h$LY7II??CtHY)4TPq)wQ+@v;dgs_ z5@b_5O?605{QKt&r~%S|7(fmf0j_{0U<0@SERfy~60`r_j^nqT5?~LpF@{_nfVY4X z#KIh~fY>raxUPURU<&EYAbeBE#|EMSX-~0#>H*;Im^zzrKKX+@GO`>1z^pw!o-qOd zTp9q}hde&s<~}~&=K%op3;?ty{B3Wa014+Igdh7ik2(VY&;kLVzWHySQ2_vafy5Yp z$-&Uc@b`V7Ay+6fGXOX(0sxfP0DuLFHKNXcD*q$i5Z_P!fxKw|(C`8Pg+TyFNd*8} z$oMfrg2Rfrf#Bg-eWwg-wWqfq_qf zPe?*SMn;B(M@dadN=-~kM*8Fg1Tlq!g+qmhM10EC$iiHMI0#Fzr zXbjL}C*%(S0-&HlPb=!L1_KKM!$CvAL!^WdK1BMLz4d^P1S+xQCx5A+SUKApJl2&O;2RFgPZBloK>2+<0Va-TnTu0R=Gma#*yLWU;wa zz7YB{wOZ(1BhB4}CpEXf4kgmaeOGh2{-JI9V^8re zOE^|N`_Exe6dEBoh^SOd5)G-)PWb_Nvp1&0nfrqLA0_UlN!{{0x%&xrE*L_6Ln0Ty zsw5AKCT?&X?|)|^C~uCLj;%;){nJnA#}HbOV(EhAbYrN39{_ls`%v#khgwP3sY4$+bIYUF@uvgyGxW|LrgP!=!B1~vtBN69WN30& zwz(ZH>$(?`-bKPB5s=@- zanBMf%dx(mRP>s(gDg@=!)X4lE&>3;S`k~E$9U<*iWLwh5&lJGoj?O-JD$ByuGzv? zOp|FJrT)h?xBMS>Z708;>;N#=0P|>QiH*4)mAhHIwWw6TB|wow>iLdQDEE)W*&)&HSgw$+~ZD@Lo}Z5>q{1T z3y~{{>f|Y2qO*cSvh>Ha28ei)76nKzSdWv)|1bu)a+x#5r#@541!|W$+eY->-4xgj zMQ1LIXS^i617PhmZX=mE=d@v(%Ri~og}xEsUTtLMxAjT>(;R>gb9|X`>CbG{xbhV( zxy0LCPwZK)kp`;jnP6@1`{P~WhT#U3mMzcH`wxO^L-lr3XV(3g@BgxfEXqn4Qyd0M zJ}2|T3_;m|&>>d-vJjSC-7i0e(kRF%bld_ma;_FL!g`H$EdI8FP~rpPSmfp9GmB{L zAH6%CH+WdZrz+OuU%2D{n;I0YmX&SWZjuQu!aCfjz!@~AjhBTk$K}6k0VpIZZ?3Bm zAA!NpS8is~-H@dIAGN2P#exoiprJvqu>VR;&>$!<0E3Q!iAB!FB?60$LqVxRMa|CX z#{o%H@Q@@1f`NJjo;SAJKKFL16sUOHRVmD95-S|Pt4J+Yah^M!K(!OQR-ekl%NSaF z)#~$>m!q+*LzKQROU47QHu|K z4~95m5%@M(WC%SLY;Jk zv}LvdGY5lnh?Xxcb@Gf^Ikr&VEJW4(5)X)2`Cy2LBHZY?X4zE#yITTV%E8#j`^G39}fA(N8WJe3r6RL2D zpPO$<_lS77rt}JuaU!7B^gbRbOd!Qb&Yz7k4{U>?+CYagkdDZ4leAVe`BmkUZ`{dn zbuGdU9P7=L?8b4$yHApC2`%6TM4dPsxXE2&{2Dpznu}O-?z;Hiw!fM(I!Ij3S7l=H ziKM!jmsxkd4(@Y^9s4dl9I?k&)`=?CoPD?XHi44Pp^r6R#Ic}Lz@li2M?`L~hXpH0 z8AOS00sG)0OYnQTh<2UBNkxz-ZdZw)b4SBvtyN2g@ixAm;<%JG)(TwvBlYD(vLC~wL3gg&Q_lxX zMN|h)tgdXTC^Td+eDR+y>n>>M&eg}v9}tYTQDI1*%F{`Xob#o{a{;l-Nab=1;5{Hk z+u{C74egG)JAtsc@dt7li<2YGSWV}-i{G&Jsi?HSchyI5l%)Agfy;4LQGRtSft!+S z+oK(R>1RRV=CqcRMyQroQyd;|YwL<_kM+&ZuE!bw`G(I2!^^I^korWD5Nl+MXrei( z^>zXs#?5wZ+$0z87#0G zYAQazkkHE^lAB4Vt~*U@zi1j#{&8%wCAaMmMUFxLl~lO6h65YR4PJGnxXw)N_Nhqy zDxY81BT!c5`r$_V%ej`dTOT}?`S_ftx9)P1>_MzN{+8X052d1{QMfRb>lFcQJ2pvRZTr3ELmmsbNgI>#E=&jFTYL4 zs1?77@4~R0ny6INmbdk^nl7wBU7{}JE>}Lwf~34>7=NTZ2!Q-EeS)B%At~=E<594& zi=bn3h*GMm89Lx_N;>-gPI?ef_X2c>Dnnv9pN4X~ndo_=E_Pn&GR`ZdyX>LDzrWrq z;bNsJo4U3Yqd4O{)z-Gk@C9?#m9APXKuS^J!y9LTI7&vjs^@C9s;#S>>MWkH^}Dmr z%$g9ly@(oa(9wl1Z3n?@!VmZ*QTtis8g%so!ESh(yRp*!Zc9~PcXC#QTEFz-5IRTF z$ysracH?5IXZj2n$u0(Rb-zTDli0-Oz)`mgLl~RZrfXj$sGmnv#(aAjw6g5GP$owz za@8bwJ6$4}CJuV79rA+P+57oX$3bI4A~B*J+O%xsoF}4b)f^jtG@Y8GauWx`suRglrOOZpTPsmi% z`iUGo0@et9Y#bt@*bWk^YQLun>S?O((V8f+%JPqY?vJkIt(NpCQQ7)PX{(o36&h3IBuQvA zRL-f%DaPc2XxsB@2o-B&Vv{5ZhT>JXbXW8gjR_s2?(tEKUQ(Sg*R8soMK;<(S;_nG z!k?2D4w@#Uvt~M~24^#D7rQKKoMTh)`5(3OnwTaMm9z2|NIMW|=e1KWdT%t=ed=!M zh?=LW<5w)+X?gd4-%C(*{x#J|z^HAL??In+S*x_C~|CiI(<;9wO z0x>2uTbva#CK~l0v0x zSouZ12p-QPAiUm5`NP+;GU0~_?Y*RinO5(;I$0|5z%y*_s|D|!I>n~6uXS>~mS~+g zW+_oi$jq?S1;e~tig@2o%@>9{*CZ5jX$N=)1?Z!9zAgw-1(ThOE^7C-(;er$h{G1f z1uIKQ%0B{lw{|BPE0~3qq5a=K!Vx0#m0Qp3wIR$(Fz1_iXPQljaBkK}^W1H#=xm8o zK40-{>b|NBdo{pN-PV!Fx?xppZ+DPD_9>a=OlW*L-4PL)7V1Mjy;txpH9F52jBy5qN^H6y^=+=EEZ@a*}n^@^6MUK`2 zgLCy)+A1RzXp|m-UtO3Z>=e75ce&lQbsY}K_}NnnT-4Yl#jN%l_(e#tTd#<5%~;}y zj&kTC9Qev4%{)SUjU1~irg_JdXj}7HXkd|fWg|#gnK%=yTB3u(E8};wP!Y?ek}@v> zlY-s#f;k!A)#`p8dJw3qCq^9nfs^8|nQX?z*lFRQS>ya7zqHcQa%&Nk^ZFr`Ac-`e z=si7`F0W@L77{=B!=&b1^k{+<91I`YN6H2nGhK&6qAkh5!=kq%gB{PWPDOLaG^#s~ zG*e2COx5Vq-d3pFqc9mx3hEf`=RN|aRV3@0uWD=0)U0Js)dwXWYI!KUw8!mnT<*@D z9V*=D0#@b94?Z6>%dQ%}^z-e@SJfH1DBbfQVIRvF$#qF3KSx0vCWm>6TjsG^TB}g{ zWBTaSOH@5*=ajLm@GZafV6b97M)q^nZBRN z)Kq|bTc@~b=JKULAV*?{OLPZ&0zZ*=FUvkYvs8RWhe2*a|8!O}F>D_%j;0QU{rxeG z{PSj2LeLufLeoNmaAgAgasBLJ;rt0P*QSl8PLEN^B;fx{R2+<3#VvoM()_HZ{#nh{&)w!{Hx)*@`6B=%FTa3f zcqVknmJJFFhW*#c6bJ*LKxY?KHFWepCTA0g%c)jr8(lf^`)hyqg5nVv`o^EfR)^8+ z57TJ=xK6QJz+00xG0+hIDT8M;8nx3(hoG6RETvI=; zmzRQ2rc23M_@x~mEV zKL*YGxj7c-+1QI?DzoQ)QEZ>r+Q>Fs%{{Ei??z_e?Iup7Vylj?48PR;{QnH)HGD0wLBd)(LSlVN>>8(Q=2#hNi7w=*dYp$zj z64;bkw!QBIFfU+q9IT!{`!$DcVmMZ4rd-nr2JwxZA-yq* zB+8O{S4l6fLe_}9Dp57E{B>hyVn?tTirRoc&|ce9I09{vcvtTtkO5_14pz6OC|7q@ zS7$X{&dyATfnaOzeA>?NGDh%)bb?RV&kO(1NhoPkh7V-Rga*mljB{{JH!8tB9huE! zSBLRY_~hnQ#d^Iv&95+=>zCByP-qx<_n6W1WIoxqWlMByQzFU-yS-FbD`R4PKnmI> zCEiBbu4Y$!&>Ys;$$IItCfs%#bJkDUWSfbCz%4lzm$1O9PW7>F>~nw3apo=OPv5UK zu{2S0as^*j+CK=ho(l0I!;KTLtXmf3bdzY_W*HuI7$Z^5@n)j}j1d_V#9x>6icZc$K0`UXLC1-2 z0>4GwJ667W&tCYvn_Sg=lsly~92K4-oRF{qhlbm1PlfaL%eCYH0bF*&aICwq42%|{ zOF_Yzgo4PRiH3X_(uU4>rQo|27%9tU){tC>{lja=VZ7u42K-A?rJ=7VXelo5EZ*`; z|H>05S+0zxeAjFfAm!YG$PDxd;;P@Q;j-P&4N)>;W}|!3P+5k=yz|(5c~}8 zM)!sJxR`5InYXz_t(Ioj3ctFXRSUduy z82;{>Wj1n;Kv8+1USV2l*ZRWx#6h57Ko}Ljc{h@m?;JGkQH}pkQz^|Eo?{-w$sB?@ z?iUG)J2@}at{a>#?#80zq~?39`V_MkCDZ#h9OJ}=(IRfGx0?I*byW5GZOl9!7-cil zu#-`5CR0AA4&GsF3ChK3&wf({R>xf8X}VW@=dRIe8hB>hS*le7jA?T{2LX7FJElbM zmeH`Iu!0|1KBBB#_2HeTl{d<#nJ0vizGO!yXyo3)YrVWWNzVB)vscmfd8UN+acdkR z1Qnv2zgCwLKXelR?ELu%XuWzx-`8@Wu0Iy7MepcqQ6t@tgVhWYL5a!g^2V3S*G08; z@VOq59)H-Gaof^GvvUbOz6;0H%zn3}40#v)E=v5a$Dp4L3;svnA+GWN$3s`+SwHc= z`Gm&X>p4RB@%N2RgI)|k&R)sW|F-R-hZHIlF;_<`{!$wf^oHySJM;l-WaB??!FLR) zqreg;3$C6$`%A686##H`;JaFa|Kyww(?9Jc*#NRffcB4ZticOvJ@EjjXRPzTeQ^#8 zJ{1oE014Sn=MV1+vr=rWkn?a0+J)*rIQ?AU8b64R1^Hg_(VyNaDxr|c0qE+-5T>32V&m#^;mgl$NV3hVL{Hp$r_00pWp80J==fh3=1}FrT?4GKlT4V z>EByx2snTS!9m0PRhW7L2kfd4SRiWXSnVIzhVGC9!3Mvf!P6-S`YozWG2@Kv_@$z- z^o4}2C)aw-UW_M*mX9AavsuTZ+JpbF|9=0uTdplt>t_q3+HY$Rd=VYUnl%?jIjN1K zi5%_2P}B@}+ge(-HFLk8m-6i*JJswJQwLqeuG0|d7BB}0`?_hMN_eSQb{TmZ&rLML zzivX7wY0sxU|*hf!94X&gopEbTM)&RMO7zzE|INw_47iyZ6Rn(Myzww)8%K!nENv& z$*^hP_PkP2tSt-lex(mhh@HX~{!*vfmVyI`SR>e2_hoN|ipyD$Jm%C0tjk{eeuPCi z#f&TQdY^vJfZkEd_>djY4VdUpXDBgqb*KYEZYWC=LCeKkuwfKapA7W~ZEzJ7xx=5e zi<;ei^~gK<$$d3U{DRs(eI$EcD##J{_GxboIk$j%LSAtHstEoLE;_lWDw~L5oTGnE zH3WAVIQ$Oo)42uuBLD|{z^sbhdx5NIIEU==+(!(a{?w9ey)aN<179rdVdZL+ICQcD zw*BU6-H@BM{W(-)PIp=5LJ^l|%#h?_rJK5tp#m}OgkA|&{nxW~?2O(Q1WF7O&Bx0| zOG10VLKg!vhR#Y6UZc?I`&Nvv4&E{-yaCA=D4?PtTw?uDQl!A>Zrkt69u-^B2q{JC ze&{D)gT_pQ@)Br+kH!3tgaFojN-3?74;2{ z-l${cOsMukdI|no&)ftM)yJT)hGWbPr$S-q=UaPt^EZx#)PMgfqW+F11jeE}`p4yn zv_Y_J^{C1}aV#XB8ARCOj7FkL!FxtP^NHk;l050SRvY)n-5`n~rnh;a(I4IgBXJl+ z#+=ZRKRe>u6Kh9i5p28XA|%48Wb(-Egh@H+=mHUc;yd6BN;sG8$?{OQiGqNN1&f5d zug*?)FHr@TosnCD{jYKDdl|h4r{2xn5%02c^oQEUjpgkJ08w$}ulm{=-jfK98F(Q~ z87+DRV;Le~#J%u$D12t`I`B~T+=go>F5oIVfQd1K(N7N(+th1={%R7>x!tHGq;IeFUt;KAb=&Ftn3H)X9P#=jX@wpA@GBJ8Al3==Q z9#U&Eq^#K&Y}H#^XackbZ|u29V4=Z2s+0icC(XCUM1@T)thmJq3d)3wQbiwbyXUFw zt6ndFOR8`}oP+$YSiMWR!n+xhWj-rWYv6MtsdEZ@Oo=mUnVh!`(KiZGsjBHl4Nq&Q zlLR?fn9E_;#>2vC-pgM=2{DhDsV8xE9qTzCBog`Fm+2{Yfy47JVLK z0Id=7%hu6L2}vE`vPcHiNEHqZ-q7h)BebXF5Vwe7wdK;3&ecU=d-G z+**Pdiu=o))Kj`1A3nz&@xcf7wZ&pT+Sr}V@cv&0#N<1qeg5H+>nR?%MCGst?B{QO zXgQHO+KnNg{0JqB+cbNi=mFa4dyYTE^mo9Xo>yIB)nS~&^Z3c6s=)aN+s8y}P|AiB zI23UWbsBgzocLot$Twy9j%W)M-(m&v_pA-H^Vp%u(XG73PyAs{6(^aOts)J1(b}Ox zUdt?JcMNJ`$!B82KVee0{SBdwi7k^C$=`;0D(q0@(y54w7BW!BN$5BPF0e=!)&>Fru!ncam^P=A8mk%E-Bo;8E_;o#!o6bL+< zLR7Y0dJ(VL$9K2_aV4vxWk+MnfRvx_kwP}r39F{Xe43bowUqcst_m~BURV?Bd1qvN zShVm2!?;uJA19-6d?8|;&5mY=97;BmFCZ*>O=x4eIblytu~mVw{o(skUcJdh`}DeM zwDcr~4ZjNcQg>RP99EQ8^3vc)IZwWv^dYf5Ur>DM^yMu8X` z_7T|i_yKN$@uBc77dP$l{_!cMc+jj?iCrgOo4hHpoHRv;f~umW2-BTr>z*_wgZ`=+ z`bzIiZKgcGJxqv680&{@^m3{h4vuR`PHO+D7_DCb<#R`t0#9FolaKav8&agfZRzxx zTN=0JCFAgOT+H4420mLD8iM5-*8CO!JBw_LSJ&QHBML*bcduM|PaFS{82p zpk&_>d;$5C%5eN<)xtHJQ(z}21La;30e5K2v=nKON^A7|6$-|WHaMb`&jMpTi#dFo zre6+^B(;sm_}I6w4KI(IP3+jbi|?e88VY)DQCM4lSbbi+?7?iNijfOq@I`p1iCs}D z3}2jL$?0w1Cosz1Zd_QLP(izUWtrN(ots5V-8MYB>37najV98Ih`kZu+nHUCpy@|% zT!_q;Khwg$arxFc>XxL~6grM=-{7TlD?L<&-%vO*h<(slRS5^B<|@VPi0h=}xh^Kx zbcJ*da+HgsR=P_dDL6T~ElOwC5MF!lDpwfZG5l_XEZl>Zkz~D9bR=s~zMy>JdTZi- z8jHK}Wy~TF=%;lPxP3%dT}0MlNn893#&lIee!n6d+62q4bc7 z9Bk0st!~vOE}Fn=W;10c$2L1UzpXKvgjKvMSTb?4|9%Ed|J*ME@7ey(1KZ1m3CXHx zyjYlY27F_j?#f+1pvu7l$3ME;zQxwctnHdkH=J=3iTwOp=ymUela}xEkwM3!NuqDyZJ?;WTEEmHm;dZbCUat}tqK})v=N*;w!j8B^kd^~% zSnF5e=WfOP#b$LNl{@ITU7bW<#BDrx4tIKxvq__Mrmb%R1*fGD=gzq#f=D!_dfq3K z23*esiuj^Fx9kZXlPkkA*s_|aw`#2%n#EIy@YKZyR@LnSO{P*Cg9gW1o&GFq-ivzAbARW(tZ zA0hF9tKmO5-0|1+GK4I2Xn9xytk^w;dx?H}XB38PjAR;7F6~14wFN}H@NkDh zW)7A+{`MU6q&L%JjHHSD4r~D^yKPJUy%PKVccRg{aHUcnO@utz&%W@^OYdQB(fjd_ zFv`j=M=|pB=*bBp{k#gzAMl!M=nxv%JscYx{f2N=d0K}Oc8UGrzUCI^L9P6T$-YWm zfK8y#$KrYGBJ*v71~=-VHTU8J1CMC4^K=D=wwScz3!I0g_;IG-*!%|ZCh%o+==I&S Q^>sN^7yJvzmUZ^O096tKKL7v# literal 0 HcmV?d00001 diff --git a/doorbird/integrationplugindoorbird.json b/doorbird/integrationplugindoorbird.json index ac31c717..9c9ba049 100644 --- a/doorbird/integrationplugindoorbird.json +++ b/doorbird/integrationplugindoorbird.json @@ -81,7 +81,7 @@ "id": "295c9700-b598-4681-898f-d63e2889cedf", "name": "lastSeenTime", "displayName": "Motion sensor last seen time", - "displayNameEvent": "Motion sensor last seen time changedd", + "displayNameEvent": "Motion sensor last seen time changed", "type": "int", "unit": "UnixTime", "defaultValue": 0 diff --git a/doorbird/meta.json b/doorbird/meta.json new file mode 100644 index 00000000..aeda49d0 --- /dev/null +++ b/doorbird/meta.json @@ -0,0 +1,13 @@ +{ + "title": "DoorBird", + "tagline": "Integrates DoorBird doorstations with nymea.", + "icon": "doorbird.jpg", + "stability": "consumer", + "offline": true, + "technologies": [ + "network" + ], + "categories": [ + "sensor" + ] +} diff --git a/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-de.ts b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-de.ts new file mode 100644 index 00000000..2369c8ad --- /dev/null +++ b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-de.ts @@ -0,0 +1,114 @@ + + + + + DevicePluginDoorbird + + + Please enter the user credentials + Bitte geben Sie die Benutzeranmeldeinformationen ein + + + + doorBird + + + + Connected + The name of the ParamType (DeviceClass: doorBird, EventType: connected, ID: {186c270b-923c-46e4-a7da-33e45427cdbb}) +---------- +The name of the StateType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird + Verbunden + + + + Connected changed + The name of the EventType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird + Verbunden geändert + + + + + + DoorBird + The name of the DeviceClass ({0485eb61-2a22-42ba-9dd2-a5961485bf08}) +---------- +The name of the vendor ({2da07435-571e-4956-a387-6caa51d6e845}) +---------- +The name of the plugin doorBird ({6fe1614a-fc47-4eb2-a47c-13c50f1798ee}) + DoorBird + + + + Doorbell pressed + The name of the EventType ({9bc89937-a2ab-4e8e-af0e-a9ba41caa89b}) of DeviceClass doorBird + Türklingel gedrückt + + + + IP address + The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {8873b17d-526e-408d-95d8-6439b501f489}) + IP Adresse + + + + Light on + The name of the ActionType ({3a6cfc5d-804c-4d21-91b5-999913d4f1a5}) of DeviceClass doorBird + Licht ein + + + + + Motion sensor last seen time + The name of the ParamType (DeviceClass: doorBird, EventType: lastSeenTime, ID: {295c9700-b598-4681-898f-d63e2889cedf}) +---------- +The name of the StateType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird + Bewegungssensor zuletzt gesehene Zeit + + + + Motion sensor last seen time changedd + The name of the EventType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird + Bewegungssensor zuletzt gesehene Zeit geändert + + + + + Motion sensor presence + The name of the ParamType (DeviceClass: doorBird, EventType: isPresent, ID: {0f5eb200-6c0d-45c5-9156-3060fd66d332}) +---------- +The name of the StateType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird + Bewegungssensors Aktivität + + + + Motion sensor presence detected + The name of the EventType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird + Bewegungssensor Aktivität geändert + + + + Open door + The name of the ActionType ({b6c3377b-91de-411a-9d48-8b509c39d67c}) of DeviceClass doorBird + Tür öffnen + + + + Relay number + The name of the ParamType (DeviceClass: doorBird, ActionType: openDoor, ID: {95dd35d7-0bc3-49e1-af96-d8da8ea5244d}) + Relaisnummer + + + + Restart + The name of the ActionType ({e874242e-5acb-4d98-94c7-0a70db65150c}) of DeviceClass doorBird + Neustarten + + + + Serial number + The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {67ea5534-330a-4291-93b5-0237034e15fa}) + Seriennummer + + + diff --git a/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts index 3dbea2ab..13499b31 100644 --- a/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts +++ b/doorbird/translations/6fe1614a-fc47-4eb2-a47c-13c50f1798ee-en_US.ts @@ -2,10 +2,16 @@ - DevicePluginDoorbird + IntegrationPluginDoorbird - - Please enter the user credentials + + Please enter username and password for the DoorBird Thing + + + + + + Wrong username or password @@ -15,15 +21,15 @@ Connected - The name of the ParamType (DeviceClass: doorBird, EventType: connected, ID: {186c270b-923c-46e4-a7da-33e45427cdbb}) + The name of the ParamType (ThingClass: doorBird, EventType: connected, ID: {186c270b-923c-46e4-a7da-33e45427cdbb}) ---------- -The name of the StateType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird +The name of the StateType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of ThingClass doorBird Connected changed - The name of the EventType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird + The name of the EventType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of ThingClass doorBird @@ -31,7 +37,7 @@ The name of the StateType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClas DoorBird - The name of the DeviceClass ({0485eb61-2a22-42ba-9dd2-a5961485bf08}) + The name of the ThingClass ({0485eb61-2a22-42ba-9dd2-a5961485bf08}) ---------- The name of the vendor ({2da07435-571e-4956-a387-6caa51d6e845}) ---------- @@ -41,73 +47,73 @@ The name of the plugin doorBird ({6fe1614a-fc47-4eb2-a47c-13c50f1798ee}) Doorbell pressed - The name of the EventType ({9bc89937-a2ab-4e8e-af0e-a9ba41caa89b}) of DeviceClass doorBird + The name of the EventType ({9bc89937-a2ab-4e8e-af0e-a9ba41caa89b}) of ThingClass doorBird IP address - The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {8873b17d-526e-408d-95d8-6439b501f489}) + The name of the ParamType (ThingClass: doorBird, Type: thing, ID: {8873b17d-526e-408d-95d8-6439b501f489}) Light on - The name of the ActionType ({3a6cfc5d-804c-4d21-91b5-999913d4f1a5}) of DeviceClass doorBird + The name of the ActionType ({3a6cfc5d-804c-4d21-91b5-999913d4f1a5}) of ThingClass doorBird Motion sensor last seen time - The name of the ParamType (DeviceClass: doorBird, EventType: lastSeenTime, ID: {295c9700-b598-4681-898f-d63e2889cedf}) + The name of the ParamType (ThingClass: doorBird, EventType: lastSeenTime, ID: {295c9700-b598-4681-898f-d63e2889cedf}) ---------- -The name of the StateType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird +The name of the StateType ({295c9700-b598-4681-898f-d63e2889cedf}) of ThingClass doorBird - Motion sensor last seen time changedd - The name of the EventType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird + Motion sensor last seen time changed + The name of the EventType ({295c9700-b598-4681-898f-d63e2889cedf}) of ThingClass doorBird Motion sensor presence - The name of the ParamType (DeviceClass: doorBird, EventType: isPresent, ID: {0f5eb200-6c0d-45c5-9156-3060fd66d332}) + The name of the ParamType (ThingClass: doorBird, EventType: isPresent, ID: {0f5eb200-6c0d-45c5-9156-3060fd66d332}) ---------- -The name of the StateType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird +The name of the StateType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of ThingClass doorBird Motion sensor presence detected - The name of the EventType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird + The name of the EventType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of ThingClass doorBird Open door - The name of the ActionType ({b6c3377b-91de-411a-9d48-8b509c39d67c}) of DeviceClass doorBird + The name of the ActionType ({b6c3377b-91de-411a-9d48-8b509c39d67c}) of ThingClass doorBird Relay number - The name of the ParamType (DeviceClass: doorBird, ActionType: openDoor, ID: {95dd35d7-0bc3-49e1-af96-d8da8ea5244d}) + The name of the ParamType (ThingClass: doorBird, ActionType: openDoor, ID: {95dd35d7-0bc3-49e1-af96-d8da8ea5244d}) Restart - The name of the ActionType ({e874242e-5acb-4d98-94c7-0a70db65150c}) of DeviceClass doorBird + The name of the ActionType ({e874242e-5acb-4d98-94c7-0a70db65150c}) of ThingClass doorBird Serial number - The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {67ea5534-330a-4291-93b5-0237034e15fa}) + The name of the ParamType (ThingClass: doorBird, Type: thing, ID: {67ea5534-330a-4291-93b5-0237034e15fa})