Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions daemon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ set(SOURCES
./src/services/dk08wechatservice.cpp
./src/services/hrmservice.cpp
./src/services/immediatealertservice.cpp
./src/services/immediatealertserverservice.cpp
./src/services/infinitimemotionservice.cpp
./src/services/infinitimenavservice.cpp
#./src/services/infinitimeresourceservice.cpp
Expand Down Expand Up @@ -224,6 +225,7 @@ set(HEADERS
./src/services/dk08wechatservice.h
./src/services/hrmservice.h
./src/services/immediatealertservice.h
./src/services/immediatealertserverservice.h
./src/services/infinitimemotionservice.h
./src/services/infinitimenavservice.h
#./src/services/infinitimeresourceservice.h
Expand Down
50 changes: 50 additions & 0 deletions daemon/src/devices/pinetimejfdevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "dfuoperation.h"
#include "infinitimenavservice.h"
#include "immediatealertservice.h"
#include "immediatealertserverservice.h"
#include "hrmservice.h"
#include "infinitimemotionservice.h"
#include "infinitimeweatherservice.h"
Expand All @@ -16,6 +17,7 @@
#include "batteryservice.h"
#include "amazfishconfig.h"
#include "realtimeactivitysample.h"
#include "qble/qblelocalapplication.h"
#include <QtXml/QtXml>

namespace {
Expand Down Expand Up @@ -192,6 +194,9 @@ void PinetimeJFDevice::initialise()
setConnectionState("connected");
parseServices();

// Start the GATT server (IAS) so InfiniTime can find this phone
setupGattServer();

AlertNotificationService *alert = qobject_cast<AlertNotificationService*>(service(AlertNotificationService::UUID_SERVICE_ALERT_NOTIFICATION));
if (alert) {
alert->enableNotification(AlertNotificationService::UUID_CHARACTERISTIC_ALERT_NOTIFICATION_EVENT);
Expand Down Expand Up @@ -573,3 +578,48 @@ void PinetimeJFDevice::immediateAlert(int level)
ias->sendAlert((ImmediateAlertService::Levels)level);
}
}

// ---------------------------------------------------------------------------
// GATT server — serve IAS so that InfiniTime FindMyPhone can alert this device
// ---------------------------------------------------------------------------

void PinetimeJFDevice::setupGattServer()
{
if (m_iasApp) {
qDebug() << Q_FUNC_INFO << "GATT server already set up";
return;
}

qDebug() << Q_FUNC_INFO << "Setting up IAS GATT server for PineTime";

// Application root — implements org.freedesktop.DBus.ObjectManager on system bus
m_iasApp = new QBLELocalApplication(QStringLiteral("/uk/co/piggz/amazfish/gatt"), this);

// IAS service (index 0), UUID 0x1802
m_iasService = new ImmediateAlertServerService(QDBusConnection::systemBus(), 0, m_iasApp);
m_iasApp->addService(m_iasService);

connect(m_iasService, &ImmediateAlertServerService::alertLevelChanged, this, &PinetimeJFDevice::onImmediateAlertFromWatch);

// Register with the BlueZ adapter from config
QString adapterPath = AmazfishConfig::instance()->localAdapter();
m_iasApp->registerWithAdapter(adapterPath);
}

void PinetimeJFDevice::onImmediateAlertFromWatch(int level)
{
qDebug() << Q_FUNC_INFO << "FindMyPhone alert level from watch:" << level;

switch (static_cast<ImmediateAlertServerService::Levels>(level)) {
case ImmediateAlertServerService::Levels::HighAlert:
case ImmediateAlertServerService::Levels::MildAlert:
emit deviceEvent(AbstractDevice::EVENT_FIND_PHONE);
break;
case ImmediateAlertServerService::Levels::NoAlert:
emit deviceEvent(AbstractDevice::EVENT_CANCEL_FIND_PHONE);
break;
default:
qWarning() << Q_FUNC_INFO << "Unknown alert level:" << level;
break;
}
}
9 changes: 9 additions & 0 deletions daemon/src/devices/pinetimejfdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include "abstractdevice.h"
#include "realtimeactivitysample.h"

class QBLELocalApplication;
class ImmediateAlertServerService;

class PinetimeJFDevice : public AbstractDevice
{
public:
Expand Down Expand Up @@ -53,6 +56,12 @@ class PinetimeJFDevice : public AbstractDevice
RealtimeActivitySample realtimeActivitySample;

Q_SLOT void sampledActivity(QDateTime dt, int kind, int intensity, int steps, int heartrate);

// GATT server — serves IAS so InfiniTime FindMyPhone can alert this device
QBLELocalApplication *m_iasApp = nullptr;
ImmediateAlertServerService *m_iasService = nullptr;
void setupGattServer();
Q_SLOT void onImmediateAlertFromWatch(int level);
};

#endif // PINETIMEJFDEVICE_H
48 changes: 48 additions & 0 deletions daemon/src/services/immediatealertserverservice.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "immediatealertserverservice.h"
#include "qble/qblelocalcharacteristic.h"

#include <QDebug>

const char *ImmediateAlertServerService::UUID_SERVICE_IMMEDIATE_ALERT = "00001802-0000-1000-8000-00805f9b34fb";
const char *ImmediateAlertServerService::UUID_CHARACTERISTIC_IMMEDIATE_ALERT_LEVEL = "00002a06-0000-1000-8000-00805f9b34fb";

// ---------------------------------------------------------------------------
// Alert Level characteristic — write-without-response, no read
// InfiniTime uses ble_gattc_write_no_rsp() so the flag must be
// "write-without-response" (not "write").
// ---------------------------------------------------------------------------
class AlertLevelCharacteristic : public QBLELocalCharacteristic
{
public:
AlertLevelCharacteristic(QDBusConnection bus, unsigned int index, QBLELocalService *service)
: QBLELocalCharacteristic(bus, index, QString::fromLatin1(ImmediateAlertServerService::UUID_CHARACTERISTIC_IMMEDIATE_ALERT_LEVEL),
{ QStringLiteral("write-without-response") },
service, service)
{
}
// WriteValue is inherited from QBLELocalCharacteristic which already
// emits valueWritten(value) — that is all we need.
};

// ---------------------------------------------------------------------------

ImmediateAlertServerService::ImmediateAlertServerService(QDBusConnection bus, unsigned int serviceIndex, QObject *parent)
: QBLELocalService(bus, serviceIndex, QString::fromLatin1(UUID_SERVICE_IMMEDIATE_ALERT), QStringLiteral("/uk/co/piggz/amazfish/gatt"), parent)
{
auto *alertLevel = new AlertLevelCharacteristic(bus, 0, this);
addCharacteristic(alertLevel);

connect(alertLevel, &QBLELocalCharacteristic::valueWritten, this, &ImmediateAlertServerService::onValueWritten);
}

void ImmediateAlertServerService::onValueWritten(const QByteArray &value)
{
if (value.isEmpty()) {
qWarning() << Q_FUNC_INFO << "Received empty write on Alert Level";
return;
}

int level = static_cast<uint8_t>(value.at(0));
qDebug() << Q_FUNC_INFO << "Alert level received from watch:" << level;
emit alertLevelChanged(level);
}
34 changes: 34 additions & 0 deletions daemon/src/services/immediatealertserverservice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#ifndef IMMEDIATEALERTSERVERSERVICE_H
#define IMMEDIATEALERTSERVERSERVICE_H

#include "qble/qblelocalservice.h"

// https://www.bluetooth.com/specifications/specs/immediate-alert-service-1-0/
//
// This class serves the IAS on the desktop/phone side so that an InfiniTime watch
// acting as GATT client (FindMyPhone screen) can write to the Alert Level
// characteristic and trigger an alert on the companion device.
//
// Contrast with ImmediateAlertService (client side) which writes *to* the watch.

class ImmediateAlertServerService : public QBLELocalService
{
Q_OBJECT
public:
explicit ImmediateAlertServerService(QDBusConnection bus, unsigned int serviceIndex, QObject *parent = nullptr);

static const char *UUID_SERVICE_IMMEDIATE_ALERT;
static const char *UUID_CHARACTERISTIC_IMMEDIATE_ALERT_LEVEL;

enum class Levels : uint8_t { NoAlert = 0, MildAlert = 1, HighAlert = 2 };
Q_ENUM(Levels)

Q_SIGNALS:
// Emitted when the watch writes a new alert level
void alertLevelChanged(int level);

private Q_SLOTS:
void onValueWritten(const QByteArray &value);
};

#endif // IMMEDIATEALERTSERVERSERVICE_H
1 change: 1 addition & 0 deletions daemon/src/services/immediatealertservice.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ImmediateAlertService : public QBLEService
static const char* UUID_SERVICE_IMMEDIATE_ALERT;
static const char* UUID_CHARACTERISTIC_IMMEDIATE_ALERT_LEVEL;
enum class Levels : uint8_t { NoAlert = 0, MildAlert = 1, HighAlert = 2 };
Q_ENUM(Levels);

Levels alertLevel();
void sendAlert(ImmediateAlertService::Levels level);
Expand Down
Loading