diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc3909c..7172a02 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,8 @@ set(SRC advertisement.cpp bluezmanager.cpp ancs.cpp - ancs_notification.cpp) + ancs_notification.cpp + cts.cpp) set(HEADERS notificationservice.h weatherservice.h @@ -30,7 +31,8 @@ set(HEADERS bluezmanager.h ancs_protocol_constants.h ancs.h - ancs_notification.h) + ancs_notification.h + cts.h) add_executable(asteroid-btsyncd ${SRC} ${HEADERS}) @@ -40,7 +42,7 @@ target_link_libraries(asteroid-btsyncd PRIVATE QtMpris::QtMpris SystemSettings::SystemSettings PkgConfig::GIOMM) - + install(TARGETS asteroid-btsyncd DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/bluezmanager.cpp b/src/bluezmanager.cpp index 19a7b38..b7b62fa 100644 --- a/src/bluezmanager.cpp +++ b/src/bluezmanager.cpp @@ -180,6 +180,7 @@ void BlueZManager::onConnectedChanged() appIcon = "ios-bluetooth-outline"; } else { mAncs.disconnect(); + mCts.disconnect(); //% "Disconnected" summary = qtTrId("id-disconnected"); body = ""; @@ -208,6 +209,8 @@ void BlueZManager::onConnectedChanged() } void BlueZManager::onServicesResolvedChanged() { - if (mServicesResolved) + if (mServicesResolved) { mAncs.searchForAncsCharacteristics(); + mCts.searchForTimeCharacteristics(); + } } diff --git a/src/bluezmanager.h b/src/bluezmanager.h index 0681d2d..82f4dac 100644 --- a/src/bluezmanager.h +++ b/src/bluezmanager.h @@ -24,6 +24,7 @@ #include #include "ancs.h" +#include "cts.h" typedef QMap> InterfaceList; @@ -41,6 +42,7 @@ class BlueZManager : public QObject QDBusServiceWatcher *mWatcher; QDBusConnection mBus; ANCS mAncs; + CTS mCts; void updateAdapter(); void setAdapter(QString adatper); diff --git a/src/cts.cpp b/src/cts.cpp new file mode 100644 index 0000000..7df23be --- /dev/null +++ b/src/cts.cpp @@ -0,0 +1,108 @@ +// Implements the Current Time Service as per: https://www.bluetooth.com/specifications/specs/cts-1-1/ +#include "cts.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" + +CTS::CTS() { + +} + +void CTS::searchForTimeCharacteristics() { + QDBusConnection bus = QDBusConnection::systemBus(); + QDBusInterface remoteOm(BLUEZ_SERVICE_NAME, "/", DBUS_OM_IFACE, bus); + QDBusMessage result = remoteOm.call("GetManagedObjects"); + QString timeChar; + qDebug() << "CTS searching for characteristic"; + const QDBusArgument argument = result.arguments().at(0).value(); + if (argument.currentType() == QDBusArgument::MapType) { + argument.beginMap(); + while (!argument.atEnd()) { + QString key; + QMap value; + + argument.beginMapEntry(); + argument >> key >> value; + argument.endMapEntry(); + if (isMatchingCharacteristic(CTS_CHARACTERISTIC_UUID, value)) { + qDebug() << "Found Current Time Characteristic:" << key; + timeChar = key; + } + + } + argument.endMap(); + } + + if (!timeChar.isEmpty()) { + qDebug() << "Current Time Characteristic found"; + bus.connect(BLUEZ_SERVICE_NAME, timeChar, DBUS_PROPERTIES_IFACE, "PropertiesChanged", this, + SLOT(TimeCharacteristicPropertiesChanged(QString, QMap, QStringList))); + QDBusInterface timeCharacteristicIface("org.bluez", timeChar, GATT_CHRC_IFACE, + QDBusConnection::systemBus()); + timeCharacteristicIface.call("StartNotify"); + + QMap empty; + QDBusMessage response = timeCharacteristicIface.call("ReadValue", empty); + QList arguments = response.arguments(); + + if(!arguments.isEmpty() && arguments.first().type() == QVariant::ByteArray) { + QByteArray bytes = arguments.first().toByteArray(); + parseCurrentTime(bytes); + } + } +} + +void CTS::parseCurrentTime(QByteArray& bytes) +{ + if(bytes.size() != 10) { + qWarning() << "Current time value is not 10 bytes long"; + return; + } + ushort year = (bytes[1] << 8) + bytes[0]; + uint8_t month = bytes[2]; + uint8_t day = bytes[3]; + uint8_t hour = bytes[4]; + uint8_t minute = bytes[5]; + uint8_t second = bytes[6]; + uint8_t day_of_week = bytes[7]; + uint8_t exact_time_256 = bytes[8]; + uint8_t adjust_reason = bytes[9]; + + Maemo::Timed::WallClock::Settings s; + QDateTime newTime(QDate(year, month, day), QTime(hour, minute, second)); + newTime.setTimeZone(QTimeZone::systemTimeZone()); + s.setTimeManual(newTime.toTime_t()); + + Maemo::Timed::Interface timed; + timed.wall_clock_settings_async(s); +} + +void CTS::TimeCharacteristicPropertiesChanged(QString /* interfaceName */, + QMap changedProperties, QStringList /* invalidatedProperties */) +{ + if (changedProperties.contains("Value")) { + QVariant value = changedProperties["Value"]; + if (value.type() == QVariant::ByteArray) { + + QByteArray bytes = value.toByteArray(); + parseCurrentTime(bytes); + } + } +} + +bool CTS::isMatchingCharacteristic(QString uuid, QMap dbusObject) +{ + if (!dbusObject.contains(GATT_CHRC_IFACE)) { + return false; + } + QString charUuid = dbusObject.value(GATT_CHRC_IFACE).value("UUID").toString(); + return charUuid.toLower() == uuid.toLower(); +} diff --git a/src/cts.h b/src/cts.h new file mode 100644 index 0000000..65b99d4 --- /dev/null +++ b/src/cts.h @@ -0,0 +1,28 @@ +#ifndef CTS_H +#define CTS_H +#include +#include +#include +#include +#include +#include +#include + +#define CTS_CHARACTERISTIC_UUID "00002a2b-0000-1000-8000-00805f9b34fb" + +class CTS: public QObject +{ + Q_OBJECT + public: + CTS(); + void searchForTimeCharacteristics(); + void disconnect() {}; + private slots: + void TimeCharacteristicPropertiesChanged( QString interfaceName, + QMap changedProperties, QStringList invalidatedProperties); + private: + bool isMatchingCharacteristic(QString uuid, QMap dbusObject); + void parseCurrentTime(QByteArray& bytes); + +}; +#endif // CTS_H