API unstability of Yeelight is bugging me

This commit is contained in:
Boernsman 2019-12-29 20:52:17 +01:00
parent 9014a17618
commit c78d3663d9
7 changed files with 69 additions and 35 deletions

View File

@ -1 +1,5 @@
# Yeelight
This plug-in supports dimmable- and color bulbs from yeelight.
Discovery works with min. firmware version 2.0.6_0065

View File

@ -46,19 +46,19 @@ void DevicePluginYeelight::init()
m_brightnessStateTypeIds.insert(colorBulbDeviceClassId, colorBulbBrightnessStateTypeId);
m_brightnessStateTypeIds.insert(dimmableBulbDeviceClassId, dimmableBulbBrightnessStateTypeId);
m_ssdp = new Ssdp(this);
m_ssdp->enable();
m_ssdp->discover();
}
void DevicePluginYeelight::discoverDevices(DeviceDiscoveryInfo *info)
{
if (info->deviceClassId() == colorBulbDeviceClassId) {
DiscoveryJob *discovery = new DiscoveryJob();
m_discoveries.insert(info, discovery);
qCDebug(dcYeelight()) << "Starting UPnP discovery...";
Ssdp *ssdp = new Ssdp(this);
ssdp->enable();
ssdp->discover();
/*
connect(ssdp, &Ssdp::discovered, this,[info, this](const QString &address, int port, int id, const QString &model) {
DeviceDescriptor descriptor(colorBulbDeviceClassId, "Yeelight"+ model + "Bulb", address);
@ -75,7 +75,7 @@ void DevicePluginYeelight::discoverDevices(DeviceDiscoveryInfo *info)
descriptor.setParams(params);
qCDebug(dcYeelight()) << "UPnP: Found Yeelight device:" << id;
info->finish(Device::DeviceErrorNoError);
});
});*/
}
}
@ -113,13 +113,15 @@ void DevicePluginYeelight::postSetupDevice(Device *device)
if (yeelight->isConnected()) {
QList<Yeelight::YeelightProperty> properties;
if (device->deviceClassId() == colorBulbDeviceClassId) {
properties << Yeelight::YeelightProperty::Name;
properties << Yeelight::YeelightProperty::Ct;
properties << Yeelight::YeelightProperty::Rgb;
properties << Yeelight::YeelightProperty::Power;
properties << Yeelight::YeelightProperty::Bright;
properties << Yeelight::YeelightProperty::ColorMode;
} //TODO add white color bulb with other property requests
} else if (device->deviceClassId() == colorBulbDeviceClassId) {
properties << Yeelight::YeelightProperty::Power;
properties << Yeelight::YeelightProperty::Bright;
}
yeelight->getParam(properties);
}
}
@ -162,14 +164,14 @@ void DevicePluginYeelight::executeAction(DeviceActionInfo *info)
});
}
} else if (action.actionTypeId() == colorBulbColorActionColorParamTypeId) {
QRgb color = action.param(colorBulbColorActionColorParamTypeId).value().toUInt();
QRgb color = QColor(action.param(colorBulbColorActionColorParamTypeId).value().toString()).rgba();
if (!device->stateValue(colorBulbPowerStateTypeId).toBool())
yeelight->setPower(true);
int requestId = yeelight->setRgb(color);
connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
} else if (action.actionTypeId() == colorBulbColorTemperatureActionTypeId) {
int colorTemperature = action.param(colorBulbColorTemperatureActionColorTemperatureParamTypeId).value().toUInt() * 11.12;
int colorTemperature = 6500 - (action.param(colorBulbColorTemperatureActionColorTemperatureParamTypeId).value().toUInt() * -11.12);
if (!device->stateValue(colorBulbPowerStateTypeId).toBool())
yeelight->setPower(true);
int requestId = yeelight->setColorTemperature(colorTemperature);
@ -201,6 +203,10 @@ void DevicePluginYeelight::executeAction(DeviceActionInfo *info)
connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
}
} else if (action.actionTypeId() == colorBulbDefaultActionTypeId) {
int requestId = yeelight->setDefault();
connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
} else {
qCWarning(dcYeelight()) << "Unhandled action";
}
@ -229,6 +235,10 @@ void DevicePluginYeelight::executeAction(DeviceActionInfo *info)
connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
}
} else if (action.actionTypeId() == dimmableBulbDefaultActionTypeId) {
int requestId = yeelight->setDefault();
connect(info, &DeviceActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
m_asyncActions.insert(requestId, info);
} else {
qCWarning(dcYeelight()) << "Unhandled action";
}
@ -239,7 +249,8 @@ void DevicePluginYeelight::executeAction(DeviceActionInfo *info)
void DevicePluginYeelight::deviceRemoved(Device *device)
{
if ((device->deviceClassId() == colorBulbDeviceClassId) || (device->deviceClassId() == dimmableBulbDeviceClassId)){
if ((device->deviceClassId() == colorBulbDeviceClassId) ||
(device->deviceClassId() == dimmableBulbDeviceClassId)){
Yeelight *yeelight = m_yeelightConnections.take(device->id());
yeelight->deleteLater();
}

View File

@ -28,6 +28,7 @@
#include "network/networkaccessmanager.h"
#include "network/upnp/upnpdiscovery.h"
#include "yeelight.h"
#include "ssdp.h"
#include <QTimer>
@ -49,15 +50,7 @@ public:
void deviceRemoved(Device *device) override;
private:
class DiscoveryJob {
public:
UpnpDiscoveryReply* upnpReply;
QNetworkReply* nUpnpReply;
DeviceDescriptors results;
};
QHash<DeviceDiscoveryInfo*, DiscoveryJob*> m_discoveries;
QList<QString> m_usedInterfaces;
Ssdp *m_ssdp = nullptr;
PluginTimer *m_pluginTimer = nullptr;
QHash<int, DeviceActionInfo *> m_asyncActions;
QHash<DeviceId, Yeelight *> m_yeelightConnections;

View File

@ -53,6 +53,7 @@ bool Ssdp::enable()
// Bind udp socket and join multicast group
m_socket = new QUdpSocket(this);
m_socket2 = new QUdpSocket(this);
m_port = 1982;
m_host = QHostAddress("239.255.255.250");
@ -67,6 +68,13 @@ bool Ssdp::enable()
return false;
}
if(!m_socket2->bind(QHostAddress::AnyIPv4, 34343, QUdpSocket::ShareAddress)){
qCWarning(dcYeelight()) << "could not bind to port" << 34343;
delete m_socket2;
m_socket2 = nullptr;
return false;
}
if(!m_socket->joinMulticastGroup(m_host)){
qCWarning(dcYeelight()) << "could not join multicast group" << m_host;
m_available = false;
@ -76,6 +84,7 @@ bool Ssdp::enable()
}
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
connect(m_socket, &QUdpSocket::readyRead, this, &Ssdp::readData);
connect(m_socket2, &QUdpSocket::readyRead, this, &Ssdp::readData2);
return true;
}
@ -84,8 +93,8 @@ void Ssdp::discover()
QByteArray searchMessage = QByteArray("M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1982\r\n"
"MAN: \"ssdp:discover\"\r\n"
"ST: wifi_bulb\r\n\r\n");
m_socket->writeDatagram(searchMessage, m_host, m_port);
"ST: wifi_bulb\r\n");
m_socket2->writeDatagram(searchMessage, m_host, m_port);
}
@ -134,6 +143,20 @@ void Ssdp::readData()
}
}
void Ssdp::readData2()
{
QByteArray data;
quint16 port;
QHostAddress hostAddress;
// read the answere from the multicast
while (m_socket->hasPendingDatagrams()) {
data.resize(m_socket->pendingDatagramSize());
m_socket->readDatagram(data.data(), data.size(), &hostAddress, &port);
}
qCDebug(dcYeelight())<< "SSDP message received " << data;
}
bool Ssdp::disable()
{

View File

@ -45,6 +45,7 @@ public:
bool disable();
private:
QUdpSocket *m_socket = nullptr;
QUdpSocket *m_socket2 = nullptr;
QHostAddress m_host;
quint16 m_port;
@ -64,6 +65,7 @@ signals:
private slots:
void error(QAbstractSocket::SocketError error);
void readData();
void readData2();
};
#endif // SSDP_H

View File

@ -145,7 +145,7 @@ int Yeelight::getParam(QList<Yeelight::YeelightProperty> properties)
QTimer::singleShot(10000, this, [requestId, this]{m_propertyRequests.remove(requestId);});
obj["params"] = params;
doc.setObject(obj);
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
//qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
m_socket->write(doc.toJson() + "\r\n");
return requestId;
}
@ -161,7 +161,7 @@ int Yeelight::setName(const QString &name)
params.append(name);
obj["params"] = params;
doc.setObject(obj);
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
//qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
m_socket->write(doc.toJson() + "\r\n");
return requestId;
}
@ -192,12 +192,12 @@ int Yeelight::setRgb(QRgb color, int msFadeTime)
obj["id"] = requestId;
obj["method"] = "set_rgb";
QJsonArray params;
params.append(QVariant(color).toInt());
params.append(static_cast<int>(color & 0x00ffffff));
params.append("smooth");
params.append(msFadeTime);
obj["params"] = params;
doc.setObject(obj);
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
//qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
m_socket->write(doc.toJson() + "\r\n");
return requestId;
}
@ -252,7 +252,7 @@ int Yeelight::setDefault()
QJsonArray params;
obj["params"] = params;
doc.setObject(obj);
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
//qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
m_socket->write(doc.toJson() + "\r\n");
return requestId;
}
@ -267,7 +267,7 @@ int Yeelight::startColorFlow()
QJsonArray params;
params.append(0); //0 means infinite loop on the state changing
params.append(0); //LED recover to the state before the color flow started
params.append("2000, 1, 255, -1, 2000, 1, 16711680, -1, 2000, 1, 65280, -1,"); //Colors
params.append("2000, 1, 255, 50, 2000, 1, 5000, 50, 2000, 1, 6000, 50"); //Colors
obj["params"] = params;
doc.setObject(obj);
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
@ -298,9 +298,9 @@ int Yeelight::flash()
obj["id"] = requestId;
obj["method"] = "start_cf";
QJsonArray params;
params.append(6);
params.append(4 * 3);
params.append(0); //LED recover to the state before the color flow started
params.append("500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0,"); //Colors
params.append("50, 2, 6500, 100, 500, 7, 6500, 1, 50, 2, 6500, 1, 500, 7, 6500, 1");
obj["params"] = params;
doc.setObject(obj);
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
@ -316,9 +316,9 @@ int Yeelight::flash15s()
obj["id"] = requestId;
obj["method"] = "start_cf";
QJsonArray params;
params.append(30);
params.append(4 * 15);
params.append(0); //LED recover to the state before the color flow started
params.append("500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0, 500, 2, 4000, 100, 500, 7, 0, 0"); //Colors
params.append("50, 2, 6500, 100, 500, 7, 6500, 1, 50, 2, 6500, 1, 500, 7, 6500, 1");
obj["params"] = params;
doc.setObject(obj);
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
@ -345,7 +345,7 @@ void Yeelight::onStateChanged(QAbstractSocket::SocketState state)
void Yeelight::onReadyRead()
{
QByteArray data = m_socket->readAll();
qCDebug(dcYeelight()) << "Message received" << data;
//qCDebug(dcYeelight()) << "Message received" << data;
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
@ -446,6 +446,7 @@ void Yeelight::onReconnectTimer()
{
if(!m_socket->isOpen()) {
m_socket->connectToHost(m_address, m_port);
m_reconnectTimer->start(10 * 1000);
}
}

View File

@ -7,9 +7,9 @@ TARGET = $$qtLibraryTarget(nymea_devicepluginyeelight)
SOURCES += \
devicepluginyeelight.cpp \
yeelight.cpp \
ssdp.cpp
ssdp.cpp \
HEADERS += \
devicepluginyeelight.h \
yeelight.h \
ssdp.h
ssdp.h \