refactored code

This commit is contained in:
nymea 2019-09-22 22:56:56 +02:00 committed by Michael Zanetti
parent cc9e225457
commit bb645fed61
7 changed files with 489 additions and 187 deletions

0
doorbird/README.md Normal file
View File

View File

@ -28,22 +28,11 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QAuthenticator> #include <QHostAddress>
#include <QTimer> #include <QTimer>
DevicePluginDoorbird::DevicePluginDoorbird() 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() DevicePluginDoorbird::~DevicePluginDoorbird()
@ -53,164 +42,97 @@ DevicePluginDoorbird::~DevicePluginDoorbird()
Device::DeviceError DevicePluginDoorbird::discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params) Device::DeviceError DevicePluginDoorbird::discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params)
{ {
Q_UNUSED(deviceClassId)
Q_UNUSED(params) Q_UNUSED(params)
if (deviceClassId == doorBirdDeviceClassId) {
// NOTE: Discovery is currently disabled in json file because we don't support discovery & login as parameters in combination ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser();
// and there isn't any setupMethod which would allow us to enter user & password. QTimer::singleShot(5000, this, [this, serviceBrowser](){
QList<DeviceDescriptor> deviceDescriptors;
ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_xbmc-jsonrpc._tcp"); foreach (const ZeroConfServiceEntry serviceEntry, serviceBrowser->serviceEntries()) {
QTimer::singleShot(5000, this, [this, serviceBrowser](){ if (serviceEntry.hostName().startsWith("bha-")) {
QList<DeviceDescriptor> deviceDescriptors; qCDebug(dcDoorBird) << "Found DoorBird device";
foreach (const ZeroConfServiceEntry serviceEntry, serviceBrowser->serviceEntries()) { DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString());
if (serviceEntry.serviceType() == "_axis-video._tcp" && serviceEntry.hostName().startsWith("bha-")) { ParamList params;
qCDebug(dcDoorBird) << "Found DoorBird device"; //TODO add rediscovery
DeviceDescriptor deviceDescriptor(doorBirdDeviceClassId, serviceEntry.name(), serviceEntry.hostAddress().toString()); params.append(Param(doorBirdDeviceSerialnumberParamTypeId, serviceEntry.hostName()));
ParamList params; params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString()));
//TODO add rediscovery deviceDescriptor.setParams(params);
params.append(Param(doorBirdDeviceAddressParamTypeId, serviceEntry.hostAddress().toString())); deviceDescriptors.append(deviceDescriptor);
deviceDescriptor.setParams(params); }
deviceDescriptors.append(deviceDescriptor);
} }
} emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors);
emit devicesDiscovered(doorBirdDeviceClassId, deviceDescriptors); serviceBrowser->deleteLater();
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::DeviceErrorAsync;
} }
return Device::DeviceErrorDeviceClassNotFound; 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())); devicePairingInfo.setStatus(Device::DeviceErrorNoError);
QNetworkReply *reply = m_nam->get(request); devicePairingInfo.setMessage(tr("Please enter username and password for your Doorbird device."));
m_networkRequests.insert(reply, device); return devicePairingInfo;
connect(reply, &QNetworkReply::downloadProgress, this, [this, device, reply](qint64 bytesReceived, qint64 bytesTotal){ }
Q_UNUSED(bytesReceived)
Q_UNUSED(bytesTotal); DevicePairingInfo DevicePluginDoorbird::confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret)
if (!myDevices().contains(device)) { {
qCWarning(dcDoorBird) << "Device disappeared for monitor stream."; qCDebug(dcDoorBird()) << "confirm pairing called";
reply->abort();
return; pluginStorage()->beginGroup(devicePairingInfo.deviceId().toString());
} pluginStorage()->setValue("username", username);
device->setStateValue(doorBirdConnectedStateTypeId, true); pluginStorage()->setValue("password", secret);
m_readBuffers[device].append(reply->readAll()); pluginStorage()->endGroup();
// qCDebug(dcDoorBird) << "Monitor data for" << device->name();
// qCDebug(dcDoorBird) << m_readBuffers[device]; devicePairingInfo.setStatus(Device::DeviceErrorNoError);
// Input data looks like: return devicePairingInfo;
// "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n" }
while (!m_readBuffers[device].isEmpty()) {
// find next --ioboundary Device::DeviceSetupStatus DevicePluginDoorbird::setupDevice(Device *device)
QString boundary = QStringLiteral("--ioboundary"); {
int startIndex = m_readBuffers[device].indexOf(boundary); if (device->deviceClassId() == doorBirdDeviceClassId) {
if (startIndex == -1) { QHostAddress address = QHostAddress(device->paramValue(doorBirdDeviceAddressParamTypeId).toString());
qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffers[device];
if (m_readBuffers[device].size() > 1024) { pluginStorage()->beginGroup(device->id().toString());
qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer..."; QString username = pluginStorage()->value("username").toString();
m_readBuffers[device].clear(); QString password = pluginStorage()->value("password").toString();
} pluginStorage()->endGroup();
// Assuming we don't have enough data yet...
return; Doorbird *doorbird = new Doorbird(address, username, password, this);
} doorbird->connectToEventMonitor();
m_doorbirdConnections.insert(device, doorbird);
QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain"); return Device::DeviceSetupStatusSuccess;
int contentTypeIndex = m_readBuffers[device].indexOf(contentType); }
if (contentTypeIndex == -1) { return Device::DeviceSetupStatusFailure;
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..."; Device::DeviceError DevicePluginDoorbird::executeAction(Device *device, const Action &action)
m_readBuffers[device].remove(0, startIndex + boundary.length()); {
continue; if (device->deviceClassId() == doorBirdDeviceClassId) {
} Doorbird *doorbird = m_doorbirdConnections.value(device);
// Assuming we don't have enough data yet... if (!doorbird)
return; return Device::DeviceErrorDeviceNotFound;
}
if (action.actionTypeId() == doorBirdOpenDoorActionTypeId) {
// 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 //Todo get action param
m_readBuffers[device].remove(0, contentTypeIndex + contentType.length()); doorbird->openDoor(1);
int nextStartIndex = m_readBuffers[device].indexOf(boundary); return Device::DeviceErrorNoError;
QByteArray data; }
if (nextStartIndex == -1) { if (action.actionTypeId() == doorBirdLightOnActionTypeId) {
data = m_readBuffers[device]; doorbird->lightOn();
m_readBuffers[device].clear(); return Device::DeviceErrorNoError;
} else { }
data = m_readBuffers[device].left(nextStartIndex); }
m_readBuffers[device].remove(0, nextStartIndex); return Device::DeviceErrorDeviceClassNotFound;
} }
QString message = data.trimmed(); void DevicePluginDoorbird::deviceRemoved(Device *device)
QStringList parts = message.split(":"); {
if (parts.count() != 2) { if (device->deviceClassId() == doorBirdDeviceClassId) {
qCWarning(dcDoorBird) << "Message has invalid format:" << message << "Expected device:state"; Doorbird *doorbirdConnection = m_doorbirdConnections.take(device);
continue; doorbirdConnection->deleteLater();
} }
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);
});
});
} }

View File

@ -23,6 +23,7 @@
#include "devices/deviceplugin.h" #include "devices/deviceplugin.h"
#include "devices/devicemanager.h" #include "devices/devicemanager.h"
#include "doorbird.h"
class QNetworkAccessManager; class QNetworkAccessManager;
class QNetworkReply; class QNetworkReply;
@ -44,13 +45,12 @@ public:
Device::DeviceSetupStatus setupDevice(Device *device) override; Device::DeviceSetupStatus setupDevice(Device *device) override;
Device::DeviceError executeAction(Device *device, const Action &action) 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: private:
QHash<Device*, Doorbird *> m_doorbirdConnections;
QNetworkAccessManager *m_nam = nullptr;
QHash<QNetworkReply*, Device*> m_networkRequests;
QHash<Device*, QByteArray> m_readBuffers;
}; };
#endif // DEVICEPLUGINDOORBIRD_H #endif // DEVICEPLUGINDOORBIRD_H

View File

@ -13,7 +13,8 @@
"name": "doorBird", "name": "doorBird",
"displayName": "DoorBird", "displayName": "DoorBird",
"createMethods": ["discovery", "user" ], "createMethods": ["discovery", "user" ],
"interfaces": [ "inputtrigger", "connectable" ], "interfaces": [ "doorbell", "presencesensor", "connectable" ],
"setupMethod": "userandpassword",
"paramTypes": [ "paramTypes": [
{ {
"id": "8873b17d-526e-408d-95d8-6439b501f489", "id": "8873b17d-526e-408d-95d8-6439b501f489",
@ -22,23 +23,30 @@
"type": "QString" "type": "QString"
}, },
{ {
"id": "7ccd8f3a-2a5f-4b90-8042-92899d0ee32a", "id": "67ea5534-330a-4291-93b5-0237034e15fa",
"name": "username", "name": "serialnumber",
"displayName": "Username", "displayName": "Serial number",
"type": "QString"
},
{
"id": "ea285a57-47c5-43f1-b0d6-e0a4d6230f3c",
"name": "password",
"displayName": "Password",
"type": "QString" "type": "QString"
} }
], ],
"actionTypes": [ "actionTypes": [
{ {
"id": "b6c3377b-91de-411a-9d48-8b509c39d67c", "id": "b6c3377b-91de-411a-9d48-8b509c39d67c",
"name": "unlatch", "name": "openDoor",
"displayName": "Unlatch the door" "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": [ "stateTypes": [
@ -49,18 +57,30 @@
"displayNameEvent": "Connected changed", "displayNameEvent": "Connected changed",
"type": "bool", "type": "bool",
"defaultValue": false "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": [ "eventTypes": [
{ {
"id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b", "id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b",
"name": "triggered", "name": "doorbellPressed",
"displayName": "Doorbell pressed" "displayName": "Doorbell pressed"
},
{
"id": "e9bb229b-8776-4110-a813-9c0dc67375db",
"name": "motionDetected",
"displayName": "Motion detected"
} }
] ]
} }

282
doorbird/doorbird.cpp Normal file
View File

@ -0,0 +1,282 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "doorbird.h"
#include "extern-plugininfo.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QAuthenticator>
#include <QTimer>
#include <QImage>
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);
});
});
}

76
doorbird/doorbird.h Normal file
View File

@ -0,0 +1,76 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DOORBIRD_H
#define DOORBIRD_H
#include <QObject>
#include <QHostAddress>
#include <QNetworkAccessManager>
#include <QUuid>
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<QByteArray> m_readBuffers;
QHostAddress m_address;
QList<QNetworkReply *> m_networkRequests;
QString m_username;
QString m_password;
QByteArray sessionId;
signals:
void requestSent(QUuid requestId, bool success);
public slots:
};
#endif // DOORBIRD_H

View File

@ -6,6 +6,8 @@ TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird)
SOURCES += \ SOURCES += \
deviceplugindoorbird.cpp \ deviceplugindoorbird.cpp \
doorbird.cpp
HEADERS += \ HEADERS += \
deviceplugindoorbird.h \ deviceplugindoorbird.h \
doorbird.h