From e9b9166e65269c9a2e6b840036b902253d23cf25 Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Fri, 20 Mar 2020 08:16:10 +0100 Subject: [PATCH] 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",