devices to things

This commit is contained in:
bernhard.trinnes 2020-03-20 08:16:10 +01:00
parent b6bb9f41f7
commit e9b9166e65
7 changed files with 349 additions and 338 deletions

View File

@ -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. 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
* All video doorbells
* Auto discovery
* Doorbell presses * Doorbell presses
* Motion events * Motion events
* Enable/disable IR light * Enable/disable IR light
* Switching door relays * Switching door relays
* No internet connection required
The user must have the permission to act as DoorBird API-operator. ## Requirements
You can check the permissions in the DoorBird app.
* 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/

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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 <QNetworkAccessManager>
#include <QNetworkReply>
#include <QHostAddress>
#include <QTimer>
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<Doorbird *>(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<Doorbird *>(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<Doorbird *>(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<Doorbird *>(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);
}
}

View File

@ -75,7 +75,7 @@ QUuid Doorbird::getSession(const QString &username, const QString &password)
reply->deleteLater(); reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error DoorBird device:" << reply->errorString(); qCWarning(dcDoorBird) << "Error DoorBird thing:" << reply->errorString();
emit requestSent(requestId, false); emit requestSent(requestId, false);
return; return;
} }

View File

@ -2,12 +2,12 @@ include(../plugins.pri)
QT += network QT += network
TARGET = $$qtLibraryTarget(nymea_deviceplugindoorbird) TARGET = $$qtLibraryTarget(nymea_integrationplugindoorbird)
SOURCES += \ SOURCES += \
deviceplugindoorbird.cpp \ integrationplugindoorbird.cpp \
doorbird.cpp \ doorbird.cpp \
HEADERS += \ HEADERS += \
deviceplugindoorbird.h \ integrationplugindoorbird.h \
doorbird.h \ doorbird.h \

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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 <QNetworkAccessManager>
#include <QNetworkReply>
#include <QHostAddress>
#include <QTimer>
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<Doorbird *>(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<Doorbird *>(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<Doorbird *>(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<Doorbird *>(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);
}
}

View File

@ -28,44 +28,43 @@
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICEPLUGINDOORBIRD_H #ifndef INTEGRATIONPLUGINDOORBIRD_H
#define DEVICEPLUGINDOORBIRD_H #define INTEGRATIONPLUGINDOORBIRD_H
#include <QImage> #include <QImage>
#include "devices/deviceplugin.h" #include "integrations/integrationplugin.h"
#include "devices/devicemanager.h"
#include "doorbird.h" #include "doorbird.h"
class QNetworkAccessManager; class QNetworkAccessManager;
class QNetworkReply; class QNetworkReply;
class DevicePluginDoorbird: public DevicePlugin class IntegrationPluginDoorbird: public IntegrationPlugin
{ {
Q_OBJECT Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindoorbird.json") Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugindoorbird.json")
Q_INTERFACES(DevicePlugin) Q_INTERFACES(IntegrationPlugin)
public: public:
explicit DevicePluginDoorbird(); explicit IntegrationPluginDoorbird();
void discoverDevices(DeviceDiscoveryInfo *info) override; void discoverThings(ThingDiscoveryInfo *info) override;
void setupDevice(DeviceSetupInfo *info) override; void setupThing(ThingSetupInfo *info) override;
void postSetupDevice(Device *device) override; void postSetupThing(Thing *thing) override;
void executeAction(DeviceActionInfo *info) override; void executeAction(ThingActionInfo *info) override;
void startPairing(DevicePairingInfo *info) override; void startPairing(ThingPairingInfo *info) override;
void confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) override; void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override;
void deviceRemoved(Device *device)override; void thingRemoved(Thing *thing)override;
private: private:
QHash<DeviceId, Doorbird *> m_doorbirdConnections; QHash<ThingId, Doorbird *> m_doorbirdConnections;
QHash<Doorbird *, DevicePairingInfo *> m_pendingPairings; QHash<Doorbird *, ThingPairingInfo *> m_pendingPairings;
QHash<Doorbird *, DeviceSetupInfo *> m_pendingDeviceSetups; QHash<Doorbird *, ThingSetupInfo *> m_pendingThingSetups;
QHash<QUuid, DeviceActionInfo *> m_asyncActions; QHash<QUuid, ThingActionInfo *> m_asyncActions;
private slots: private slots:
void onDoorBirdConnected(bool status); void onDoorBirdConnected(bool status);
@ -74,4 +73,4 @@ private slots:
void onSessionIdReceived(const QString &sessionId); void onSessionIdReceived(const QString &sessionId);
}; };
#endif // DEVICEPLUGINDOORBIRD_H #endif // INTEGRATIONPLUGINDOORBIRD_H

View File

@ -7,7 +7,7 @@
"name": "doorBird", "name": "doorBird",
"displayName": "DoorBird", "displayName": "DoorBird",
"id": "2da07435-571e-4956-a387-6caa51d6e845", "id": "2da07435-571e-4956-a387-6caa51d6e845",
"deviceClasses": [ "thingClasses": [
{ {
"id": "0485eb61-2a22-42ba-9dd2-a5961485bf08", "id": "0485eb61-2a22-42ba-9dd2-a5961485bf08",
"name": "doorBird", "name": "doorBird",