mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-10-23 14:43:44 +02:00
added custom ssdp discovery
This commit is contained in:
parent
e335524421
commit
6d956ce808
@ -25,11 +25,11 @@
|
|||||||
#include "devices/device.h"
|
#include "devices/device.h"
|
||||||
#include "types/param.h"
|
#include "types/param.h"
|
||||||
#include "plugininfo.h"
|
#include "plugininfo.h"
|
||||||
#include "network/upnp/upnpdiscovery.h"
|
#include "ssdp.h"
|
||||||
#include "network/upnp/upnpdiscoveryreply.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
#include <QRgb>
|
||||||
|
|
||||||
DevicePluginYeelight::DevicePluginYeelight()
|
DevicePluginYeelight::DevicePluginYeelight()
|
||||||
{
|
{
|
||||||
@ -44,70 +44,157 @@ void DevicePluginYeelight::discoverDevices(DeviceDiscoveryInfo *info)
|
|||||||
m_discoveries.insert(info, discovery);
|
m_discoveries.insert(info, discovery);
|
||||||
|
|
||||||
qCDebug(dcYeelight()) << "Starting UPnP discovery...";
|
qCDebug(dcYeelight()) << "Starting UPnP discovery...";
|
||||||
UpnpDiscoveryReply *upnpReply = hardwareManager()->upnpDiscovery()->discoverDevices("wifi_bulb");
|
Ssdp *ssdp = new Ssdp(this);
|
||||||
discovery->upnpReply = upnpReply;
|
ssdp->enable();
|
||||||
// Always clean up the upnp discovery
|
ssdp->discover();
|
||||||
connect(upnpReply, &UpnpDiscoveryReply::finished, upnpReply, &UpnpDiscoveryReply::deleteLater);
|
connect(ssdp, &Ssdp::discovered, this,[info, this](const QString &address, int port, int id, const QString &model) {
|
||||||
|
|
||||||
// Process results if info is still around
|
DeviceDescriptor descriptor(colorBulbDeviceClassId, "Yeelight"+ model + "Bulb", address);
|
||||||
connect(upnpReply, &UpnpDiscoveryReply::finished, info, [this, upnpReply, discovery](){
|
ParamList params;
|
||||||
|
foreach (Device *existingDevice, myDevices()) {
|
||||||
// Indicate we're done...
|
if (existingDevice->paramValue(colorBulbDeviceIdParamTypeId).toString() == id) {
|
||||||
discovery->upnpReply = nullptr;
|
descriptor.setDeviceId(existingDevice->id());
|
||||||
|
break;
|
||||||
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);
|
params.append(Param(colorBulbDeviceHostParamTypeId, address));
|
||||||
|
params.append(Param(colorBulbDeviceIdParamTypeId, id));
|
||||||
|
params.append(Param(colorBulbDevicePortParamTypeId, port));
|
||||||
|
descriptor.setParams(params);
|
||||||
|
qCDebug(dcYeelight()) << "UPnP: Found Yeelight device:" << id;
|
||||||
|
info->finish(Device::DeviceErrorNoError);
|
||||||
});
|
});
|
||||||
|
|
||||||
info->finish(Device::DeviceErrorNoError);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePluginYeelight::setupDevice(DeviceSetupInfo *info)
|
void DevicePluginYeelight::setupDevice(DeviceSetupInfo *info)
|
||||||
{
|
{
|
||||||
Device *device = info->device();
|
Device *device = info->device();
|
||||||
|
|
||||||
if (device->deviceClassId() == colorBulbDeviceClassId) {
|
if (device->deviceClassId() == colorBulbDeviceClassId) {
|
||||||
QHostAddress *address = device->paramValue(colorBulbDeviceAddressParamTypeId).toString();
|
QHostAddress address = QHostAddress(device->paramValue(colorBulbDeviceHostParamTypeId).toString());
|
||||||
int port;
|
quint16 port = device->paramValue(colorBulbDevicePortParamTypeId).toUInt();
|
||||||
Yeelight *yeelight = new Yeelight(hardwareManager(), address, port, this);
|
Yeelight *yeelight = new Yeelight(hardwareManager()->networkManager(), address, port, this);
|
||||||
|
m_yeelightConnections.insert(device->id(), yeelight);
|
||||||
|
connect(yeelight, &Yeelight::connectionChanged, this, &DevicePluginYeelight::onConnectionChanged);
|
||||||
|
connect(yeelight, &Yeelight::requestExecuted, this, &DevicePluginYeelight::onRequestExecuted);
|
||||||
|
connect(yeelight, &Yeelight::propertyListReceived, this, &DevicePluginYeelight::onPropertyListReceived);
|
||||||
info->finish(Device::DeviceErrorNoError);
|
info->finish(Device::DeviceErrorNoError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DevicePluginYeelight::postSetupDevice(Device *device)
|
||||||
|
{
|
||||||
|
connect(device, &Device::nameChanged, this, &DevicePluginYeelight::onDeviceNameChanged);
|
||||||
|
|
||||||
|
if (device->deviceClassId() == colorBulbDeviceClassId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_pluginTimer) {
|
||||||
|
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60);
|
||||||
|
connect(m_pluginTimer, &PluginTimer::timeout, this, [this]() {
|
||||||
|
foreach (Yeelight *yeelight, m_yeelightConnections.values()) {
|
||||||
|
if (yeelight->isConnected()) {
|
||||||
|
QList<Yeelight::Property> properties;
|
||||||
|
properties << Yeelight::Property::Name;
|
||||||
|
properties << Yeelight::Property::Ct;
|
||||||
|
properties << Yeelight::Property::Rgb;
|
||||||
|
properties << Yeelight::Property::Power;
|
||||||
|
properties << Yeelight::Property::Bright;
|
||||||
|
properties << Yeelight::Property::ColorMode;
|
||||||
|
yeelight->getParam(properties);
|
||||||
|
} else {
|
||||||
|
yeelight->connectDevice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DevicePluginYeelight::executeAction(DeviceActionInfo *info)
|
void DevicePluginYeelight::executeAction(DeviceActionInfo *info)
|
||||||
{
|
{
|
||||||
Device *device = info->device();
|
Device *device = info->device();
|
||||||
Action action = info->action();
|
Action action = info->action();
|
||||||
|
|
||||||
if (device->deviceClassId() == colorBulbDeviceClassId) {
|
if (device->deviceClassId() == colorBulbDeviceClassId) {
|
||||||
|
Yeelight *yeelight = m_yeelightConnections.value(device->id());
|
||||||
|
|
||||||
// if (action.actionTypeId() == ) {
|
if (action.actionTypeId() == colorBulbPowerActionTypeId) {
|
||||||
//}
|
bool power = action.param(colorBulbPowerActionPowerParamTypeId).value().toBool();
|
||||||
|
yeelight->setPower(power);
|
||||||
|
} else if (action.actionTypeId() == colorBulbBrightnessActionTypeId) {
|
||||||
|
int brightness = action.param(colorBulbBrightnessActionBrightnessParamTypeId).value().toInt();
|
||||||
|
yeelight->setBrightness(brightness);
|
||||||
|
} else if (action.actionTypeId() == colorBulbColorActionColorParamTypeId) {
|
||||||
|
QRgb color = action.param(colorBulbColorActionColorParamTypeId).value().toUInt();
|
||||||
|
yeelight->setRgb(color);
|
||||||
|
} else if (action.actionTypeId() == colorBulbColorTemperatureActionTypeId) {
|
||||||
|
int colorTemperature = action.param(colorBulbColorTemperatureActionColorTemperatureParamTypeId).value().toUInt() * 11.12;
|
||||||
|
yeelight->setColorTemperature(colorTemperature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePluginYeelight::deviceRemoved(Device *device)
|
void DevicePluginYeelight::deviceRemoved(Device *device)
|
||||||
{
|
{
|
||||||
|
if (device->deviceClassId() == colorBulbDeviceClassId){
|
||||||
|
Yeelight *yeelight = m_yeelightConnections.take(device->id());
|
||||||
|
yeelight->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DevicePluginYeelight::onDeviceNameChanged()
|
||||||
|
{
|
||||||
|
Device *device = static_cast<Device*>(sender());
|
||||||
|
Yeelight *yeelight = m_yeelightConnections.value(device->id());
|
||||||
|
yeelight->setName(device->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DevicePluginYeelight::onConnectionChanged(bool connected)
|
||||||
|
{
|
||||||
|
Yeelight *yeelight = static_cast<Yeelight *>(sender());
|
||||||
|
Device * device = myDevices().findById(m_yeelightConnections.key(yeelight));
|
||||||
|
if(!device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
device->setStateValue(colorBulbConnectedStateTypeId, connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DevicePluginYeelight::onRequestExecuted(int requestId, bool success)
|
||||||
|
{
|
||||||
|
if (m_asyncActions.contains(requestId)) {
|
||||||
|
DeviceActionInfo *info = m_asyncActions.take(requestId);
|
||||||
|
if (success) {
|
||||||
|
info->finish(Device::DeviceErrorNoError);
|
||||||
|
} else {
|
||||||
|
info->finish(Device::DeviceErrorHardwareFailure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DevicePluginYeelight::onPropertyListReceived(QVariantList value)
|
||||||
|
{
|
||||||
|
Yeelight *yeelight = static_cast<Yeelight *>(sender());
|
||||||
|
Device * device = myDevices().findById(m_yeelightConnections.key(yeelight));
|
||||||
|
if(!device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* properties << Yeelight::Property::Name;
|
||||||
|
* properties << Yeelight::Property::Ct;
|
||||||
|
* properties << Yeelight::Property::Rgb;
|
||||||
|
* properties << Yeelight::Property::Power;
|
||||||
|
* properties << Yeelight::Property::Bright;
|
||||||
|
* properties << Yeelight::Property::ColorMode;
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(value.length() < 6)
|
||||||
|
return;
|
||||||
|
|
||||||
|
device->setName(value[0].toString());
|
||||||
|
device->setStateValue(colorBulbColorTemperatureStateTypeId, value[1].toInt());
|
||||||
|
device->setStateValue(colorBulbColorStateTypeId, value[2].toString());
|
||||||
|
device->setStateValue(colorBulbPowerStateTypeId, (value[3].toString() == "on"));
|
||||||
|
device->setStateValue(colorBulbBrightnessStateTypeId, value[4].toInt());
|
||||||
|
device->setStateValue(colorBulbColorModeStateTypeId, value[5].toString());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,7 @@ public:
|
|||||||
|
|
||||||
void discoverDevices(DeviceDiscoveryInfo *info) override;
|
void discoverDevices(DeviceDiscoveryInfo *info) override;
|
||||||
void setupDevice(DeviceSetupInfo *info) override;
|
void setupDevice(DeviceSetupInfo *info) override;
|
||||||
|
void postSetupDevice(Device *device) override;
|
||||||
void executeAction(DeviceActionInfo *info) override;
|
void executeAction(DeviceActionInfo *info) override;
|
||||||
void deviceRemoved(Device *device) override;
|
void deviceRemoved(Device *device) override;
|
||||||
|
|
||||||
@ -55,15 +56,16 @@ private:
|
|||||||
};
|
};
|
||||||
QHash<DeviceDiscoveryInfo*, DiscoveryJob*> m_discoveries;
|
QHash<DeviceDiscoveryInfo*, DiscoveryJob*> m_discoveries;
|
||||||
QList<QString> m_usedInterfaces;
|
QList<QString> m_usedInterfaces;
|
||||||
QTimer *m_reconnectTimer = nullptr;
|
|
||||||
|
PluginTimer *m_pluginTimer = nullptr;
|
||||||
QHash<int, DeviceActionInfo *> m_asyncActions;
|
QHash<int, DeviceActionInfo *> m_asyncActions;
|
||||||
|
QHash<DeviceId, Yeelight *> m_yeelightConnections;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onReadyRead();
|
void onDeviceNameChanged();
|
||||||
void onReconnectTimer();
|
void onConnectionChanged(bool connected);
|
||||||
|
void onRequestExecuted(int requestId, bool success);
|
||||||
signals:
|
void onPropertyListReceived(QVariantList value);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DEVICEPLUGINYEELIGHT_H
|
#endif // DEVICEPLUGINYEELIGHT_H
|
||||||
|
|||||||
@ -23,6 +23,13 @@
|
|||||||
"inputType": "IPv4Address",
|
"inputType": "IPv4Address",
|
||||||
"readOnly": true
|
"readOnly": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "99790910-ce6c-4b14-bb1f-f56133522dce",
|
||||||
|
"name": "port",
|
||||||
|
"displayName": "Port",
|
||||||
|
"type" : "int",
|
||||||
|
"readOnly": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "bfe184d2-6e62-4979-8ff2-3fd190903c82",
|
"id": "bfe184d2-6e62-4979-8ff2-3fd190903c82",
|
||||||
"name": "id",
|
"name": "id",
|
||||||
@ -35,8 +42,7 @@
|
|||||||
{
|
{
|
||||||
"id": "e80aea31-7ac0-4f71-9650-efb70ea1e768",
|
"id": "e80aea31-7ac0-4f71-9650-efb70ea1e768",
|
||||||
"name": "default",
|
"name": "default",
|
||||||
"displayName": "Set default",
|
"displayName": "Set default"
|
||||||
"type": "bool"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stateTypes": [
|
"stateTypes": [
|
||||||
@ -93,6 +99,16 @@
|
|||||||
"minValue": 0,
|
"minValue": 0,
|
||||||
"maxValue": 100,
|
"maxValue": 100,
|
||||||
"writable": true
|
"writable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6babaf04-0247-447c-b429-d39f3d330f45",
|
||||||
|
"name": "colorMode",
|
||||||
|
"displayName": "Color mode",
|
||||||
|
"displayNameEvent": "Color mode changed",
|
||||||
|
"displayNameAction": "Set color mode",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": "-",
|
||||||
|
"writable": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
130
yeelight/ssdp.cpp
Normal file
130
yeelight/ssdp.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "ssdp.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
#include <QMetaObject>
|
||||||
|
#include <QNetworkInterface>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <QXmlStreamWriter>
|
||||||
|
#include <QNetworkConfiguration>
|
||||||
|
#include <QNetworkConfigurationManager>
|
||||||
|
|
||||||
|
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_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_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);
|
||||||
|
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\r\n");
|
||||||
|
m_socket->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
47
yeelight/ssdp.h
Normal file
47
yeelight/ssdp.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef SSDP_H
|
||||||
|
#define SSDP_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <QUdpSocket>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUdpSocket>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
|
class Ssdp : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Ssdp(QObject *parent = nullptr);
|
||||||
|
~Ssdp();
|
||||||
|
bool enable();
|
||||||
|
void discover();
|
||||||
|
bool disable();
|
||||||
|
private:
|
||||||
|
QUdpSocket *m_socket = nullptr;
|
||||||
|
QHostAddress m_host;
|
||||||
|
quint16 m_port;
|
||||||
|
|
||||||
|
QTimer *m_notificationTimer = nullptr;
|
||||||
|
|
||||||
|
QNetworkAccessManager *m_networkAccessManager = nullptr;
|
||||||
|
|
||||||
|
//QList<UpnpDiscoveryRequest *> m_discoverRequests;
|
||||||
|
// QHash<QNetworkReply*, UpnpDeviceDescriptor> 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();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SSDP_H
|
||||||
@ -28,6 +28,7 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
#include <QRandomGenerator>
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
Yeelight::Yeelight(NetworkAccessManager *networkManager, const QHostAddress &address, quint16 port, QObject *parent) :
|
Yeelight::Yeelight(NetworkAccessManager *networkManager, const QHostAddress &address, quint16 port, QObject *parent) :
|
||||||
@ -38,14 +39,20 @@ Yeelight::Yeelight(NetworkAccessManager *networkManager, const QHostAddress &add
|
|||||||
{
|
{
|
||||||
m_socket = new QTcpSocket(this);
|
m_socket = new QTcpSocket(this);
|
||||||
connect(m_socket, &QTcpSocket::stateChanged, this, &Yeelight::onStateChanged);
|
connect(m_socket, &QTcpSocket::stateChanged, this, &Yeelight::onStateChanged);
|
||||||
|
connect(m_socket, &QTcpSocket::readyRead, this, &Yeelight::onReadyRead);
|
||||||
m_socket->connectToHost(address, port);
|
m_socket->connectToHost(address, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Yeelight::connected()
|
bool Yeelight::isConnected()
|
||||||
{
|
{
|
||||||
return m_socket->isOpen();
|
return m_socket->isOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Yeelight::connectDevice()
|
||||||
|
{
|
||||||
|
m_socket->connectToHost(m_address, m_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int Yeelight::getParam(QList<Yeelight::Property> properties)
|
int Yeelight::getParam(QList<Yeelight::Property> properties)
|
||||||
{
|
{
|
||||||
@ -62,7 +69,7 @@ int Yeelight::getParam(QList<Yeelight::Property> properties)
|
|||||||
params.append("ct");
|
params.append("ct");
|
||||||
break;
|
break;
|
||||||
case Property::Power:
|
case Property::Power:
|
||||||
params.append("powr");
|
params.append("power");
|
||||||
break;
|
break;
|
||||||
case Property::Bright:
|
case Property::Bright:
|
||||||
params.append("bright");
|
params.append("bright");
|
||||||
@ -132,7 +139,8 @@ int Yeelight::getParam(QList<Yeelight::Property> properties)
|
|||||||
|
|
||||||
obj["params"] = params;
|
obj["params"] = params;
|
||||||
doc.setObject(obj);
|
doc.setObject(obj);
|
||||||
m_socket->write(doc.toJson());
|
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
|
||||||
|
m_socket->write(doc.toJson() + "\r\n");
|
||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +154,9 @@ int Yeelight::setName(const QString &name)
|
|||||||
QJsonArray params;
|
QJsonArray params;
|
||||||
params.append(name);
|
params.append(name);
|
||||||
obj["params"] = params;
|
obj["params"] = params;
|
||||||
|
doc.setObject(obj);
|
||||||
|
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
|
||||||
|
m_socket->write(doc.toJson() + "\r\n");
|
||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,6 +172,9 @@ int Yeelight::setColorTemperature(int mirad, int msFadeTime)
|
|||||||
params.append("smooth");
|
params.append("smooth");
|
||||||
params.append(msFadeTime);
|
params.append(msFadeTime);
|
||||||
obj["params"] = params;
|
obj["params"] = params;
|
||||||
|
doc.setObject(obj);
|
||||||
|
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
|
||||||
|
m_socket->write(doc.toJson() + "\r\n");
|
||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +190,9 @@ int Yeelight::setRgb(QRgb color, int msFadeTime)
|
|||||||
params.append("smooth");
|
params.append("smooth");
|
||||||
params.append(msFadeTime);
|
params.append(msFadeTime);
|
||||||
obj["params"] = params;
|
obj["params"] = params;
|
||||||
|
doc.setObject(obj);
|
||||||
|
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
|
||||||
|
m_socket->write(doc.toJson() + "\r\n");
|
||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,6 +208,9 @@ int Yeelight::setBrightness(int percentage, int msFadeTime)
|
|||||||
params.append("smooth");
|
params.append("smooth");
|
||||||
params.append(msFadeTime);
|
params.append(msFadeTime);
|
||||||
obj["params"] = params;
|
obj["params"] = params;
|
||||||
|
doc.setObject(obj);
|
||||||
|
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
|
||||||
|
m_socket->write(doc.toJson() + "\r\n");
|
||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,6 +230,9 @@ int Yeelight::setPower(bool power, int msFadeTime)
|
|||||||
params.append("smooth");
|
params.append("smooth");
|
||||||
params.append(msFadeTime);
|
params.append(msFadeTime);
|
||||||
obj["params"] = params;
|
obj["params"] = params;
|
||||||
|
doc.setObject(obj);
|
||||||
|
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
|
||||||
|
m_socket->write(doc.toJson() + "\r\n");
|
||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,6 +245,9 @@ int Yeelight::setDefault()
|
|||||||
obj["method"] = "set_default";
|
obj["method"] = "set_default";
|
||||||
QJsonArray params;
|
QJsonArray params;
|
||||||
obj["params"] = params;
|
obj["params"] = params;
|
||||||
|
doc.setObject(obj);
|
||||||
|
qCDebug(dcYeelight()) << "Sending request" << doc.toJson();
|
||||||
|
m_socket->write(doc.toJson() + "\r\n");
|
||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,11 +255,68 @@ void Yeelight::onStateChanged(QAbstractSocket::SocketState state)
|
|||||||
{
|
{
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case QAbstractSocket::SocketState::ConnectedState:
|
case QAbstractSocket::SocketState::ConnectedState:
|
||||||
|
emit connectionChanged(true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
emit connectionChanged(false);
|
||||||
break;
|
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("method")) {
|
||||||
|
if (map["method"] == "props") {
|
||||||
|
QVariantMap params = map["params"].toMap();
|
||||||
|
if (params.contains("power")) {
|
||||||
|
emit notificationReveiced(Property::Power, params["power"]);
|
||||||
|
}
|
||||||
|
if (params.contains("bright")) {
|
||||||
|
emit notificationReveiced(Property::Bright, params["bright"]);
|
||||||
|
}
|
||||||
|
if (params.contains("ct")) {
|
||||||
|
emit notificationReveiced(Property::Ct, params["ct"]);
|
||||||
|
}
|
||||||
|
if (params.contains("rgb")) {
|
||||||
|
emit notificationReveiced(Property::Rgb, params["rgb"]);
|
||||||
|
}
|
||||||
|
if (params.contains("hue")) {
|
||||||
|
emit notificationReveiced(Property::Hue, params["hue"]);
|
||||||
|
}
|
||||||
|
if (params.contains("name")) {
|
||||||
|
emit notificationReveiced(Property::Name, params["name"]);
|
||||||
|
}
|
||||||
|
if (params.contains("color_mode")) {
|
||||||
|
emit notificationReveiced(Property::ColorMode, params["color_mode"]);
|
||||||
|
}
|
||||||
|
if (params.contains("sat")) {
|
||||||
|
emit notificationReveiced(Property::Sat, params["sat"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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 {
|
||||||
|
//TODO parse error
|
||||||
|
//emit errorReceived()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit propertyListReceived(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -37,6 +37,12 @@ class Yeelight : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
enum ColorMode {
|
||||||
|
RGB = 1,
|
||||||
|
Temperature,
|
||||||
|
HSV
|
||||||
|
};
|
||||||
|
|
||||||
enum Property {
|
enum Property {
|
||||||
Power, //on: smart LED is turned on / off: smart LED is turned off
|
Power, //on: smart LED is turned on / off: smart LED is turned off
|
||||||
Bright, //Brightness percentage. Range 1 ~ 100
|
Bright, //Brightness percentage. Range 1 ~ 100
|
||||||
@ -64,7 +70,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
explicit Yeelight(NetworkAccessManager *networkManager, const QHostAddress &address, quint16 port = 55443, QObject *parent = nullptr);
|
explicit Yeelight(NetworkAccessManager *networkManager, const QHostAddress &address, quint16 port = 55443, QObject *parent = nullptr);
|
||||||
bool connected();
|
bool isConnected();
|
||||||
|
void connectDevice();
|
||||||
|
|
||||||
int getParam(QList<Property> properties);
|
int getParam(QList<Property> properties);
|
||||||
int setName(const QString &name);
|
int setName(const QString &name);
|
||||||
@ -79,17 +86,17 @@ private:
|
|||||||
|
|
||||||
QTcpSocket *m_socket = nullptr;
|
QTcpSocket *m_socket = nullptr;
|
||||||
QHostAddress m_address;
|
QHostAddress m_address;
|
||||||
qint16 m_port;
|
quint16 m_port;
|
||||||
NetworkAccessManager *m_networkManager = nullptr;
|
NetworkAccessManager *m_networkManager = nullptr;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onRefreshTimeout();
|
|
||||||
void onStateChanged(QAbstractSocket::SocketState state);
|
void onStateChanged(QAbstractSocket::SocketState state);
|
||||||
|
void onReadyRead();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void connectionChanged(bool connected);
|
void connectionChanged(bool connected);
|
||||||
void authenticationStatusChanged(bool authenticated);
|
|
||||||
void requestExecuted(int requestId, bool success);
|
void requestExecuted(int requestId, bool success);
|
||||||
void propertyReceived(Property property, QVariant value);
|
void propertyListReceived(QVariantList value);
|
||||||
|
void notificationReveiced(Property property, QVariant value);
|
||||||
};
|
};
|
||||||
#endif // YEELIGHT_H
|
#endif // YEELIGHT_H
|
||||||
|
|||||||
@ -7,7 +7,9 @@ TARGET = $$qtLibraryTarget(nymea_devicepluginyeelight)
|
|||||||
SOURCES += \
|
SOURCES += \
|
||||||
devicepluginyeelight.cpp \
|
devicepluginyeelight.cpp \
|
||||||
yeelight.cpp \
|
yeelight.cpp \
|
||||||
|
ssdp.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
devicepluginyeelight.h \
|
devicepluginyeelight.h \
|
||||||
yeelight.h \
|
yeelight.h \
|
||||||
|
ssdp.h
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user