Adjust plugins to migrated hardware manager
This commit is contained in:
parent
36e21fca29
commit
cd1b39cd37
@ -32,6 +32,7 @@
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QPointer>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class DevicePluginAwattar : public DevicePlugin
|
||||
{
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include <QTimeZone>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class DevicePluginDateTime : public DevicePlugin
|
||||
{
|
||||
|
||||
@ -18,562 +18,147 @@
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifdef BLUETOOTH_LE
|
||||
|
||||
#include "aveabulb.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
AveaBulb::AveaBulb(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType, QObject *parent) :
|
||||
BluetoothLowEnergyDevice(deviceInfo, addressType, parent),
|
||||
m_colorService(0),
|
||||
m_imageService(0),
|
||||
m_queueRunning(false),
|
||||
m_brigthness("57ff0f"),
|
||||
m_liveliness("493300")
|
||||
AveaBulb::AveaBulb(Device *device, BluetoothLowEnergyDevice *bluetoothDevice, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_device(device),
|
||||
m_bluetoothDevice(bluetoothDevice)
|
||||
{
|
||||
m_colorSeviceUuid = QBluetoothUuid(QUuid("f815e810-456c-6761-746f-4d756e696368"));
|
||||
m_colorCharacteristicUuid = QBluetoothUuid(QUuid("f815e811-456c-6761-746f-4d756e696368"));
|
||||
|
||||
m_imageSeviceUuid = QBluetoothUuid(QUuid("f815e500-456c-6761-746f-4d756e696368"));
|
||||
m_imageCharacteristicUuid = QBluetoothUuid(QUuid("f815e501-456c-6761-746f-4d756e696368"));
|
||||
|
||||
connect(this, SIGNAL(connectionStatusChanged()), this,SLOT(onConnectionStatusChanged()));
|
||||
connect(this, SIGNAL(servicesDiscoveryFinished()), this, SLOT(serviceScanFinished()));
|
||||
connect(m_bluetoothDevice, &BluetoothLowEnergyDevice::connectedChanged, this, &AveaBulb::onConnectedChanged);
|
||||
connect(m_bluetoothDevice, &BluetoothLowEnergyDevice::servicesDiscoveryFinished, this, &AveaBulb::onServiceDiscoveryFinished);
|
||||
}
|
||||
|
||||
bool AveaBulb::isAvailable()
|
||||
Device *AveaBulb::device()
|
||||
{
|
||||
return m_isAvailable;
|
||||
return m_device;
|
||||
}
|
||||
|
||||
void AveaBulb::serviceScanFinished()
|
||||
BluetoothLowEnergyDevice *AveaBulb::bluetoothDevice()
|
||||
{
|
||||
if (!controller()->services().contains(m_colorSeviceUuid)) {
|
||||
qCWarning(dcElgato) << "ERROR: Color service not found for device" << name() << address().toString();
|
||||
return m_bluetoothDevice;
|
||||
}
|
||||
|
||||
bool AveaBulb::setColor(const QColor &color)
|
||||
{
|
||||
if (!m_bluetoothDevice->connected())
|
||||
return false;
|
||||
|
||||
if (!m_colorService)
|
||||
return false;
|
||||
|
||||
// Convert rgb to wrgb
|
||||
QByteArray command;
|
||||
command.append(QByteArray::fromHex("35"));
|
||||
|
||||
qCDebug(dcElgato()) << color << command;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AveaBulb::onConnectedChanged(const bool &connected)
|
||||
{
|
||||
qCDebug(dcElgato()) << "Bulb" << m_bluetoothDevice->name() << m_bluetoothDevice->address().toString() << (connected ? "connected" : "disconnected");
|
||||
m_device->setStateValue(connectedStateTypeId, connected);
|
||||
|
||||
if (!connected) {
|
||||
// Clean up services
|
||||
m_colorService->deleteLater();
|
||||
//m_imageService->deleteLater();
|
||||
|
||||
m_colorService = nullptr;
|
||||
m_imageService = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AveaBulb::onServiceDiscoveryFinished()
|
||||
{
|
||||
qCDebug(dcElgato()) << "Service discovery finished";
|
||||
|
||||
if (!m_bluetoothDevice->serviceUuids().contains(colorServiceUuid)) {
|
||||
qCWarning(dcElgato()) << "Could not find color service";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_colorService || m_imageService) {
|
||||
qCWarning(dcElgato) << "ERROR: Attention! bad implementation of service handling!!";
|
||||
if (!m_bluetoothDevice->serviceUuids().contains(imageServiceUuid)) {
|
||||
qCWarning(dcElgato()) << "Could not find image service";
|
||||
return;
|
||||
}
|
||||
|
||||
// service for colors
|
||||
m_colorService = controller()->createServiceObject(m_colorSeviceUuid, this);
|
||||
|
||||
// Color service
|
||||
if (!m_colorService) {
|
||||
qCWarning(dcElgato) << "ERROR: could not create color service for device" << name() << address().toString();
|
||||
m_colorService = m_bluetoothDevice->controller()->createServiceObject(colorServiceUuid, this);
|
||||
if (!m_colorService) {
|
||||
qCWarning(dcElgato()) << "Could not create color service.";
|
||||
return;
|
||||
}
|
||||
|
||||
connect(m_colorService, SIGNAL(stateChanged(QLowEnergyService::ServiceState)), this, SLOT(serviceStateChanged(QLowEnergyService::ServiceState)));
|
||||
connect(m_colorService, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)), this, SLOT(serviceCharacteristicChanged(QLowEnergyCharacteristic, QByteArray)));
|
||||
connect(m_colorService, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)), this, SLOT(confirmedCharacteristicWritten(QLowEnergyCharacteristic, QByteArray)));
|
||||
connect(m_colorService, SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)), this, SLOT(confirmedDescriptorWritten(QLowEnergyDescriptor, QByteArray)));
|
||||
connect(m_colorService, SIGNAL(error(QLowEnergyService::ServiceError)), this, SLOT(serviceError(QLowEnergyService::ServiceError)));
|
||||
|
||||
// service for images
|
||||
m_imageService = controller()->createServiceObject(m_imageSeviceUuid, this);
|
||||
|
||||
if (!m_imageService) {
|
||||
qCWarning(dcElgato) << "ERROR: could not create color service for device" << name() << address().toString();
|
||||
return;
|
||||
}
|
||||
|
||||
connect(m_imageService, SIGNAL(stateChanged(QLowEnergyService::ServiceState)), this, SLOT(serviceStateChanged(QLowEnergyService::ServiceState)));
|
||||
connect(m_imageService, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)), this, SLOT(serviceCharacteristicChanged(QLowEnergyCharacteristic, QByteArray)));
|
||||
connect(m_imageService, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)), this, SLOT(confirmedCharacteristicWritten(QLowEnergyCharacteristic, QByteArray)));
|
||||
connect(m_imageService, SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)), this, SLOT(confirmedDescriptorWritten(QLowEnergyDescriptor, QByteArray)));
|
||||
connect(m_imageService, SIGNAL(error(QLowEnergyService::ServiceError)), this, SLOT(serviceError(QLowEnergyService::ServiceError)));
|
||||
connect(m_colorService, &QLowEnergyService::stateChanged, this, &AveaBulb::onColorServiceStateChanged);
|
||||
connect(m_colorService, &QLowEnergyService::characteristicChanged, this, &AveaBulb::onColorServiceCharacteristicChanged);
|
||||
|
||||
m_colorService->discoverDetails();
|
||||
}
|
||||
|
||||
void AveaBulb::onConnectionStatusChanged()
|
||||
}
|
||||
|
||||
void AveaBulb::onColorServiceStateChanged(const QLowEnergyService::ServiceState &state)
|
||||
{
|
||||
if (!isConnected()) {
|
||||
// delete the services, they need to be recreated and
|
||||
// rediscovered once the device will be reconnected
|
||||
delete m_colorService;
|
||||
m_colorService = 0;
|
||||
// Only continue if discovered
|
||||
if (state != QLowEnergyService::ServiceDiscovered)
|
||||
return;
|
||||
|
||||
delete m_imageService;
|
||||
m_imageService = 0;
|
||||
qCDebug(dcElgato()) << "Color service discovered.";
|
||||
|
||||
m_commandQueue.clear();
|
||||
|
||||
m_isAvailable = false;
|
||||
emit availableChanged();
|
||||
foreach (const QLowEnergyCharacteristic &characteristic, m_colorService->characteristics()) {
|
||||
qCDebug(dcElgato()) << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value();
|
||||
foreach (const QLowEnergyDescriptor &desciptor, characteristic.descriptors()) {
|
||||
qCDebug(dcElgato()) << " -->" << desciptor.name() << desciptor.uuid().toString() << desciptor.value();
|
||||
}
|
||||
}
|
||||
|
||||
void AveaBulb::serviceStateChanged(const QLowEnergyService::ServiceState &state)
|
||||
{
|
||||
QLowEnergyService *service =static_cast<QLowEnergyService *>(sender());
|
||||
|
||||
switch (state) {
|
||||
case QLowEnergyService::DiscoveringServices:
|
||||
if (service->serviceUuid() == m_colorService->serviceUuid()) {
|
||||
qCDebug(dcElgato) << "start discovering color service...";
|
||||
} else if (service->serviceUuid() == m_imageService->serviceUuid()) {
|
||||
qCDebug(dcElgato) << "start discovering image service...";
|
||||
}
|
||||
break;
|
||||
case QLowEnergyService::ServiceDiscovered:
|
||||
// check which service is discovered
|
||||
if (service->serviceUuid() == m_colorService->serviceUuid()) {
|
||||
qCDebug(dcElgato) << "...color service discovered.";
|
||||
|
||||
m_colorCharacteristic = m_colorService->characteristic(m_colorCharacteristicUuid);
|
||||
|
||||
// Data characteristic
|
||||
m_colorCharacteristic = m_colorService->characteristic(QBluetoothUuid(QUuid("f815e811-456c-6761-746f-4d756e696368")));
|
||||
if (!m_colorCharacteristic.isValid()) {
|
||||
qCWarning(dcElgato) << "ERROR: color characteristc not found for device " << name() << address().toString();
|
||||
return;
|
||||
qCWarning(dcElgato()) << "Invalid color data characteristic.";
|
||||
}
|
||||
|
||||
m_imageService->discoverDetails();
|
||||
}
|
||||
if (service->serviceUuid() == m_imageService->serviceUuid()) {
|
||||
qCDebug(dcElgato) << "...image service discovered.";
|
||||
// Enable notifications
|
||||
QLowEnergyDescriptor notificationDescriptor = m_colorCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
|
||||
m_colorService->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
|
||||
|
||||
m_imageCharacteristic = m_imageService->characteristic(m_imageCharacteristicUuid);
|
||||
// Get current configuration
|
||||
|
||||
// Color
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex("35"));
|
||||
|
||||
// Brightness
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex("57"));
|
||||
|
||||
// Name
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex("58"));
|
||||
|
||||
if (!m_imageCharacteristic.isValid()) {
|
||||
qCWarning(dcElgato) << "ERROR: image characteristc not found for device " << name() << address().toString();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_colorService->state() == QLowEnergyService::ServiceDiscovered && m_imageService->state() == QLowEnergyService::ServiceDiscovered) {
|
||||
m_isAvailable = true;
|
||||
emit availableChanged();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AveaBulb::serviceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
|
||||
void AveaBulb::onColorServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
|
||||
{
|
||||
qCDebug(dcElgato) << "service characteristic changed" << characteristic.name() << value.toHex();
|
||||
}
|
||||
qCDebug(dcElgato()) << "Color characteristic changed" << characteristic.uuid().toString() << value;
|
||||
|
||||
void AveaBulb::confirmedCharacteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
|
||||
{
|
||||
if (characteristic.handle() == m_colorCharacteristic.handle()) {
|
||||
//qCDebug(dcElgato) << "color char written successfully" << value.toHex();
|
||||
if (m_actions.contains(value.toHex())) {
|
||||
ActionId actionId = m_actions.take(value.toHex());
|
||||
emit actionExecutionFinished(actionId, true);
|
||||
}
|
||||
} else if (characteristic.handle() == m_imageCharacteristic.handle()) {
|
||||
//qCDebug(dcElgato) << "image char written successfully" << value.toHex();
|
||||
if (m_actions.contains(value.toHex())) {
|
||||
ActionId actionId = m_actions.take(value.toHex());
|
||||
emit actionExecutionFinished(actionId, true);
|
||||
}
|
||||
}
|
||||
if (characteristic.uuid() == m_currentRequest.characteristic().uuid()) {
|
||||
if (m_commandQueue.isEmpty()) {
|
||||
m_queueRunning = false;
|
||||
//qCDebug(dcElgato) << "queue finished";
|
||||
}
|
||||
sendNextCommand();
|
||||
if (value.startsWith(QByteArray::fromHex("35"))) {
|
||||
qCDebug(dcElgato()) << "Received color notification";
|
||||
qCDebug(dcElgato()) << " Fade" << value.mid(1, 2);
|
||||
qCDebug(dcElgato()) << " Fixed value" << value.mid(3, 2);
|
||||
qCDebug(dcElgato()) << " White" << value.mid(5, 2);
|
||||
qCDebug(dcElgato()) << " Red" << value.mid(7, 2);
|
||||
qCDebug(dcElgato()) << " Green" << value.mid(9, 2);
|
||||
qCDebug(dcElgato()) << " Blue" << value.mid(11, 2);
|
||||
} else if (value.startsWith(QByteArray::fromHex("57"))) {
|
||||
qCDebug(dcElgato()) << "Received brightness notification";
|
||||
qCDebug(dcElgato()) << " Fade" << value.mid(1, 2);
|
||||
} else if (value.startsWith(QByteArray::fromHex("58"))) {
|
||||
qCDebug(dcElgato()) << "Received name notification";
|
||||
qCDebug(dcElgato()) << " Name" << value.mid(1, value.count() - 2);
|
||||
}
|
||||
}
|
||||
|
||||
void AveaBulb::confirmedDescriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value)
|
||||
{
|
||||
qCDebug(dcElgato) << "descriptor:" << descriptor.name() << "value:" << value.toHex() << "written successfully";
|
||||
}
|
||||
|
||||
void AveaBulb::serviceError(const QLowEnergyService::ServiceError &error)
|
||||
{
|
||||
QString errorString;
|
||||
switch (error) {
|
||||
case QLowEnergyService::NoError:
|
||||
errorString = "No error";
|
||||
break;
|
||||
case QLowEnergyService::OperationError:
|
||||
errorString = "Operation error";
|
||||
break;
|
||||
case QLowEnergyService::CharacteristicWriteError:
|
||||
errorString = "Characteristic write error";
|
||||
break;
|
||||
case QLowEnergyService::DescriptorWriteError:
|
||||
errorString = "Descriptor write error";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
qCWarning(dcElgato) << "ERROR: color service of " << name() << address().toString() << ":" << errorString;
|
||||
}
|
||||
|
||||
void AveaBulb::enqueueCommand(QLowEnergyService *service, const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
|
||||
{
|
||||
CommandRequest r = CommandRequest(service, characteristic, QByteArray::fromHex(value));
|
||||
m_commandQueue.enqueue(r);
|
||||
}
|
||||
|
||||
void AveaBulb::sendNextCommand()
|
||||
{
|
||||
if (!isAvailable()) {
|
||||
m_commandQueue.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_commandQueue.isEmpty())
|
||||
return;
|
||||
|
||||
//qCDebug(dcElgato) << "send next command";
|
||||
m_queueRunning = true;
|
||||
m_currentRequest = m_commandQueue.dequeue();
|
||||
|
||||
|
||||
QLowEnergyService *service = m_currentRequest.service();
|
||||
service->writeCharacteristic(m_currentRequest.characteristic(), m_currentRequest.value());
|
||||
}
|
||||
|
||||
bool AveaBulb::actionPowerOff(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
QByteArray value = "35f4010a00008000b000a00090";
|
||||
m_actions.insert(value, actionId);
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setWhite(ActionId actionId)
|
||||
{
|
||||
// warm white 3514000a00ff0f703bd2259d11
|
||||
// cool white 3514000a00ff0f0030a220a31f
|
||||
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
QByteArray value = "3532000a00ff8f00b058a4899d";
|
||||
m_actions.insert(value, actionId);
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setRed(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
QByteArray value = "3532000a000080ffbf00a0b390";
|
||||
m_actions.insert(value, actionId);
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setGreen(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
QByteArray value = "3514000a00a3005231ff2f0010";
|
||||
m_actions.insert(value, actionId);
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setBlue(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
QByteArray value = "3532000a00c8805fb6ada2ff9f";
|
||||
m_actions.insert(value, actionId);
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setYellow(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
QByteArray value = "3532000a001481e2bfffaf0090";
|
||||
m_actions.insert(value, actionId);
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setPurple(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
QByteArray value = "3532000a000080f6b700a02498";
|
||||
m_actions.insert(value, actionId);
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setOrange(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
QByteArray value = "3532000a000080ffbf36aa0090";
|
||||
m_actions.insert(value, actionId);
|
||||
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setCalmProvence(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_brigthness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_liveliness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "340040203300208017");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1500cd0110cd1280811543016100a40acd00"
|
||||
"20cd0040f1f80773027302f200507df13403"
|
||||
"7202f200507db1147400b50af200608d0a7d"
|
||||
"0ecd16a0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1600cd0210cd1280811543016100a40af1f8"
|
||||
"077302f200507df1c4097202f200507db128"
|
||||
"7301f200608d0a7d0ef134037202f200507d"
|
||||
"b13c7301f200607d8d0a0ecdd453cd4036b1"
|
||||
"c87400b50af200607d8d0a0ecd17a0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1700cd0310cd1280811543016100a40af1cc"
|
||||
"017202f200507df1f8077302f200507db16e"
|
||||
"7400b50af200607d8d0a0ecd18a0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1800cd0410cd1280811543016100a40af1d4"
|
||||
"037202f200507db15a7400b50af200607d8d"
|
||||
"0a0ef134037202f200507df1f807f200507d"
|
||||
"b1c87400b50af200607d8d0a0ecd15a0");
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "2a15");
|
||||
|
||||
m_actions.insert("2a15", actionId);
|
||||
|
||||
sendNextCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setCozyFlames(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_brigthness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_liveliness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "340040ff3fc8200010");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1500cd0110cd0020cdff5fcdc840cd0030cd"
|
||||
"14608d0a0ecd05708216c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1600cd0280a16e6300cd0210cdff5fcdc840"
|
||||
"cd0a608d0a0ecd1ba0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1700cd0310cd02807100b464f5c4097bcdc8"
|
||||
"708278c200404dcd5070823cc200604d8d0b"
|
||||
"0ecdc870c200404dcd64608d0b0e0ccdff5b"
|
||||
"cd0f608d0a0ecd05708216c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1800cd0410cdc840cd0a608d0a0ecd028071"
|
||||
"00b464f5c4097bcd0072c2004082284dcd20"
|
||||
"738232c200604d8d0b0e0ccdff5fcdf842f1"
|
||||
"f4017400b564f200607d8d110ecd05708216"
|
||||
"c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1900cd0280a16e6300cd0510cdff5fcdc840"
|
||||
"cd0a608d0a0ecd1ba0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1a00cd0610cd0280a16e63007100b464f5c4"
|
||||
"097bcd207382c844028564c200404dcd5070"
|
||||
"82a0c200604d8d0b0ecdc870c200404dcd64"
|
||||
"608d0b0e0ccda040cdff5bcd0a608d0a0ecd"
|
||||
"15a0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1b00cd0280a16e6300cd07107100b464f5e8"
|
||||
"03b3017bcdff73c22c0144028564c200404d"
|
||||
"cd50708264c200604d8d0b0ecd587282c844"
|
||||
"028564c200404dcd64608d0b0e0ccdff5bcd"
|
||||
"05608d0a0ecd05708216c200a04d");
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "2a15");
|
||||
|
||||
m_actions.insert("2a15", actionId);
|
||||
|
||||
sendNextCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setCherryBlossom(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_brigthness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_liveliness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "346c47483d00207013");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1500cd0110cd128081654301cd0040cd5c23"
|
||||
"cd7033f19e0c7201f200507db1507201f200"
|
||||
"607d8d0a0ecd1aa0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1600cd0210cd128081654301f108077201f2"
|
||||
"00207df1660d7201f200507df104017301f2"
|
||||
"00608d0a7d0e8d0a0ecdc023b1aa7301f200"
|
||||
"608d0a7d0eb16e73007ecd17a0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1700cd0310cd128081654301cd782acd9838"
|
||||
"b1967301f200608d0a7d0ef1780a7301f200"
|
||||
"207df1280f7201f200507db1dc7301f20060"
|
||||
"8d0a7d0ecd5827cd7033b1967301f200608d"
|
||||
"0a7d0ecd18a0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1800cd0410cd128081654301cd6c27cd345d"
|
||||
"b1a07301f200608d0a7d0ecdc023b1b47301"
|
||||
"f200608d0a7d0ecd402bcd6039b1a07301f2"
|
||||
"00607d8d0a0ecdc023cdf05fb1a07301f200"
|
||||
"607d8d0a0ecd19a0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1900cd0410cd128081654301cd502acd7033"
|
||||
"b1a07301f200608d0a7d0ecd1aa0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1a00cd0510cd128081654301cd2033cdcc21"
|
||||
"7101b20af200607d8d0a0ecd04708216c200"
|
||||
"a04d");
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "2a15");
|
||||
|
||||
m_actions.insert("2a15", actionId);
|
||||
|
||||
sendNextCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setMountainBreeze(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_brigthness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_liveliness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "34584200300020ff1f");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1500cd0110cd5822cd0050cd0040cdff3fcd"
|
||||
"14608d0a0ecd03708216c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1600cd0210cd1280a1646301cd9828f16801"
|
||||
"7402b564f200608d0a7d0ecd5822f1040174"
|
||||
"02b564f200608d0a7d0ecd03708216c200a0"
|
||||
"4d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1700cd0310cd1280a1646301cd283acd4c24"
|
||||
"cddc70825044028564c200608d0a4d0ecda0"
|
||||
"20cddc70825044028564c200604d8d0a0ecd"
|
||||
"03708216c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1800cd0410cd1280a1646301f178057302f2"
|
||||
"00207df12c017402b564f200608d0a7d0ecd"
|
||||
"2023f1480d7302f200307df178057302f200"
|
||||
"207dcdb470825044028564c200604d8d0a0e"
|
||||
"cd03708216c200a04d");
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "2a15");
|
||||
|
||||
m_actions.insert("2a15", actionId);
|
||||
|
||||
sendNextCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setNorthernGlow(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_brigthness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_liveliness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "34004000301420e011");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1500cd1280cd0110cd0020cd0050cd1440cd"
|
||||
"e031cd14608d0a0ecdf07082e64201c20030"
|
||||
"4db1b47301f200607d8d0a0ecd04708216c2"
|
||||
"00a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1600cd0210cd1280b1a07301f200607dcd50"
|
||||
"7082504301c200404dcdf070c2b8014201c2"
|
||||
"00304d8d0a0ecd03708217c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1700cd0310cd1280b1967301f200607dcd2c"
|
||||
"40cd0050cdb4308d0a0ecd04708216c200a0"
|
||||
"4d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1800cd0410cd1280b1aa7301f200607dcd50"
|
||||
"70821ec200404dcd0050c168014201c20030"
|
||||
"4d8d0a0ecd04708216c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1900cd0510cd1280b1a07301f200607de170"
|
||||
"08e31c026401a564f170087302f200407dcd"
|
||||
"28308d110eb1a07301f200607df1f8027200"
|
||||
"f200407dcd28308d110ecddc70823c4301c2"
|
||||
"00604dcd3c40cda0308d0a0ecd15a0");
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "2a15");
|
||||
|
||||
m_actions.insert("2a15", actionId);
|
||||
|
||||
sendNextCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setFairyWoods(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "57ff0f");
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "340040143058220010");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1500cd0110cd0020cd0030cd2850cd5842cd"
|
||||
"14608d0a0ecd04708216c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1600cd0210cd1280a1646301cd14608d080e"
|
||||
"cd9041f190017402b564f200608d0a7d0ecd"
|
||||
"6851b1c87402b564f200608d0a7d0ecdff47"
|
||||
"cd6450cdb470823c44028564c200604d8d0a"
|
||||
"0ecd17a0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1700cd0310cd1280a1646301cde041cd2850"
|
||||
"b1f07402b564f200608d0a7d0ecd04708216"
|
||||
"c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1800cd0410cd1280a1646301cdb841cdb470"
|
||||
"823c7402b564c200604d8d0a0ecd0e41cd28"
|
||||
"50b1c87402b564f200608d0a7d0ecd6440f1"
|
||||
"90017402b564f200608d0a7d0e8d0a0ecd04"
|
||||
"708216c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1900cd0510cd1280a1646301cd6841cdb470"
|
||||
"823cc200604d8d0a0ecda050f12c017402b5"
|
||||
"64f200608d0a7d0ecdf040cd2850f1680174"
|
||||
"02b564f200608d0a7d0ecdcc41b1c87402b5"
|
||||
"64f200608d0a7d0ecd04708216c200a04d");
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "2a15");
|
||||
|
||||
m_actions.insert("2a15", actionId);
|
||||
|
||||
sendNextCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveaBulb::setMagicHour(ActionId actionId)
|
||||
{
|
||||
if (!isAvailable())
|
||||
return false;
|
||||
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_brigthness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, m_liveliness);
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "340040d03c90210010");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1500cd0110cd0020cdd05ccd9041cd0030cd"
|
||||
"14608d0a0ecd02708216c200a04d");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1600cd0210cd0a60cd0030cd0020cd29408d"
|
||||
"0a0ecd128081664301e1ff0fe3ff07a50a64"
|
||||
"01a50a5100cd0a706400a50af1ff0f7302f2"
|
||||
"00507dcd28708229c200404dcd32704201c2"
|
||||
"00604d8d0a0ecd17a0");
|
||||
enqueueCommand(m_imageService, m_imageCharacteristic, "1700cd0310cd1460cd0030cd00208d0a0ecd"
|
||||
"1280a16663018d0d85808250c200404dcd32"
|
||||
"70820a4202c200604d8d0a0ecd16a0");
|
||||
enqueueCommand(m_colorService, m_colorCharacteristic, "2a15");
|
||||
|
||||
m_actions.insert("2a15", actionId);
|
||||
|
||||
sendNextCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // BLUETOOTH_LE
|
||||
|
||||
@ -21,84 +21,47 @@
|
||||
#ifndef AVEABULB_H
|
||||
#define AVEABULB_H
|
||||
|
||||
#ifdef BLUETOOTH_LE
|
||||
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
#include <QColor>
|
||||
|
||||
#include "typeutils.h"
|
||||
#include "bluetooth/bluetoothlowenergydevice.h"
|
||||
#include "commandrequest.h"
|
||||
#include "plugin/device.h"
|
||||
#include "hardware/bluetoothlowenergy/bluetoothlowenergydevice.h"
|
||||
|
||||
class AveaBulb : public BluetoothLowEnergyDevice
|
||||
static QBluetoothUuid colorServiceUuid = QBluetoothUuid(QUuid("f815e810-456c-6761-746f-4d756e696368"));
|
||||
static QBluetoothUuid imageServiceUuid = QBluetoothUuid(QUuid("f815e500-456c-6761-746f-4d756e696368"));
|
||||
|
||||
class AveaBulb : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AveaBulb(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType, QObject *parent = 0);
|
||||
explicit AveaBulb(Device *device, BluetoothLowEnergyDevice *bluetoothDevice, QObject *parent = nullptr);
|
||||
|
||||
bool isAvailable();
|
||||
Device *device();
|
||||
BluetoothLowEnergyDevice *bluetoothDevice();
|
||||
|
||||
signals:
|
||||
void availableChanged();
|
||||
void actionExecutionFinished(const ActionId &actionId, const bool &success);
|
||||
bool setColor(const QColor &color);
|
||||
|
||||
private:
|
||||
QBluetoothUuid m_colorSeviceUuid;
|
||||
QLowEnergyService *m_colorService;
|
||||
Device *m_device;
|
||||
BluetoothLowEnergyDevice *m_bluetoothDevice;
|
||||
|
||||
QBluetoothUuid m_imageSeviceUuid;
|
||||
QLowEnergyService *m_imageService;
|
||||
private:
|
||||
QLowEnergyService *m_colorService = nullptr;
|
||||
QLowEnergyService *m_imageService = nullptr;
|
||||
|
||||
QBluetoothUuid m_imageCharacteristicUuid;
|
||||
QLowEnergyCharacteristic m_imageCharacteristic;
|
||||
|
||||
QBluetoothUuid m_colorCharacteristicUuid;
|
||||
QLowEnergyCharacteristic m_colorCharacteristic;
|
||||
|
||||
bool m_isAvailable;
|
||||
|
||||
QHash<QByteArray, ActionId> m_actions;
|
||||
|
||||
QQueue<CommandRequest> m_commandQueue;
|
||||
CommandRequest m_currentRequest;
|
||||
bool m_queueRunning;
|
||||
|
||||
QByteArray m_brigthness;
|
||||
QByteArray m_liveliness;
|
||||
|
||||
private slots:
|
||||
void serviceScanFinished();
|
||||
void onConnectionStatusChanged();
|
||||
void onConnectedChanged(const bool &connected);
|
||||
void onServiceDiscoveryFinished();
|
||||
|
||||
// Color service
|
||||
void serviceStateChanged(const QLowEnergyService::ServiceState &state);
|
||||
void serviceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
|
||||
void confirmedCharacteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
|
||||
void confirmedDescriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value);
|
||||
void serviceError(const QLowEnergyService::ServiceError &error);
|
||||
|
||||
void enqueueCommand(QLowEnergyService *service, const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
|
||||
void sendNextCommand();
|
||||
|
||||
public slots:
|
||||
bool actionPowerOff(ActionId actionId);
|
||||
bool setWhite(ActionId actionId);
|
||||
bool setRed(ActionId actionId);
|
||||
bool setGreen(ActionId actionId);
|
||||
bool setBlue(ActionId actionId);
|
||||
bool setYellow(ActionId actionId);
|
||||
bool setPurple(ActionId actionId);
|
||||
bool setOrange(ActionId actionId);
|
||||
|
||||
bool setCalmProvence(ActionId actionId);
|
||||
bool setCozyFlames(ActionId actionId);
|
||||
bool setCherryBlossom(ActionId actionId);
|
||||
bool setMountainBreeze(ActionId actionId);
|
||||
bool setNorthernGlow(ActionId actionId);
|
||||
bool setFairyWoods(ActionId actionId);
|
||||
bool setMagicHour(ActionId actionId);
|
||||
void onColorServiceStateChanged(const QLowEnergyService::ServiceState &state);
|
||||
void onColorServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
|
||||
|
||||
};
|
||||
#endif // BLUETOOTH_LE
|
||||
|
||||
#endif // AVEABULB_H
|
||||
|
||||
70
elgato/aveacolor.cpp
Normal file
70
elgato/aveacolor.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include "aveacolor.h"
|
||||
|
||||
AveaColor::AveaColor(const QColor &color)
|
||||
{
|
||||
// Convert rgb to wrgb
|
||||
|
||||
ColorRgbw rgbw = rgbToRgbw(color.red(), color.green(), color.blue());
|
||||
Q_UNUSED(rgbw)
|
||||
|
||||
}
|
||||
|
||||
uint AveaColor::white() const
|
||||
{
|
||||
return m_white;
|
||||
}
|
||||
|
||||
uint AveaColor::red() const
|
||||
{
|
||||
return m_red;
|
||||
}
|
||||
|
||||
uint AveaColor::green() const
|
||||
{
|
||||
return m_green;
|
||||
}
|
||||
|
||||
uint AveaColor::blue() const
|
||||
{
|
||||
return m_blue;
|
||||
}
|
||||
|
||||
QByteArray AveaColor::toByteArray()
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// The saturation is the colorfulness of a color relative to its own brightness.
|
||||
uint AveaColor::saturation(ColorRgbw rgbw)
|
||||
{
|
||||
// Find the smallest of all three parameters.
|
||||
float low = qMin(rgbw.red, qMin(rgbw.green, rgbw.blue));
|
||||
// Find the highest of all three parameters.
|
||||
float high = qMax(rgbw.red, qMax(rgbw.green, rgbw.blue));
|
||||
// The difference between the last two variables
|
||||
// divided by the highest is the saturation.
|
||||
return qRound(100 * ((high - low) / high));
|
||||
}
|
||||
|
||||
uint AveaColor::getWhite(ColorRgbw rgbw)
|
||||
{
|
||||
return (255 - saturation(rgbw)) / 255 * (rgbw.red + rgbw.green + rgbw.blue) / 3;
|
||||
}
|
||||
|
||||
uint AveaColor::getWhite(ColorRgbw rgbw, int redMax, int greenMax, int blueMax)
|
||||
{
|
||||
// Set the maximum value for all colors.
|
||||
rgbw.red = (float)rgbw.red / 255.0 * (float)redMax;
|
||||
rgbw.green = (float)rgbw.green / 255.0 * (float)greenMax;
|
||||
rgbw.blue = (float)rgbw.blue / 255.0 * (float)blueMax;
|
||||
return (255 - saturation(rgbw)) / 255 * (rgbw.red + rgbw.green + rgbw.blue) / 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ColorRgbw AveaColor::rgbToRgbw(uint red, uint green, uint blue)
|
||||
{
|
||||
uint white = 0;
|
||||
ColorRgbw rgbw = {red, green, blue, white};
|
||||
rgbw.white = getWhite(rgbw);
|
||||
return rgbw;
|
||||
}
|
||||
39
elgato/aveacolor.h
Normal file
39
elgato/aveacolor.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef AVEACOLOR_H
|
||||
#define AVEACOLOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QColor>
|
||||
|
||||
struct ColorRgbw {
|
||||
uint red;
|
||||
uint green;
|
||||
uint blue;
|
||||
uint white;
|
||||
};
|
||||
|
||||
|
||||
class AveaColor
|
||||
{
|
||||
public:
|
||||
AveaColor(const QColor &color);
|
||||
|
||||
uint white() const;
|
||||
uint red() const;
|
||||
uint green() const;
|
||||
uint blue() const;
|
||||
|
||||
QByteArray toByteArray();
|
||||
|
||||
private:
|
||||
uint m_white = 0;
|
||||
uint m_red = 0;
|
||||
uint m_green = 0;
|
||||
uint m_blue = 0;
|
||||
|
||||
uint saturation(ColorRgbw rgbw);
|
||||
uint getWhite(ColorRgbw rgbw);
|
||||
uint getWhite(ColorRgbw rgbw, int redMax, int greenMax, int blueMax);
|
||||
ColorRgbw rgbToRgbw(unsigned int red, unsigned int green, unsigned int blue);
|
||||
};
|
||||
|
||||
#endif // AVEACOLOR_H
|
||||
@ -1,54 +0,0 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* This file is part of guh. *
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifdef BLUETOOTH_LE
|
||||
|
||||
#include "commandrequest.h"
|
||||
|
||||
CommandRequest::CommandRequest() :
|
||||
m_service(0),
|
||||
m_characteristic(QLowEnergyCharacteristic()),
|
||||
m_value(QByteArray())
|
||||
{
|
||||
}
|
||||
|
||||
CommandRequest::CommandRequest(QLowEnergyService *service, const QLowEnergyCharacteristic &characteristic, const QByteArray &value) :
|
||||
m_service(service),
|
||||
m_characteristic(characteristic),
|
||||
m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
QLowEnergyService *CommandRequest::service()
|
||||
{
|
||||
return m_service;
|
||||
}
|
||||
|
||||
QLowEnergyCharacteristic CommandRequest::characteristic() const
|
||||
{
|
||||
return m_characteristic;
|
||||
}
|
||||
|
||||
QByteArray CommandRequest::value() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
#endif // BLUETOOTH_LE
|
||||
@ -1,50 +0,0 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* This file is part of guh. *
|
||||
* *
|
||||
* 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 COMMANDREQUEST_H
|
||||
#define COMMANDREQUEST_H
|
||||
|
||||
#ifdef BLUETOOTH_LE
|
||||
|
||||
#include <QLowEnergyService>
|
||||
#include <QLowEnergyCharacteristic>
|
||||
#include <QByteArray>
|
||||
|
||||
class CommandRequest
|
||||
{
|
||||
|
||||
public:
|
||||
CommandRequest();
|
||||
CommandRequest(QLowEnergyService *service, const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
|
||||
|
||||
QLowEnergyService *service();
|
||||
QLowEnergyCharacteristic characteristic() const;
|
||||
QByteArray value() const;
|
||||
|
||||
private:
|
||||
QLowEnergyService *m_service;
|
||||
QLowEnergyCharacteristic m_characteristic;
|
||||
QByteArray m_value;
|
||||
|
||||
};
|
||||
|
||||
#endif // BLUETOOTH_LE
|
||||
|
||||
#endif // COMMANDREQUEST_H
|
||||
@ -396,16 +396,16 @@
|
||||
\quotefile plugins/deviceplugins/elgato/devicepluginelgato.json
|
||||
*/
|
||||
|
||||
#ifdef BLUETOOTH_LE
|
||||
|
||||
#include "devicepluginelgato.h"
|
||||
|
||||
#include "plugin/device.h"
|
||||
#include "devicemanager.h"
|
||||
#include "plugininfo.h"
|
||||
#include "hardware/bluetoothlowenergy/bluetoothlowenergymanager.h"
|
||||
|
||||
DevicePluginElgato::DevicePluginElgato()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DeviceManager::DeviceError DevicePluginElgato::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
|
||||
@ -415,155 +415,70 @@ DeviceManager::DeviceError DevicePluginElgato::discoverDevices(const DeviceClass
|
||||
if (deviceClassId != aveaDeviceClassId)
|
||||
return DeviceManager::DeviceErrorDeviceClassNotFound;
|
||||
|
||||
if (!discoverBluetooth())
|
||||
if (!hardwareManager()->bluetoothLowEnergyManager()->available())
|
||||
return DeviceManager::DeviceErrorHardwareNotAvailable;
|
||||
|
||||
if (!hardwareManager()->bluetoothLowEnergyManager()->enabled())
|
||||
return DeviceManager::DeviceErrorHardwareNotAvailable;
|
||||
|
||||
BluetoothDiscoveryReply *reply = hardwareManager()->bluetoothLowEnergyManager()->discoverDevices();
|
||||
connect(reply, &BluetoothDiscoveryReply::finished, this, &DevicePluginElgato::onBluetoothDiscoveryFinished);
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
|
||||
DeviceManager::DeviceSetupStatus DevicePluginElgato::setupDevice(Device *device)
|
||||
{
|
||||
qCDebug(dcElgato()) << "Setup device" << device->name() << device->params();
|
||||
|
||||
if (device->deviceClassId() == aveaDeviceClassId) {
|
||||
QBluetoothAddress address = QBluetoothAddress(device->paramValue(macAddressParamTypeId).toString());
|
||||
QString name = device->paramValue(nameParamTypeId).toString();
|
||||
QBluetoothDeviceInfo deviceInfo = QBluetoothDeviceInfo(address, name, 0);
|
||||
|
||||
AveaBulb *avea = new AveaBulb(deviceInfo, QLowEnergyController::PublicAddress, this);
|
||||
connect(avea, &AveaBulb::availableChanged, this, &DevicePluginElgato::bulbAvailableChanged);
|
||||
connect(avea, &AveaBulb::actionExecutionFinished, this, &DevicePluginElgato::actionFinished);
|
||||
m_bulbs.insert(avea, device);
|
||||
BluetoothLowEnergyDevice *bluetoothDevice = hardwareManager()->bluetoothLowEnergyManager()->registerDevice(deviceInfo, QLowEnergyController::PublicAddress);
|
||||
|
||||
avea->connectDevice();
|
||||
AveaBulb *bulb = new AveaBulb(device, bluetoothDevice, this);
|
||||
|
||||
m_bulbs.insert(device, bulb);
|
||||
bulb->bluetoothDevice()->connectDevice();
|
||||
|
||||
return DeviceManager::DeviceSetupStatusSuccess;
|
||||
}
|
||||
return DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
DeviceManager::HardwareResources DevicePluginElgato::requiredHardware() const
|
||||
{
|
||||
return DeviceManager::HardwareResourceBluetoothLE;
|
||||
}
|
||||
|
||||
DeviceManager::DeviceError DevicePluginElgato::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
Q_UNUSED(action)
|
||||
// check deviceClassId
|
||||
if (device->deviceClassId() == aveaDeviceClassId) {
|
||||
AveaBulb *bulb = m_bulbs.key(device);
|
||||
AveaBulb *bulb = m_bulbs.value(device);
|
||||
|
||||
// reconnect action does not need available true
|
||||
if (action.actionTypeId() == connectActionTypeId) {
|
||||
bulb->reconnectDevice();
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
Q_UNUSED(bulb)
|
||||
// // check actionTypeId
|
||||
// if (action.actionTypeId() == powerOffActionTypeId) {
|
||||
// bulb->actionPowerOff(action.id());
|
||||
// return DeviceManager::DeviceErrorAsync;
|
||||
// } else if (action.actionTypeId() == colorActionTypeId) {
|
||||
|
||||
if (action.actionTypeId() == disconnectActionTypeId) {
|
||||
bulb->disconnectDevice();
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
// return DeviceManager::DeviceErrorNoError;
|
||||
// }
|
||||
|
||||
// check available
|
||||
if (!bulb->isAvailable())
|
||||
return DeviceManager::DeviceErrorHardwareNotAvailable;
|
||||
|
||||
// check actionTypeId
|
||||
if (action.actionTypeId() == powerOffActionTypeId) {
|
||||
bulb->actionPowerOff(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
} else if (action.actionTypeId() == colorActionTypeId) {
|
||||
if (action.param(colorParamTypeId).value().toString() == "green") {
|
||||
bulb->setGreen(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(colorParamTypeId).value().toString() == "blue") {
|
||||
bulb->setBlue(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(colorParamTypeId).value().toString() == "red") {
|
||||
bulb->setRed(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(colorParamTypeId).value().toString() == "yellow") {
|
||||
bulb->setYellow(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(colorParamTypeId).value().toString() == "orange") {
|
||||
bulb->setOrange(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(colorParamTypeId).value().toString() == "purple") {
|
||||
bulb->setPurple(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(colorParamTypeId).value().toString() == "white") {
|
||||
bulb->setWhite(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
return DeviceManager::DeviceErrorInvalidParameter;
|
||||
} else if (action.actionTypeId() == moodActionTypeId) {
|
||||
if (action.param(moodParamTypeId).value().toString() == "calm provence") {
|
||||
bulb->setCalmProvence(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(moodParamTypeId).value().toString() == "cozy flames") {
|
||||
bulb->setCozyFlames(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(moodParamTypeId).value().toString() == "cherry blossom") {
|
||||
bulb->setCherryBlossom(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(moodParamTypeId).value().toString() == "mountain breeze") {
|
||||
bulb->setMountainBreeze(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(moodParamTypeId).value().toString() == "northern glow") {
|
||||
bulb->setNorthernGlow(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(moodParamTypeId).value().toString() == "fairy woods") {
|
||||
bulb->setFairyWoods(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
if (action.param(moodParamTypeId).value().toString() == "magic hour") {
|
||||
bulb->setMagicHour(action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
return DeviceManager::DeviceErrorInvalidParameter;
|
||||
}
|
||||
return DeviceManager::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
return DeviceManager::DeviceErrorDeviceClassNotFound;
|
||||
}
|
||||
|
||||
void DevicePluginElgato::bluetoothDiscoveryFinished(const QList<QBluetoothDeviceInfo> &deviceInfos)
|
||||
{
|
||||
QList<DeviceDescriptor> deviceDescriptors;
|
||||
foreach (QBluetoothDeviceInfo deviceInfo, deviceInfos) {
|
||||
if (deviceInfo.name().contains("Avea")) {
|
||||
if (!verifyExistingDevices(deviceInfo)) {
|
||||
DeviceDescriptor descriptor(aveaDeviceClassId, "Avea", deviceInfo.address().toString());
|
||||
ParamList params;
|
||||
params.append(Param(nameParamTypeId, deviceInfo.name()));
|
||||
params.append(Param(macAddressParamTypeId, deviceInfo.address().toString()));
|
||||
descriptor.setParams(params);
|
||||
deviceDescriptors.append(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit devicesDiscovered(aveaDeviceClassId, deviceDescriptors);
|
||||
}
|
||||
|
||||
void DevicePluginElgato::deviceRemoved(Device *device)
|
||||
{
|
||||
if (!m_bulbs.values().contains(device))
|
||||
if (!m_bulbs.keys().contains(device))
|
||||
return;
|
||||
|
||||
AveaBulb *bulb= m_bulbs.key(device);
|
||||
|
||||
m_bulbs.take(bulb);
|
||||
delete bulb;
|
||||
AveaBulb *bulb = m_bulbs.value(device);
|
||||
m_bulbs.remove(device);
|
||||
hardwareManager()->bluetoothLowEnergyManager()->unregisterDevice(bulb->bluetoothDevice());
|
||||
bulb->deleteLater();
|
||||
}
|
||||
|
||||
bool DevicePluginElgato::verifyExistingDevices(const QBluetoothDeviceInfo &deviceInfo)
|
||||
@ -576,23 +491,32 @@ bool DevicePluginElgato::verifyExistingDevices(const QBluetoothDeviceInfo &devic
|
||||
return false;
|
||||
}
|
||||
|
||||
void DevicePluginElgato::bulbAvailableChanged()
|
||||
void DevicePluginElgato::onBluetoothDiscoveryFinished()
|
||||
{
|
||||
AveaBulb *bulb =static_cast<AveaBulb *>(sender());
|
||||
Device *device = m_bulbs.value(bulb);
|
||||
device->setStateValue(availableStateTypeId, bulb->isAvailable());
|
||||
|
||||
BluetoothDiscoveryReply *reply = static_cast<BluetoothDiscoveryReply *>(sender());
|
||||
if (reply->error() != BluetoothDiscoveryReply::BluetoothDiscoveryReplyErrorNoError) {
|
||||
qCWarning(dcElgato()) << "Bluetooth discovery error:" << reply->error();
|
||||
reply->deleteLater();
|
||||
emit devicesDiscovered(aveaDeviceClassId, QList<DeviceDescriptor>());
|
||||
return;
|
||||
}
|
||||
|
||||
void DevicePluginElgato::actionFinished(const ActionId actionId, const bool &success)
|
||||
{
|
||||
if (success) {
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError);
|
||||
} else {
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure);
|
||||
QList<DeviceDescriptor> deviceDescriptors;
|
||||
foreach (const QBluetoothDeviceInfo &deviceInfo, reply->discoveredDevices()) {
|
||||
if (deviceInfo.name().contains("Avea")) {
|
||||
if (!verifyExistingDevices(deviceInfo)) {
|
||||
DeviceDescriptor descriptor(aveaDeviceClassId, "Avea", deviceInfo.address().toString());
|
||||
ParamList params;
|
||||
params.append(Param(nameParamTypeId, deviceInfo.name()));
|
||||
params.append(Param(macAddressParamTypeId, deviceInfo.address().toString()));
|
||||
descriptor.setParams(params);
|
||||
deviceDescriptors.append(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BLUETOOTH_LE
|
||||
reply->deleteLater();
|
||||
|
||||
emit devicesDiscovered(aveaDeviceClassId, deviceDescriptors);
|
||||
}
|
||||
|
||||
|
||||
@ -21,10 +21,8 @@
|
||||
#ifndef DEVICEPLUGINELGATO_H
|
||||
#define DEVICEPLUGINELGATO_H
|
||||
|
||||
#ifdef BLUETOOTH_LE
|
||||
|
||||
#include "plugin/deviceplugin.h"
|
||||
#include "bluetooth/bluetoothlowenergydevice.h"
|
||||
#include "hardware/bluetoothlowenergy/bluetoothlowenergydevice.h"
|
||||
#include "aveabulb.h"
|
||||
|
||||
class DevicePluginElgato : public DevicePlugin
|
||||
@ -39,20 +37,16 @@ public:
|
||||
|
||||
DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
|
||||
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
DeviceManager::HardwareResources requiredHardware() const override;
|
||||
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
|
||||
void bluetoothDiscoveryFinished(const QList<QBluetoothDeviceInfo> &deviceInfos);
|
||||
void deviceRemoved(Device *device) override;
|
||||
|
||||
private:
|
||||
QHash<AveaBulb *, Device *> m_bulbs;
|
||||
QHash<Device *, AveaBulb *> m_bulbs;
|
||||
bool verifyExistingDevices(const QBluetoothDeviceInfo &deviceInfo);
|
||||
|
||||
private slots:
|
||||
void bulbAvailableChanged();
|
||||
void actionFinished(const ActionId actionId, const bool &success);
|
||||
void onBluetoothDiscoveryFinished();
|
||||
|
||||
};
|
||||
#endif // BLUETOOTH_LE
|
||||
|
||||
#endif // DEVICEPLUGINELGATO_H
|
||||
|
||||
@ -39,16 +39,16 @@
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "6d5e792a-c786-40d2-ae35-a48ac6fafcbc",
|
||||
"idName": "available",
|
||||
"name": "available",
|
||||
"eventTypeName": "available changed",
|
||||
"idName": "connected",
|
||||
"name": "connected",
|
||||
"eventTypeName": "connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "c38181a0-e19b-423f-8b66-dedda94e89b5",
|
||||
"idName": "brightness",
|
||||
"name": "brightness",
|
||||
"name": "Brightness",
|
||||
"eventTypeName": "brightness changed",
|
||||
"actionTypeName": "Set brightness",
|
||||
"type": "int",
|
||||
@ -60,79 +60,17 @@
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "2073b44b-2f99-4abf-8d9f-6b33be8e1763",
|
||||
"idName": "liveliness",
|
||||
"name": "liveliness",
|
||||
"eventTypeName": "liveliness changed",
|
||||
"actionTypeName": "Set liveliness",
|
||||
"type": "int",
|
||||
"defaultValue": 100,
|
||||
"ruleRelevant": false,
|
||||
"eventRuleRelevant": false,
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"writable": true
|
||||
}
|
||||
],
|
||||
"actionTypes": [
|
||||
{
|
||||
"id": "30b6334d-37cd-4b94-a397-3b9b642c762e",
|
||||
"idName": "connect",
|
||||
"name": "connect"
|
||||
},
|
||||
{
|
||||
"id": "1daa8def-85d7-44fb-b0bd-77a4301056e5",
|
||||
"idName": "disconnect",
|
||||
"name": "disconnect"
|
||||
},
|
||||
{
|
||||
"id": "b2f39b7b-dd11-4db4-a82b-dd3d4b973bd5",
|
||||
"idName": "powerOff",
|
||||
"name": "power off"
|
||||
},
|
||||
{
|
||||
"id": "7936bd51-7ea3-4df4-9998-7325de85b677",
|
||||
"idName": "color",
|
||||
"name": "set color",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "f5f02666-576d-4dee-afa3-f38e1b24f469",
|
||||
"id": "3f15637a-8272-4714-bd08-04806e05bbef",
|
||||
"idName": "color",
|
||||
"name": "color",
|
||||
"type": "QString",
|
||||
"allowedValues": [
|
||||
"green",
|
||||
"yellow",
|
||||
"orange",
|
||||
"red",
|
||||
"purple",
|
||||
"blue",
|
||||
"white"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dfbc86e8-3891-4782-ae14-05228e70423e",
|
||||
"idName": "mood",
|
||||
"name": "set mood",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "c5d42d41-f25c-4d15-aed1-b67071847246",
|
||||
"idName": "mood",
|
||||
"name": "mood",
|
||||
"type": "QString",
|
||||
"allowedValues": [
|
||||
"calm provence",
|
||||
"cozy flames",
|
||||
"cherry blossom",
|
||||
"mountain breeze",
|
||||
"northern glow",
|
||||
"fairy woods",
|
||||
"magic hour"
|
||||
]
|
||||
}
|
||||
]
|
||||
"eventTypeName": "color changed",
|
||||
"actionTypeName": "Set color",
|
||||
"type": "QColor",
|
||||
"ruleRelevant": false,
|
||||
"eventRuleRelevant": false,
|
||||
"defaultValue": "#000000",
|
||||
"writable": true
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -9,12 +9,12 @@ TARGET = $$qtLibraryTarget(guh_devicepluginelgato)
|
||||
SOURCES += \
|
||||
devicepluginelgato.cpp \
|
||||
aveabulb.cpp \
|
||||
commandrequest.cpp
|
||||
aveacolor.cpp
|
||||
|
||||
HEADERS += \
|
||||
devicepluginelgato.h \
|
||||
aveabulb.h \
|
||||
commandrequest.h
|
||||
aveacolor.h
|
||||
|
||||
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ DevicePluginElro::DevicePluginElro()
|
||||
|
||||
DeviceManager::DeviceError DevicePluginElro::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
if (!hardwareManager()->isAvailable(HardwareResource::TypeRadio433))
|
||||
if (!hardwareManager()->radio433()->available())
|
||||
return DeviceManager::DeviceErrorHardwareNotAvailable;
|
||||
|
||||
if (action.actionTypeId() != powerActionTypeId)
|
||||
|
||||
@ -20,7 +20,7 @@ PLUGIN_DIRS = \
|
||||
udpcommander \
|
||||
tcpcommander \
|
||||
kodi \
|
||||
#elgato \
|
||||
elgato \
|
||||
#senic \
|
||||
awattar \
|
||||
netatmo \
|
||||
|
||||
@ -58,7 +58,7 @@ DevicePluginIntertechno::DevicePluginIntertechno()
|
||||
|
||||
DeviceManager::DeviceError DevicePluginIntertechno::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
if (!hardwareManager()->isAvailable(HardwareResource::TypeRadio433))
|
||||
if (!hardwareManager()->radio433()->available())
|
||||
return DeviceManager::DeviceErrorHardwareNotAvailable;
|
||||
|
||||
QList<int> rawData;
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "plugintimer.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class DevicePluginOrderButton : public DevicePlugin
|
||||
{
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
|
||||
#include <QHash>
|
||||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "coap/coap.h"
|
||||
#include "plugintimer.h"
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "plugintimer.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class DevicePluginPlantCare : public DevicePlugin
|
||||
{
|
||||
|
||||
@ -11,11 +11,6 @@ LIBS += -lguh
|
||||
|
||||
PLUGIN_PATH=/usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')/guh/plugins/
|
||||
|
||||
# Check for Bluetoot LE support (Qt >= 5.4)
|
||||
equals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 3) {
|
||||
DEFINES += BLUETOOTH_LE
|
||||
}
|
||||
|
||||
# Check if this is a snap build
|
||||
snappy{
|
||||
INCLUDEPATH+=$$(SNAPCRAFT_STAGE)/usr/include/guh
|
||||
|
||||
@ -26,6 +26,8 @@
|
||||
#include "plugintimer.h"
|
||||
#include "plugin/deviceplugin.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
class DevicePluginWemo : public DevicePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
|
||||
#include <QColor>
|
||||
#include <QHash>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class DevicePluginWs2812 : public DevicePlugin
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user