New Plugin: UniFi network controller
parent
028e99539f
commit
f3d5763242
|
|
@ -595,6 +595,21 @@ Description: nymea.io plugin for UDP commander
|
|||
This package will install the nymea.io plugin for udpcommander
|
||||
|
||||
|
||||
Package: nymea-plugin-unifi
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
nymea-plugins-translations,
|
||||
Description: nymea.io plugin for UniFi network controllers
|
||||
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 UniFi network controllers
|
||||
|
||||
|
||||
Package: nymea-plugin-unitec
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
|
|
@ -874,6 +889,7 @@ Depends: nymea-plugin-anel,
|
|||
nymea-plugin-senic,
|
||||
nymea-plugin-sonos,
|
||||
nymea-plugin-keba,
|
||||
nymea-plugin-unifi,
|
||||
Replaces: guh-plugins
|
||||
Description: Plugins for nymea IoT server - the default plugin collection
|
||||
The nymea daemon is a plugin based IoT (Internet of Things) server. The
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginunifi.so
|
||||
|
|
@ -48,6 +48,7 @@ PLUGIN_DIRS = \
|
|||
tcpcommander \
|
||||
texasinstruments \
|
||||
udpcommander \
|
||||
unifi \
|
||||
unitec \
|
||||
wakeonlan \
|
||||
wemo \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# UniFi
|
||||
|
||||
This plugin adds support to connect nymea to a UniFi network controller and monitor devices for presence.
|
||||
|
||||
## Setup
|
||||
|
||||
In order to monitor network devices via a UniFi controller, it is required to configure the UniFi controller.
|
||||
The IP, as well as username and password for the UniFi controller must be provided.
|
||||
|
||||
Once the controller is added to the system, additional Wi-Fi devices may be added by starting a discovery of
|
||||
UniFi clients. After a client is addded, it will appear as presence sensor in the system.
|
||||
Client devices, by default have a one minute grace period before they are marked as offline. This value can
|
||||
be changed in the device settings. A value of 0 will immediately mark a device as offline.
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "devicepluginunifi.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <hardwaremanager.h>
|
||||
#include <network/networkaccessmanager.h>
|
||||
#include <plugintimer.h>
|
||||
|
||||
DevicePluginUnifi::DevicePluginUnifi(QObject *parent) : DevicePlugin(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DevicePluginUnifi::~DevicePluginUnifi()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DevicePluginUnifi::init()
|
||||
{
|
||||
}
|
||||
|
||||
void DevicePluginUnifi::discoverDevices(DeviceDiscoveryInfo *info)
|
||||
{
|
||||
Q_ASSERT_X(info->deviceClassId() == clientDeviceClassId, "discoverDevices", "Invalid device class in discovery");
|
||||
|
||||
Devices controllers = myDevices().filterByDeviceClassId(controllerDeviceClassId);
|
||||
if (controllers.isEmpty()) {
|
||||
info->finish(Device::DeviceErrorHardwareNotAvailable, QT_TR_NOOP("Please configure a UniFi controller first."));
|
||||
return;
|
||||
}
|
||||
|
||||
connect(info, &DeviceDiscoveryInfo::aborted, this, [this, info](){
|
||||
m_pendingDiscoveries.remove(info);
|
||||
});
|
||||
|
||||
foreach (Device *controller, controllers) {
|
||||
m_pendingDiscoveries[info].append(controller);
|
||||
QNetworkRequest request = createRequest(controller, "/api/self/sites");
|
||||
QNetworkReply *sitesReply = hardwareManager()->networkManager()->get(request);
|
||||
connect(sitesReply, &QNetworkReply::finished, sitesReply, &QNetworkReply::deleteLater);
|
||||
connect(sitesReply, &QNetworkReply::finished, info, [this, info, sitesReply, controller](){
|
||||
if (sitesReply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcUnifi()) << "Error fetching sites";
|
||||
m_pendingDiscoveries[info].removeAll(controller);
|
||||
if (m_pendingDiscoveries[info].isEmpty()) {
|
||||
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Fetching sites from controller failed."));
|
||||
}
|
||||
return;
|
||||
}
|
||||
QByteArray data = sitesReply->readAll();
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcUnifi()) << "Error parsing data" << data;
|
||||
m_pendingDiscoveries[info].removeAll(controller);
|
||||
if (m_pendingDiscoveries[info].isEmpty()) {
|
||||
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Error communicating with the controller."));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonDoc.toVariant().toMap().value("meta").toMap().value("rc").toString() != "ok") {
|
||||
qCWarning(dcUnifi()) << "Controller did not responde with OK" << qUtf8Printable(jsonDoc.toJson());
|
||||
m_pendingDiscoveries[info].removeAll(controller);
|
||||
if (m_pendingDiscoveries[info].isEmpty()) {
|
||||
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Fetching sites from controller failed."));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcUnifi()) << "**** sites reply finished" << data;
|
||||
foreach (const QVariant &siteVariant, jsonDoc.toVariant().toMap().value("data").toList()) {
|
||||
qCDebug(dcUnifi()) << "Have site:" << siteVariant.toMap().value("_id").toString() << siteVariant.toMap().value("name").toString() << siteVariant.toMap().value("desc").toString();
|
||||
|
||||
QString site = siteVariant.toMap().value("_id").toString();
|
||||
QString siteName = siteVariant.toMap().value("name").toString();
|
||||
QString siteDescription = siteVariant.toMap().value("desc").toString();
|
||||
QNetworkRequest request = createRequest(controller, QString("/api/s/%1/stat/sta/").arg(siteName));
|
||||
|
||||
qCDebug(dcUnifi()) << "Fetching clients for site" << site << siteName << request.url();
|
||||
|
||||
m_pendingSiteDiscoveries[controller].append(siteName);
|
||||
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, info, [this, info, reply, controller, siteName](){
|
||||
m_pendingSiteDiscoveries[controller].removeAll(siteName);
|
||||
if (m_pendingSiteDiscoveries[controller].isEmpty()) {
|
||||
m_pendingDiscoveries[info].removeAll(controller);
|
||||
}
|
||||
|
||||
bool hasError = false;
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcUnifi()) << "Error fetching clients from site" << reply->error() << reply->errorString();
|
||||
hasError = true;
|
||||
} else {
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcUnifi()) << "Error parsing json for clients reply:" << error.errorString() << data;
|
||||
hasError = true;
|
||||
} else {
|
||||
QVariantMap response = jsonDoc.toVariant().toMap();
|
||||
if (response.value("meta").toMap().value("rc").toString() != "ok") {
|
||||
qCWarning(dcUnifi()) << "Error response from controller:" << qUtf8Printable(jsonDoc.toJson());
|
||||
hasError = true;
|
||||
} else {
|
||||
QVariantList clients = response.value("data").toList();
|
||||
foreach (const QVariant &clientVariant, clients) {
|
||||
// qCDebug(dcUnifi()) << "client:" << qUtf8Printable(QJsonDocument::fromVariant(clientVariant).toJson());
|
||||
|
||||
QString name = clientVariant.toMap().value("name").toString();
|
||||
if (name.isEmpty()) {
|
||||
name = clientVariant.toMap().value("hostname").toString();
|
||||
}
|
||||
if (name.isEmpty()) {
|
||||
name = clientVariant.toMap().value("oui").toString();
|
||||
}
|
||||
DeviceDescriptor d(clientDeviceClassId, name, clientVariant.toMap().value("mac").toString());
|
||||
ParamList params;
|
||||
params << Param(clientDeviceMacParamTypeId, clientVariant.toMap().value("mac").toString());
|
||||
params << Param(clientDeviceSiteParamTypeId, siteName);
|
||||
d.setParams(params);
|
||||
|
||||
Device *existingDevice = myDevices().findByParams(params);
|
||||
if (existingDevice) {
|
||||
d.setDeviceId(existingDevice->id());
|
||||
}
|
||||
|
||||
d.setParentDeviceId(controller->id());
|
||||
|
||||
info->addDeviceDescriptor(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_pendingDiscoveries[info].isEmpty()) {
|
||||
info->finish(hasError ? Device::DeviceErrorHardwareFailure : Device::DeviceErrorNoError);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginUnifi::startPairing(DevicePairingInfo *info)
|
||||
{
|
||||
info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please enter your login credentials for the UniFi controller."));
|
||||
}
|
||||
|
||||
void DevicePluginUnifi::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret)
|
||||
{
|
||||
QString host = info->params().paramValue(controllerDeviceIpAddressParamTypeId).toString();
|
||||
QNetworkRequest request = createRequest(host, "/api/login");
|
||||
QVariantMap login;
|
||||
login.insert("username", username);
|
||||
login.insert("password", secret);
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QJsonDocument::fromVariant(login).toJson());
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, info, [this, info, reply, username, secret](){
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcUnifi()) << "Network request error:" << reply->error() << reply->errorString();
|
||||
info->finish(Device::DeviceErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcUnifi()) << "Error parsing JSON response from controller:" << error.errorString() << data;
|
||||
info->finish(Device::DeviceErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
pluginStorage()->beginGroup(info->deviceId().toString());
|
||||
pluginStorage()->setValue("username", username);
|
||||
pluginStorage()->setValue("password", secret);
|
||||
pluginStorage()->endGroup();
|
||||
info->finish(Device::DeviceErrorNoError);
|
||||
});
|
||||
}
|
||||
|
||||
void DevicePluginUnifi::setupDevice(DeviceSetupInfo *info)
|
||||
{
|
||||
if (info->device()->deviceClassId() == controllerDeviceClassId) {
|
||||
QNetworkRequest request = createRequest(info->device(), "/api/login");
|
||||
QVariantMap login;
|
||||
pluginStorage()->beginGroup(info->device()->id().toString());
|
||||
login.insert("username", pluginStorage()->value("username").toString());
|
||||
login.insert("password", pluginStorage()->value("password").toString());
|
||||
pluginStorage()->endGroup();
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QJsonDocument::fromVariant(login).toJson());
|
||||
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, info, [this, info, reply](){
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcUnifi()) << "Network request error:" << reply->error() << reply->errorString();
|
||||
info->finish(Device::DeviceErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcUnifi()) << "Error parsing JSON response from controller:" << error.errorString() << data;
|
||||
info->finish(Device::DeviceErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
info->device()->setStateValue(controllerConnectedStateTypeId, true);
|
||||
info->finish(Device::DeviceErrorNoError);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
if (info->device()->deviceClassId() == clientDeviceClassId) {
|
||||
info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginUnifi::postSetupDevice(Device *device)
|
||||
{
|
||||
if (device->deviceClassId() == controllerDeviceClassId && !m_loginTimer) {
|
||||
// Let's refresh the login every minute
|
||||
m_loginTimer = hardwareManager()->pluginTimerManager()->registerTimer();
|
||||
connect(m_loginTimer, &PluginTimer::timeout, this, [this](){
|
||||
foreach (Device *controller, myDevices().filterByDeviceClassId(controllerDeviceClassId)) {
|
||||
QNetworkRequest request = createRequest(controller, "/api/login");
|
||||
QVariantMap login;
|
||||
pluginStorage()->beginGroup(controller->id().toString());
|
||||
login.insert("username", pluginStorage()->value("username"));
|
||||
login.insert("password", pluginStorage()->value("password"));
|
||||
pluginStorage()->endGroup();
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QJsonDocument::fromVariant(login).toJson());
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == clientDeviceClassId && !m_pollTimer) {
|
||||
m_pollTimer = hardwareManager()->pluginTimerManager()->registerTimer(1);
|
||||
connect(m_pollTimer, &PluginTimer::timeout, this, [this](){
|
||||
foreach (Device *client, myDevices().filterByDeviceClassId(clientDeviceClassId)) {
|
||||
Device *controller = myDevices().findById(client->parentId());
|
||||
QString mac = client->paramValue(clientDeviceMacParamTypeId).toString();
|
||||
QString site = client->paramValue(clientDeviceSiteParamTypeId).toString();
|
||||
QNetworkRequest request = createRequest(controller, QString("/api/s/%1/stat/sta/%2").arg(site).arg(mac));
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, client, [this, client, reply](){
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
// If the device is not present we'll get an InvalidOperationError, silence that as it's expected but print other failures
|
||||
if (reply->error() != QNetworkReply::ProtocolInvalidOperationError) {
|
||||
qCDebug(dcUnifi()) << "Error fetching device state from controller" << reply->error() << reply->errorString();
|
||||
}
|
||||
markOffline(client);
|
||||
return;
|
||||
}
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcUnifi()) << "Error parsing json from controller:" << error.error << error.errorString() << "\n" << data;
|
||||
markOffline(client);
|
||||
return;
|
||||
}
|
||||
|
||||
// qCDebug(dcUnifi()) << "Client is present reply" << qUtf8Printable(jsonDoc.toJson());
|
||||
QVariantList clientEntries = jsonDoc.toVariant().toMap().value("data").toList();
|
||||
if (clientEntries.count() != 1) {
|
||||
qCWarning(dcUnifi()) << "Client data not found in controller reply";
|
||||
markOffline(client);
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap clientData = clientEntries.first().toMap();
|
||||
|
||||
client->setStateValue(clientLastSeenTimeStateTypeId, clientData.value("last_seen").toInt());
|
||||
client->setStateValue(clientIsPresentStateTypeId, true);
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginUnifi::deviceRemoved(Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (myDevices().filterByDeviceClassId(controllerDeviceClassId).isEmpty() && m_loginTimer) {
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_loginTimer);
|
||||
m_loginTimer = nullptr;
|
||||
}
|
||||
if (myDevices().filterByDeviceClassId(clientDeviceClassId).isEmpty() && m_pollTimer) {
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pollTimer);
|
||||
m_pollTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkRequest DevicePluginUnifi::createRequest(const QString &address, const QString &path)
|
||||
{
|
||||
QUrl url;
|
||||
url.setScheme("https");
|
||||
url.setHost(address);
|
||||
url.setPort(8443);
|
||||
url.setPath(path);
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
|
||||
config.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||
request.setSslConfiguration(config);
|
||||
return request;
|
||||
}
|
||||
|
||||
QNetworkRequest DevicePluginUnifi::createRequest(Device *device, const QString &path)
|
||||
{
|
||||
QString ipAddress = device->paramValue(controllerDeviceIpAddressParamTypeId).toString();
|
||||
return createRequest(ipAddress, path);
|
||||
}
|
||||
|
||||
void DevicePluginUnifi::markOffline(Device *device)
|
||||
{
|
||||
uint gracePeriod = device->setting(clientSettingsGracePeriodParamTypeId).toUInt();
|
||||
QDateTime lastSeenTime = QDateTime::fromMSecsSinceEpoch(device->stateValue(clientLastSeenTimeStateTypeId).toInt() * 1000);
|
||||
if (lastSeenTime.addSecs(gracePeriod * 60) < QDateTime::currentDateTime()) {
|
||||
device->setStateValue(clientIsPresentStateTypeId, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef DEVICEPLUGINUNIFI_H
|
||||
#define DEVICEPLUGINUNIFI_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "devices/deviceplugin.h"
|
||||
|
||||
#include <QNetworkRequest>
|
||||
|
||||
class PluginTimer;
|
||||
|
||||
class DevicePluginUnifi : public DevicePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginunifi.json")
|
||||
Q_INTERFACES(DevicePlugin)
|
||||
|
||||
public:
|
||||
explicit DevicePluginUnifi(QObject *parent = nullptr);
|
||||
~DevicePluginUnifi() override;
|
||||
|
||||
void init() override;
|
||||
void discoverDevices(DeviceDiscoveryInfo *info) override;
|
||||
void startPairing(DevicePairingInfo *info) override;
|
||||
void confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) override;
|
||||
void setupDevice(DeviceSetupInfo *info) override;
|
||||
void postSetupDevice(Device *device) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
// void executeAction(DeviceActionInfo *info) override;
|
||||
|
||||
private:
|
||||
QNetworkRequest createRequest(const QString &address, const QString &path);
|
||||
QNetworkRequest createRequest(Device *device, const QString &path);
|
||||
|
||||
void markOffline(Device *device);
|
||||
private:
|
||||
QHash<DeviceDiscoveryInfo*, Devices> m_pendingDiscoveries;
|
||||
QHash<Device*, QStringList> m_pendingSiteDiscoveries;
|
||||
|
||||
PluginTimer *m_loginTimer = nullptr;
|
||||
PluginTimer *m_pollTimer = nullptr;
|
||||
};
|
||||
|
||||
#endif // DEVICEPLUGINUNIFI_H
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
{
|
||||
"displayName": "UniFi",
|
||||
"name": "unifi",
|
||||
"id": "88bc00c7-9ea8-4aa6-8aec-831639e8fccc",
|
||||
"vendors": [
|
||||
{
|
||||
"id": "0ccc026c-4454-4948-8fcb-be2436d232dd",
|
||||
"name": "ubiquiti",
|
||||
"displayName": "Ubiquiti",
|
||||
"deviceClasses": [
|
||||
{
|
||||
"id": "1da7534c-dd51-4cd2-ab56-48428892c436",
|
||||
"name": "controller",
|
||||
"displayName": "UniFi Controller",
|
||||
"createMethods": ["user"],
|
||||
"setupMethod": "userandpassword",
|
||||
"interfaces": ["gateway"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "9210506a-8c6a-41eb-8462-be93211fc9fe",
|
||||
"name": "ipAddress",
|
||||
"displayName": "IP Address",
|
||||
"type": "QString"
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "2efc35f6-dc58-4cd2-98cc-7e0a1a4f4e01",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected/disconnected",
|
||||
"type": "bool",
|
||||
"cached": false,
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cf1a99ce-ad17-4cc7-8558-480daba20e72",
|
||||
"name": "client",
|
||||
"displayName": "UniFi client",
|
||||
"createMethods": ["discovery"],
|
||||
"interfaces": ["presencesensor"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "a3e7ea90-3f92-4ccf-aec1-b9bc18bfa76f",
|
||||
"name": "mac",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString"
|
||||
},
|
||||
{
|
||||
"id": "32358acf-f5ea-4a7f-b4cb-325963118398",
|
||||
"name": "site",
|
||||
"displayName": "UniFi Site",
|
||||
"type": "QString"
|
||||
}
|
||||
],
|
||||
"settingsTypes": [
|
||||
{
|
||||
"id": "aa10389e-f4a4-44b8-ba1e-e641914425b6",
|
||||
"name": "gracePeriod",
|
||||
"displayName": "Leave timeout",
|
||||
"type": "uint",
|
||||
"defaultValue": "1",
|
||||
"unit": "Minutes"
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "7c2420eb-31eb-43b8-b28c-0dba4a4a3910",
|
||||
"name": "isPresent",
|
||||
"displayName": "Client is connected",
|
||||
"displayNameEvent": "Client connected/disconnected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "8491c998-6100-4a1c-b0b7-6d44696aceba",
|
||||
"name": "lastSeenTime",
|
||||
"displayName": "Last seen time",
|
||||
"displayNameEvent": "Last seen time changed",
|
||||
"type": "int",
|
||||
"unit": "UnixTime",
|
||||
"defaultValue": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>DevicePluginUnifi</name>
|
||||
<message>
|
||||
<location filename="../devicepluginunifi.cpp" line="54"/>
|
||||
<source>Please configure a UniFi controller first.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../devicepluginunifi.cpp" line="72"/>
|
||||
<location filename="../devicepluginunifi.cpp" line="92"/>
|
||||
<source>Fetching sites from controller failed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../devicepluginunifi.cpp" line="83"/>
|
||||
<source>Error communicating with the controller.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../devicepluginunifi.cpp" line="179"/>
|
||||
<source>Please enter your login credentials for the UniFi controller.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>unifi</name>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="37"/>
|
||||
<source>Client connected/disconnected</source>
|
||||
<extracomment>The name of the EventType ({7c2420eb-31eb-43b8-b28c-0dba4a4a3910}) of DeviceClass client</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="40"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="43"/>
|
||||
<source>Client is connected</source>
|
||||
<extracomment>The name of the ParamType (DeviceClass: client, EventType: isPresent, ID: {7c2420eb-31eb-43b8-b28c-0dba4a4a3910})
|
||||
----------
|
||||
The name of the StateType ({7c2420eb-31eb-43b8-b28c-0dba4a4a3910}) of DeviceClass client</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="46"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="49"/>
|
||||
<source>Connected</source>
|
||||
<extracomment>The name of the ParamType (DeviceClass: controller, EventType: connected, ID: {2efc35f6-dc58-4cd2-98cc-7e0a1a4f4e01})
|
||||
----------
|
||||
The name of the StateType ({2efc35f6-dc58-4cd2-98cc-7e0a1a4f4e01}) of DeviceClass controller</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="52"/>
|
||||
<source>Connected/disconnected</source>
|
||||
<extracomment>The name of the EventType ({2efc35f6-dc58-4cd2-98cc-7e0a1a4f4e01}) of DeviceClass controller</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="55"/>
|
||||
<source>IP Address</source>
|
||||
<extracomment>The name of the ParamType (DeviceClass: controller, Type: device, ID: {9210506a-8c6a-41eb-8462-be93211fc9fe})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="58"/>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="61"/>
|
||||
<source>Last seen time</source>
|
||||
<extracomment>The name of the ParamType (DeviceClass: client, EventType: lastSeenTime, ID: {8491c998-6100-4a1c-b0b7-6d44696aceba})
|
||||
----------
|
||||
The name of the StateType ({8491c998-6100-4a1c-b0b7-6d44696aceba}) of DeviceClass client</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="64"/>
|
||||
<source>Last seen time changed</source>
|
||||
<extracomment>The name of the EventType ({8491c998-6100-4a1c-b0b7-6d44696aceba}) of DeviceClass client</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="67"/>
|
||||
<source>Leave timeout</source>
|
||||
<extracomment>The name of the ParamType (DeviceClass: client, Type: settings, ID: {aa10389e-f4a4-44b8-ba1e-e641914425b6})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="70"/>
|
||||
<source>MAC address</source>
|
||||
<extracomment>The name of the ParamType (DeviceClass: client, Type: device, ID: {a3e7ea90-3f92-4ccf-aec1-b9bc18bfa76f})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="73"/>
|
||||
<source>Ubiquiti</source>
|
||||
<extracomment>The name of the vendor ({0ccc026c-4454-4948-8fcb-be2436d232dd})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="76"/>
|
||||
<source>UniFi</source>
|
||||
<extracomment>The name of the plugin unifi ({88bc00c7-9ea8-4aa6-8aec-831639e8fccc})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="79"/>
|
||||
<source>UniFi Controller</source>
|
||||
<extracomment>The name of the DeviceClass ({1da7534c-dd51-4cd2-ab56-48428892c436})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="82"/>
|
||||
<source>UniFi Site</source>
|
||||
<extracomment>The name of the ParamType (DeviceClass: client, Type: device, ID: {32358acf-f5ea-4a7f-b4cb-325963118398})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build-nymea-plugins-Desktop-Debug/unifi/plugininfo.h" line="85"/>
|
||||
<source>UniFi client</source>
|
||||
<extracomment>The name of the DeviceClass ({cf1a99ce-ad17-4cc7-8558-480daba20e72})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
include(../plugins.pri)
|
||||
|
||||
QT += network
|
||||
|
||||
HEADERS += \
|
||||
devicepluginunifi.h \
|
||||
|
||||
SOURCES += \
|
||||
devicepluginunifi.cpp \
|
||||
|
||||
|
||||
Loading…
Reference in New Issue