diff --git a/yeelight/README.md b/yeelight/README.md index 1aeecc2d..9ad64714 100644 --- a/yeelight/README.md +++ b/yeelight/README.md @@ -1 +1,5 @@ # Yeelight + +This plug-in supports dimmable- and color bulbs from yeelight. + +Discovery works with min. firmware version 2.0.6_0065 diff --git a/yeelight/devicepluginyeelight.cpp b/yeelight/devicepluginyeelight.cpp index bb4ba167..5d3f268a 100644 --- a/yeelight/devicepluginyeelight.cpp +++ b/yeelight/devicepluginyeelight.cpp @@ -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 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(); } diff --git a/yeelight/devicepluginyeelight.h b/yeelight/devicepluginyeelight.h index c9c04293..4ec7a28a 100644 --- a/yeelight/devicepluginyeelight.h +++ b/yeelight/devicepluginyeelight.h @@ -28,6 +28,7 @@ #include "network/networkaccessmanager.h" #include "network/upnp/upnpdiscovery.h" #include "yeelight.h" +#include "ssdp.h" #include @@ -49,15 +50,7 @@ public: void deviceRemoved(Device *device) override; private: - class DiscoveryJob { - public: - UpnpDiscoveryReply* upnpReply; - QNetworkReply* nUpnpReply; - DeviceDescriptors results; - }; - QHash m_discoveries; - QList m_usedInterfaces; - + Ssdp *m_ssdp = nullptr; PluginTimer *m_pluginTimer = nullptr; QHash m_asyncActions; QHash m_yeelightConnections; diff --git a/yeelight/ssdp.cpp b/yeelight/ssdp.cpp index 82b97171..6ece3812 100644 --- a/yeelight/ssdp.cpp +++ b/yeelight/ssdp.cpp @@ -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() { diff --git a/yeelight/ssdp.h b/yeelight/ssdp.h index 8d439592..2cc7714c 100644 --- a/yeelight/ssdp.h +++ b/yeelight/ssdp.h @@ -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 diff --git a/yeelight/yeelight.cpp b/yeelight/yeelight.cpp index 25a36448..5dcd0dd5 100644 --- a/yeelight/yeelight.cpp +++ b/yeelight/yeelight.cpp @@ -145,7 +145,7 @@ int Yeelight::getParam(QList 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(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); } } diff --git a/yeelight/yeelight.pro b/yeelight/yeelight.pro index 1de5ae03..bab61090 100644 --- a/yeelight/yeelight.pro +++ b/yeelight/yeelight.pro @@ -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 \