// SPDX-FileCopyrightText: 2024 Joshua Goins // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL #include "deskcontrol.h" #include #include const auto DESK_UUID_KEY = QStringLiteral("DeskUUID"); const auto ADV_SVC = QBluetoothUuid("99fa0001-338a-1024-8a49-009c0215f78a"); const auto COMMAND_CHARACTERISTIC = QBluetoothUuid("99fa0002-338a-1024-8a49-009c0215f78a"); const auto POSITION_CHARACTERISTIC = QBluetoothUuid("99fa0021-338a-1024-8a49-009c0215f78a"); const auto upCommand = QByteArray::fromHex("4700"); const auto downCommand = QByteArray::fromHex("4600"); constexpr auto MAX_HEIGHT = 1.27; constexpr auto MIN_HEIGHT = 0.62; DeskControl::DeskControl(QObject *parent, const KPluginMetaData &data, const QVariantList &args) : Applet(parent, data, args) { m_localDevice.powerOn(); // If we have connected to the desk previously, connect. Otherwise discover one. if (config().hasKey(DESK_UUID_KEY)) { connectToDesk(); } else { searchForDesk(); } } DeskControl::~DeskControl() = default; DeskControl::DeskState DeskControl::state() const { return DeskState::NotFound; } void DeskControl::up() const { Q_ASSERT(m_commandService); Q_ASSERT(m_commandCharacteristic.isValid()); m_commandService->writeCharacteristic(m_commandCharacteristic, upCommand); } void DeskControl::down() const { Q_ASSERT(m_commandService); Q_ASSERT(m_commandCharacteristic.isValid()); m_commandService->writeCharacteristic(m_commandCharacteristic, downCommand); } void DeskControl::connectToDesk() { const auto lastDeskUUID = config().readEntry(DESK_UUID_KEY); qInfo() << "Connecting to desk:" << lastDeskUUID; m_controller = QLowEnergyController::createCentral(QBluetoothDeviceInfo(QBluetoothAddress(lastDeskUUID), QStringLiteral("Desk"), 0)); connect(m_controller, &QLowEnergyController::connected, this, [this]() { m_controller->discoverServices(); }); connect(m_controller, &QLowEnergyController::discoveryFinished, this, [this]() { for (auto service: m_controller->services()) { if (service.toString() == QStringLiteral("{99fa0001-338a-1024-8a49-009c0215f78a}")) { m_commandService = m_controller->createServiceObject(service); m_commandService->discoverDetails(); connect(m_commandService, &QLowEnergyService::stateChanged, this, [this](QLowEnergyService::ServiceState state) { m_commandCharacteristic = m_commandService->characteristic(COMMAND_CHARACTERISTIC); }); connect(m_commandService, &QLowEnergyService::errorOccurred, [](QLowEnergyService::ServiceError error) { qWarning() << error; }); } if (service.toString() == QStringLiteral("{99fa0020-338a-1024-8a49-009c0215f78a}")) { m_positionCommandService = m_controller->createServiceObject(service); m_positionCommandService->discoverDetails(); connect(m_positionCommandService, &QLowEnergyService::errorOccurred, [](QLowEnergyService::ServiceError error) { qWarning() << error; }); connect(m_positionCommandService, &QLowEnergyService::stateChanged, this, [this](QLowEnergyService::ServiceState state) { QLowEnergyCharacteristic batteryLevel = m_positionCommandService->characteristic(POSITION_CHARACTERISTIC); if (!batteryLevel.isValid()) return; QLowEnergyDescriptor notification = batteryLevel.descriptor( QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration); if (!notification.isValid()) return; m_positionCommandService->writeDescriptor(notification, QByteArray::fromHex("0100")); }); connect(m_positionCommandService, &QLowEnergyService::characteristicChanged, this, [this](const QLowEnergyCharacteristic &info, const QByteArray &value) { const int32_t highByte = value.at(1); const int32_t lowByte = value.at(0); const int32_t number = (highByte << 8) + lowByte; m_height = (static_cast(number) / 1000.0) + MIN_HEIGHT; Q_EMIT heightChanged(); }); } } }); m_controller->connectToDevice(); } void DeskControl::searchForDesk() { qInfo() << "Searching for desk..."; QList remotes = m_localDevice.connectedDevices(); if (remotes.empty()) { return; } for (const auto &address : remotes) { auto controller = QLowEnergyController::createCentral(QBluetoothDeviceInfo(address, QStringLiteral("Desk"), 0)); qInfo() << controller->services(); connect(controller, &QLowEnergyController::connected, this, [this, controller]() { controller->discoverServices(); }); connect(controller, &QLowEnergyController::discoveryFinished, this, [this, controller, address]() { controller->deleteLater(); for (auto service: controller->services()) { if (service == ADV_SVC) { qInfo() << "Found desk:" << DESK_UUID_KEY; config().writeEntry(DESK_UUID_KEY, address.toString()); connectToDesk(); break; } } }); controller->connectToDevice(); } } K_PLUGIN_CLASS(DeskControl) #include "deskcontrol.moc" #include "moc_deskcontrol.cpp"