Link bluetooth.
Implement class that talks to BLE miflora devices. Have UI show current values from new class.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
|
||||
set(qiflora_SRCS
|
||||
miflora/miflora.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
qt5_add_resources(RESOURCES resources.qrc)
|
||||
add_executable(qiflora ${qiflora_SRCS} ${RESOURCES})
|
||||
target_link_libraries(qiflora Qt5::Core Qt5::Qml Qt5::Quick Qt5::Svg)
|
||||
target_link_libraries(qiflora Qt5::Core Qt5::Qml Qt5::Quick Qt5::Svg Qt5::Bluetooth)
|
||||
install(TARGETS qiflora ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
@@ -2,13 +2,19 @@ import QtQuick 2.6
|
||||
import org.kde.kirigami 2.4 as Kirigami
|
||||
import QtQuick.Controls 2.0 as Controls
|
||||
import QtQuick.Layouts 1.12 as Layouts
|
||||
import org.eyecreate.qiflora 1.0
|
||||
|
||||
Kirigami.ApplicationWindow {
|
||||
id: root
|
||||
|
||||
title: "QiFlora"
|
||||
|
||||
|
||||
pageStack.initialPage: mainPageComponent
|
||||
|
||||
QiFlora {
|
||||
id: qiflora
|
||||
}
|
||||
|
||||
Component {
|
||||
id: mainPageComponent
|
||||
@@ -17,6 +23,9 @@ Kirigami.ApplicationWindow {
|
||||
mainAction: Kirigami.Action {
|
||||
iconName: "view-refresh"
|
||||
text: i18n("Query Device")
|
||||
onTriggered: {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
title: "Monitor"
|
||||
|
||||
@@ -53,7 +62,12 @@ Kirigami.ApplicationWindow {
|
||||
Kirigami.Heading {
|
||||
Layouts.Layout.alignment: Qt.AlignCenter
|
||||
level: 3
|
||||
text: "40" //TODO: replace
|
||||
text: {
|
||||
if(model.chartType == "temperature") qiflora.temperature + "°C"
|
||||
else if(model.chartType == "moisture") qiflora.moisture + "%"
|
||||
else if(model.chartType == "conductivity") qiflora.conduction + " µS/cm"
|
||||
else if(model.chartType == "brightness") qiflora.brightness + " lux"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QtQml>
|
||||
#include <QUrl>
|
||||
#include "miflora/miflora.h"
|
||||
|
||||
Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||
{
|
||||
@@ -12,7 +13,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||
QCoreApplication::setApplicationName("qiflora");
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
qmlRegisterType<MiFlora>("org.eyecreate.qiflora",1,0,"QiFlora");
|
||||
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
|
||||
|
||||
if (engine.rootObjects().isEmpty()) {
|
||||
|
||||
143
src/miflora/miflora.cpp
Normal file
143
src/miflora/miflora.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Kevin Whitaker <eyecreate@eyecreate.org>
|
||||
*
|
||||
* This program 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "miflora.h"
|
||||
#include <QDataStream>
|
||||
|
||||
void MiFlora::startSearch()
|
||||
{
|
||||
qDebug() << "Starting search...";
|
||||
agent = new QBluetoothDeviceDiscoveryAgent();
|
||||
agent->setLowEnergyDiscoveryTimeout(5000);
|
||||
devices.clear();
|
||||
connect(agent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &MiFlora::foundDevice);
|
||||
agent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
|
||||
}
|
||||
|
||||
void MiFlora::stopSearch()
|
||||
{
|
||||
if(agent != NULL) {
|
||||
agent->stop();
|
||||
agent->disconnect();
|
||||
delete agent;
|
||||
}
|
||||
}
|
||||
|
||||
void MiFlora::updateDataFromDevice ( QString mac )
|
||||
{
|
||||
for(QBluetoothDeviceInfo info: devices) {
|
||||
if(info.address().toString() == mac) {
|
||||
qDebug() << "Discovering on:" + info.name();
|
||||
currentController = QLowEnergyController::createCentral(info);
|
||||
currentController->setRemoteAddressType(QLowEnergyController::PublicAddress);
|
||||
connect(currentController, &QLowEnergyController::serviceDiscovered, this, &MiFlora::serviceDiscovered);
|
||||
connect(currentController, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error), this, &MiFlora::logControllerError);
|
||||
connect(currentController, &QLowEnergyController::connected, this, [this]{
|
||||
currentController->discoverServices();
|
||||
});
|
||||
currentController->connectToDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MiFlora::foundDevice ( const QBluetoothDeviceInfo& device )
|
||||
{
|
||||
if(device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration && device.address().toString().startsWith("C4:7C")) {
|
||||
devices.append(device);
|
||||
emit newDeviceFound(device.name(), device.address().toString());
|
||||
}
|
||||
}
|
||||
|
||||
void MiFlora::logControllerError ( QLowEnergyController::Error err )
|
||||
{
|
||||
qDebug() << "Error:" << err;
|
||||
}
|
||||
|
||||
void MiFlora::logServiceError(QLowEnergyService::ServiceError err)
|
||||
{
|
||||
qDebug() << "Service Error:" << err;
|
||||
}
|
||||
|
||||
|
||||
void MiFlora::serviceStateChanges ( QLowEnergyService::ServiceState state )
|
||||
{
|
||||
if(state == QLowEnergyService::ServiceState::ServiceDiscovered) {
|
||||
for(QLowEnergyCharacteristic chrt : sensorService->characteristics()) {
|
||||
if(chrt.uuid().toUInt16() == magicChar) {
|
||||
sensorService->writeCharacteristic(chrt, QByteArray::fromHex("a01f"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MiFlora::serviceCharWritten(QLowEnergyCharacteristic changedChar, QByteArray value)
|
||||
{
|
||||
if(changedChar.uuid().toUInt16() == magicChar) {
|
||||
//Used to only check sensor data after writting magic bytes.
|
||||
for(QLowEnergyCharacteristic chrt : sensorService->characteristics()) {
|
||||
if(chrt.uuid().toUInt16() == batteryFirmwareChar) {
|
||||
sensorService->readCharacteristic(chrt);
|
||||
}
|
||||
if(chrt.uuid().toUInt16() == sensorsChar) {
|
||||
sensorService->readCharacteristic(chrt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MiFlora::serviceCharRead(QLowEnergyCharacteristic readChar, QByteArray value)
|
||||
{
|
||||
if(readChar.uuid().toUInt16() == batteryFirmwareChar) {
|
||||
QDataStream parser(value);
|
||||
parser >> battery;
|
||||
parser.skipRawData(1);
|
||||
QByteArray version_buf(5, Qt::Uninitialized);
|
||||
parser.readRawData(version_buf.data(), 5);
|
||||
version = QString(version_buf);
|
||||
emit batteryChanged(battery);
|
||||
qDebug() << "Firmware: " << version;
|
||||
} else if(readChar.uuid().toUInt16() == sensorsChar) {
|
||||
quint16 origTemp;
|
||||
QDataStream parser(value);
|
||||
parser.setByteOrder(QDataStream::LittleEndian);
|
||||
parser >> origTemp;
|
||||
temp = origTemp/ 10.0; //original value in 0.1 C
|
||||
parser.skipRawData(1);
|
||||
parser >> bright;
|
||||
parser >> moisture;
|
||||
parser >> conduct;
|
||||
emit brightnessChanged(bright);
|
||||
emit temperatureChanged(temp);
|
||||
emit moistureChanged(moisture);
|
||||
emit conductionChanged(conduct);
|
||||
}
|
||||
}
|
||||
|
||||
void MiFlora::serviceDiscovered ( const QBluetoothUuid& gatt )
|
||||
{
|
||||
if(gatt == QBluetoothUuid(dataService)) {
|
||||
sensorService = currentController->createServiceObject(gatt);
|
||||
connect(sensorService, &QLowEnergyService::stateChanged, this, &MiFlora::serviceStateChanges);
|
||||
connect(sensorService, static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(&QLowEnergyService::error), this, &MiFlora::logServiceError);
|
||||
connect(sensorService, &QLowEnergyService::characteristicWritten, this, &MiFlora::serviceCharWritten);
|
||||
connect(sensorService, &QLowEnergyService::characteristicRead, this, &MiFlora::serviceCharRead);
|
||||
sensorService->discoverDetails();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
83
src/miflora/miflora.h
Normal file
83
src/miflora/miflora.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Kevin Whitaker <eyecreate@eyecreate.org>
|
||||
*
|
||||
* This program 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MIFLORA_H
|
||||
#define MIFLORA_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QtBluetooth/QBluetoothDeviceInfo>
|
||||
#include <QtBluetooth/QLowEnergyController>
|
||||
#include <QtBluetooth/QLowEnergyService>
|
||||
#include <QtBluetooth/QLowEnergyCharacteristic>
|
||||
|
||||
/**
|
||||
* Class using QtBluetooth to find and retrive info from Mi Flora devices.
|
||||
*/
|
||||
class MiFlora : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float temperature NOTIFY temperatureChanged MEMBER temp)
|
||||
Q_PROPERTY(quint32 brightness NOTIFY brightnessChanged MEMBER bright)
|
||||
Q_PROPERTY(quint8 moisture NOTIFY moistureChanged MEMBER moisture)
|
||||
Q_PROPERTY(quint16 conduction NOTIFY conductionChanged MEMBER conduct)
|
||||
Q_PROPERTY(quint8 battery NOTIFY batteryChanged MEMBER battery)
|
||||
|
||||
public:
|
||||
Q_INVOKABLE void startSearch();
|
||||
Q_INVOKABLE void stopSearch();
|
||||
|
||||
Q_INVOKABLE void updateDataFromDevice(QString mac);
|
||||
//TODO:History?
|
||||
|
||||
signals:
|
||||
void newDeviceFound(QString name, QString address);
|
||||
void temperatureChanged(float temperature);
|
||||
void brightnessChanged(quint32 brightness);
|
||||
void moistureChanged(quint8 moisture);
|
||||
void conductionChanged(quint16 conduction);
|
||||
void batteryChanged(quint8 battery);
|
||||
|
||||
private:
|
||||
void foundDevice(const QBluetoothDeviceInfo &device);
|
||||
void serviceDiscovered(const QBluetoothUuid &gatt);
|
||||
void logControllerError(QLowEnergyController::Error err);
|
||||
void logServiceError(QLowEnergyService::ServiceError err);
|
||||
void serviceStateChanges(QLowEnergyService::ServiceState state);
|
||||
void serviceCharWritten(QLowEnergyCharacteristic changedChar, QByteArray value);
|
||||
void serviceCharRead(QLowEnergyCharacteristic readChar, QByteArray value);
|
||||
|
||||
QBluetoothDeviceDiscoveryAgent *agent;
|
||||
QList<QBluetoothDeviceInfo> devices;
|
||||
QLowEnergyController *currentController;
|
||||
QLowEnergyService *sensorService;
|
||||
|
||||
const quint16 dataService = 4612; //0x1204
|
||||
const quint16 batteryFirmwareChar = 6658;//0x1a02
|
||||
const quint16 sensorsChar = 6657; //0x1a01
|
||||
const quint16 magicChar = 6656; //0x1a00
|
||||
|
||||
float temp = 0.0;
|
||||
quint32 bright = 0;
|
||||
quint8 moisture = 0;
|
||||
quint16 conduct = 0;
|
||||
quint8 battery = 0;
|
||||
QString version;
|
||||
|
||||
};
|
||||
|
||||
#endif // MIFLORA_H
|
||||
Reference in New Issue
Block a user