602 lines
20 KiB
C++
602 lines
20 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright (C) 2013 - 2024, nymea GmbH
|
|
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
|
*
|
|
* This file is part of nymea-plugins.
|
|
*
|
|
* nymea-plugins 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.
|
|
*
|
|
* nymea-plugins 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 nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#include "nuki.h"
|
|
#include "extern-plugininfo.h"
|
|
|
|
#include <QBitArray>
|
|
#include <QtEndian>
|
|
#include <QByteArray>
|
|
#include <QDataStream>
|
|
#include <QTimer>
|
|
|
|
Nuki::Nuki(Thing *thing, BluetoothDevice *bluetoothDevice, QObject *parent) :
|
|
QObject(parent),
|
|
m_thing(thing),
|
|
m_bluetoothDevice(bluetoothDevice)
|
|
{
|
|
connect(m_bluetoothDevice, &BluetoothDevice::stateChanged, this, &Nuki::onBluetoothDeviceStateChanged);
|
|
onBluetoothDeviceStateChanged(m_bluetoothDevice->state());
|
|
}
|
|
|
|
Thing *Nuki::thing()
|
|
{
|
|
return m_thing;
|
|
}
|
|
|
|
BluetoothDevice *Nuki::bluetoothDevice()
|
|
{
|
|
return m_bluetoothDevice;
|
|
}
|
|
|
|
bool Nuki::startAuthenticationProcess(const PairingTransactionId &pairingTransactionId)
|
|
{
|
|
if (m_nukiAction != NukiActionNone) {
|
|
qCWarning(dcNuki()) << "Cannot start authentication process. Nuki is busy and already processing an action. Please retry again." << m_nukiAction;
|
|
return false;
|
|
}
|
|
|
|
m_nukiAction = NukiActionAuthenticate;
|
|
m_pairingId = pairingTransactionId;
|
|
|
|
if (m_available) {
|
|
executeCurrentAction();
|
|
} else {
|
|
m_bluetoothDevice->connectDevice();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Nuki::refreshStates()
|
|
{
|
|
return executeNukiAction(NukiActionRefresh);
|
|
}
|
|
|
|
bool Nuki::executeNukiAction(Nuki::NukiAction action)
|
|
{
|
|
if (m_nukiAction != NukiActionNone) {
|
|
qCWarning(dcNuki()) << "Cannot execute Nuki action. Nuki is busy and already processing an action." << m_nukiAction;
|
|
return false;
|
|
}
|
|
|
|
m_nukiAction = action;
|
|
|
|
if (m_available) {
|
|
executeCurrentAction();
|
|
} else {
|
|
m_bluetoothDevice->connectDevice();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Nuki::executeDeviceAction(Nuki::NukiAction action, ThingActionInfo *actionInfo)
|
|
{
|
|
if (m_nukiAction != NukiActionNone || !m_actionInfo.isNull()) {
|
|
qCWarning(dcNuki()) << "Nuki is busy and already processing an action. Please retry again." << m_nukiAction;
|
|
return false;
|
|
}
|
|
|
|
m_actionInfo = QPointer<ThingActionInfo>(actionInfo);
|
|
m_nukiAction = action;
|
|
|
|
if (m_available) {
|
|
executeCurrentAction();
|
|
} else {
|
|
m_bluetoothDevice->connectDevice();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Nuki::connectDevice()
|
|
{
|
|
if (!m_bluetoothDevice)
|
|
return;
|
|
|
|
m_bluetoothDevice->connectDevice();
|
|
}
|
|
|
|
void Nuki::disconnectDevice()
|
|
{
|
|
if (!m_bluetoothDevice)
|
|
return;
|
|
|
|
m_bluetoothDevice->disconnectDevice();
|
|
}
|
|
|
|
void Nuki::clearSettings()
|
|
{
|
|
if (m_nukiAuthenticator) {
|
|
m_nukiAuthenticator->clearSettings();
|
|
}
|
|
}
|
|
|
|
void Nuki::printServices()
|
|
{
|
|
foreach (BluetoothGattService *service, m_bluetoothDevice->services()) {
|
|
qCDebug(dcNuki()) << service;
|
|
foreach (BluetoothGattCharacteristic *characteristic, service->characteristics()) {
|
|
qCDebug(dcNuki()) << " " << characteristic;
|
|
foreach (BluetoothGattDescriptor *descriptor, characteristic->descriptors()) {
|
|
qCDebug(dcNuki()) << " " << descriptor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Nuki::readDeviceInformationCharacteristics()
|
|
{
|
|
qCDebug(dcNuki()) << "Start reading device information";
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
m_initUuidsToRead.append(QBluetoothUuid::CharacteristicType::SerialNumberString);
|
|
m_initUuidsToRead.append(QBluetoothUuid::CharacteristicType::HardwareRevisionString);
|
|
m_initUuidsToRead.append(QBluetoothUuid::CharacteristicType::FirmwareRevisionString);
|
|
|
|
m_deviceInformationService->readCharacteristic(QBluetoothUuid::CharacteristicType::SerialNumberString);
|
|
m_deviceInformationService->readCharacteristic(QBluetoothUuid::CharacteristicType::HardwareRevisionString);
|
|
m_deviceInformationService->readCharacteristic(QBluetoothUuid::CharacteristicType::FirmwareRevisionString);
|
|
#else
|
|
m_initUuidsToRead.append(QBluetoothUuid::SerialNumberString);
|
|
m_initUuidsToRead.append(QBluetoothUuid::HardwareRevisionString);
|
|
m_initUuidsToRead.append(QBluetoothUuid::FirmwareRevisionString);
|
|
|
|
m_deviceInformationService->readCharacteristic(QBluetoothUuid::SerialNumberString);
|
|
m_deviceInformationService->readCharacteristic(QBluetoothUuid::HardwareRevisionString);
|
|
m_deviceInformationService->readCharacteristic(QBluetoothUuid::FirmwareRevisionString);
|
|
#endif
|
|
|
|
}
|
|
|
|
void Nuki::executeCurrentAction()
|
|
{
|
|
qCDebug(dcNuki()) << "Executing" << m_nukiAction;
|
|
|
|
switch (m_nukiAction) {
|
|
case NukiActionAuthenticate:
|
|
m_nukiAuthenticator->startAuthenticationProcess();
|
|
break;
|
|
case NukiActionRefresh:
|
|
if (!m_nukiController->readLockState()) {
|
|
finishCurrentAction(false);
|
|
}
|
|
break;
|
|
case NukiActionLock:
|
|
if (!m_nukiController->lock()) {
|
|
finishCurrentAction(false);
|
|
}
|
|
break;
|
|
case NukiActionUnlock:
|
|
if (!m_nukiController->unlock()) {
|
|
finishCurrentAction(false);
|
|
}
|
|
break;
|
|
case NukiActionUnlatch:
|
|
if (!m_nukiController->unlatch()) {
|
|
finishCurrentAction(false);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool Nuki::enableNotificationsIndications(BluetoothGattCharacteristic *characteristic)
|
|
{
|
|
qCDebug(dcNuki()) << "Enable notifications on" << characteristic;
|
|
if (!characteristic->startNotifications()) {
|
|
qCDebug(dcNuki()) << "Failed to start notifications on" << characteristic;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Nuki::onBluetoothDeviceStateChanged(const BluetoothDevice::State &state)
|
|
{
|
|
qCDebug(dcNuki()) << m_bluetoothDevice << "state changed --> " << state;
|
|
switch (state) {
|
|
case BluetoothDevice::Connecting:
|
|
break;
|
|
case BluetoothDevice::Connected:
|
|
if (m_bluetoothDevice->servicesResolved()) {
|
|
// Services already discovered
|
|
if (!init()) {
|
|
qCWarning(dcNuki()) << "Could not initialze device" << m_bluetoothDevice;
|
|
m_bluetoothDevice->disconnectDevice();
|
|
} else {
|
|
readDeviceInformationCharacteristics();
|
|
}
|
|
}
|
|
break;
|
|
case BluetoothDevice::Pairing:
|
|
break;
|
|
case BluetoothDevice::Discovering:
|
|
break;
|
|
case BluetoothDevice::Discovered:
|
|
printServices();
|
|
if (!init()) {
|
|
qCWarning(dcNuki()) << "Could not initialze device" << m_bluetoothDevice;
|
|
m_bluetoothDevice->disconnectDevice();
|
|
} else {
|
|
readDeviceInformationCharacteristics();
|
|
}
|
|
break;
|
|
case BluetoothDevice::Disconnecting:
|
|
setAvailable(false);
|
|
clean();
|
|
break;
|
|
case BluetoothDevice::Disconnected:
|
|
setAvailable(false);
|
|
clean();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Nuki::onDeviceInfoCharacteristicReadFinished(BluetoothGattCharacteristic *characteristic, const QByteArray &value)
|
|
{
|
|
qCDebug(dcNuki()) << "Read thing information characteristic finished" << characteristic->chararcteristicName() << qUtf8Printable(value);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
if (characteristic->uuid() == QBluetoothUuid::CharacteristicType::SerialNumberString) {
|
|
m_serialNumber = QString::fromUtf8(value);
|
|
m_initUuidsToRead.removeOne(QBluetoothUuid::CharacteristicType::SerialNumberString);
|
|
} else if (characteristic->uuid() == QBluetoothUuid::CharacteristicType::HardwareRevisionString) {
|
|
m_hardwareRevision = QString::fromUtf8(value);
|
|
m_initUuidsToRead.removeOne(QBluetoothUuid::CharacteristicType::HardwareRevisionString);
|
|
} else if (characteristic->uuid() == QBluetoothUuid::CharacteristicType::FirmwareRevisionString) {
|
|
m_firmwareRevision = QString::fromUtf8(value);
|
|
m_initUuidsToRead.removeOne(QBluetoothUuid::CharacteristicType::FirmwareRevisionString);
|
|
}
|
|
#else
|
|
if (characteristic->uuid() == QBluetoothUuid::SerialNumberString) {
|
|
m_serialNumber = QString::fromUtf8(value);
|
|
m_initUuidsToRead.removeOne(QBluetoothUuid::SerialNumberString);
|
|
} else if (characteristic->uuid() == QBluetoothUuid::HardwareRevisionString) {
|
|
m_hardwareRevision = QString::fromUtf8(value);
|
|
m_initUuidsToRead.removeOne(QBluetoothUuid::HardwareRevisionString);
|
|
} else if (characteristic->uuid() == QBluetoothUuid::FirmwareRevisionString) {
|
|
m_firmwareRevision = QString::fromUtf8(value);
|
|
m_initUuidsToRead.removeOne(QBluetoothUuid::FirmwareRevisionString);
|
|
}
|
|
#endif
|
|
|
|
if (m_initUuidsToRead.isEmpty()) {
|
|
// Initial read done. Make thing available
|
|
setAvailable(true);
|
|
}
|
|
}
|
|
|
|
void Nuki::onAuthenticationError(NukiUtils::ErrorCode error)
|
|
{
|
|
qCWarning(dcNuki()) << "Authentication error occured" << error;
|
|
|
|
if (m_pairingId.isNull())
|
|
return;
|
|
|
|
// If we have a pairing id
|
|
emit authenticationProcessFinished(m_pairingId, false);
|
|
m_pairingId = PairingTransactionId();
|
|
}
|
|
|
|
void Nuki::onAuthenticationFinished(bool success)
|
|
{
|
|
qCDebug(dcNuki()) << "Authentication process finished" << (success ? "successfully." : "with error.");
|
|
|
|
if (m_pairingId.isNull())
|
|
return;
|
|
|
|
// If we have a pairing id
|
|
emit authenticationProcessFinished(m_pairingId, success);
|
|
m_pairingId = PairingTransactionId();
|
|
}
|
|
|
|
void Nuki::onNukiReadStatesFinished(bool success)
|
|
{
|
|
m_nukiAction = NukiActionNone;
|
|
|
|
if (success) {
|
|
// Update states
|
|
onNukiStatesChanged();
|
|
}
|
|
|
|
// Check if this was an action call
|
|
if (m_actionInfo.isNull()) {
|
|
// Looks like this was a refresh call, lets disconnect to minimize the not reachable time for other apps
|
|
QTimer::singleShot(0, m_bluetoothDevice, &BluetoothDevice::disconnectDevice);
|
|
return;
|
|
}
|
|
|
|
finishCurrentAction(true);
|
|
}
|
|
|
|
void Nuki::onNukiStatesChanged()
|
|
{
|
|
if (!m_thing)
|
|
return;
|
|
|
|
m_thing->setStateValue(nukiHardwareRevisionStateTypeId, m_hardwareRevision);
|
|
m_thing->setStateValue(nukiFirmwareRevisionStateTypeId, m_firmwareRevision);
|
|
m_thing->setStateValue(nukiBatteryCriticalStateTypeId, m_nukiController->batteryCritical());
|
|
|
|
switch (m_nukiController->nukiLockTrigger()) {
|
|
case NukiUtils::LockTriggerBluetooth:
|
|
m_thing->setStateValue(nukiTriggerStateTypeId, "Bluetooth");
|
|
break;
|
|
case NukiUtils::LockTriggerButton:
|
|
m_thing->setStateValue(nukiTriggerStateTypeId, "Button");
|
|
break;
|
|
case NukiUtils::LockTriggerManual:
|
|
m_thing->setStateValue(nukiTriggerStateTypeId, "Manual");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (m_nukiController->nukiState()) {
|
|
case NukiUtils::NukiStateDoorMode:
|
|
m_thing->setStateValue(nukiModeStateTypeId, "Door");
|
|
break;
|
|
case NukiUtils::NukiStatePairingMode:
|
|
m_thing->setStateValue(nukiModeStateTypeId, "Pairing");
|
|
break;
|
|
case NukiUtils::NukiStateUninitialized:
|
|
m_thing->setStateValue(nukiModeStateTypeId, "Uninitialized");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (m_nukiController->nukiLockState()) {
|
|
case NukiUtils::LockStateLocked:
|
|
m_thing->setStateValue(nukiStateStateTypeId, "locked");
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "Ok");
|
|
break;
|
|
case NukiUtils::LockStateLocking:
|
|
m_thing->setStateValue(nukiStateStateTypeId, "locking");
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "Ok");
|
|
break;
|
|
case NukiUtils::LockStateMotorBlocked:
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "Motor blocked");
|
|
break;
|
|
case NukiUtils::LockStateUncalibrated:
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "Uncalibrated");
|
|
break;
|
|
case NukiUtils::LockStateUndefined:
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "Undefined");
|
|
break;
|
|
case NukiUtils::LockStateUnlatched:
|
|
m_thing->setStateValue(nukiStateStateTypeId, "unlatched");
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "Ok");
|
|
break;
|
|
case NukiUtils::LockStateUnlatching:
|
|
m_thing->setStateValue(nukiStateStateTypeId, "unlatching");
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "Ok");
|
|
break;
|
|
case NukiUtils::LockStateUnlockedLocknGoActive:
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "unlocked");
|
|
break;
|
|
case NukiUtils::LockStateUnlocked:
|
|
m_thing->setStateValue(nukiStateStateTypeId, "unlocked");
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "Ok");
|
|
break;
|
|
case NukiUtils::LockStateUnlocking:
|
|
m_thing->setStateValue(nukiStateStateTypeId, "unlocking");
|
|
m_thing->setStateValue(nukiStatusStateTypeId, "Ok");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool Nuki::init()
|
|
{
|
|
if (!m_bluetoothDevice)
|
|
return false;
|
|
|
|
qCDebug(dcNuki()) << "Init" << m_bluetoothDevice;
|
|
|
|
// If not connected, connect
|
|
if (!m_bluetoothDevice->connected()) {
|
|
qCWarning(dcNuki()) << "Device is not connected" << m_bluetoothDevice;
|
|
return false;
|
|
}
|
|
|
|
// If services not resolved yet, wait
|
|
if (!m_bluetoothDevice->servicesResolved()) {
|
|
qCWarning(dcNuki()) << "Device services not resolved yet" << m_bluetoothDevice;
|
|
return false;
|
|
}
|
|
|
|
// Verify services
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
if (!m_bluetoothDevice->hasService(QBluetoothUuid::ServiceClassUuid::DeviceInformation)) {
|
|
#else
|
|
if (!m_bluetoothDevice->hasService(QBluetoothUuid::DeviceInformation)) {
|
|
#endif
|
|
qCWarning(dcNuki()) << "Could not find device information service on device" << m_bluetoothDevice;
|
|
return false;
|
|
}
|
|
|
|
if (!m_bluetoothDevice->hasService(pairingServiceUuid())) {
|
|
qCWarning(dcNuki()) << "Could not find pairing service on device" << m_bluetoothDevice;
|
|
return false;
|
|
}
|
|
|
|
if (!m_bluetoothDevice->hasService(keyturnerServiceUuid())) {
|
|
qCWarning(dcNuki()) << "Could not find key turner service on device" << m_bluetoothDevice;
|
|
return false;
|
|
}
|
|
|
|
// Create service and characteristic objects
|
|
// Device information
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
m_deviceInformationService = m_bluetoothDevice->getService(QBluetoothUuid::ServiceClassUuid::DeviceInformation);
|
|
#else
|
|
m_deviceInformationService = m_bluetoothDevice->getService(QBluetoothUuid::DeviceInformation);
|
|
#endif
|
|
connect(m_deviceInformationService, &BluetoothGattService::characteristicReadFinished, this, &Nuki::onDeviceInfoCharacteristicReadFinished);
|
|
|
|
// Keyturner service
|
|
m_keyturnerService = m_bluetoothDevice->getService(keyturnerServiceUuid());
|
|
if (!m_keyturnerService->hasCharacteristic(keyturnerUserDataCharacteristicUuid())) {
|
|
qCWarning(dcNuki()) << "Could not find user data characteristc on device" << m_bluetoothDevice;
|
|
return false;
|
|
}
|
|
// Set key turner characteristics for data and user data
|
|
if (!m_keyturnerService->hasCharacteristic(keyturnerDataCharacteristicUuid())) {
|
|
qCWarning(dcNuki()) << "Could not find data characteristc on device" << m_bluetoothDevice;
|
|
return false;
|
|
}
|
|
|
|
// Enable notifications/indications
|
|
m_keyturnerUserDataCharacteristic = m_keyturnerService->getCharacteristic(keyturnerUserDataCharacteristicUuid());
|
|
if (!enableNotificationsIndications(m_keyturnerUserDataCharacteristic)) {
|
|
qCWarning(dcNuki()) << "Could not enable notifications/indications for user data characteristic.";
|
|
return false;
|
|
}
|
|
|
|
m_keyturnerDataCharacteristic = m_keyturnerService->getCharacteristic(keyturnerDataCharacteristicUuid());
|
|
if (!enableNotificationsIndications(m_keyturnerDataCharacteristic)) {
|
|
qCWarning(dcNuki()) << "Could not enable notifications/indications for key turner data characteristic.";
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
// Pairing service
|
|
m_pairingService = m_bluetoothDevice->getService(pairingServiceUuid());
|
|
if (!m_pairingService->hasCharacteristic(pairingDataCharacteristicUuid())) {
|
|
qCWarning(dcNuki()) << "Could not find pairing data characteristc on device" << m_bluetoothDevice;
|
|
return false;
|
|
}
|
|
|
|
m_pairingDataCharacteristic = m_pairingService->getCharacteristic(pairingDataCharacteristicUuid());
|
|
if (!enableNotificationsIndications(m_pairingDataCharacteristic)) {
|
|
qCWarning(dcNuki()) << "Could not enable notifications for pairing characteristic.";
|
|
return false;
|
|
}
|
|
|
|
// Create authenticator
|
|
if (m_nukiAuthenticator) {
|
|
delete m_nukiAuthenticator;
|
|
m_nukiAuthenticator = nullptr;
|
|
}
|
|
|
|
m_nukiAuthenticator = new NukiAuthenticator(m_bluetoothDevice->hostInfo(), m_pairingDataCharacteristic, this);
|
|
connect(m_nukiAuthenticator, &NukiAuthenticator::errorOccured, this, &Nuki::onAuthenticationError);
|
|
connect(m_nukiAuthenticator, &NukiAuthenticator::authenticationProcessFinished, this, &Nuki::onAuthenticationFinished);
|
|
|
|
// Create nuki handler for encrypted communication
|
|
if (m_nukiController) {
|
|
delete m_nukiController;
|
|
m_nukiController = nullptr;
|
|
}
|
|
|
|
m_nukiController = new NukiController(m_nukiAuthenticator, m_keyturnerUserDataCharacteristic, this);
|
|
connect(m_nukiController, &NukiController::readNukiStatesFinished, this, &Nuki::onNukiReadStatesFinished);
|
|
connect(m_nukiController, &NukiController::lockFinished, this, &Nuki::finishCurrentAction);
|
|
connect(m_nukiController, &NukiController::unlockFinished, this, &Nuki::finishCurrentAction);
|
|
connect(m_nukiController, &NukiController::unlatchFinished, this, &Nuki::finishCurrentAction);
|
|
connect(m_nukiController, &NukiController::nukiStatesChanged, this, &Nuki::onNukiStatesChanged);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Nuki::clean()
|
|
{
|
|
// Reset properties
|
|
m_hardwareRevision = QString();
|
|
m_serialNumber = QString();
|
|
m_firmwareRevision = QString();
|
|
m_initUuidsToRead.clear();
|
|
|
|
finishCurrentAction(false);
|
|
|
|
// Forget all services and characteristics
|
|
if (m_deviceInformationService) {
|
|
disconnect(m_deviceInformationService, &BluetoothGattService::characteristicReadFinished, this, &Nuki::onDeviceInfoCharacteristicReadFinished);
|
|
m_deviceInformationService = nullptr;
|
|
}
|
|
|
|
m_keyturnerService = nullptr;
|
|
m_keyturnerDataCharacteristic = nullptr;
|
|
m_keyturnerUserDataCharacteristic = nullptr;
|
|
|
|
m_pairingService = nullptr;
|
|
m_pairingDataCharacteristic = nullptr;
|
|
|
|
// Delete handler
|
|
if (m_nukiController) {
|
|
delete m_nukiController;
|
|
m_nukiController = nullptr;
|
|
}
|
|
|
|
// Note: delete the authenticator after the handler
|
|
if (m_nukiAuthenticator) {
|
|
delete m_nukiAuthenticator;
|
|
m_nukiAuthenticator = nullptr;
|
|
}
|
|
}
|
|
|
|
void Nuki::finishCurrentAction(bool success)
|
|
{
|
|
m_nukiAction = NukiActionNone;
|
|
if (m_actionInfo.isNull())
|
|
return;
|
|
|
|
m_actionInfo->finish(success ? Thing::ThingErrorNoError : Thing::ThingErrorHardwareFailure);
|
|
m_actionInfo.clear();
|
|
}
|
|
|
|
void Nuki::setAvailable(bool available)
|
|
{
|
|
if (m_available == available)
|
|
return;
|
|
|
|
m_available = available;
|
|
emit availableChanged(m_available);
|
|
|
|
qCDebug(dcNuki()) << "Bluetooth device" << m_bluetoothDevice->name() << "is now" << (m_available ? "available" : "unavailable");
|
|
|
|
if (m_available) {
|
|
executeCurrentAction();
|
|
} else {
|
|
// Finish any running actions
|
|
finishCurrentAction(false);
|
|
|
|
// Finish possible running pairing transations
|
|
if (!m_pairingId.isNull()) {
|
|
qCWarning(dcNuki()) << "Cancel authentication process because of disconnection.";
|
|
emit authenticationProcessFinished(m_pairingId, false);
|
|
m_pairingId = PairingTransactionId();
|
|
}
|
|
}
|
|
|
|
if (!m_thing)
|
|
return;
|
|
|
|
m_thing->setStateValue(nukiConnectedStateTypeId, m_available);
|
|
}
|