added nanoleaf plugin

master
Boernsman 2019-12-23 20:44:34 +01:00
parent 9fca1ed4e1
commit 1a8ed79269
10 changed files with 708 additions and 0 deletions

16
debian/control vendored
View File

@ -434,6 +434,21 @@ Description: nymea.io plugin for netatmo
This package will install the nymea.io plugin for netatmo
Package: nymea-plugin-nanoleaf
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
nymea-plugins-translations,
Description: nymea.io plugin for nanoleaf
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 nanoleaf
Package: nymea-plugin-networkdetector
Architecture: any
Depends: ${shlibs:Depends},
@ -845,6 +860,7 @@ Depends: nymea-plugin-anel,
nymea-plugin-lgsmarttv,
nymea-plugin-mailnotification,
nymea-plugin-texasinstruments,
nymea-plugin-nanoleaf,
nymea-plugin-netatmo,
nymea-plugin-networkdetector,
nymea-plugin-openweathermap,

View File

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

1
nanoleaf/README.md Normal file
View File

@ -0,0 +1 @@
# Nanoleaf

View File

@ -0,0 +1,183 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2020 Bernhard Trinnes <bernhard.trinnes@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 "devicepluginnanoleaf.h"
#include "plugininfo.h"
#include "network/zeroconf/zeroconfservicebrowser.h"
#include "platform/platformzeroconfcontroller.h"
#include <QHash>
#include <QDebug>
DevicePluginNanoleaf::DevicePluginNanoleaf()
{
}
void DevicePluginNanoleaf::init()
{
m_zeroconfBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_nanoleafapi._tcp");
}
void DevicePluginNanoleaf::discoverDevices(DeviceDiscoveryInfo *info)
{
QStringList serialNumbers;
foreach (const ZeroConfServiceEntry &entry, m_zeroconfBrowser->serviceEntries()) {
if (info->deviceClassId() == lightPanelsDeviceClassId) {
}
//TODO skip duplicated devices
DeviceDescriptor descriptor(lightPanelsDeviceClassId, entry.name(), entry.hostAddress().toString());
ParamList params;
QString serialNo;
QString model;
QString firmwareVersion;
foreach (QString value, entry.txt()) {
if (value.contains("id=")) {
serialNo = value.split("=").last();
} else if (value.contains("md=")) {
model = value.split("=").last();
} else if (value.contains("srcvers=")) {
firmwareVersion = value.split("=").last();
}
}
if (serialNumbers.contains(serialNo)) {
continue; //To avoid duplicated devices
}
Device *existingDevice = myDevices().findByParams(ParamList() << Param(lightPanelsDeviceSerialNoParamTypeId, serialNo));
if (existingDevice) {
descriptor.setDeviceId(existingDevice->id());
}
serialNumbers.append(serialNo);
qCDebug(dcNanoleaf()) << "Have device" << entry.name() << serialNo << model << firmwareVersion;
params << Param(lightPanelsDeviceAddressParamTypeId, entry.hostAddress().toString());
params << Param(lightPanelsDevicePortParamTypeId, entry.port());
params << Param(lightPanelsDeviceModelParamTypeId, model);
params << Param(lightPanelsDeviceSerialNoParamTypeId, serialNo);
params << Param(lightPanelsDeviceFirmwareVersionParamTypeId, firmwareVersion);
descriptor.setParams(params);
info->addDeviceDescriptor(descriptor);
}
info->finish(Device::DeviceErrorNoError);
}
void DevicePluginNanoleaf::startPairing(DevicePairingInfo *info)
{
info->finish(Device::DeviceErrorNoError, tr("Please press the button"));
}
void DevicePluginNanoleaf::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret)
{
Q_UNUSED(username)
Q_UNUSED(secret)
Nanoleaf *nanoleaf = new Nanoleaf(hardwareManager()->networkManager(), QHostAddress(info->params().paramValue(lightPanelsDeviceAddressParamTypeId).toString()), info->params().paramValue(lightPanelsDevicePortParamTypeId).toInt(), this);
nanoleaf->addUser();
info->finish(Device::DeviceErrorNoError);
}
void DevicePluginNanoleaf::setupDevice(DeviceSetupInfo *info)
{
Device *device = info->device();
if(device->deviceClassId() == lightPanelsDeviceClassId) {
return info->finish(Device::DeviceErrorNoError);
}
}
void DevicePluginNanoleaf::postSetupDevice(Device *device)
{
if (device->deviceClassId() == lightPanelsDeviceClassId) {
//TODO get all the information and set the device states
}
if(!m_pluginTimer) {
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
connect(m_pluginTimer, &PluginTimer::timeout, this, [this]() {
});
}
}
void DevicePluginNanoleaf::deviceRemoved(Device *device)
{
if(device->deviceClassId() == lightPanelsDeviceClassId) {
}
if (myDevices().isEmpty()) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
m_pluginTimer = nullptr;
}
}
void DevicePluginNanoleaf::executeAction(DeviceActionInfo *info)
{
Device *device = info->device();
Action action = info->action();
if (device->deviceClassId() == lightPanelsDeviceClassId) {
Nanoleaf *nanoleaf = m_nanoleafConnections.value(device->id());
if (action.actionTypeId() == lightPanelsPowerActionTypeId) {
bool power = action.param(lightPanelsPowerActionPowerParamTypeId).value().toBool();
QUuid requestId = nanoleaf->setPower(power);
connect(info, &DeviceActionInfo::aborted,[requestId, this](){m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
} else if (action.actionTypeId() == lightPanelsBrightnessActionTypeId) {
int brightness = action.param(lightPanelsBrightnessActionBrightnessParamTypeId).value().toInt();
QUuid requestId = nanoleaf->setBrightness(brightness);
connect(info, &DeviceActionInfo::aborted,[requestId, this](){m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
} else if (action.actionTypeId() == lightPanelsColorActionTypeId) {
QColor color(action.param(lightPanelsColorActionColorParamTypeId).value().toString());
QUuid requestId = nanoleaf->setHue(color);
connect(info, &DeviceActionInfo::aborted,[requestId, this](){m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
} else if (action.actionTypeId() == lightPanelsColorTemperatureActionTypeId) {
int colorTemperature = action.param(lightPanelsColorTemperatureActionColorTemperatureParamTypeId).value().toInt();
QUuid requestId = nanoleaf->setColorTemperature(colorTemperature);
connect(info, &DeviceActionInfo::aborted,[requestId, this](){m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
}
}
}
void DevicePluginNanoleaf::getDeviceStates(Nanoleaf *nanoleaf)
{
nanoleaf->getPower();
nanoleaf->getHue();
nanoleaf->getColorMode();
nanoleaf->getBrightness();
nanoleaf->getSaturation();
nanoleaf->getColorTemperature();
}

View File

@ -0,0 +1,64 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2020 Bernhard Trinnes <bernhard.trinnes@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 DEVICEPLUGINNANOLEAF_H
#define DEVICEPLUGINNANOLEAF_H
#include "devices/deviceplugin.h"
#include "nanoleaf.h"
#include "plugintimer.h"
#include "network/networkaccessmanager.h"
#include "network/zeroconf/zeroconfservicebrowser.h"
#include <QHostAddress>
class DevicePluginNanoleaf: public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginnanoleaf.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginNanoleaf();
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:
ZeroConfServiceBrowser *m_zeroconfBrowser = nullptr;
PluginTimer *m_pluginTimer = nullptr;
QHash<DeviceId, Nanoleaf*> m_nanoleafConnections;
QHash<QUuid, DeviceActionInfo *> m_asyncActions;
void getDeviceStates(Nanoleaf *nanoleaf);
};
#endif // DEVICEPLUGINNANOLEAF_H

View File

@ -0,0 +1,115 @@
{
"name": "Nanoleaf",
"displayName": "Nanoleaf",
"id": "360867ec-1594-498d-8182-fbab1fe17489",
"vendors": [
{
"id": "3d7fdaa6-7896-419b-8be3-c90c42bcac7f",
"name": "nanoleaf",
"displayName": "Nanoleaf",
"deviceClasses": [
{
"id": "d44ee383-9fa5-4751-babd-1129ac20896a",
"name": "lightPanels",
"displayName": "Light panels",
"interfaces": ["colorlight", "colortemperaturelight", "connectable"],
"createMethods": ["discovery"],
"paramTypes": [
{
"id": "ff57079f-d5ab-4511-8a5c-0726e7b82af6",
"name": "address",
"displayName": "Address",
"type" : "QString"
},
{
"id": "ba4fd45b-990d-480a-859d-fff7ffba3ba4",
"name": "port",
"displayName": "Port",
"type" : "int",
"readOnly": true
},
{
"id": "353d3c71-0ad2-40d5-99f6-cc305e2073f1",
"name": "model",
"displayName": "Model",
"type" : "QString",
"readOnly": true
},
{
"id": "18be4a5f-e2c2-4070-bc3e-ea9fe64f2276",
"name": "serialNo",
"displayName": "Serial number",
"type" : "QString",
"readOnly": true
},
{
"id": "1b85eebe-3b1a-49a9-bddb-2175d6599b95",
"name": "firmwareVersion",
"displayName": "Firmware version",
"type" : "QString",
"readOnly": true
}
],
"stateTypes": [
{
"id": "a3102107-a825-4ec8-a9ec-b2c2a9fb5c89",
"name": "connected",
"displayName": "Reachable",
"displayNameEvent": "Reachable changed",
"defaultValue": false,
"type": "bool",
"cached": false
},
{
"id": "44bee9ec-513d-4834-991a-ee9ae69d9f2a",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "41248127-844b-40be-87e6-38aee48b6687",
"name": "colorTemperature",
"displayName": "Color temperature",
"displayNameEvent": "Color temperature changed",
"displayNameAction": "Set color temperature",
"type": "int",
"unit": "Mired",
"defaultValue": 170,
"minValue": 153,
"maxValue": 500,
"writable": true
},
{
"id": "d4a52cdc-93b2-44fc-a36c-ae65f1d98f2e",
"name": "color",
"displayName": "Color",
"displayNameEvent": "Color changed",
"displayNameAction": "Set color",
"type": "QColor",
"defaultValue": "#000000",
"writable": true
},
{
"id": "4e5d6460-d42e-4b7c-a8f3-6e953451c1ef",
"name": "brightness",
"displayName": "Brightness",
"displayNameEvent": "Brightness changed",
"displayNameAction": "Set brigtness",
"type": "int",
"unit": "Percentage",
"defaultValue": 0,
"minValue": 0,
"maxValue": 100,
"writable": true
}
]
}
]
}
]
}

213
nanoleaf/nanoleaf.cpp Normal file
View File

@ -0,0 +1,213 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2020 Bernhard Trinnes <bernhard.trinnes@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 "nanoleaf.h"
#include "extern-plugininfo.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QUrlQuery>
Nanoleaf::Nanoleaf(NetworkAccessManager *networkManager, const QHostAddress &address, int port, QObject *parent) :
QObject(parent),
m_networkManager(networkManager),
m_address(address),
m_port(port)
{
}
void Nanoleaf::setIpAddress(const QHostAddress &address)
{
m_address = address;
}
QHostAddress Nanoleaf::ipAddress()
{
return m_address;
}
void Nanoleaf::setPort(int port)
{
m_port = port;
}
int Nanoleaf::port()
{
return m_port;
}
void Nanoleaf::addUser()
{
QUrl url;
url.setHost(m_address.toString());
url.setPort(m_port);
url.setPath("/api/v1/new");
QNetworkRequest request;
request.setUrl(url);
QNetworkReply *reply = m_networkManager->post(request, "");
qDebug(dcNanoleaf()) << "Sending request" << request.url();
connect(reply, &QNetworkReply::finished, this, [reply, this] {
reply->deleteLater();
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// Check HTTP status code
if (status != 200 || reply->error() != QNetworkReply::NoError) {
if (reply->error() == QNetworkReply::HostNotFoundError) {
emit connectionChanged(false);
}
if (status == 400 || status == 401) {
emit authenticationStatusChanged(false);
}
qCWarning(dcNanoleaf()) << "Request error:" << status << reply->errorString();
return;
}
emit connectionChanged(true);
emit authenticationStatusChanged(true);
QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
if (error.error != QJsonParseError::NoError) {
qDebug(dcNanoleaf()) << "Recieved invalide JSON object";
return;
}
m_authToken = data.toVariant().toMap().value("auth_token").toString();
});
}
void Nanoleaf::deleteUser()
{
QUrl url;
url.setHost(m_address.toString());
url.setPort(m_port);
url.setPath("/api/v1/"+m_authToken);
QNetworkRequest request;
request.setUrl(url);
QNetworkReply *reply = m_networkManager->deleteResource(request);
qDebug(dcNanoleaf()) << "Sending request" << request.url();
connect(reply, &QNetworkReply::finished, this, [reply, this] {
reply->deleteLater();
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 204 || reply->error() != QNetworkReply::NoError) {
qCWarning(dcNanoleaf()) << "Request error:" << status << reply->errorString();
return;
}
});
}
void Nanoleaf::getPower()
{
}
void Nanoleaf::getHue()
{
}
void Nanoleaf::getBrightness()
{
}
void Nanoleaf::getSaturation()
{
}
void Nanoleaf::getColorTemperature()
{
}
void Nanoleaf::getColorMode()
{
}
QUuid Nanoleaf::setPower(bool power)
{
QUuid requestId = QUuid::createUuid();
QUrl url;
url.setHost(m_address.toString());
url.setPort(m_port);
url.setPath(QString("/api/v1/%1/state/on").arg(m_authToken));
QVariantMap map;
QVariantMap value;
value["value"] = power;
map.insert("on", value);
QJsonDocument body = QJsonDocument::fromVariant(map);
QNetworkRequest request;
request.setUrl(url);
QNetworkReply *reply = m_networkManager->put(request, body.toJson());
qDebug(dcNanoleaf()) << "Sending request" << request.url();
connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] {
reply->deleteLater();
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 204 || reply->error() != QNetworkReply::NoError) {
emit requestedExecuted(requestId, false);
qCWarning(dcNanoleaf()) << "Request error:" << status << reply->errorString();
return;
}
emit requestedExecuted(requestId, true);
});
return requestId;
}
QUuid Nanoleaf::setHue(QColor color)
{
Q_UNUSED(color);
QUuid requestId = QUuid::createUuid();
return requestId;
}
QUuid Nanoleaf::setBrightness(int percentage)
{
Q_UNUSED(percentage);
QUuid requestId = QUuid::createUuid();
return requestId;
}
QUuid Nanoleaf::setSaturation(int percentage)
{
Q_UNUSED(percentage);
QUuid requestId = QUuid::createUuid();
return requestId;
}
QUuid Nanoleaf::setColorTemperature(int mired)
{
Q_UNUSED(mired);
QUuid requestId = QUuid::createUuid();
return requestId;
}

98
nanoleaf/nanoleaf.h Normal file
View File

@ -0,0 +1,98 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2020 Bernhard Trinnes <bernhard.trinnes@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 NANOLEAF_H
#define NANOLEAF_H
#include <QObject>
#include <QTimer>
#include <QUuid>
#include <QHostAddress>
#include <QColor>
#include "network/networkaccessmanager.h"
#include "devices/device.h"
class Nanoleaf : public QObject
{
Q_OBJECT
public:
explicit Nanoleaf(NetworkAccessManager *networkManager, const QHostAddress &address, int port = 16021, QObject *parent = nullptr);
void setIpAddress(const QHostAddress &address);
QHostAddress ipAddress();
void setPort(int port);
int port();
//AUTHORIZATION
void addUser();
void deleteUser();
//GET ALL PANEL INFORMATION
//STATES
void getPower();
void getHue();
void getBrightness();
void getSaturation();
void getColorTemperature();
void getColorMode();
QUuid setPower(bool power);
QUuid setHue(QColor color);
QUuid setBrightness(int percentage);
QUuid setSaturation(int percentage);
QUuid setColorTemperature(int mired);
//EFFECTS
//PANEL LAYOUT
//IDENTIFY
//EXTERNAL CONTROL
//RHYTHM
private:
NetworkAccessManager *m_networkManager = nullptr;
QString m_authToken;
QHostAddress m_address;
int m_port;
signals:
void connectionChanged(bool connected);
void authenticationStatusChanged(bool authenticated);
void requestedExecuted(QUuid requestId, bool success);
void powerReceived(bool power);
void brightnessReceived(int percentage);
void colorModeReceived();
void hueReceived(QColor color);
void saturationReceived(int percentage);
};
#endif // NANOLEAF_H

16
nanoleaf/nanoleaf.pro Normal file
View File

@ -0,0 +1,16 @@
include(../plugins.pri)
TARGET = $$qtLibraryTarget(nymea_devicepluginnanoleaf)
QT += network
SOURCES += \
devicepluginnanoleaf.cpp \
nanoleaf.cpp \
HEADERS += \
devicepluginnanoleaf.h \
nanoleaf.h \

View File

@ -28,6 +28,7 @@ PLUGIN_DIRS = \
lgsmarttv \
mailnotification \
mqttclient \
nanoleaf \
netatmo \
networkdetector \
onewire \