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