From 832cb2b03a4d8c6e830ca160157a746e6803a15a Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Fri, 2 Oct 2020 15:26:53 +0200 Subject: [PATCH] removed deprecated files --- yeelight/ssdp.cpp | 183 ----------------- yeelight/ssdp.h | 79 -------- yeelight/yeelight.cpp | 460 ------------------------------------------ yeelight/yeelight.h | 128 ------------ yeelight/yeelight.png | Bin 22634 -> 0 bytes yeelight/yeelight.pro | 4 - 6 files changed, 854 deletions(-) delete mode 100644 yeelight/ssdp.cpp delete mode 100644 yeelight/ssdp.h delete mode 100644 yeelight/yeelight.cpp delete mode 100644 yeelight/yeelight.h delete mode 100644 yeelight/yeelight.png delete mode 100644 yeelight/yeelight.pro diff --git a/yeelight/ssdp.cpp b/yeelight/ssdp.cpp deleted file mode 100644 index 5e5e8a12..00000000 --- a/yeelight/ssdp.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project 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 project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "ssdp.h" -#include "extern-plugininfo.h" - -#include -#include -#include -#include -#include -#include - -Ssdp::Ssdp(QObject *parent) : QObject(parent) -{ - -} - -Ssdp::~Ssdp() -{ - if (m_socket) { - m_socket->waitForBytesWritten(1000); - m_socket->close(); - } -} - -bool Ssdp::enable() -{ - // Clean up - if (m_socket) { - delete m_socket; - m_socket = nullptr; - } - - // 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"); - - m_socket->setSocketOption(QAbstractSocket::MulticastTtlOption,QVariant(1)); - m_socket->setSocketOption(QAbstractSocket::MulticastLoopbackOption,QVariant(1)); - - if(!m_socket->bind(QHostAddress::AnyIPv4, m_port, QUdpSocket::ShareAddress)){ - qCWarning(dcYeelight()) << "could not bind to port" << m_port; - m_available = false; - delete m_socket; - m_socket = nullptr; - 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; - delete m_socket; - m_socket = nullptr; - return false; - } - 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; -} - -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"); - m_socket2->writeDatagram(searchMessage, m_host, m_port); -} - - -void Ssdp::error(QAbstractSocket::SocketError error) -{ - qCWarning(dcYeelight()) << "socket error:" << error << m_socket->errorString(); -} - -void Ssdp::readData() -{ - QByteArray data; - quint16 port; - QHostAddress hostAddress; - QUrl location; - int id; - QString model; - - // 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; - - if (data.contains("NOTIFY") && !QNetworkInterface::allAddresses().contains(hostAddress)) { - return; - } - - // if the data contains the HTTP OK header... - if (data.contains("HTTP/1.1 200 OK")) { - const QStringList lines = QString(data).split("\r\n"); - foreach (const QString& line, lines) { - int separatorIndex = line.indexOf(':'); - QString key = line.left(separatorIndex).toUpper(); - QString value = line.mid(separatorIndex+1).trimmed(); - - if (key.contains("Location")) { - location = QUrl(value); - } else if (key.contains("id")) { - id = value.toUInt(); - } else if (key.contains("model")) { - - } - } - emit discovered(location.host(), location.port(), id, model); - } -} - -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() -{ - if (!m_socket) { - return false; - } - - m_socket->waitForBytesWritten(); - m_socket->close(); - delete m_socket; - m_socket = nullptr; - - m_notificationTimer->stop(); - return true; -} - diff --git a/yeelight/ssdp.h b/yeelight/ssdp.h deleted file mode 100644 index 31f1a2cb..00000000 --- a/yeelight/ssdp.h +++ /dev/null @@ -1,79 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project 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 project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef SSDP_H -#define SSDP_H - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -class Ssdp : public QObject -{ - Q_OBJECT -public: - explicit Ssdp(QObject *parent = nullptr); - ~Ssdp(); - bool enable(); - void discover(); - bool disable(); -private: - QUdpSocket *m_socket = nullptr; - QUdpSocket *m_socket2 = nullptr; - QHostAddress m_host; - quint16 m_port; - - QTimer *m_notificationTimer = nullptr; - - QNetworkAccessManager *m_networkAccessManager = nullptr; - - //QList m_discoverRequests; - // QHash m_informationRequestList; - - bool m_available = false; - bool m_enabled = false; - -signals: - void discovered(const QString &address, int port, int id, const QString &model); - -private slots: - void error(QAbstractSocket::SocketError error); - void readData(); - void readData2(); -}; - -#endif // SSDP_H diff --git a/yeelight/yeelight.cpp b/yeelight/yeelight.cpp deleted file mode 100644 index d09ae55d..00000000 --- a/yeelight/yeelight.cpp +++ /dev/null @@ -1,460 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project 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 project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "yeelight.h" -#include "extern-plugininfo.h" - -#include -#include -#include - -#include -#include - -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); - connect(m_socket, &QTcpSocket::readyRead, this, &Yeelight::onReadyRead); - m_socket->connectToHost(address, port); - - m_reconnectTimer = new QTimer(this); - m_reconnectTimer->setSingleShot(true); - connect(m_reconnectTimer, &QTimer::timeout, this, &Yeelight::onReconnectTimer); -} - -bool Yeelight::isConnected() -{ - return m_socket->isOpen(); -} - -void Yeelight::connectDevice() -{ - m_socket->connectToHost(m_address, m_port); -} - - -int Yeelight::getParam(QList properties) -{ - int requestId = static_cast(QRandomGenerator::global()->generate()); - QJsonDocument doc; - QJsonObject obj; - obj["id"] = requestId; - obj["method"] = "get_prop"; - QJsonArray params; - m_propertyRequests.insert(requestId, properties); - - foreach (YeelightProperty property, properties) { - switch (property) { - case YeelightProperty::Ct: - params.append("ct"); - break; - case YeelightProperty::Power: - params.append("power"); - break; - case YeelightProperty::Bright: - params.append("bright"); - break; - case YeelightProperty::Hue: - params.append("hue"); - break; - case YeelightProperty::Rgb: - params.append("rgb"); - break; - case YeelightProperty::Sat: - params.append("sat"); - break; - case YeelightProperty::Name: - params.append("name"); - break; - case YeelightProperty::BgCt: - params.append("bg_ct"); - break; - case YeelightProperty::NlBr: - params.append("nl_br"); - break; - case YeelightProperty::BgHue: - params.append("bg_hue"); - break; - case YeelightProperty::BgRgb: - params.append("bg_rgb"); - break; - case YeelightProperty::BgSat: - params.append("bg_sat"); - break; - case YeelightProperty::BgLmode: - params.append("bg_lmode"); - break; - case YeelightProperty::BgPower: - params.append("bg_power"); - break; - case YeelightProperty::Flowing: - params.append("flowing"); - break; - case YeelightProperty::MusicOn: - params.append("music_on"); - break; - case YeelightProperty::BgBright: - params.append("bg_bright"); - break; - case YeelightProperty::DelayOff: - params.append("delay_off"); - break; - case YeelightProperty::BgFlowing: - params.append("bg_flowing"); - break; - case YeelightProperty::ColorMode: - params.append("color_mode"); - break; - case YeelightProperty::ActiveMode: - params.append("active_mode"); - break; - case YeelightProperty::FlowParams: - params.append("flow_params"); - break; - case YeelightProperty::BgFlowParams: - params.append("bg_flow_params"); - break; - } - } - - QTimer::singleShot(10000, this, [requestId, this]{m_propertyRequests.remove(requestId);}); - obj["params"] = params; - doc.setObject(obj); - //qCDebug(dcYeelight()) << "Sending request" << doc.toJson(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::setName(const QString &name) -{ - int requestId = static_cast(QRandomGenerator::global()->generate()); - QJsonDocument doc; - QJsonObject obj; - obj["id"] = requestId; - obj["method"] = "set_name"; - QJsonArray params; - params.append(name); - obj["params"] = params; - doc.setObject(obj); - //qCDebug(dcYeelight()) << "Sending request" << doc.toJson(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::setColorTemperature(int mirad, int msFadeTime) -{ - int requestId = static_cast(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; - doc.setObject(obj); - //qCDebug(dcYeelight()) << "Sending request" << doc.toJson(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::setRgb(QRgb color, int msFadeTime) -{ - int requestId = static_cast(QRandomGenerator::global()->generate()); - QJsonDocument doc; - QJsonObject obj; - obj["id"] = requestId; - obj["method"] = "set_rgb"; - QJsonArray params; - params.append(static_cast(color & 0x00ffffff)); - params.append("smooth"); - params.append(msFadeTime); - obj["params"] = params; - doc.setObject(obj); - //qCDebug(dcYeelight()) << "Sending request" << doc.toJson(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::setBrightness(int percentage, int msFadeTime) -{ - int requestId = static_cast(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; - doc.setObject(obj); - //qCDebug(dcYeelight()) << "Sending request" << doc.toJson(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::setPower(bool power, int msFadeTime) -{ - int requestId = static_cast(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; - doc.setObject(obj); - //qCDebug(dcYeelight()) << "Sending request" << doc.toJson(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::setDefault() -{ - int requestId = static_cast(QRandomGenerator::global()->generate()); - QJsonDocument doc; - QJsonObject obj; - obj["id"] = requestId; - obj["method"] = "set_default"; - QJsonArray params; - obj["params"] = params; - doc.setObject(obj); - //qCDebug(dcYeelight()) << "Sending request" << doc.toJson(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::startColorFlow() -{ - int requestId = static_cast(QRandomGenerator::global()->generate()); - QJsonDocument doc; - QJsonObject obj; - obj["id"] = requestId; - obj["method"] = "start_cf"; - 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, 50, 2000, 1, 5000, 50, 2000, 1, 6000, 50"); //Colors - obj["params"] = params; - doc.setObject(obj); - qCDebug(dcYeelight()) << "Sending request" << doc.toJson(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::stopColorFlow() -{ - int requestId = static_cast(QRandomGenerator::global()->generate()); - QJsonDocument doc; - QJsonObject obj; - obj["id"] = requestId; - obj["method"] = "stop_cf"; - QJsonArray params; - obj["params"] = params; - doc.setObject(obj); - qCDebug(dcYeelight()) << "Sending request" << doc.toJson(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::flash() -{ - int requestId = static_cast(QRandomGenerator::global()->generate()); - QJsonDocument doc; - QJsonObject obj; - obj["id"] = requestId; - obj["method"] = "start_cf"; - QJsonArray params; - params.append(4 * 3); - params.append(0); //LED recover to the state before the color flow started - 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(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -int Yeelight::flash15s() -{ - int requestId = static_cast(QRandomGenerator::global()->generate()); - QJsonDocument doc; - QJsonObject obj; - obj["id"] = requestId; - obj["method"] = "start_cf"; - QJsonArray params; - params.append(4 * 15); - params.append(0); //LED recover to the state before the color flow started - 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(); - m_socket->write(doc.toJson() + "\r\n"); - return requestId; -} - -void Yeelight::onStateChanged(QAbstractSocket::SocketState state) -{ - switch (state) { - case QAbstractSocket::SocketState::ConnectedState: - emit connectionChanged(true); - break; - case QAbstractSocket::SocketState::UnconnectedState: - m_reconnectTimer->start(10 * 1000); - emit connectionChanged(false); - break; - default: - emit connectionChanged(false); - break; - } -} - -void Yeelight::onReadyRead() -{ - QByteArray data = m_socket->readAll(); - //qCDebug(dcYeelight()) << "Message received" << data; - - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(data, &error); - if (error.error != QJsonParseError::NoError) { - qDebug(dcYeelight()) << "Recieved invalide JSON object"; - return; - } - QVariantMap map = doc.toVariant().toMap(); - if (map.contains("error")) { - if(map.contains("id")) { - emit requestExecuted(map["id"].toInt(), false); - } - QVariantMap error = map["error"].toMap(); - int code = error["code"].toInt(); - QString message = error["message"].toString(); - qCWarning(dcYeelight()) << "Error received: Code" << code << message; - emit errorReceived(code, message); - - }else if (map.contains("method")) { - if (map["method"] == "props") { - QVariantMap params = map["params"].toMap(); - if (params.contains("power")) { - emit powerNotificationReceived((params["power"].toString() == "on")); - } - if (params.contains("bright")) { - emit brightnessNotificationReceived(params["bright"].toInt()); - } - if (params.contains("ct")) { - emit colorTemperatureNotificationReceived(params["ct"].toInt()); - } - if (params.contains("rgb")) { - emit rgbNotificationReceived(QRgb(params["rgb"].toInt())); - } - if (params.contains("hue")) { - emit hueNotificationReceived(params["hue"].toInt()); - } - if (params.contains("name")) { - emit nameNotificationReceived(params["name"].toString()); - } - if (params.contains("color_mode")) { - emit colorModeNotificationReceived(YeelightColorMode(params["color_mode"].toInt())); - } - if (params.contains("sat")) { - emit saturationNotificationReceived(params["sat"].toInt()); - } - } - } else { - int id = map["id"].toInt(); - QVariantList result = map["result"].toList(); - if ((result.length() == 1)) { - if (result.first().toString() == "ok") { - emit requestExecuted(id, true); - } - } else { - if (m_propertyRequests.contains(id)) { - QList properties = m_propertyRequests.take(id); - foreach (YeelightProperty property, properties) { - if (result.isEmpty()){ - qCWarning(dcYeelight()) << "Value count does not match properties" << properties.count(); - break; - } - QVariant value = result.takeFirst(); - switch (property) { - case YeelightProperty::Name: - emit nameNotificationReceived(value.toString()); - break; - case YeelightProperty::Ct: - emit colorTemperatureNotificationReceived(value.toInt()); - break; - case YeelightProperty::Rgb: - emit rgbNotificationReceived(QRgb(value.toInt())); - break; - case YeelightProperty::Hue: - emit hueNotificationReceived(value.toInt()); - break; - case YeelightProperty::Bright: - emit brightnessNotificationReceived(value.toInt()); - break; - case YeelightProperty::Power: - emit powerNotificationReceived((value.toString() == "on")); - break; - case YeelightProperty::ColorMode: - emit colorModeNotificationReceived(YeelightColorMode(value.toInt())); - break; - case YeelightProperty::Sat: - emit saturationNotificationReceived(value.toInt()); - break; - default: - qCWarning(dcYeelight()) << "Unhandled Yeelight property"; - } - } - } - } - } -} - -void Yeelight::onReconnectTimer() -{ - if(!m_socket->isOpen()) { - m_socket->connectToHost(m_address, m_port); - m_reconnectTimer->start(10 * 1000); - } -} - - diff --git a/yeelight/yeelight.h b/yeelight/yeelight.h deleted file mode 100644 index 91290c84..00000000 --- a/yeelight/yeelight.h +++ /dev/null @@ -1,128 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project 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 project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef YEELIGHT_H -#define YEELIGHT_H - -#include -#include -#include -#include - -#include "network/networkaccessmanager.h" -#include - -class Yeelight : public QObject -{ - Q_OBJECT -public: - enum YeelightColorMode { - RGB = 1, - ColorTemperature, - HSV - }; - - enum YeelightProperty { - 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 isConnected(); - void connectDevice(); - - int getParam(QList 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(); - int startColorFlow(); - int stopColorFlow(); - int flash(); - int flash15s(); - -private: - QTimer *m_reconnectTimer = nullptr; - QTcpSocket *m_socket = nullptr; - QHostAddress m_address; - quint16 m_port; - NetworkAccessManager *m_networkManager = nullptr; - QHash> m_propertyRequests; - -private slots: - void onStateChanged(QAbstractSocket::SocketState state); - void onReadyRead(); - void onReconnectTimer(); - -signals: - void connectionChanged(bool connected); - void requestExecuted(int requestId, bool success); - void errorReceived(int code, const QString &message); - - /* - * Whenever there is state change of smart LED, it will send a notification message - * to all connected 3rd party devices. This is to make sure all 3rd party devices - * will get the latest state of the smart LED in time without having to poll the status - * from time to time. - */ - void notificationReceived(YeelightProperty property, QVariant value); - void powerNotificationReceived(bool status); - void brightnessNotificationReceived(int percentage); - void colorTemperatureNotificationReceived(int kelvin); - void rgbNotificationReceived(QRgb rgbColor); - void hueNotificationReceived(int hueColor); - void nameNotificationReceived(const QString &name); - void saturationNotificationReceived(int percentage); - void colorModeNotificationReceived(YeelightColorMode colorMode); -}; -#endif // YEELIGHT_H diff --git a/yeelight/yeelight.png b/yeelight/yeelight.png deleted file mode 100644 index ad4270a5bb75c05e31a8fd84d8c3819a82b72f04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22634 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