first working version of avea example plugin for bluetoot le

This commit is contained in:
Simon Stürz 2015-03-31 10:31:46 +02:00 committed by Michael Zanetti
parent 3348fd18de
commit 6ea0e0c4a9
16 changed files with 886 additions and 119 deletions

View File

@ -18,4 +18,5 @@ usr/lib/guh/plugins/libguh_devicepluginleynew.so
usr/lib/guh/plugins/libguh_deviceplugintune.so
usr/lib/guh/plugins/libguh_devicepluginudpcommander.so
usr/lib/guh/plugins/libguh_devicepluginkodi.so
usr/lib/guh/plugins/libguh_devicepluginelgato.so

7
doc/bluetooth.qdoc Normal file
View File

@ -0,0 +1,7 @@
/*!
\page bluetooth-index.html
\title Bluetooth Plugins
\annotatedlist bluetooth
*/

View File

@ -23,14 +23,8 @@
\li \l{RF 433 MHz Plugins}
\li \l{Service Plugins}
\li \l{Network Plugins}
\endlist
\endlist
\section1 API's
The guh daemon provides two API's:
\list
\li \l{JSON-RPC API}{JSON-RPC}
\li \l{https://github.com/guh/guh/wiki/REST-Api-documentation}{REST}
\li \l{Bluetooth Plugins}
\endlist
\endlist
\section1 Write your own plugin

View File

@ -13,6 +13,8 @@
\section2 Network Plugins
\annotatedlist network
\section1 Bluetooth Plugins
\annotatedlist bluetooth
*/

View File

@ -26,9 +26,8 @@ BluetoothLowEnergyDevice::BluetoothLowEnergyDevice(const QBluetoothDeviceInfo &d
connect(m_controller, &QLowEnergyController::connected, this, &BluetoothLowEnergyDevice::connected);
connect(m_controller, &QLowEnergyController::disconnected, this, &BluetoothLowEnergyDevice::disconnected);
connect(m_controller, &QLowEnergyController::serviceDiscovered, this, &BluetoothLowEnergyDevice::serviceDiscovered);
connect(m_controller, &QLowEnergyController::discoveryFinished, this, &BluetoothLowEnergyDevice::serviceScanFinished);
connect(m_controller, SIGNAL(error(QLowEnergyController::Error)), this, SLOT(deviceError(QLowEnergyController::Error)));
connect(m_controller, SIGNAL(discoveryFinished()), this, SIGNAL(servicesDiscoveryFinished()));
}
QString BluetoothLowEnergyDevice::name() const
@ -41,14 +40,9 @@ QBluetoothAddress BluetoothLowEnergyDevice::address() const
return m_deviceInfo.address();
}
QList<QLowEnergyService *> BluetoothLowEnergyDevice::services() const
QLowEnergyController *BluetoothLowEnergyDevice::controller() const
{
return m_services;
}
QList<QLowEnergyCharacteristic> BluetoothLowEnergyDevice::characteristics() const
{
return m_characteristics;
return m_controller;
}
void BluetoothLowEnergyDevice::connectDevice()
@ -66,90 +60,22 @@ bool BluetoothLowEnergyDevice::isConnected() const
return m_connected;
}
bool BluetoothLowEnergyDevice::isDiscovered() const
{
return m_discovered;
}
void BluetoothLowEnergyDevice::discoverService(const QBluetoothUuid &serviceUuid)
{
Q_UNUSED(serviceUuid);
}
void BluetoothLowEnergyDevice::connected()
{
m_connected = true;
qDebug() << "connected successfully to" << name() << address().toString();
emit connectionStatusChanged(true);
qDebug() << "connected successfully to bluetooth LE device:" << name() << address().toString();
emit connectionStatusChanged();
m_controller->discoverServices();
}
void BluetoothLowEnergyDevice::disconnected()
{
m_connected = false;
qWarning() << "disconnected from" << name() << address().toString();
emit connectionStatusChanged(false);
qWarning() << "disconnected from bluetooth LE device:" << name() << address().toString();
emit connectionStatusChanged();
}
void BluetoothLowEnergyDevice::deviceError(const QLowEnergyController::Error &error)
{
qWarning() << "ERROR: Bluetooth device" << name() << address().toString() << ": " << error << m_controller->errorString();
}
void BluetoothLowEnergyDevice::serviceDiscovered(const QBluetoothUuid &serviceUuid)
{
QLowEnergyService *service = m_controller->createServiceObject(serviceUuid);
if (!service) {
qWarning() << "Cannot create service for uuid" << serviceUuid;
return;
}
connect(service, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)), this, SLOT(serviceCharacteristicChanged(QLowEnergyCharacteristic,QByteArray)));
connect(service, SIGNAL(error(QLowEnergyService::ServiceError)), this, SLOT(serviceError(QLowEnergyService::ServiceError)));
connect(service, SIGNAL(stateChanged(QLowEnergyService::ServiceState)), this, SLOT(serviceStateChanged(QLowEnergyService::ServiceState)));
m_services.append(service);
}
void BluetoothLowEnergyDevice::serviceStateChanged(const QLowEnergyService::ServiceState &newState)
{
if (newState != QLowEnergyService::ServiceDiscovered) {
return;
}
QLowEnergyService *service = qobject_cast<QLowEnergyService *>(sender());
foreach (QLowEnergyCharacteristic characteristic, service->characteristics()) {
if (!characteristic.descriptors().isEmpty()) {
foreach (QLowEnergyDescriptor descriptor, characteristic.descriptors()) {
if (descriptor.isValid()) {
m_descriptors.append(descriptor);
}
}
}
m_characteristics.append(characteristic);
}
qDebug() << "details discovered for service " << service->serviceName() << service->serviceUuid().toString();
}
void BluetoothLowEnergyDevice::serviceScanFinished()
{
qDebug() << "service scan finished";
emit serviceScanFinished();
}
void BluetoothLowEnergyDevice::serviceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
qDebug() << "characteristic" << characteristic.name() << "changed:" << value;
}
void BluetoothLowEnergyDevice::serviceError(const QLowEnergyService::ServiceError &error)
{
QLowEnergyService *service = qobject_cast<QLowEnergyService *>(sender());
qWarning() << "ERROR: Bluetooth service " << service->serviceName() << service->serviceUuid() << ": " << error;
}
void BluetoothLowEnergyDevice::confirmedDescriptorWrite(const QLowEnergyDescriptor &descriptor, const QByteArray &value)
{
qDebug() << "descriptor" << descriptor.name() << "written successfully " << value;
qWarning() << "ERROR: Bluetooth LE device:" << name() << address().toString() << ": " << error << m_controller->errorString();
}

View File

@ -33,8 +33,6 @@ public:
QString name() const;
QBluetoothAddress address() const;
QList<QLowEnergyService *> services() const;
QList<QLowEnergyCharacteristic> characteristics() const;
void connectDevice();
void disconnectDevice();
@ -42,35 +40,24 @@ public:
bool isConnected() const;
bool isDiscovered() const;
void discoverService(const QBluetoothUuid &serviceUuid);
protected:
QLowEnergyController *controller() const;
private:
QBluetoothDeviceInfo m_deviceInfo;
QLowEnergyController *m_controller;
QList<QLowEnergyService *> m_services;
QList<QLowEnergyCharacteristic> m_characteristics;
QList<QLowEnergyDescriptor> m_descriptors;
bool m_connected;
bool m_discovered;
signals:
void connectionStatusChanged(const bool &connectionStatus);
void servicesDiscovered();
void connectionStatusChanged();
void servicesDiscoveryFinished();
private slots:
// Device
void connected();
void disconnected();
void deviceError(const QLowEnergyController::Error &error);
void serviceDiscovered(const QBluetoothUuid &serviceUuid);
void serviceStateChanged(const QLowEnergyService::ServiceState &newState);
void serviceScanFinished();
// Service
void serviceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
void serviceError(const QLowEnergyService::ServiceError &error);
void confirmedDescriptorWrite(const QLowEnergyDescriptor &descriptor, const QByteArray &value);
};
#endif // BLUETOOTHLOWENERGYDEVICE_H

View File

@ -23,7 +23,7 @@ BluetoothScanner::BluetoothScanner(QObject *parent) :
{
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
m_timer->setInterval(10000);
m_timer->setInterval(5000);
connect(m_timer, &QTimer::timeout, this, &BluetoothScanner::discoveryTimeout);
}
@ -86,7 +86,7 @@ bool BluetoothScanner::discover(const PluginId &pluginId)
void BluetoothScanner::deviceDiscovered(const QBluetoothDeviceInfo &device)
{
// check if this is LE device
// check if this is a LE device
bool bluetoothLE = false;
if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
bluetoothLE = true;
@ -98,14 +98,16 @@ void BluetoothScanner::deviceDiscovered(const QBluetoothDeviceInfo &device)
void BluetoothScanner::onError(QBluetoothDeviceDiscoveryAgent::Error error)
{
Q_UNUSED(error);
Q_UNUSED(error)
m_available = false;
if (m_timer->isActive()) {
if (m_timer->isActive())
m_timer->stop();
}
if (isRunning()) {
if (isRunning())
m_discoveryAgent->stop();
}
qWarning() << "ERROR: Bluetooth discovery:" << m_discoveryAgent->errorString();
}

View File

@ -98,16 +98,16 @@ bool Radio433::enable()
if (gpioFile.exists()) {
// bool receiverAvailable = m_receiver->startReceiver();
// if (!receiverAvailable) {
// //qCWarning(dcHardware) << "ERROR: radio 433 MHz receiver not available on GPIO's";
// //qWarning() << "ERROR: radio 433 MHz receiver not available on GPIO's";
// }
// bool transmitterAvailable = m_transmitter->startTransmitter();
// if (!transmitterAvailable) {
// //qCWarning(dcHardware) << "ERROR: radio 433 MHz transmitter not available on GPIO's";
// //qWarning() << "ERROR: radio 433 MHz transmitter not available on GPIO's";
// }
// if (!receiverAvailable && !transmitterAvailable) {
// qCWarning(dcHardware) << "--> Radio 433 MHz GPIO's not available.";
// qWarning() << "--> Radio 433 MHz GPIO's not available.";
// return false;
// }
}

View File

@ -26,5 +26,5 @@ boblight {
}
contains(DEFINES, BLUETOOTH_LE) {
# bluetooth plugins
SUBDIRS += elgato
}

View File

@ -0,0 +1,146 @@
#include "aveabulb.h"
AveaBulb::AveaBulb(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType, QObject *parent) :
BluetoothLowEnergyDevice(deviceInfo, addressType, parent),
m_colorService(0)
{
m_colorSeviceUuid = QBluetoothUuid(QUuid("f815e810-456c-6761-746f-4d756e696368"));
m_colorCharacteristicUuid = QBluetoothUuid(QUuid("f815e811-456c-6761-746f-4d756e696368"));
connect(this, SIGNAL(connectionStatusChanged()), this,SLOT(onConnectionStatusChanged()));
connect(this, SIGNAL(servicesDiscoveryFinished()), this, SLOT(serviceScanFinished()));
}
bool AveaBulb::isAvailable()
{
return m_isAvailable;
}
void AveaBulb::serviceScanFinished()
{
if (!controller()->services().contains(m_colorSeviceUuid)) {
qWarning() << "ERROR: Color service not found for device" << name() << address().toString();
return;
}
if (m_colorService) {
qWarning() << "ERROR: Attention! bad implementation of service handling!!";
return;
}
// create color service and discover it
m_colorService = controller()->createServiceObject(m_colorSeviceUuid, this);
if (!m_colorService) {
qWarning() << "ERROR: could not create color service for device" << name() << address().toString();
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(error(QLowEnergyService::ServiceError)), this, SLOT(serviceError(QLowEnergyService::ServiceError)));
m_colorService->discoverDetails();
}
void AveaBulb::onConnectionStatusChanged()
{
if (!isConnected()) {
// delete the service, needs to be recreatedand rediscovered once the device will be reconnected
delete m_colorService;
m_colorService = 0;
m_isAvailable = false;
emit availableChanged();
}
}
void AveaBulb::serviceStateChanged(const QLowEnergyService::ServiceState &state)
{
switch (state) {
case QLowEnergyService::ServiceDiscovered:
m_colorCharacteristic = m_colorService->characteristic(m_colorCharacteristicUuid);
if (!m_colorCharacteristic.isValid()) {
qWarning() << "ERROR: color characteristc not found for device " << name() << address().toString();
return;
}
m_isAvailable = true;
emit availableChanged();
break;
default:
break;
}
}
void AveaBulb::serviceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
qDebug() << "service characteristic changed" << characteristic.name() << value.toHex();
}
void AveaBulb::confirmedCharacteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
if (characteristic.handle() == m_colorCharacteristic.handle()) {
if (m_actions.contains(value.toHex())) {
ActionId actionId = m_actions.take(value.toHex());
emit actionExecutionFinished(actionId, true);
}
}
}
void AveaBulb::serviceError(const QLowEnergyService::ServiceError &error)
{
qWarning() << "ERROR: color service of " << name() << address().toString() << ":" << error;
}
bool AveaBulb::enableNotification()
{
if (!isAvailable())
return false;
qDebug() << "enable notify";
QByteArray value = "0100";
m_colorService->writeCharacteristic(m_colorCharacteristic, value, QLowEnergyService::WriteWithResponse);
return true;
}
bool AveaBulb::testMethod()
{
if (!isAvailable())
return false;
QByteArray value = "34";
qDebug() << "test" << value;
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
return true;
}
bool AveaBulb::actionPowerOff(ActionId actionId)
{
if (!isAvailable())
return false;
QByteArray value = "35f4010a00008000b000a00090";
qDebug() << "set" << name() << "power OFF";
m_actions.insert(value, actionId);
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
return true;
}
bool AveaBulb::setWhite(ActionId actionId)
{
if (!isAvailable())
return false;
QByteArray value = "3532000a00ff0f003000200010";
qDebug() << "set" << name() << "white";
m_actions.insert(value, actionId);
m_colorService->writeCharacteristic(m_colorCharacteristic, QByteArray::fromHex(value), QLowEnergyService::WriteWithResponse);
return true;
}

View File

@ -0,0 +1,51 @@
#ifndef AVEABULB_H
#define AVEABULB_H
#include <QObject>
#include "typeutils.h"
#include "bluetooth/bluetoothlowenergydevice.h"
class AveaBulb : public BluetoothLowEnergyDevice
{
Q_OBJECT
public:
explicit AveaBulb(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType, QObject *parent = 0);
bool isAvailable();
signals:
void availableChanged();
void actionExecutionFinished(const ActionId &actionId, const bool &success);
private:
QBluetoothUuid m_colorSeviceUuid;
QLowEnergyService *m_colorService;
QBluetoothUuid m_colorCharacteristicUuid;
QLowEnergyCharacteristic m_colorCharacteristic;
bool m_isAvailable;
QHash<QByteArray, ActionId> m_actions;
private slots:
void serviceScanFinished();
void onConnectionStatusChanged();
// 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 serviceError(const QLowEnergyService::ServiceError &error);
bool enableNotification();
bool testMethod();
public slots:
// actions
bool actionPowerOff(ActionId actionId);
bool setWhite(ActionId actionId);
};
#endif // AVEABULB_H

View File

@ -0,0 +1,526 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\page elgato.html
\title Elgato
\ingroup plugins
\ingroup bluetooth
This plugin allows to find and controll the Bluetooth Low Energy bulb from \l{https://www.elgato.com/en/smart/avea}{Elgato Avea}.
\chapter Device Overviw of \tt{Avea_44A9}
\section1 Services
\code
----------------------------------------------------------------
name : "Generic Access"
type : "<primary>"
uuid : "{00001800-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x1800"
----------------------------------------------------------------
name : "Generic Attribute"
type : "<primary>"
uuid : "{00001801-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x1801"
----------------------------------------------------------------
name : "Device Information"
type : "<primary>"
uuid : "{0000180a-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x180a"
----------------------------------------------------------------
name : "Unknown Service"
type : "<primary>"
uuid : "{f815e600-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e600-456c-6761-746f-4d756e696368"
----------------------------------------------------------------
name : "Unknown Service"
type : "<primary>"
uuid : "{f815e500-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e500-456c-6761-746f-4d756e696368"
----------------------------------------------------------------
name : "Unknown Service"
type : "<primary>"
uuid : "{f815e810-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e810-456c-6761-746f-4d756e696368"
----------------------------------------------------------------
name : "Unknown Service"
type : "<primary>"
uuid : "{f815e900-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e900-456c-6761-746f-4d756e696368"
----------------------------------------------------------------
\endcode
\section2 Service: "Generic Access" {00001800-0000-1000-8000-00805f9b34fb} details
\code
----------------------------------------------------------------
characteristics:
name : "GAP Device Name"
uuid : "{00002a00-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a00"
handle : "0x3"
permission : ("Read")
value : "Avea_44A9"
value (hex) : "417665615f34344139"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "GAP Appearance"
uuid : "{00002a01-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a01"
handle : "0x5"
permission : ("Read")
value : "
value (hex) : "0000"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "GAP Peripheral Privacy Flag"
uuid : "{00002a02-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a02"
handle : "0x7"
permission : ("Read", "Write")
value : "
value (hex) : "00"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "GAP Reconnection Address"
uuid : "{00002a03-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a03"
handle : "0x9"
permission : ("Read", "Write")
value : "
value (hex) : "000000000000"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "GAP Peripheral Preferred Connection Parameters"
uuid : "{00002a04-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a04"
handle : "0xb"
permission : ("Read")
value : "P
value (hex) : "5000a0000000e803"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
\endcode
\section2 Service: "Generic Attribute" {00001801-0000-1000-8000-00805f9b34fb} details
\code
----------------------------------------------------------------
characteristics:
name : "GATT Service Changed"
uuid : "{00002a05-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a05"
handle : "0xe"
permission : ("Indicate")
value : ""
value (hex) : ""
---------------------------------------------------------
descriptor count: 1
---------------------------------------------------------
name : "Client Characteristic Configuration"
uuid : "{00002902-0000-1000-8000-00805f9b34fb}"
handle : 15
value : "
value (hex) : "0000"
-----------------------------------------------------
\endcode
\section2 Service: "Device Information" {0000180a-0000-1000-8000-00805f9b34fb} details
\code
----------------------------------------------------------------
characteristics:
name : "System ID"
uuid : "{00002a23-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a23"
handle : "0x12"
permission : ("Read")
value : "D<EFBFBD>j<EFBFBD><EFBFBD><18><>"
value (hex) : "44a96afeff18eb84"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "Model Number String"
uuid : "{00002a24-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a24"
handle : "0x14"
permission : ("Read")
value : "1
value (hex) : "3100"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "Serial Number String"
uuid : "{00002a25-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a25"
handle : "0x16"
permission : ("Read")
value : "44A96A18EB84"
value (hex) : "343441393641313845423834"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "Firmware Revision String"
uuid : "{00002a26-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a26"
handle : "0x18"
permission : ("Read")
value : "1.0.0.296Af
value (hex) : "312e302e302e323936416600"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "Hardware Revision String"
uuid : "{00002a27-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a27"
handle : "0x1a"
permission : ("Read")
value : "Elgato Avea
value (hex) : "456c6761746f204176656100"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "Software Revision String"
uuid : "{00002a28-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a28"
handle : "0x1c"
permission : ("Read")
value : "1.0
value (hex) : "312e3000"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "Manufacturer Name String"
uuid : "{00002a29-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a29"
handle : "0x1e"
permission : ("Read")
value : "Elgato Systems GmbH
value (hex) : "456c6761746f2053797374656d7320476d624800"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
name : "PnP ID"
uuid : "{00002a50-0000-1000-8000-00805f9b34fb}"
uuid (hex) : "0x2a50"
handle : "0x20"
permission : ("Read")
value : "<02>
value (hex) : "02d90f00000001"
---------------------------------------------------------
descriptor count: 0
---------------------------------------------------------
\endcode
\section2 Service: "Unknown Service" {f815e600-456c-6761-746f-4d756e696368} details
\code
----------------------------------------------------------------
characteristics:
name : "Alert"
uuid : "{f815e601-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e601-456c-6761-746f-4d756e696368"
handle : "0x23"
permission : ("Notify")
value : ""
value (hex) : ""
---------------------------------------------------------
descriptor count: 2
---------------------------------------------------------
name : "Client Characteristic Configuration"
uuid : "{00002902-0000-1000-8000-00805f9b34fb}"
handle : 36
value : "
value (hex) : "0000"
-----------------------------------------------------
name : "Characteristic User Description"
uuid : "{00002901-0000-1000-8000-00805f9b34fb}"
handle : 37
value : "Alert"
value (hex) : "416c657274"
-----------------------------------------------------
\endcode
\section2 Service: "Unknown Service" {f815e500-456c-6761-746f-4d756e696368} details
\code
----------------------------------------------------------------
characteristics:
name : "Seq Upload"
uuid : "{f815e501-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e501-456c-6761-746f-4d756e696368"
handle : "0x28"
permission : ("Write", "Notify", "WriteNoResp")
value : ""
value (hex) : ""
---------------------------------------------------------
descriptor count: 2
---------------------------------------------------------
name : "Client Characteristic Configuration"
uuid : "{00002902-0000-1000-8000-00805f9b34fb}"
handle : 41
value : "
value (hex) : "0000"
-----------------------------------------------------
name : "Characteristic User Description"
uuid : "{00002901-0000-1000-8000-00805f9b34fb}"
handle : 42
value : "Seq Upload"
value (hex) : "5365712055706c6f6164"
-----------------------------------------------------
\endcode
\section2 Service: "Unknown Service" {f815e810-456c-6761-746f-4d756e696368} details
Tis service will be used to set the color (handle \tt{0x2d}).
\code
----------------------------------------------------------------
characteristics:
name : "Debug"
uuid : "{f815e811-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e811-456c-6761-746f-4d756e696368"
handle : "0x2d"
permission : ("Write", "Notify")
value : ""
value (hex) : ""
---------------------------------------------------------
descriptor count: 2
---------------------------------------------------------
name : "Client Characteristic Configuration"
uuid : "{00002902-0000-1000-8000-00805f9b34fb}"
handle : 46
value : "
value (hex) : "0000"
-----------------------------------------------------
name : "Characteristic User Description"
uuid : "{00002901-0000-1000-8000-00805f9b34fb}"
handle : 47
value : "Debug"
value (hex) : "4465627567"
-----------------------------------------------------
name : "User Name"
uuid : "{f815e812-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e812-456c-6761-746f-4d756e696368"
handle : "0x31"
permission : ("Read", "Write")
value : ""
value (hex) : ""
---------------------------------------------------------
descriptor count: 1
---------------------------------------------------------
name : "Characteristic User Description"
uuid : "{00002901-0000-1000-8000-00805f9b34fb}"
handle : 50
value : "User Name"
value (hex) : "55736572204e616d65"
-----------------------------------------------------
\endcode
\section2 Service: "Unknown Service" {f815e900-456c-6761-746f-4d756e696368} details
\code
----------------------------------------------------------------
characteristics:
name : "Img Identify"
uuid : "{f815e901-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e901-456c-6761-746f-4d756e696368"
handle : "0x35"
permission : ("Write", "Notify", "WriteNoResp")
value : ""
value (hex) : ""
---------------------------------------------------------
descriptor count: 2
---------------------------------------------------------
name : "Client Characteristic Configuration"
uuid : "{00002902-0000-1000-8000-00805f9b34fb}"
handle : 54
value : "
value (hex) : "0000"
-----------------------------------------------------
name : "Characteristic User Description"
uuid : "{00002901-0000-1000-8000-00805f9b34fb}"
handle : 55
value : "Img Identify"
value (hex) : "496d67204964656e74696679"
-----------------------------------------------------
name : "Img Block"
uuid : "{f815e902-456c-6761-746f-4d756e696368}"
uuid (hex) : "f815e902-456c-6761-746f-4d756e696368"
handle : "0x39"
permission : ("Write", "Notify", "WriteNoResp")
value : ""
value (hex) : ""
---------------------------------------------------------
descriptor count: 2
---------------------------------------------------------
name : "Client Characteristic Configuration"
uuid : "{00002902-0000-1000-8000-00805f9b34fb}"
handle : 58
value : "
value (hex) : "0000"
-----------------------------------------------------
name : "Characteristic User Description"
uuid : "{00002901-0000-1000-8000-00805f9b34fb}"
handle : 59
value : "Img Block"
value (hex) : "496d6720426c6f636b"
-----------------------------------------------------
\endcode
\chapter Plugin properties
Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses}
and \l{Vendor}{Vendors} of this \l{DevicePlugin}.
Each \l{DeviceClass} has a list of \l{ParamType}{paramTypes}, \l{ActionType}{actionTypes}, \l{StateType}{stateTypes}
and \l{EventType}{eventTypes}. The \l{DeviceClass::CreateMethod}{createMethods} parameter describes how the \l{Device}
will be created in the system. A device can have more than one \l{DeviceClass::CreateMethod}{CreateMethod}.
The \l{DeviceClass::SetupMethod}{setupMethod} describes the setup method of the \l{Device}.
The detailed implementation of each \l{DeviceClass} can be found in the source code.
\quotefile plugins/deviceplugins/elgato/devicepluginelgato.json
*/
#include "devicepluginelgato.h"
#include "plugin/device.h"
#include "devicemanager.h"
#include "plugininfo.h"
DevicePluginElgato::DevicePluginElgato()
{
}
DeviceManager::DeviceError DevicePluginElgato::discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params)
{
Q_UNUSED(params)
if (deviceClassId != aveaDeviceClassId)
return DeviceManager::DeviceErrorDeviceClassNotFound;
if (!discoverBluetooth())
return DeviceManager::DeviceErrorHardwareNotAvailable;
return DeviceManager::DeviceErrorAsync;
}
DeviceManager::DeviceSetupStatus DevicePluginElgato::setupDevice(Device *device)
{
if (device->deviceClassId() == aveaDeviceClassId) {
QBluetoothAddress address = QBluetoothAddress(device->paramValue("mac address").toString());
QString name = device->paramValue("name").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);
avea->connectDevice();
return DeviceManager::DeviceSetupStatusSuccess;
}
return DeviceManager::DeviceSetupStatusFailure;
}
DeviceManager::HardwareResources DevicePluginElgato::requiredHardware() const
{
return DeviceManager::HardwareResourceBluetoothLE;
}
DeviceManager::DeviceError DevicePluginElgato::executeAction(Device *device, const Action &action)
{
// check deviceClassId
if (device->deviceClassId() == aveaDeviceClassId) {
AveaBulb *bulb = m_bulbs.key(device);
if (!bulb->isAvailable())
return DeviceManager::DeviceErrorHardwareNotAvailable;
// check actionTypeId
if (action.actionTypeId() == powerOffActionTypeId) {
bulb->actionPowerOff(action.id());
return DeviceManager::DeviceErrorAsync;
} else if (action.actionTypeId() == whiteActionTypeId) {
bulb->setWhite(action.id());
return DeviceManager::DeviceErrorAsync;
}
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("name", deviceInfo.name()));
params.append(Param("mac address", deviceInfo.address().toString()));
descriptor.setParams(params);
deviceDescriptors.append(descriptor);
}
}
}
emit devicesDiscovered(aveaDeviceClassId, deviceDescriptors);
}
void DevicePluginElgato::deviceRemoved(Device *device)
{
if (!m_bulbs.values().contains(device))
return;
AveaBulb *bulb= m_bulbs.key(device);
m_bulbs.take(bulb);
delete bulb;
}
bool DevicePluginElgato::verifyExistingDevices(const QBluetoothDeviceInfo &deviceInfo)
{
foreach (Device *device, myDevices()) {
if (device->paramValue("mac address").toString() == deviceInfo.address().toString())
return true;
}
return false;
}
void DevicePluginElgato::bulbAvailableChanged()
{
AveaBulb *bulb =static_cast<AveaBulb *>(sender());
Device *device = m_bulbs.value(bulb);
device->setStateValue(availableStateTypeId, bulb->isAvailable());
}
void DevicePluginElgato::actionFinished(const ActionId actionId, const bool &success)
{
if (success) {
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError);
} else {
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure);
}
}

View File

@ -0,0 +1,53 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICEPLUGINELGATO_H
#define DEVICEPLUGINELGATO_H
#include "plugin/deviceplugin.h"
#include "bluetooth/bluetoothlowenergydevice.h"
#include "aveabulb.h"
class DevicePluginElgato : public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "devicepluginelgato.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginElgato();
DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params) 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;
bool verifyExistingDevices(const QBluetoothDeviceInfo &deviceInfo);
private slots:
void bulbAvailableChanged();
void actionFinished(const ActionId actionId, const bool &success);
};
#endif // DEVICEPLUGINELGATO_H

View File

@ -0,0 +1,56 @@
{
"name": "Elgato",
"id": "c5c03ad4-bfdb-444a-8eca-2c234c46cc27",
"vendors": [
{
"name": "Elgato",
"id": "90a3091d-1053-4f77-8dc3-92e27bbcebe7",
"deviceClasses": [
{
"deviceClassId": "164f9602-90ee-4693-bda3-9cafae37603e",
"idName": "avea",
"name": "Avea",
"createMethods": ["discovery"],
"paramTypes": [
{
"name": "name",
"type": "QString",
"inputType": "TextLine"
},
{
"name": "mac address",
"type": "QString",
"inputType": "MacAddress"
}
],
"stateTypes": [
{
"id": "6d5e792a-c786-40d2-ae35-a48ac6fafcbc",
"idName": "available",
"name": "available",
"type": "bool",
"defaultValue": false
}
],
"actionTypes": [
{
"id": "30b6334d-37cd-4b94-a397-3b9b642c762e",
"idName": "reconnect",
"name": "reconnect"
},
{
"id": "7936bd51-7ea3-4df4-9998-7325de85b677",
"idName": "white",
"name": "white"
},
{
"id": "b2f39b7b-dd11-4db4-a82b-dd3d4b973bd5",
"idName": "powerOff",
"name": "power off"
}
]
}
]
}
]
}

View File

@ -0,0 +1,16 @@
include(../../plugins.pri)
TARGET = $$qtLibraryTarget(guh_devicepluginelgato)
QT+= bluetooth
SOURCES += \
devicepluginelgato.cpp \
aveabulb.cpp
HEADERS += \
devicepluginelgato.h \
aveabulb.h

View File

@ -227,7 +227,7 @@ void JsonReply::setCommandId(int commandId)
void JsonReply::startWait()
{
m_timeout.start(5000);
m_timeout.start(10000);
}
void JsonReply::timeout()