added yeelight

This commit is contained in:
Boernsman 2019-12-25 23:21:11 +01:00
parent 6e781113e5
commit e335524421
8 changed files with 633 additions and 0 deletions

View File

@ -65,6 +65,7 @@ PLUGIN_DIRS = \
wakeonlan \
wemo \
ws2812fx \
yeelight \
message(============================================)

1
yeelight/README.md Normal file
View File

@ -0,0 +1 @@
# Yeelight

View File

@ -0,0 +1,113 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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 "devicepluginyeelight.h"
#include "devices/device.h"
#include "types/param.h"
#include "plugininfo.h"
#include "network/upnp/upnpdiscovery.h"
#include "network/upnp/upnpdiscoveryreply.h"
#include <QDebug>
#include <QColor>
DevicePluginYeelight::DevicePluginYeelight()
{
}
void DevicePluginYeelight::discoverDevices(DeviceDiscoveryInfo *info)
{
if (info->deviceClassId() == colorBulbDeviceClassId) {
DiscoveryJob *discovery = new DiscoveryJob();
m_discoveries.insert(info, discovery);
qCDebug(dcYeelight()) << "Starting UPnP discovery...";
UpnpDiscoveryReply *upnpReply = hardwareManager()->upnpDiscovery()->discoverDevices("wifi_bulb");
discovery->upnpReply = upnpReply;
// Always clean up the upnp discovery
connect(upnpReply, &UpnpDiscoveryReply::finished, upnpReply, &UpnpDiscoveryReply::deleteLater);
// Process results if info is still around
connect(upnpReply, &UpnpDiscoveryReply::finished, info, [this, upnpReply, discovery](){
// Indicate we're done...
discovery->upnpReply = nullptr;
if (upnpReply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
qCWarning(dcYeelight()) << "Upnp discovery error" << upnpReply->error();
}
foreach (const UpnpDeviceDescriptor &upnpDevice, upnpReply->deviceDescriptors()) {
if (upnpDevice.modelDescription().contains("color")) {
DeviceDescriptor descriptor(bridgeDeviceClassId, "Yeelight Color Bulb", upnpDevice.hostAddress().toString());
ParamList params;
QString bridgeId = upnpDevice.serialNumber().toLower();
foreach (Device *existingDevice, myDevices()) {
if (existingDevice->paramValue(bridgeDeviceIdParamTypeId).toString() == bridgeId) {
descriptor.setDeviceId(existingDevice->id());
break;
}
}
params.append(Param(bridgeDeviceHostParamTypeId, upnpDevice.hostAddress().toString()));
params.append(Param(bridgeDeviceIdParamTypeId, bridgeId));
descriptor.setParams(params);
qCDebug(dcYeelight()) << "UPnP: Found Hue bridge:" << bridgeId;
discovery->results.append(descriptor);
}
}
//finishDiscovery(discovery);
});
info->finish(Device::DeviceErrorNoError);
}
}
void DevicePluginYeelight::setupDevice(DeviceSetupInfo *info)
{
Device *device = info->device();
if (device->deviceClassId() == colorBulbDeviceClassId) {
QHostAddress *address = device->paramValue(colorBulbDeviceAddressParamTypeId).toString();
int port;
Yeelight *yeelight = new Yeelight(hardwareManager(), address, port, this);
info->finish(Device::DeviceErrorNoError);
}
}
void DevicePluginYeelight::executeAction(DeviceActionInfo *info)
{
Device *device = info->device();
Action action = info->action();
if (device->deviceClassId() == colorBulbDeviceClassId) {
// if (action.actionTypeId() == ) {
//}
}
}
void DevicePluginYeelight::deviceRemoved(Device *device)
{
}

View File

@ -0,0 +1,69 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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 DEVICEPLUGINYEELIGHT_H
#define DEVICEPLUGINYEELIGHT_H
#include "devices/deviceplugin.h"
#include "plugintimer.h"
#include "network/networkaccessmanager.h"
#include "network/upnp/upnpdiscovery.h"
#include "yeelight.h"
#include <QTimer>
class DevicePluginYeelight : public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginyeelight.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginYeelight();
void discoverDevices(DeviceDiscoveryInfo *info) override;
void setupDevice(DeviceSetupInfo *info) override;
void executeAction(DeviceActionInfo *info) override;
void deviceRemoved(Device *device) override;
private:
class DiscoveryJob {
public:
UpnpDiscoveryReply* upnpReply;
QNetworkReply* nUpnpReply;
DeviceDescriptors results;
};
QHash<DeviceDiscoveryInfo*, DiscoveryJob*> m_discoveries;
QList<QString> m_usedInterfaces;
QTimer *m_reconnectTimer = nullptr;
QHash<int, DeviceActionInfo *> m_asyncActions;
private slots:
void onReadyRead();
void onReconnectTimer();
signals:
};
#endif // DEVICEPLUGINYEELIGHT_H

View File

@ -0,0 +1,102 @@
{
"displayName": "Yeelight",
"name": "yeelight",
"id": "40012951-be9f-4de6-b6e1-0a7455fdd416",
"vendors": [
{
"name": "Yeelight",
"displayName": "Yeelight",
"id": "74011456-0a0f-4e43-bd94-bf4bb69593a4",
"deviceClasses": [
{
"id": "7d801b71-0aee-4b5d-ae62-a950473b20f5",
"name": "colorBulb",
"displayName": "Color bulb",
"createMethods": ["user", "discovery"],
"interfaces": ["colorlight", "connectable"],
"paramTypes": [
{
"id": "a603ff66-0da8-42e8-942b-1632a22adefb",
"name": "host",
"displayName": "Host address",
"type" : "QString",
"inputType": "IPv4Address",
"readOnly": true
},
{
"id": "bfe184d2-6e62-4979-8ff2-3fd190903c82",
"name": "id",
"displayName": "Id",
"type" : "QString",
"readOnly": true
}
],
"actionTypes": [
{
"id": "e80aea31-7ac0-4f71-9650-efb70ea1e768",
"name": "default",
"displayName": "Set default",
"type": "bool"
}
],
"stateTypes": [
{
"id": "7cb7d7ae-f281-46c9-83ea-52b1d7e8e2c8",
"name": "connected",
"displayName": "Reachable",
"displayNameEvent": "Reachable changed",
"defaultValue": false,
"type": "bool"
},
{
"id": "759b661e-8726-41c8-9c29-c5885eb19018",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "01f058cc-4bfb-439c-8344-f7ffdd2b2ed0",
"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": "d74cfe47-b6e6-47ae-a8e9-bb99b8ae63fe",
"name": "color",
"displayName": "Color",
"displayNameEvent": "Color changed",
"displayNameAction": "Set color",
"type": "QColor",
"defaultValue": "#000000",
"writable": true
},
{
"id": "a7a16fb8-0e8b-4aa3-977d-3ff7cd65c708",
"name": "brightness",
"displayName": "Brightness",
"displayNameEvent": "Brightness changed",
"displayNameAction": "Set brigtness",
"type": "int",
"unit": "Percentage",
"defaultValue": 0,
"minValue": 0,
"maxValue": 100,
"writable": true
}
]
}
]
}
]
}

239
yeelight/yeelight.cpp Normal file
View File

@ -0,0 +1,239 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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 "yeelight.h"
#include "extern-plugininfo.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QRandomGenerator>
Yeelight::Yeelight(NetworkAccessManager *networkManager, const QHostAddress &address, quint16 port, QObject *parent) :
QObject(parent),
m_address(address),
m_port(port),
m_networkManager(networkManager)
{
m_socket = new QTcpSocket(this);
connect(m_socket, &QTcpSocket::stateChanged, this, &Yeelight::onStateChanged);
m_socket->connectToHost(address, port);
}
bool Yeelight::connected()
{
return m_socket->isOpen();
}
int Yeelight::getParam(QList<Yeelight::Property> properties)
{
int requestId = static_cast<int>(QRandomGenerator::global()->generate());
QJsonDocument doc;
QJsonObject obj;
obj["id"] = requestId;
obj["method"] = "get_prop";
QJsonArray params;
foreach (Property property, properties) {
switch (property) {
case Property::Ct:
params.append("ct");
break;
case Property::Power:
params.append("powr");
break;
case Property::Bright:
params.append("bright");
break;
case Property::Hue:
params.append("hue");
break;
case Property::Rgb:
params.append("rgb");
break;
case Property::Sat:
params.append("sat");
break;
case Property::Name:
params.append("name");
break;
case Property::BgCt:
params.append("bg_ct");
break;
case Property::NlBr:
params.append("nl_br");
break;
case Property::BgHue:
params.append("bg_hue");
break;
case Property::BgRgb:
params.append("bg_rgb");
break;
case Property::BgSat:
params.append("bg_sat");
break;
case Property::BgLmode:
params.append("bg_lmode");
break;
case Property::BgPower:
params.append("bg_power");
break;
case Property::Flowing:
params.append("flowing");
break;
case Property::MusicOn:
params.append("music_on");
break;
case Property::BgBright:
params.append("bg_bright");
break;
case Property::DelayOff:
params.append("delay_off");
break;
case Property::BgFlowing:
params.append("bg_flowing");
break;
case Property::ColorMode:
params.append("color_mode");
break;
case Property::ActiveMode:
params.append("active_mode");
break;
case Property::FlowParams:
params.append("flow_params");
break;
case Property::BgFlowParams:
params.append("bg_flow_params");
break;
}
}
obj["params"] = params;
doc.setObject(obj);
m_socket->write(doc.toJson());
return requestId;
}
int Yeelight::setName(const QString &name)
{
int requestId = static_cast<int>(QRandomGenerator::global()->generate());
QJsonDocument doc;
QJsonObject obj;
obj["id"] = requestId;
obj["method"] = "set_name";
QJsonArray params;
params.append(name);
obj["params"] = params;
return requestId;
}
int Yeelight::setColorTemperature(int mirad, int msFadeTime)
{
int requestId = static_cast<int>(QRandomGenerator::global()->generate());
QJsonDocument doc;
QJsonObject obj;
obj["id"] = requestId;
obj["method"] = "set_ct_abx";
QJsonArray params;
params.append(mirad);
params.append("smooth");
params.append(msFadeTime);
obj["params"] = params;
return requestId;
}
int Yeelight::setRgb(QRgb color, int msFadeTime)
{
int requestId = static_cast<int>(QRandomGenerator::global()->generate());
QJsonDocument doc;
QJsonObject obj;
obj["id"] = requestId;
obj["method"] = "set_rgb";
QJsonArray params;
params.append(QVariant(color).toInt());
params.append("smooth");
params.append(msFadeTime);
obj["params"] = params;
return requestId;
}
int Yeelight::setBrightness(int percentage, int msFadeTime)
{
int requestId = static_cast<int>(QRandomGenerator::global()->generate());
QJsonDocument doc;
QJsonObject obj;
obj["id"] = requestId;
obj["method"] = "set_bright";
QJsonArray params;
params.append(percentage);
params.append("smooth");
params.append(msFadeTime);
obj["params"] = params;
return requestId;
}
int Yeelight::setPower(bool power, int msFadeTime)
{
int requestId = static_cast<int>(QRandomGenerator::global()->generate());
QJsonDocument doc;
QJsonObject obj;
obj["id"] = requestId;
obj["method"] = "set_power";
QJsonArray params;
if(power) {
params.append("on");
} else {
params.append("off");
}
params.append("smooth");
params.append(msFadeTime);
obj["params"] = params;
return requestId;
}
int Yeelight::setDefault()
{
int requestId = static_cast<int>(QRandomGenerator::global()->generate());
QJsonDocument doc;
QJsonObject obj;
obj["id"] = requestId;
obj["method"] = "set_default";
QJsonArray params;
obj["params"] = params;
return requestId;
}
void Yeelight::onStateChanged(QAbstractSocket::SocketState state)
{
switch (state) {
case QAbstractSocket::SocketState::ConnectedState:
break;
default:
break;
}
}

95
yeelight/yeelight.h Normal file
View File

@ -0,0 +1,95 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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 YEELIGHT_H
#define YEELIGHT_H
#include <QObject>
#include <QTimer>
#include <QHostAddress>
#include <QTcpSocket>
#include "network/networkaccessmanager.h"
#include "devices/device.h"
#include <QRgb>
class Yeelight : public QObject
{
Q_OBJECT
public:
enum Property {
Power, //on: smart LED is turned on / off: smart LED is turned off
Bright, //Brightness percentage. Range 1 ~ 100
Ct, //Color temperature. Range 1700 ~ 6500(k)
Rgb, //Color. Range 1 ~ 16777215
Hue, //Hue. Range 0 ~ 359
Sat, //Saturation. Range 0 ~ 100
ColorMode, //1: rgb mode / 2: color temperature mode / 3: hsv mode
Flowing, //0: no flow is running / 1:color flow is running
DelayOff, //The remaining time of a sleep timer. Range 1 ~ 60 (minutes)
FlowParams, //Current flow parameters (only meaningful when 'flowing' is 1)
MusicOn, //1: Music mode is on / 0: Music mode is off
Name, //The name of the device set by “set_name” command
BgPower, //Background light power status
BgFlowing, //Background light is flowing
BgFlowParams, //Current flow parameters of background light
BgCt, //Color temperature of background light
BgLmode, //1: rgb mode / 2: color temperature mode / 3: hsv mode
BgBright, //Brightness percentage of background light
BgRgb, //Color of background light
BgHue, //Hue of background light
BgSat, //Saturation of background light
NlBr, //Brightness of night mode light
ActiveMode //0: daylight mode / 1: moonlight mode (ceiling light only)
};
explicit Yeelight(NetworkAccessManager *networkManager, const QHostAddress &address, quint16 port = 55443, QObject *parent = nullptr);
bool connected();
int getParam(QList<Property> properties);
int setName(const QString &name);
int setColorTemperature(int mirad, int msFadeTime=500);
int setRgb(QRgb color, int msFadeTime = 500);
int setBrightness(int percentage, int msFadeTime = 500);
int setPower(bool power, int msFadeTime = 500);
int setDefault();
private:
QTcpSocket *m_socket = nullptr;
QHostAddress m_address;
qint16 m_port;
NetworkAccessManager *m_networkManager = nullptr;
private slots:
void onRefreshTimeout();
void onStateChanged(QAbstractSocket::SocketState state);
signals:
void connectionChanged(bool connected);
void authenticationStatusChanged(bool authenticated);
void requestExecuted(int requestId, bool success);
void propertyReceived(Property property, QVariant value);
};
#endif // YEELIGHT_H

13
yeelight/yeelight.pro Normal file
View File

@ -0,0 +1,13 @@
include(../plugins.pri)
QT += network
TARGET = $$qtLibraryTarget(nymea_devicepluginyeelight)
SOURCES += \
devicepluginyeelight.cpp \
yeelight.cpp \
HEADERS += \
devicepluginyeelight.h \
yeelight.h \