add flowercare plugin
parent
03d0c95df0
commit
c5d826bec3
|
|
@ -0,0 +1,197 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2018 Michael Zanetti <michael.zanetti@guh.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
|
||||
/*
|
||||
* ServieUUIDs:
|
||||
* {00001204-0000-1000-8000-00805f9b34fb}
|
||||
* {00001206-0000-1000-8000-00805f9b34fb}
|
||||
* {00001800-0000-1000-8000-00805f9b34fb}
|
||||
* {00001801-0000-1000-8000-00805f9b34fb}
|
||||
* {0000fe95-0000-1000-8000-00805f9b34fb}
|
||||
* {0000fef5-0000-1000-8000-00805f9b34fb}
|
||||
*/
|
||||
#include "plugininfo.h"
|
||||
#include "devicemanager.h"
|
||||
#include "hardware/bluetoothlowenergy/bluetoothlowenergymanager.h"
|
||||
#include "devicepluginflowercare.h"
|
||||
#include "flowercare.h"
|
||||
|
||||
DevicePluginFlowercare::DevicePluginFlowercare()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DevicePluginFlowercare::~DevicePluginFlowercare()
|
||||
{
|
||||
if (m_reconnectTimer) {
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_reconnectTimer);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginFlowercare::init()
|
||||
{
|
||||
}
|
||||
|
||||
DeviceManager::DeviceError DevicePluginFlowercare::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
Q_UNUSED(deviceClassId)
|
||||
|
||||
if (!hardwareManager()->bluetoothLowEnergyManager()->available())
|
||||
return DeviceManager::DeviceErrorHardwareNotAvailable;
|
||||
|
||||
if (!hardwareManager()->bluetoothLowEnergyManager()->enabled())
|
||||
return DeviceManager::DeviceErrorHardwareNotAvailable;
|
||||
|
||||
BluetoothDiscoveryReply *reply = hardwareManager()->bluetoothLowEnergyManager()->discoverDevices();
|
||||
connect(reply, &BluetoothDiscoveryReply::finished, this, &DevicePluginFlowercare::onBluetoothDiscoveryFinished);
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
|
||||
DeviceManager::DeviceSetupStatus DevicePluginFlowercare::setupDevice(Device *device)
|
||||
{
|
||||
qCDebug(dcFlowerCare) << "Setting up Flower care" << device->name() << device->params();
|
||||
|
||||
if (device->deviceClassId() == flowerCareDeviceClassId) {
|
||||
QBluetoothAddress address = QBluetoothAddress(device->paramValue(flowerCareMacParamTypeId).toString());
|
||||
QString name = device->paramValue(flowerCareNameParamTypeId).toString();
|
||||
QBluetoothDeviceInfo deviceInfo = QBluetoothDeviceInfo(address, name, 0);
|
||||
|
||||
BluetoothLowEnergyDevice *bluetoothDevice = hardwareManager()->bluetoothLowEnergyManager()->registerDevice(deviceInfo, QLowEnergyController::PublicAddress);
|
||||
FlowerCare *flowerCare = new FlowerCare(bluetoothDevice, this);
|
||||
connect(flowerCare, &FlowerCare::finished, this, &DevicePluginFlowercare::onSensorDataReceived);
|
||||
m_list.insert(device, flowerCare);
|
||||
|
||||
m_refreshMinutes[flowerCare] = 0;
|
||||
|
||||
if (!m_reconnectTimer) {
|
||||
m_reconnectTimer = hardwareManager()->pluginTimerManager()->registerTimer();
|
||||
connect(m_reconnectTimer, &PluginTimer::timeout, this, &DevicePluginFlowercare::onPluginTimer);
|
||||
}
|
||||
return DeviceManager::DeviceSetupStatusSuccess;
|
||||
}
|
||||
return DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
void DevicePluginFlowercare::postSetupDevice(Device *device)
|
||||
{
|
||||
FlowerCare *flowerCare = m_list.value(device);
|
||||
flowerCare->refreshData();
|
||||
}
|
||||
|
||||
void DevicePluginFlowercare::deviceRemoved(Device *device)
|
||||
{
|
||||
FlowerCare *flowerCare = m_list.take(device);
|
||||
if (!flowerCare) {
|
||||
return;
|
||||
}
|
||||
|
||||
hardwareManager()->bluetoothLowEnergyManager()->unregisterDevice(flowerCare->btDevice());
|
||||
flowerCare->deleteLater();
|
||||
|
||||
if (m_list.isEmpty() && m_reconnectTimer) {
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_reconnectTimer);
|
||||
m_reconnectTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DeviceManager::DeviceError DevicePluginFlowercare::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
Q_UNUSED(action)
|
||||
return DeviceManager::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
bool DevicePluginFlowercare::verifyExistingDevices(const QBluetoothDeviceInfo &deviceInfo)
|
||||
{
|
||||
foreach (Device *device, m_list.keys()) {
|
||||
if (device->paramValue(flowerCareMacParamTypeId).toString() == deviceInfo.address().toString())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DevicePluginFlowercare::onPluginTimer()
|
||||
{
|
||||
|
||||
foreach (FlowerCare *flowerCare, m_list) {
|
||||
if (--m_refreshMinutes[flowerCare] <= 0) {
|
||||
qCDebug(dcFlowerCare()) << "Refreshing" << flowerCare->btDevice()->address();
|
||||
flowerCare->refreshData();
|
||||
} else {
|
||||
qCDebug(dcFlowerCare()) << "Not refreshing" << flowerCare->btDevice()->address() << " Next refresh in" << m_refreshMinutes[flowerCare] << "minutes";
|
||||
}
|
||||
|
||||
// If we had 2 or more failed connection attempts, mark it as disconnected
|
||||
if (m_refreshMinutes[flowerCare] < -2) {
|
||||
qCDebug(dcFlowerCare()) << "Failed to refresh for"<< (m_refreshMinutes[flowerCare] * -1) << "minutes. Marking as unreachable";
|
||||
m_list.key(flowerCare)->setStateValue(flowerCareConnectedStateTypeId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginFlowercare::onBluetoothDiscoveryFinished()
|
||||
{
|
||||
BluetoothDiscoveryReply *reply = static_cast<BluetoothDiscoveryReply *>(sender());
|
||||
if (reply->error() != BluetoothDiscoveryReply::BluetoothDiscoveryReplyErrorNoError) {
|
||||
qCWarning(dcFlowerCare()) << "Bluetooth discovery error:" << reply->error();
|
||||
reply->deleteLater();
|
||||
emit devicesDiscovered(flowerCareDeviceClassId, QList<DeviceDescriptor>());
|
||||
return;
|
||||
}
|
||||
|
||||
QList<DeviceDescriptor> deviceDescriptors;
|
||||
qCDebug(dcFlowerCare()) << "Discovery finished";
|
||||
foreach (const QBluetoothDeviceInfo &deviceInfo, reply->discoveredDevices()) {
|
||||
qCDebug(dcFlowerCare()) << "Discovered device" << deviceInfo.name();
|
||||
if (deviceInfo.name().contains("Flower care")) {
|
||||
if (!verifyExistingDevices(deviceInfo)) {
|
||||
DeviceDescriptor descriptor(flowerCareDeviceClassId, "Flower Care", deviceInfo.address().toString());
|
||||
ParamList params;
|
||||
params.append(Param(flowerCareNameParamTypeId, deviceInfo.name()));
|
||||
params.append(Param(flowerCareMacParamTypeId, deviceInfo.address().toString()));
|
||||
descriptor.setParams(params);
|
||||
deviceDescriptors.append(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
emit devicesDiscovered(flowerCareDeviceClassId, deviceDescriptors);
|
||||
}
|
||||
|
||||
void DevicePluginFlowercare::onSensorDataReceived(quint8 batteryLevel, double degreeCelsius, double lux, double moisture, double fertility)
|
||||
{
|
||||
FlowerCare *flowerCare = static_cast<FlowerCare*>(sender());
|
||||
Device *device = m_list.key(flowerCare);
|
||||
device->setStateValue(flowerCareConnectedStateTypeId, true);
|
||||
device->setStateValue(flowerCareBatteryLevelStateTypeId, batteryLevel);
|
||||
device->setStateValue(flowerCareBatteryCriticalStateTypeId, batteryLevel <= 10);
|
||||
device->setStateValue(flowerCareTemperatureStateTypeId, degreeCelsius);
|
||||
device->setStateValue(flowerCareLightIntensityStateTypeId, lux);
|
||||
device->setStateValue(flowerCareMoistureStateTypeId, moisture);
|
||||
device->setStateValue(flowerCareConductivityStateTypeId, fertility);
|
||||
|
||||
m_refreshMinutes[flowerCare] = 20;
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2018 Michael Zanetti <michael.zanetti@guh.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* 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 DEVICEPLUGINFLOWERCARE_H
|
||||
#define DEVICEPLUGINFLOWERCARE_H
|
||||
|
||||
|
||||
#include <QPointer>
|
||||
#include <QHash>
|
||||
#include "plugin/deviceplugin.h"
|
||||
#include "devicemanager.h"
|
||||
#include "plugintimer.h"
|
||||
#include "hardware/bluetoothlowenergy/bluetoothlowenergydevice.h"
|
||||
|
||||
class FlowerCare;
|
||||
|
||||
class DevicePluginFlowercare : public DevicePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginflowercare.json")
|
||||
Q_INTERFACES(DevicePlugin)
|
||||
|
||||
public:
|
||||
explicit DevicePluginFlowercare();
|
||||
~DevicePluginFlowercare();
|
||||
|
||||
void init() override;
|
||||
DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
|
||||
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
void postSetupDevice(Device *device) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
|
||||
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
|
||||
private:
|
||||
PluginTimer *m_reconnectTimer = nullptr;
|
||||
QHash<Device*, FlowerCare*> m_list;
|
||||
QHash<FlowerCare*, int> m_refreshMinutes;
|
||||
|
||||
bool verifyExistingDevices(const QBluetoothDeviceInfo &deviceInfo);
|
||||
|
||||
private slots:
|
||||
void onPluginTimer();
|
||||
void onBluetoothDiscoveryFinished();
|
||||
void onSensorDataReceived(quint8 batteryLevel, double degreeCelsius, double lux, double moisture, double fertility);
|
||||
|
||||
};
|
||||
|
||||
#endif // DEVICEPLUGINFLOWERCARE_H
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
{
|
||||
"displayName": "Flower Care",
|
||||
"name": "flowerCare",
|
||||
"id": "74e2106a-3407-4e89-a27a-1c890d78bee7",
|
||||
"vendors": [
|
||||
{
|
||||
"id": "f037aa1a-f764-42f9-a613-338e683e4da5",
|
||||
"name": "xiaomi",
|
||||
"displayName": "Xiaomi",
|
||||
"deviceClasses": [
|
||||
{
|
||||
"id": "297e2efa-168c-4eb8-bf3f-f8694dc0a2b9",
|
||||
"name": "flowerCare",
|
||||
"displayName": "Flower Care",
|
||||
"createMethods": ["discovery"],
|
||||
"interfaces": ["temperaturesensor", "lightsensor", "moisturesensor", "conductivitysensor", "connectable", "batterylevel"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "65f95e3c-59fe-47d6-ba4a-2469e5f554e2",
|
||||
"name": "name",
|
||||
"displayName": "Name",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine"
|
||||
},
|
||||
{
|
||||
"id": "fe2dba0f-2a47-463e-a645-bcd55cf09750",
|
||||
"name": "mac",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "MacAddress"
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "6254103a-ff8b-4f62-afda-6b5be1c933ec",
|
||||
"name": "connected",
|
||||
"displayName": "Reachable",
|
||||
"displayNameEvent": "Reachable changed",
|
||||
"type": "bool",
|
||||
"cached": false,
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "801a00ab-ce3b-4b10-9236-2f845c469a26",
|
||||
"name": "batteryLevel",
|
||||
"displayName": "Battery level",
|
||||
"displayNameEvent": "Battery level changed",
|
||||
"type": "int",
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "700a83e6-9fbb-4330-b00e-e9d0874b4f66",
|
||||
"name": "batteryCritical",
|
||||
"displayName": "Battery level critical",
|
||||
"displayNameEvent": "Battery level critical changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "4577a032-bafd-49ca-8625-6dcf90819e88",
|
||||
"name": "temperature",
|
||||
"displayName": "Temperature",
|
||||
"displayNameEvent": "Temperature changed",
|
||||
"type": "double",
|
||||
"unit": "DegreeCelsius",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "5e3ac82a-1dda-4399-888e-529b391a814a",
|
||||
"name": "moisture",
|
||||
"displayName": "Soil moisture",
|
||||
"displayNameEvent": "Soil moisture changed",
|
||||
"type": "double",
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"unit": "Percentage",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "7e0a3b3d-72f4-4547-bd1e-2711be61c1a7",
|
||||
"name": "lightIntensity",
|
||||
"displayName": "Light intensity",
|
||||
"displayNameEvent": "Light intensity changed",
|
||||
"type": "double",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "f77608b3-8b5b-43a9-aab6-0dc35c694ec3",
|
||||
"name": "conductivity",
|
||||
"displayName": "Conductivity",
|
||||
"displayNameEvent": "Conductivity changed",
|
||||
"type": "double",
|
||||
"defaultValue": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
#include "flowercare.h"
|
||||
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QVersionNumber>
|
||||
|
||||
FlowerCare::FlowerCare(BluetoothLowEnergyDevice *device, QObject *parent):
|
||||
QObject(parent),
|
||||
m_bluetoothDevice(device)
|
||||
{
|
||||
connect(m_bluetoothDevice, &BluetoothLowEnergyDevice::connectedChanged, this, &FlowerCare::onConnectedChanged);
|
||||
connect(m_bluetoothDevice, &BluetoothLowEnergyDevice::servicesDiscoveryFinished, this, &FlowerCare::onServiceDiscoveryFinished);
|
||||
}
|
||||
|
||||
void FlowerCare::refreshData()
|
||||
{
|
||||
qCDebug(dcFlowerCare()) << "Connecting to device";
|
||||
m_bluetoothDevice->connectDevice();
|
||||
}
|
||||
|
||||
BluetoothLowEnergyDevice *FlowerCare::btDevice() const
|
||||
{
|
||||
return m_bluetoothDevice;
|
||||
}
|
||||
|
||||
void FlowerCare::onConnectedChanged(bool connected)
|
||||
{
|
||||
qCDebug(dcFlowerCare()) << "Connection changed:" << connected;
|
||||
if (!connected) {
|
||||
m_sensorService->deleteLater();
|
||||
m_sensorService = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FlowerCare::onServiceDiscoveryFinished()
|
||||
{
|
||||
BluetoothLowEnergyDevice *btDev = static_cast<BluetoothLowEnergyDevice*>(sender());
|
||||
qCDebug(dcFlowerCare()) << "have service uuids" << btDev->serviceUuids();
|
||||
|
||||
m_sensorService = btDev->controller()->createServiceObject(sensorServiceUuid, this);
|
||||
connect(m_sensorService, &QLowEnergyService::stateChanged, this, &FlowerCare::onSensorServiceStateChanged);
|
||||
connect(m_sensorService, &QLowEnergyService::characteristicRead, this, &FlowerCare::onSensorServiceCharacteristicRead);
|
||||
connect(m_sensorService, &QLowEnergyService::characteristicChanged, this, &FlowerCare::onSensorServiceCharacteristicChanged);
|
||||
m_sensorService->discoverDetails();
|
||||
}
|
||||
|
||||
void FlowerCare::onSensorServiceStateChanged(const QLowEnergyService::ServiceState &state)
|
||||
{
|
||||
if (state != QLowEnergyService::ServiceDiscovered) {
|
||||
return;
|
||||
}
|
||||
// printServiceDetails(m_sensorService);
|
||||
|
||||
QLowEnergyCharacteristic batteryFirmwareCharacteristic = m_sensorService->characteristic(batteryFirmwareCharacteristicUuid);
|
||||
if (!batteryFirmwareCharacteristic.isValid()) {
|
||||
qCWarning(dcFlowerCare()) << "Invalid battery/firmware characteristic.";
|
||||
emit failed();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray value = batteryFirmwareCharacteristic.value();
|
||||
QDataStream stream(&value, QIODevice::ReadOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
stream >> m_batteryLevel;
|
||||
|
||||
QString firmwareVersionString = value.right(5);
|
||||
qCDebug(dcFlowerCare()) << "Battery level:" << m_batteryLevel;
|
||||
|
||||
QVersionNumber firmwareVersion = QVersionNumber::fromString(firmwareVersionString);
|
||||
qCDebug(dcFlowerCare()) << "Firmware version:" << firmwareVersion;
|
||||
|
||||
if (firmwareVersion >= QVersionNumber::fromString("2.6.6")) {
|
||||
QLowEnergyCharacteristic sensorControlCharacteristic = m_sensorService->characteristic(sensorControlCharacteristicUuid);
|
||||
m_sensorService->writeCharacteristic(sensorControlCharacteristic, QByteArray::fromHex("A01F"));
|
||||
qCDebug(dcFlowerCare()) << "Wrote to handle 0x0033: A01F";
|
||||
}
|
||||
|
||||
m_sensorDataCharacteristic = m_sensorService->characteristic(sensorDataCharacteristicUuid);
|
||||
if (!m_sensorDataCharacteristic.isValid()) {
|
||||
qCWarning(dcFlowerCare()) << "Invalid sensor data characteristic.";
|
||||
}
|
||||
|
||||
// Enable notifications
|
||||
QLowEnergyDescriptor notificationDescriptor = m_sensorDataCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
|
||||
m_sensorService->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
|
||||
|
||||
// Read the data manually
|
||||
// Sometimes if we read the light intensty right now we might get wrong values
|
||||
// because the LED flashes upon connect. Let's not read it manually but instead wait for
|
||||
// the values to come in with the notification
|
||||
// m_sensorService->readCharacteristic(m_sensorDataCharacteristic);
|
||||
}
|
||||
|
||||
void FlowerCare::onSensorServiceCharacteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
|
||||
{
|
||||
qCDebug(dcFlowerCare()) << "Characteristic read" << QString::number(characteristic.handle(), 16) << value.toHex();
|
||||
if (characteristic != m_sensorDataCharacteristic) {
|
||||
return;
|
||||
}
|
||||
processSensorData(value);
|
||||
}
|
||||
|
||||
void FlowerCare::onSensorServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
|
||||
{
|
||||
qCDebug(dcFlowerCare()) << "Notification received" << QString::number(characteristic.handle(), 16) << value.toHex();
|
||||
if (characteristic != m_sensorDataCharacteristic) {
|
||||
return;
|
||||
}
|
||||
processSensorData(value);
|
||||
}
|
||||
|
||||
|
||||
void FlowerCare::printServiceDetails(QLowEnergyService *service) const
|
||||
{
|
||||
foreach (const QLowEnergyCharacteristic &characteristic, service->characteristics()) {
|
||||
qCDebug(dcFlowerCare()).nospace() << "C: --> " << characteristic.uuid().toString() << " (" << characteristic.handle() << " Name: " << characteristic.name() << "): " << characteristic.value() << ", " << characteristic.value().toHex();
|
||||
foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) {
|
||||
qCDebug(dcFlowerCare()).nospace() << "D: --> " << descriptor.uuid().toString() << " (" << descriptor.handle() << " Name: " << descriptor.name() << "): " << descriptor.value() << ", " << descriptor.value().toHex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlowerCare::processSensorData(const QByteArray &data)
|
||||
{
|
||||
QByteArray copy = data;
|
||||
QDataStream stream(©, QIODevice::ReadOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
quint16 temp;
|
||||
stream >> temp;
|
||||
qint8 skip;
|
||||
stream >> skip;
|
||||
quint32 lux;
|
||||
stream >> lux;
|
||||
qint8 moisture;
|
||||
stream >> moisture;
|
||||
qint16 fertility;
|
||||
stream >> fertility;
|
||||
|
||||
qCDebug(dcFlowerCare()) << "Temperature:" << temp << "Lux:" << lux << "moisture:" << moisture << "fertility" << fertility;
|
||||
|
||||
m_bluetoothDevice->disconnectDevice();
|
||||
emit finished(m_batteryLevel, 1.0 * temp / 10, lux, moisture, fertility);
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef FLOWERCARE_H
|
||||
#define FLOWERCARE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "hardware/bluetoothlowenergy/bluetoothlowenergydevice.h"
|
||||
|
||||
static QBluetoothUuid sensorServiceUuid = QBluetoothUuid(QUuid("00001204-0000-1000-8000-00805f9b34fb"));
|
||||
|
||||
// Contains Battery level and firmware version
|
||||
static QBluetoothUuid batteryFirmwareCharacteristicUuid = QBluetoothUuid(QUuid("00001a02-0000-1000-8000-00805f9b34fb"));
|
||||
|
||||
// Need to write 0xA01F to this
|
||||
static QBluetoothUuid sensorControlCharacteristicUuid = QBluetoothUuid(QUuid("00001a00-0000-1000-8000-00805f9b34fb"));
|
||||
|
||||
// contains sensor data
|
||||
static QBluetoothUuid sensorDataCharacteristicUuid = QBluetoothUuid(QUuid("00001a01-0000-1000-8000-00805f9b34fb"));
|
||||
|
||||
|
||||
class FlowerCare : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FlowerCare(BluetoothLowEnergyDevice* device, QObject *parent = nullptr);
|
||||
|
||||
void refreshData();
|
||||
|
||||
BluetoothLowEnergyDevice* btDevice() const;
|
||||
|
||||
signals:
|
||||
void finished(quint8 batteryLevel, double degreeCelsius, double lux, double moisture, double fertility);
|
||||
void failed();
|
||||
|
||||
private slots:
|
||||
void onConnectedChanged(bool connected);
|
||||
void onServiceDiscoveryFinished();
|
||||
|
||||
void onSensorServiceStateChanged(const QLowEnergyService::ServiceState &state);
|
||||
void onSensorServiceCharacteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
|
||||
void onSensorServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value);
|
||||
|
||||
private:
|
||||
void printServiceDetails(QLowEnergyService* service) const;
|
||||
|
||||
void processSensorData(const QByteArray &data);
|
||||
|
||||
BluetoothLowEnergyDevice *m_bluetoothDevice;
|
||||
|
||||
// Services
|
||||
QLowEnergyService *m_sensorService = nullptr;
|
||||
QLowEnergyCharacteristic m_sensorDataCharacteristic;
|
||||
|
||||
// cache
|
||||
quint8 m_batteryLevel = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // FLOWERCARE_H
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
include(../plugins.pri)
|
||||
|
||||
QT += bluetooth
|
||||
|
||||
TARGET = $$qtLibraryTarget(nymea_devicepluginflowercare)
|
||||
|
||||
SOURCES += \
|
||||
devicepluginflowercare.cpp \
|
||||
flowercare.cpp
|
||||
|
||||
HEADERS += \
|
||||
devicepluginflowercare.h \
|
||||
flowercare.h
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
</TS>
|
||||
|
|
@ -37,7 +37,7 @@ PLUGIN_DIRS = \
|
|||
keba \
|
||||
remotessh \
|
||||
dweetio \
|
||||
|
||||
flowercare \
|
||||
|
||||
CONFIG+=all
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue