Merge PR #147: New Plug-In: Doorbird

master
Jenkins nymea 2020-06-09 18:19:21 +02:00
commit b9cc7bf421
14 changed files with 1378 additions and 0 deletions

16
debian/control vendored
View File

@ -216,6 +216,21 @@ Description: nymea.io plugin for denon
This package will install the nymea.io plugin for denon
Package: nymea-plugin-doorbird
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
nymea-plugins-translations,
Description: nymea.io plugin for DoorBird
The nymea daemon is a plugin based IoT (Internet of Things) server. The
server works like a translator for devices, things and services and
allows them to interact.
With the powerful rule engine you are able to connect any device available
in the system and create individual scenes and behaviors for your environment.
.
This package will install the nymea.io plugin for DoorBird
Package: nymea-plugin-dweetio
Architecture: any
Depends: ${shlibs:Depends},
@ -998,6 +1013,7 @@ Depends: nymea-plugin-anel,
nymea-plugin-datetime,
nymea-plugin-daylightsensor,
nymea-plugin-denon,
nymea-plugin-doorbird,
nymea-plugin-eq-3,
nymea-plugin-flowercare,
nymea-plugin-kodi,

View File

@ -0,0 +1 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationplugindoorbird.so

28
doorbird/README.md Normal file
View File

@ -0,0 +1,28 @@
# DoorBird
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.
## Supported Things
* All video doorbells
* Auto discovery
* Doorbell presses
* Motion events
* Enable/disable IR light
* Switching door relays
* No internet connection required
NOTE: This plug-in does not handle any video- or audio stream.
## Requirements
* The DoorBird device must be in the same local area network as nymea.
* The router must not block ZeroConf/mDNS multicast messages.
* TCP Sockets on port 80 must not be blocked by the router.
* The user must have the permission to act as DoorBird API-operator.
* You can check the permissions in the DoorBird app.
* The package "nymea-plugin-doorbird" must be installed.
## More
https://www.doorbird.com

474
doorbird/doorbird.cpp Normal file
View File

@ -0,0 +1,474 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "doorbird.h"
#include "extern-plugininfo.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QAuthenticator>
#include <QTimer>
#include <QImage>
#include <QUrl>
#include <QUrlQuery>
Doorbird::Doorbird(const QHostAddress &address, QObject *parent) :
QObject(parent),
m_address(address)
{
m_networkAccessManager = new QNetworkAccessManager(this);
}
QHostAddress Doorbird::address()
{
return m_address;
}
void Doorbird::setAddress(const QHostAddress &address)
{
m_address = address;
}
QUuid Doorbird::getSession(const QString &username, const QString &password)
{
QUrl url;
url.setHost(m_address.toString());
url.setScheme("http");
url.setPath("/bha-api/getsession.cgi");
url.setUserName(username);
url.setPassword(password);
QNetworkRequest request(url);
qCDebug(dcDoorBird) << "Sending request:" << request.url();
QNetworkReply *reply = m_networkAccessManager->get(request);
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error DoorBird thing:" << reply->errorString();
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;
}
QVariantMap map = jsonDoc.toVariant().toMap().value("BHA").toMap();
if (map.contains("SESSIONID")) {
QString sessionId = map.value("SESSIONID").toString();
qCDebug(dcDoorBird) << "Got sessionId" << sessionId;
emit sessionIdReceived(sessionId);
}
});
return requestId;
}
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 light on" << reply->error() << reply->errorString();
emit requestSent(requestId, false);
return;
}
qCDebug(dcDoorBird) << "DoorBird light on:";
emit requestSent(requestId, true);
});
return requestId;
}
QUuid Doorbird::liveVideoRequest()
{
QNetworkRequest request(QString("http://%1/bha-api/video.cgi").arg(m_address.toString()));
qCDebug(dcDoorBird) << "Sending request:" << request.url();
QNetworkReply *reply = m_networkAccessManager->get(request);
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error live video request" << reply->error() << reply->errorString();
emit requestSent(requestId, false);
return;
}
qCDebug(dcDoorBird) << "DoorBird live video request" ;
emit requestSent(requestId, true);
});
return requestId;
}
QUuid Doorbird::liveImageRequest()
{
QNetworkRequest request(QString("http://%1/bha-api/image.cgi").arg(m_address.toString()));
qCDebug(dcDoorBird) << "Sending request:" << request.url();
QNetworkReply *reply = m_networkAccessManager->get(request);
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error live image request" << reply->error() << reply->errorString();
emit requestSent(requestId, false);
return;
}
QByteArrayList tokens = reply->readAll().split('\n');
QImage image = QImage::fromData(tokens.last());
//image.save("testfile");
emit liveImageReceived(image);
qCDebug(dcDoorBird) << "DoorBird live image request:";
emit requestSent(requestId, true);
});
return requestId;
}
QUuid Doorbird::historyImageRequest(int index)
{
QUrl url(QString("http://%1/bha-api/history.cgi").arg(m_address.toString()));
QUrlQuery query;
query.addQueryItem("index", QString::number(index));
url.setQuery(query);
qCDebug(dcDoorBird) << "Sending request:" << url;
QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(url));
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error history image request" << reply->error() << reply->errorString();
emit requestSent(requestId, false);
return;
}
qCDebug(dcDoorBird) << "DoorBird history image received:";
emit requestSent(requestId, true);
});
return requestId;
}
QUuid Doorbird::liveAudioReceive()
{
QNetworkRequest request(QString("http://%1/bha-api/audio-receive.cgi").arg(m_address.toString()));
qCDebug(dcDoorBird) << "Sending request:" << request.url();
QNetworkReply *reply = m_networkAccessManager->get(request);
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error live audio receive";
emit requestSent(requestId, false);
return;
}
qCDebug(dcDoorBird) << "DoorBird live audio receive";
emit requestSent(requestId, true);
});
return requestId;
}
QUuid Doorbird::liveAudioTransmit()
{
return QUuid::createUuid();
}
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);
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error DoorBird" << reply->error() << reply->errorString();
emit requestSent(requestId, false);
return;
}
qCDebug(dcDoorBird) << "DoorBird info:" << reply->readAll() ;
emit requestSent(requestId, true);
});
return requestId;
}
QUuid Doorbird::listFavorites()
{
QNetworkRequest request(QString("http://%1/bha-api/favorites.cgi").arg(m_address.toString()));
qCDebug(dcDoorBird) << "Sending request:" << request.url();
QNetworkReply *reply = m_networkAccessManager->get(request);
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString();
emit requestSent(requestId, false);
return;
}
emit requestSent(requestId, true);
});
return requestId;
}
QUuid Doorbird::addFavorite(FavoriteType type, const QString &name, const QUrl &commandUrl, int id)
{
QUrl url(QString("http://%1/bha-api/favorites.cgi").arg(m_address.toString()));
QUrlQuery query;
query.addQueryItem("action", "save");
if (type == FavoriteType::Http) {
query.addQueryItem("type", "http");
} else {
query.addQueryItem("type", "sip");
}
query.addQueryItem("title", name);
query.addQueryItem("value", commandUrl.toString());
query.addQueryItem("id", QString::number(id));
url.setQuery(query);
QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(url));
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString();
emit requestSent(requestId, false);
return;
}
emit requestSent(requestId, true);
});
return requestId;
}
QUuid Doorbird::listSchedules()
{
QNetworkRequest request(QString("http://%1/bha-api/schedule.cgi").arg(m_address.toString()));
qCDebug(dcDoorBird) << "Sending request:" << request.url();
QNetworkReply *reply = m_networkAccessManager->get(request);
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error DoorBird device" << reply->error() << reply->errorString();
emit requestSent(requestId, false);
return;
}
emit requestSent(requestId, true);
});
return requestId;
}
QUuid Doorbird::restart()
{
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);
QUuid requestId = QUuid::createUuid();
connect(reply, &QNetworkReply::finished, this, [this, reply, requestId](){
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcDoorBird) << "Error restarting DoorBird device" << reply;
emit requestSent(requestId, false);
return;
}
qCDebug(dcDoorBird) << "DoorBird restarting";
emit requestSent(requestId, true);
});
return requestId;
}
void Doorbird::connectToEventMonitor()
{
qCDebug(dcDoorBird) << "Starting monitoring";
QNetworkRequest request(QString("http://%1/bha-api/monitor.cgi?ring=doorbell,motionsensor").arg(m_address.toString()));
QNetworkReply *reply = m_networkAccessManager->get(request);
connect(reply, &QNetworkReply::downloadProgress, this, [this, reply](qint64 bytesReceived, qint64 bytesTotal){
Q_UNUSED(bytesReceived)
Q_UNUSED(bytesTotal);
emit deviceConnected(true);
m_readBuffer.append(reply->readAll());
qCDebug(dcDoorBird) << "Event received" << m_readBuffer;
// Input data looks like:
// "--ioboundary\r\nContent-Type: text/plain\r\n\r\ndoorbell:H\r\n\r\n"
while (!m_readBuffer.isEmpty()) {
// find next --ioboundary
QString boundary = QStringLiteral("--ioboundary");
int startIndex = m_readBuffer.indexOf(boundary);
if (startIndex == -1) {
qCWarning(dcDoorBird) << "No meaningful data in buffer:" << m_readBuffer;
if (m_readBuffer.size() > 1024) {
qCWarning(dcDoorBird) << "Buffer size > 1KB and still no meaningful data. Discarding buffer...";
m_readBuffer.clear();
}
// Assuming we don't have enough data yet...
return;
}
QByteArray contentType = QByteArrayLiteral("Content-Type: text/plain");
int contentTypeIndex = m_readBuffer.indexOf(contentType);
if (contentTypeIndex == -1) {
qCWarning(dcDoorBird) << "Cannot find Content-Type in buffer:" << m_readBuffer;
if (m_readBuffer.size() > startIndex + 50) {
qCWarning(dcDoorBird) << boundary << "found but unexpected data follows. Skipping this element...";
m_readBuffer.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_readBuffer.remove(0, contentTypeIndex + contentType.length());
int nextStartIndex = m_readBuffer.indexOf(boundary);
QByteArray data;
if (nextStartIndex == -1) {
data = m_readBuffer;
m_readBuffer.clear();
} else {
data = m_readBuffer.left(nextStartIndex);
m_readBuffer.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!";
emit eventReveiced(EventType::Doorbell, true);
} else {
emit eventReveiced(EventType::Doorbell, false);
}
} else if (parts.first() == "motionsensor") {
if (parts.at(1) == "H") {
qCDebug(dcDoorBird) << "Motion sensor detected a person";
emit eventReveiced(EventType::Motion, true);
} else {
emit eventReveiced(EventType::Motion, false);
}
} else {
qCWarning(dcDoorBird) << "Unhandled DoorBird data:" << message;
}
}
});
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
emit deviceConnected(false);
qCDebug(dcDoorBird) << "Monitor request finished:" << reply->error();
QTimer::singleShot(2000, this, [this] {
connectToEventMonitor();
});
});
}
void Doorbird::onUdpBroadcast(const QByteArray &data)
{
Q_UNUSED(data);
//TODO decryption
}
/*NotifyBroadcastCiphertext decryptBroadcastNotification(const NotifyBroadcast* notification, const
StretchedPassword* password) {
NotifyBroadcastCiphertext decrypted = {{0},{0},0};
if(crypto_aead_chacha20poly1305_decrypt((unsigned char*)&decrypted, NULL, NULL, notification->ciphertext,
sizeof(notification->ciphertext), NULL, 0, notification->nonce, password->key)!=0){
LOGGING("crypto_aead_chacha20poly1305_decrypt() failed");
}
return decrypted;
}
unsigned char* stretchPasswordArgon(const char *password, unsigned char *salt, unsigned* oplimit, unsigned* memlimit) {
if (sodium_is_zero(salt, CRYPTO_SALT_BYTES) && random_bytes(salt, CRYPTO_SALT_BYTES)) {
return NULL;
}
unsigned char* key = malloc(CRYPTO_ARGON_OUT_SIZE);
if (!*oplimit) {
*oplimit = crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE;
}
if (!*memlimit) {
*memlimit = crypto_pwhash_MEMLIMIT_MIN;
}
if (crypto_pwhash(key, CRYPTO_ARGON_OUT_SIZE, password, str_len(password), salt, *oplimit, *memlimit, crypto_pwhash_ALG_ARGON2I13)) {
LOGGING("Argon2 Failed");
*oplimit = 0;
*memlimit = 0;
CLEAN(key);
return NULL;
}
return key;
}*/

117
doorbird/doorbird.h Normal file
View File

@ -0,0 +1,117 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DOORBIRD_H
#define DOORBIRD_H
#include <QObject>
#include <QHostAddress>
#include <QNetworkAccessManager>
#include <QUuid>
#include <QImage>
#include "network/networkaccessmanager.h"
class Doorbird : public QObject
{
Q_OBJECT
public:
explicit Doorbird(const QHostAddress &address, QObject *parent = nullptr);
enum EventType {
Doorbell,
Motion,
Input,
Rfid
};
enum FavoriteType {
Http,
Sip
};
struct FavoriteObject {
FavoriteType type;
QString title;
QUrl value;
int id;
};
QHostAddress address();
void setAddress(const QHostAddress &address);
QUuid getSession(const QString &username, const QString &password);
QUuid openDoor(int value);
QUuid lightOn();
QUuid liveVideoRequest();
QUuid liveImageRequest();
QUuid historyImageRequest(int index);
QUuid liveAudioReceive();
QUuid liveAudioTransmit();
QUuid infoRequest();
QUuid listFavorites();
QUuid addFavorite(FavoriteType type, const QString &name, const QUrl &url, int id);
QUuid deleteFavorite();
QUuid listSchedules();
QUuid addScheduleEntry(EventType event, int favoriteNumber, bool enabled);
QUuid deleteScheduleEntry();
QUuid restart();
void connectToEventMonitor();
private:
QHostAddress m_address;
QNetworkAccessManager *m_networkAccessManager;
QByteArray m_readBuffer;
QList<QNetworkReply *> m_networkRequests;
QList<QNetworkReply *> m_pendingAuthentications;
QString m_username;
QString m_password;
signals:
void deviceConnected(bool status);
void requestSent(QUuid requestId, bool success);
void eventReveiced(EventType eventType, bool status);
void favoritesReceived(QList<FavoriteObject> favourites);
void sessionIdReceived(const QString &sessionId);
void liveImageReceived(QImage image);
public slots:
void onUdpBroadcast(const QByteArray &data);
};
#endif // DOORBIRD_H

BIN
doorbird/doorbird.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

11
doorbird/doorbird.pro Normal file
View File

@ -0,0 +1,11 @@
include(../plugins.pri)
QT += network
SOURCES += \
integrationplugindoorbird.cpp \
doorbird.cpp \
HEADERS += \
integrationplugindoorbird.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::ThingErrorThingClassNotFound);
}
}
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::ThingErrorThingClassNotFound);
}
}
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

@ -0,0 +1,76 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef INTEGRATIONPLUGINDOORBIRD_H
#define INTEGRATIONPLUGINDOORBIRD_H
#include <QImage>
#include "integrations/integrationplugin.h"
#include "doorbird.h"
class QNetworkAccessManager;
class QNetworkReply;
class IntegrationPluginDoorbird: public IntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugindoorbird.json")
Q_INTERFACES(IntegrationPlugin)
public:
explicit IntegrationPluginDoorbird();
void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void executeAction(ThingActionInfo *info) override;
void startPairing(ThingPairingInfo *info) override;
void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override;
void thingRemoved(Thing *thing)override;
private:
QHash<ThingId, Doorbird *> m_doorbirdConnections;
QHash<Doorbird *, ThingPairingInfo *> m_pendingPairings;
QHash<Doorbird *, ThingSetupInfo *> m_pendingThingSetups;
QHash<QUuid, ThingActionInfo *> m_asyncActions;
private slots:
void onDoorBirdConnected(bool status);
void onDoorBirdEvent(Doorbird::EventType eventType, bool status);
void onDoorBirdRequestSent(QUuid requestId, bool success);
void onSessionIdReceived(const QString &sessionId);
};
#endif // INTEGRATIONPLUGINDOORBIRD_H

View File

@ -0,0 +1,101 @@
{
"name": "doorBird",
"displayName": "DoorBird",
"id": "6fe1614a-fc47-4eb2-a47c-13c50f1798ee",
"vendors": [
{
"name": "doorBird",
"displayName": "DoorBird",
"id": "2da07435-571e-4956-a387-6caa51d6e845",
"thingClasses": [
{
"id": "0485eb61-2a22-42ba-9dd2-a5961485bf08",
"name": "doorBird",
"displayName": "DoorBird",
"createMethods": ["discovery", "user" ],
"interfaces": [ "doorbell", "presencesensor", "connectable" ],
"setupMethod": "userandpassword",
"paramTypes": [
{
"id": "8873b17d-526e-408d-95d8-6439b501f489",
"name": "address",
"displayName": "IP address",
"type": "QString"
},
{
"id": "67ea5534-330a-4291-93b5-0237034e15fa",
"name": "serialnumber",
"displayName": "Serial number",
"type": "QString"
}
],
"actionTypes": [
{
"id": "b6c3377b-91de-411a-9d48-8b509c39d67c",
"name": "openDoor",
"displayName": "Open door",
"paramTypes": [
{
"id": "95dd35d7-0bc3-49e1-af96-d8da8ea5244d",
"name": "number",
"displayName": "Relay number",
"type": "QString",
"allowedValues": [
"1",
"2"
],
"defaultValue": 1
}
]
},
{
"id": "3a6cfc5d-804c-4d21-91b5-999913d4f1a5",
"name": "lightOn",
"displayName": "Light on"
},
{
"id": "e874242e-5acb-4d98-94c7-0a70db65150c",
"name": "restart",
"displayName": "Restart"
}
],
"stateTypes": [
{
"id": "186c270b-923c-46e4-a7da-33e45427cdbb",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"cached": 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 changed",
"type": "int",
"unit": "UnixTime",
"defaultValue": 0
}
],
"eventTypes": [
{
"id": "9bc89937-a2ab-4e8e-af0e-a9ba41caa89b",
"name": "doorbellPressed",
"displayName": "Doorbell pressed"
}
]
}
]
}
]
}

13
doorbird/meta.json Normal file
View File

@ -0,0 +1,13 @@
{
"title": "DoorBird",
"tagline": "Integrates DoorBird doorstations with nymea.",
"icon": "doorbird.jpg",
"stability": "consumer",
"offline": true,
"technologies": [
"network"
],
"categories": [
"sensor"
]
}

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de">
<context>
<name>DevicePluginDoorbird</name>
<message>
<location filename="../deviceplugindoorbird.cpp" line="92"/>
<source>Please enter the user credentials</source>
<translation>Bitte geben Sie die Benutzeranmeldeinformationen ein</translation>
</message>
</context>
<context>
<name>doorBird</name>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="39"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="42"/>
<source>Connected</source>
<extracomment>The name of the ParamType (DeviceClass: doorBird, EventType: connected, ID: {186c270b-923c-46e4-a7da-33e45427cdbb})
----------
The name of the StateType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird</extracomment>
<translation>Verbunden</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="45"/>
<source>Connected changed</source>
<extracomment>The name of the EventType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of DeviceClass doorBird</extracomment>
<translation>Verbunden geändert</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="48"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="51"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="54"/>
<source>DoorBird</source>
<extracomment>The name of the DeviceClass ({0485eb61-2a22-42ba-9dd2-a5961485bf08})
----------
The name of the vendor ({2da07435-571e-4956-a387-6caa51d6e845})
----------
The name of the plugin doorBird ({6fe1614a-fc47-4eb2-a47c-13c50f1798ee})</extracomment>
<translation>DoorBird</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="57"/>
<source>Doorbell pressed</source>
<extracomment>The name of the EventType ({9bc89937-a2ab-4e8e-af0e-a9ba41caa89b}) of DeviceClass doorBird</extracomment>
<translation>Türklingel gedrückt</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="60"/>
<source>IP address</source>
<extracomment>The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {8873b17d-526e-408d-95d8-6439b501f489})</extracomment>
<translation>IP Adresse</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="63"/>
<source>Light on</source>
<extracomment>The name of the ActionType ({3a6cfc5d-804c-4d21-91b5-999913d4f1a5}) of DeviceClass doorBird</extracomment>
<translation>Licht ein</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="66"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="69"/>
<source>Motion sensor last seen time</source>
<extracomment>The name of the ParamType (DeviceClass: doorBird, EventType: lastSeenTime, ID: {295c9700-b598-4681-898f-d63e2889cedf})
----------
The name of the StateType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird</extracomment>
<translation>Bewegungssensor zuletzt gesehene Zeit</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="72"/>
<source>Motion sensor last seen time changedd</source>
<extracomment>The name of the EventType ({295c9700-b598-4681-898f-d63e2889cedf}) of DeviceClass doorBird</extracomment>
<translation>Bewegungssensor zuletzt gesehene Zeit geändert</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="75"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="78"/>
<source>Motion sensor presence</source>
<extracomment>The name of the ParamType (DeviceClass: doorBird, EventType: isPresent, ID: {0f5eb200-6c0d-45c5-9156-3060fd66d332})
----------
The name of the StateType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird</extracomment>
<translation>Bewegungssensors Aktivität</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="81"/>
<source>Motion sensor presence detected</source>
<extracomment>The name of the EventType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of DeviceClass doorBird</extracomment>
<translation>Bewegungssensor Aktivität geändert</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="84"/>
<source>Open door</source>
<extracomment>The name of the ActionType ({b6c3377b-91de-411a-9d48-8b509c39d67c}) of DeviceClass doorBird</extracomment>
<translation>Tür öffnen</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="87"/>
<source>Relay number</source>
<extracomment>The name of the ParamType (DeviceClass: doorBird, ActionType: openDoor, ID: {95dd35d7-0bc3-49e1-af96-d8da8ea5244d})</extracomment>
<translation>Relaisnummer</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="90"/>
<source>Restart</source>
<extracomment>The name of the ActionType ({e874242e-5acb-4d98-94c7-0a70db65150c}) of DeviceClass doorBird</extracomment>
<translation>Neustarten</translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="93"/>
<source>Serial number</source>
<extracomment>The name of the ParamType (DeviceClass: doorBird, Type: device, ID: {67ea5534-330a-4291-93b5-0237034e15fa})</extracomment>
<translation>Seriennummer</translation>
</message>
</context>
</TS>

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>IntegrationPluginDoorbird</name>
<message>
<location filename="../integrationplugindoorbird.cpp" line="96"/>
<source>Please enter username and password for the DoorBird Thing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationplugindoorbird.cpp" line="283"/>
<location filename="../integrationplugindoorbird.cpp" line="288"/>
<source>Wrong username or password</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>doorBird</name>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="39"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="42"/>
<source>Connected</source>
<extracomment>The name of the ParamType (ThingClass: doorBird, EventType: connected, ID: {186c270b-923c-46e4-a7da-33e45427cdbb})
----------
The name of the StateType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="45"/>
<source>Connected changed</source>
<extracomment>The name of the EventType ({186c270b-923c-46e4-a7da-33e45427cdbb}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="48"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="51"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="54"/>
<source>DoorBird</source>
<extracomment>The name of the ThingClass ({0485eb61-2a22-42ba-9dd2-a5961485bf08})
----------
The name of the vendor ({2da07435-571e-4956-a387-6caa51d6e845})
----------
The name of the plugin doorBird ({6fe1614a-fc47-4eb2-a47c-13c50f1798ee})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="57"/>
<source>Doorbell pressed</source>
<extracomment>The name of the EventType ({9bc89937-a2ab-4e8e-af0e-a9ba41caa89b}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="60"/>
<source>IP address</source>
<extracomment>The name of the ParamType (ThingClass: doorBird, Type: thing, ID: {8873b17d-526e-408d-95d8-6439b501f489})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="63"/>
<source>Light on</source>
<extracomment>The name of the ActionType ({3a6cfc5d-804c-4d21-91b5-999913d4f1a5}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="66"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="69"/>
<source>Motion sensor last seen time</source>
<extracomment>The name of the ParamType (ThingClass: doorBird, EventType: lastSeenTime, ID: {295c9700-b598-4681-898f-d63e2889cedf})
----------
The name of the StateType ({295c9700-b598-4681-898f-d63e2889cedf}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="72"/>
<source>Motion sensor last seen time changed</source>
<extracomment>The name of the EventType ({295c9700-b598-4681-898f-d63e2889cedf}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="75"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="78"/>
<source>Motion sensor presence</source>
<extracomment>The name of the ParamType (ThingClass: doorBird, EventType: isPresent, ID: {0f5eb200-6c0d-45c5-9156-3060fd66d332})
----------
The name of the StateType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="81"/>
<source>Motion sensor presence detected</source>
<extracomment>The name of the EventType ({0f5eb200-6c0d-45c5-9156-3060fd66d332}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="84"/>
<source>Open door</source>
<extracomment>The name of the ActionType ({b6c3377b-91de-411a-9d48-8b509c39d67c}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="87"/>
<source>Relay number</source>
<extracomment>The name of the ParamType (ThingClass: doorBird, ActionType: openDoor, ID: {95dd35d7-0bc3-49e1-af96-d8da8ea5244d})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="90"/>
<source>Restart</source>
<extracomment>The name of the ActionType ({e874242e-5acb-4d98-94c7-0a70db65150c}) of ThingClass doorBird</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/doorbird/plugininfo.h" line="93"/>
<source>Serial number</source>
<extracomment>The name of the ParamType (ThingClass: doorBird, Type: thing, ID: {67ea5534-330a-4291-93b5-0237034e15fa})</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@ -13,6 +13,7 @@ PLUGIN_DIRS = \
datetime \
daylightsensor \
denon \
doorbird \
dweetio \
dynatrace \
elgato \