diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | README.md | 27 | ||||
| -rw-r--r-- | app/pebbledinterface.cpp | 9 | ||||
| -rw-r--r-- | daemon/daemon.pro | 8 | ||||
| -rw-r--r-- | daemon/dbusconnector.cpp | 52 | ||||
| -rw-r--r-- | daemon/dbusconnector.h | 9 | ||||
| -rw-r--r-- | daemon/manager.cpp | 82 | ||||
| -rw-r--r-- | daemon/manager.h | 16 | ||||
| -rw-r--r-- | daemon/voicecallhandler.cpp | 85 | ||||
| -rw-r--r-- | daemon/voicecallhandler.h | 1 | ||||
| -rw-r--r-- | daemon/watchcommands.cpp | 37 | ||||
| -rw-r--r-- | daemon/watchcommands.h | 30 | ||||
| -rw-r--r-- | daemon/watchconnector.cpp | 128 | ||||
| -rw-r--r-- | daemon/watchconnector.h | 68 | ||||
| -rw-r--r-- | rpm/pebble.spec | 88 | ||||
| -rw-r--r-- | rpm/pebble.yaml | 2 |
16 files changed, 482 insertions, 161 deletions
@@ -12,5 +12,4 @@ *.la *.a -/rpm/*.spec /*.pro.user @@ -9,6 +9,16 @@ http://getpebble.com +Features +-------- +* Voice Calls notification and control +* SMS, IM Messages forwarding +* MPRIS compatible media player support +* daemon management app +* "org.pebbled" DBus interface + + + Building -------- @@ -27,24 +37,11 @@ You need to pair your Pebble in Settings -> System settings -> Bluetooth Application will try to connect to any paired device which name starts with "Pebble" -Development ------------ - -For now this application is still under developement. By default you will be shown -development UI options, which you can utilize to test the connection. - -Incoming voice calls are shown in Pebble. You can test this by calling to your device. -Pressing hangup button in Pebble should end the call. - -Incoming messages are also sent to Pebble. Send SMS or instant message to your device to test it. - - -!!! NO WARRANTY OF ANY KIND !!! - - References ---------- * http://pebbledev.org/wiki/Protocol * https://github.com/Hexxeh/libpebble/ +* http://developer.getpebble.com/2/guides/javascript-guide.html +* http://specifications.freedesktop.org/mpris-spec/latest/ diff --git a/app/pebbledinterface.cpp b/app/pebbledinterface.cpp index 1bd9b50..50ece6e 100644 --- a/app/pebbledinterface.cpp +++ b/app/pebbledinterface.cpp @@ -38,12 +38,9 @@ PebbledInterface::PebbledInterface(QObject *parent) : getUnitProperties(); QDBusConnection::sessionBus().connect( - "org.freedesktop.systemd1", - unitPath.path(), - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - this, - SLOT(onPropertiesChanged(QString,QMap<QString,QVariant>,QStringList))); + "org.freedesktop.systemd1", unitPath.path(), + "org.freedesktop.DBus.Properties", "PropertiesChanged", + this, SLOT(onPropertiesChanged(QString,QMap<QString,QVariant>,QStringList))); } else { qWarning() << unit.error().message(); } diff --git a/daemon/daemon.pro b/daemon/daemon.pro index e52e8e8..cfcdae1 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -17,19 +17,21 @@ SOURCES += \ manager.cpp \ voicecallmanager.cpp \ voicecallhandler.cpp \ + notificationmanager.cpp \ watchconnector.cpp \ dbusconnector.cpp \ dbusadaptor.cpp \ - notificationmanager.cpp + watchcommands.cpp HEADERS += \ manager.h \ voicecallmanager.h \ voicecallhandler.h \ + notificationmanager.h \ watchconnector.h \ dbusconnector.h \ dbusadaptor.h \ - notificationmanager.h + watchcommands.h OTHER_FILES += \ org.pebbled.xml \ @@ -58,6 +60,6 @@ confile.path = /usr/share/pebble lib.files += $$OUT_PWD/../ext/Log4Qt/*.s* lib.path = /usr/share/pebble/lib -# so QtCreator could find commhistory headers... :-( +# unnecesary includes, just so QtCreator could find headers... :-( INCLUDEPATH += $$[QT_HOST_PREFIX]/include/commhistory-qt5 INCLUDEPATH += $$[QT_HOST_PREFIX]/include/mlite5 diff --git a/daemon/dbusconnector.cpp b/daemon/dbusconnector.cpp index 2ad753d..1ee4c67 100644 --- a/daemon/dbusconnector.cpp +++ b/daemon/dbusconnector.cpp @@ -6,6 +6,7 @@ #include <QDBusReply> #include <QDBusArgument> #include <QDBusObjectPath> +#include <QDBusConnectionInterface> //dbus-send --system --dest=org.bluez --print-reply / org.bluez.Manager.ListAdapters //dbus-send --system --dest=org.bluez --print-reply $path org.bluez.Adapter.GetProperties @@ -14,16 +15,28 @@ DBusConnector::DBusConnector(QObject *parent) : QObject(parent) -{} +{ + QDBusConnection bus = QDBusConnection::sessionBus(); + QDBusConnectionInterface *interface = bus.interface(); + + QDBusReply<QStringList> serviceNames = interface->registeredServiceNames(); + if (serviceNames.isValid()) { + dbusServices = serviceNames.value(); + } + else { + logger()->error() << serviceNames.error().message(); + } + connect(interface, SIGNAL(serviceRegistered(QString&)), SLOT(onServiceRegistered(QString&))); + connect(interface, SIGNAL(serviceUnregistered(QString&)), SIGNAL(onServiceUnregistered(QString&))); +} bool DBusConnector::findPebble() { QDBusConnection system = QDBusConnection::systemBus(); - QDBusReply<QList<QDBusObjectPath>> ListAdaptersReply = system.call(QDBusMessage::createMethodCall("org.bluez", - "/", - "org.bluez.Manager", - "ListAdapters")); + QDBusReply<QList<QDBusObjectPath>> ListAdaptersReply = system.call( + QDBusMessage::createMethodCall("org.bluez", "/", "org.bluez.Manager", + "ListAdapters")); if (not ListAdaptersReply.isValid()) { logger()->error() << ListAdaptersReply.error().message(); return false; @@ -36,10 +49,9 @@ bool DBusConnector::findPebble() return false; } - QDBusReply<QVariantMap> AdapterPropertiesReply = system.call(QDBusMessage::createMethodCall("org.bluez", - adapters[0].path(), - "org.bluez.Adapter", - "GetProperties")); + QDBusReply<QVariantMap> AdapterPropertiesReply = system.call( + QDBusMessage::createMethodCall("org.bluez", adapters[0].path(), "org.bluez.Adapter", + "GetProperties")); if (not AdapterPropertiesReply.isValid()) { logger()->error() << AdapterPropertiesReply.error().message(); return false; @@ -48,14 +60,10 @@ bool DBusConnector::findPebble() QList<QDBusObjectPath> devices; AdapterPropertiesReply.value()["Devices"].value<QDBusArgument>() >> devices; - QString name; - QString address; - foreach (QDBusObjectPath path, devices) { - QDBusReply<QVariantMap> DevicePropertiesReply = system.call(QDBusMessage::createMethodCall("org.bluez", - path.path(), - "org.bluez.Device", - "GetProperties")); + QDBusReply<QVariantMap> DevicePropertiesReply = system.call( + QDBusMessage::createMethodCall("org.bluez", path.path(), "org.bluez.Device", + "GetProperties")); if (not DevicePropertiesReply.isValid()) { logger()->error() << DevicePropertiesReply.error().message(); continue; @@ -75,3 +83,15 @@ bool DBusConnector::findPebble() return false; } + +void DBusConnector::onServiceRegistered(QString &name) +{ + logger()->debug() << "DBus service online:" << name; + if (!dbusServices.contains(name)) dbusServices.append(name); +} + +void DBusConnector::onServiceUnregistered(QString &name) +{ + logger()->debug() << "DBus service offline:" << name; + if (dbusServices.contains(name)) dbusServices.removeAll(name); +} diff --git a/daemon/dbusconnector.h b/daemon/dbusconnector.h index 98f6e58..4498867 100644 --- a/daemon/dbusconnector.h +++ b/daemon/dbusconnector.h @@ -2,6 +2,7 @@ #define DBUSCONNECTOR_H #include <QObject> +#include <QStringList> #include <QVariantMap> #include "Logger" @@ -11,20 +12,28 @@ class DBusConnector : public QObject LOG4QT_DECLARE_QCLASS_LOGGER Q_PROPERTY(QVariantMap pebble READ pebble NOTIFY pebbleChanged) + Q_PROPERTY(QStringList services READ services NOTIFY servicesChanged) QVariantMap pebbleProps; + QStringList dbusServices; public: explicit DBusConnector(QObject *parent = 0); QVariantMap pebble() { return pebbleProps; } + QStringList services() { return dbusServices; } signals: void pebbleChanged(); + void servicesChanged(); public slots: bool findPebble(); +protected slots: + void onServiceRegistered(QString&); + void onServiceUnregistered(QString&); + }; #endif // DBUSCONNECTOR_H diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 0a5d722..ec0d21b 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -6,7 +6,7 @@ #include <QtContacts/QContactPhoneNumber> Manager::Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallManager *voice, NotificationManager *notifications) : - QObject(0), watch(watch), dbus(dbus), voice(voice), notifications(notifications), + QObject(0), watch(watch), dbus(dbus), voice(voice), notifications(notifications), commands(new WatchCommands(watch, this)), notification(MNotification::DeviceEvent) { // We don't need to handle presence changes, so report them separately and ignore them @@ -17,6 +17,8 @@ Manager::Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallMan numberFilter.setDetailType(QContactDetail::TypePhoneNumber, QContactPhoneNumber::FieldNumber); numberFilter.setMatchFlags(QContactFilter::MatchPhoneNumber); + connect(watch, SIGNAL(connectedChanged()), SLOT(onConnectedChanged())); + connect(voice, SIGNAL(activeVoiceCallChanged()), SLOT(onActiveVoiceCallChanged())); connect(voice, SIGNAL(error(const QString &)), SLOT(onVoiceError(const QString &))); @@ -24,8 +26,8 @@ Manager::Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallMan connect(notifications, SIGNAL(emailNotify(const QString &,const QString &,const QString &)), SLOT(onEmailNotify(const QString &,const QString &,const QString &))); connect(notifications, SIGNAL(smsNotify(const QString &,const QString &)), SLOT(onSmsNotify(const QString &,const QString &))); - connect(watch, SIGNAL(hangup()), SLOT(hangupAll())); - connect(watch, SIGNAL(connectedChanged()), SLOT(onConnectedChanged())); + connect(watch, SIGNAL(messageDecoded(uint,uint,QByteArray)), commands, SLOT(processMessage(uint,uint,QByteArray))); + connect(commands, SIGNAL(hangup()), SLOT(hangupAll())); // Set BT icon for notification notification.setImage("icon-system-bluetooth-device"); @@ -38,11 +40,18 @@ Manager::Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallMan PebbledProxy *proxy = new PebbledProxy(this); PebbledAdaptor *adaptor = new PebbledAdaptor(proxy); - QDBusConnection connection = QDBusConnection::sessionBus(); - connection.registerObject("/", proxy); - connection.registerService("org.pebbled"); + QDBusConnection session = QDBusConnection::sessionBus(); + session.registerObject("/", proxy); + session.registerService("org.pebbled"); connect(dbus, SIGNAL(pebbleChanged()), adaptor, SIGNAL(pebbleChanged())); connect(watch, SIGNAL(connectedChanged()), adaptor, SIGNAL(connectedChanged())); + + // Music Control interface + session.connect("", "/org/mpris/MediaPlayer2", + "org.freedesktop.DBus.Properties", "PropertiesChanged", + this, SLOT(onMprisPropertiesChanged(QString,QMap<QString,QVariant>,QStringList))); + + connect(this, SIGNAL(mprisMetadataChanged(QVariantMap)), commands, SLOT(onMprisMetadataChanged(QVariantMap))); } void Manager::onPebbleChanged() @@ -69,6 +78,22 @@ void Manager::onConnectedChanged() if (!notification.publish()) { logger()->debug() << "Failed publishing notification"; } + + if (watch->isConnected()) { + QString mpris = this->mpris(); + if (not mpris.isEmpty()) { + QDBusReply<QDBusVariant> Metadata = QDBusConnection::sessionBus().call( + QDBusMessage::createMethodCall(mpris, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get") + << "org.mpris.MediaPlayer2.Player" << "Metadata"); + if (Metadata.isValid()) { + setMprisMetadata(Metadata.value().variant().value<QDBusArgument>()); + } + else { + logger()->error() << Metadata.error().message(); + setMprisMetadata(QVariantMap()); + } + } + } } void Manager::onActiveVoiceCallChanged() @@ -173,3 +198,48 @@ void Manager::hangupAll() handler->hangup(); } } + +void Manager::onMprisPropertiesChanged(QString interface, QMap<QString,QVariant> changed, QStringList invalidated) +{ + qDebug() << interface << changed << invalidated; + + if (changed.contains("Metadata")) { + setMprisMetadata(changed.value("Metadata").value<QDBusArgument>()); + } + + if (changed.contains("PlaybackStatus")) { + QString PlaybackStatus = changed.value("PlaybackStatus").toString(); + if (PlaybackStatus == "Stopped") { + setMprisMetadata(QVariantMap()); + } + } + + lastSeenMpris = message().service(); +} + +QString Manager::mpris() +{ + const QStringList &services = dbus->services(); + if (not lastSeenMpris.isEmpty() && services.contains(lastSeenMpris)) + return lastSeenMpris; + + foreach (QString service,services) + if (service.startsWith("org.mpris.MediaPlayer2.")) + return service; + + return QString(); +} + +void Manager::setMprisMetadata(QDBusArgument metadata) +{ + if (metadata.currentType() == QDBusArgument::MapType) { + metadata >> mprisMetadata; + emit mprisMetadataChanged(mprisMetadata); + } +} + +void Manager::setMprisMetadata(QVariantMap metadata) +{ + mprisMetadata = metadata; + emit mprisMetadataChanged(mprisMetadata); +} diff --git a/daemon/manager.h b/daemon/manager.h index 45ba50a..ea24f9e 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -5,9 +5,11 @@ #include "dbusconnector.h" #include "voicecallmanager.h" #include "notificationmanager.h" +#include "watchcommands.h" #include <QObject> #include <QBluetoothLocalDevice> +#include <QDBusContext> #include <QtContacts/QContactManager> #include <QtContacts/QContactDetailFilter> #include <MNotification> @@ -15,7 +17,9 @@ using namespace QtContacts; -class Manager : public QObject +class Manager : + public QObject, + protected QDBusContext { Q_OBJECT LOG4QT_DECLARE_QCLASS_LOGGER @@ -29,17 +33,24 @@ class Manager : public QObject VoiceCallManager *voice; NotificationManager *notifications; + WatchCommands *commands; + MNotification notification; QContactManager *contacts; QContactDetailFilter numberFilter; + QString lastSeenMpris; + public: explicit Manager(watch::WatchConnector *watch, DBusConnector *dbus, VoiceCallManager *voice, NotificationManager *notifications); Q_INVOKABLE QString findPersonByNumber(QString number); + Q_INVOKABLE QString mpris(); + QVariantMap mprisMetadata; signals: + void mprisMetadataChanged(QVariantMap); public slots: void hangupAll(); @@ -53,6 +64,9 @@ protected slots: void onNotifyError(const QString &message); void onSmsNotify(const QString &sender, const QString &data); void onEmailNotify(const QString &sender, const QString &data,const QString &subject); + void onMprisPropertiesChanged(QString,QMap<QString,QVariant>,QStringList); + void setMprisMetadata(QDBusArgument metadata); + void setMprisMetadata(QVariantMap metadata); }; class PebbledProxy : public QObject diff --git a/daemon/voicecallhandler.cpp b/daemon/voicecallhandler.cpp index 428785f..55b7c84 100644 --- a/daemon/voicecallhandler.cpp +++ b/daemon/voicecallhandler.cpp @@ -1,7 +1,6 @@ #include "voicecallhandler.h" #include <QDebug> -#include <QTimer> #include <QDBusInterface> #include <QDBusPendingReply> #include <QDBusReply> @@ -19,7 +18,7 @@ class VoiceCallHandlerPrivate public: VoiceCallHandlerPrivate(VoiceCallHandler *q, const QString &pHandlerId) - : q_ptr(q), handlerId(pHandlerId), interface(NULL), connected(false) + : q_ptr(q), handlerId(pHandlerId), interface(NULL) , duration(0), status(0), emergency(false), multiparty(false), forwarded(false) { /* ... */ } @@ -29,7 +28,6 @@ public: QDBusInterface *interface; - bool connected; int duration; int status; QString statusText; @@ -153,29 +151,8 @@ method return sender=:1.13 -> dest=:1.150 reply_serial=2 " */ - if (not d->connected) - { - QTimer::singleShot(2000, this, SLOT(initialize())); - if(notifyError) emit this->error("Failed to connect to VCM D-Bus service."); - } - else if (d->interface->isValid()) { - QDBusInterface props(d->interface->service(), d->interface->path(), - "org.freedesktop.DBus.Properties", d->interface->connection()); - - QDBusReply<QVariantMap> reply = props.call("GetAll", d->interface->interface()); - if (reply.isValid()) { - QVariantMap props = reply.value(); - QString str; QDebug(&str) << props; - logger()->debug() << str; - d->providerId = props["providerId"].toString(); - d->duration = props["duration"].toInt(); - d->status = props["status"].toInt(); - d->statusText = props["statusText"].toString(); - d->lineId = props["lineId"].toString(); - d->startedAt = QDateTime::fromMSecsSinceEpoch(props["startedAt"].toULongLong()); - d->multiparty = props["isMultiparty"].toBool(); - d->emergency = props["isEmergency"].toBool(); - d->forwarded = props["isForwarded"].toBool(); + if (d->interface->isValid()) { + if (getProperties()) { emit durationChanged(); emit statusChanged(); emit lineIdChanged(); @@ -183,20 +160,51 @@ method return sender=:1.13 -> dest=:1.150 reply_serial=2 emit multipartyChanged(); emit emergencyChanged(); emit forwardedChanged(); + + connect(d->interface, SIGNAL(error(QString)), SIGNAL(error(QString))); + connect(d->interface, SIGNAL(statusChanged()), SLOT(onStatusChanged())); + connect(d->interface, SIGNAL(lineIdChanged()), SLOT(onLineIdChanged())); + connect(d->interface, SIGNAL(durationChanged()), SLOT(onDurationChanged())); + connect(d->interface, SIGNAL(startedAtChanged()), SLOT(onStartedAtChanged())); + connect(d->interface, SIGNAL(emergencyChanged()), SLOT(onEmergencyChanged())); + connect(d->interface, SIGNAL(multipartyChanged()), SLOT(onMultipartyChanged())); + connect(d->interface, SIGNAL(forwardedChanged()), SLOT(onForwardedChanged())); } else { - logger()->error() << "Failed to get VoiceCall properties from VCM D-Bus service."; if (notifyError) emit this->error("Failed to get VoiceCall properties from VCM D-Bus service."); } + } + else { + logger()->error() << d->interface->lastError().name() << d->interface->lastError().message(); + } +} - connect(d->interface, SIGNAL(error(QString)), SIGNAL(error(QString))); - connect(d->interface, SIGNAL(statusChanged()), SLOT(onStatusChanged())); - connect(d->interface, SIGNAL(lineIdChanged()), SLOT(onLineIdChanged())); - connect(d->interface, SIGNAL(durationChanged()), SLOT(onDurationChanged())); - connect(d->interface, SIGNAL(startedAtChanged()), SLOT(onStartedAtChanged())); - connect(d->interface, SIGNAL(emergencyChanged()), SLOT(onEmergencyChanged())); - connect(d->interface, SIGNAL(multipartyChanged()), SLOT(onMultipartyChanged())); - connect(d->interface, SIGNAL(forwardedChanged()), SLOT(onForwardedChanged())); +bool VoiceCallHandler::getProperties() +{ + Q_D(VoiceCallHandler); + + QDBusInterface props(d->interface->service(), d->interface->path(), + "org.freedesktop.DBus.Properties", d->interface->connection()); + + QDBusReply<QVariantMap> reply = props.call("GetAll", d->interface->interface()); + if (reply.isValid()) { + QVariantMap props = reply.value(); + QString str; QDebug(&str) << props; + logger()->debug() << str; + d->providerId = props["providerId"].toString(); + d->duration = props["duration"].toInt(); + d->status = props["status"].toInt(); + d->statusText = props["statusText"].toString(); + d->lineId = props["lineId"].toString(); + d->startedAt = QDateTime::fromMSecsSinceEpoch(props["startedAt"].toULongLong()); + d->multiparty = props["isMultiparty"].toBool(); + d->emergency = props["isEmergency"].toBool(); + d->forwarded = props["isForwarded"].toBool(); + return true; + } + else { + logger()->error() << "Failed to get VoiceCall properties from VCM D-Bus service."; + return false; } } @@ -209,9 +217,10 @@ void VoiceCallHandler::onDurationChanged() void VoiceCallHandler::onStatusChanged() { - Q_D(VoiceCallHandler); - d->status = d->interface->property("status").toInt(); - d->statusText = d->interface->property("statusText").toString(); + // a) initialize() might returned crap with STATUS_NULL (no lineId) + // b) we need to fetch two properties "status" and "statusText" + // so, we might aswell get them all + getProperties(); emit statusChanged(); } diff --git a/daemon/voicecallhandler.h b/daemon/voicecallhandler.h index f96a97a..bd0d71d 100644 --- a/daemon/voicecallhandler.h +++ b/daemon/voicecallhandler.h @@ -71,6 +71,7 @@ public Q_SLOTS: protected Q_SLOTS: void initialize(bool notifyError = false); + bool getProperties(); void onPendingCallFinished(QDBusPendingCallWatcher *watcher); void onDurationChanged(); diff --git a/daemon/watchcommands.cpp b/daemon/watchcommands.cpp new file mode 100644 index 0000000..7fc84fc --- /dev/null +++ b/daemon/watchcommands.cpp @@ -0,0 +1,37 @@ +#include "watchcommands.h" + +using namespace watch; + +WatchCommands::WatchCommands(WatchConnector *watch, QObject *parent) : + QObject(parent), watch(watch) +{} + +void WatchCommands::processMessage(uint endpoint, uint datalen, QByteArray data) +{ + logger()->debug() << __FUNCTION__ << endpoint << "/" << data.length(); + switch (endpoint) { + case WatchConnector::watchPHONE_VERSION: + watch->sendPhoneVersion(); + break; + case WatchConnector::watchPHONE_CONTROL: + if (data.length() >= 5 && data.at(4) == WatchConnector::callHANGUP) { + emit hangup(); + } + break; + case WatchConnector::watchMUSIC_CONTROL: + logger()->debug() << "MUSIC_CONTROL" << data.toHex(); + break; + + default: + logger()->info() << __FUNCTION__ << "endpoint" << endpoint << "not supported yet"; + } +} + +void WatchCommands::onMprisMetadataChanged(QVariantMap metadata) +{ + QString track = metadata.value("xesam:title").toString(); + QString album = metadata.value("xesam:album").toString(); + QString artist = metadata.value("xesam:artist").toString(); + logger()->debug() << __FUNCTION__ << track << album << artist; + watch->sendMusicNowPlaying(track, album, artist); +} diff --git a/daemon/watchcommands.h b/daemon/watchcommands.h new file mode 100644 index 0000000..6ce70bb --- /dev/null +++ b/daemon/watchcommands.h @@ -0,0 +1,30 @@ +#ifndef WATCHCOMMANDS_H +#define WATCHCOMMANDS_H + +#include "watchconnector.h" +#include "Logger" + +#include <QObject> + +class WatchCommands : public QObject +{ + Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER + + watch::WatchConnector *watch; + +public: + explicit WatchCommands(watch::WatchConnector *watch, QObject *parent = 0); + +signals: + void hangup(); + +public slots: + void processMessage(uint endpoint, uint datalen, QByteArray data); + +protected slots: + void onMprisMetadataChanged(QVariantMap metadata); + +}; + +#endif // WATCHCOMMANDS_H diff --git a/daemon/watchconnector.cpp b/daemon/watchconnector.cpp index 48fd52f..0ce0607 100644 --- a/daemon/watchconnector.cpp +++ b/daemon/watchconnector.cpp @@ -1,6 +1,7 @@ #include "watchconnector.h" #include <QTimer> #include <QDateTime> +#include <QMetaEnum> using namespace watch; @@ -76,51 +77,11 @@ void WatchConnector::handleWatch(const QString &name, const QString &address) socket->connectToService(QBluetoothAddress(address), 1); } -QString WatchConnector::decodeEndpoint(unsigned int val) +QString WatchConnector::decodeEndpoint(uint val) { - //FIXME: Create a map of these values - switch(val) { - case watchTIME: - return "TIME"; - case watchVERSION: - return "VERSION"; - case watchPHONE_VERSION: - return "PHONE_VERSION"; - case watchSYSTEM_MESSAGE: - return "SYSTEM_MESSAGE"; - case watchMUSIC_CONTROL: - return "MUSIC_CONTROL"; - case watchPHONE_CONTROL: - return "PHONE_CONTROL"; - case watchAPPLICATION_MESSAGE: - return "APP_MSG"; - case watchLAUNCHER: - return "LAUNCHER"; - case watchLOGS: - return "LOGS"; - case watchPING: - return "PING"; - case watchLOG_DUMP: - return "DUMP"; - case watchRESET: - return "RESET"; - case watchAPP: - return "APP"; - case watchAPP_LOGS: - return "APP_LOGS"; - case watchNOTIFICATION: - return "NOTIFICATION"; - case watchRESOURCE: - return "RESOURCE"; - case watchAPP_MANAGER: - return "APP_MANAG"; - case watchSCREENSHOT: - return "SCREENSHOT"; - case watchPUTBYTES: - return "PUTBYTES"; - default: - return "Unknown: "+ QString::number(val); - } + QMetaEnum Endpoints = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("Endpoints")); + const char *endpoint = Endpoints.valueToKey(val); + return endpoint ? QString(endpoint) : QString("watchUNKNOWN_%1").arg(val); } void WatchConnector::decodeMsg(QByteArray data) @@ -136,13 +97,8 @@ void WatchConnector::decodeMsg(QByteArray data) logger()->debug() << "Length:" << datalen << " Endpoint:" << decodeEndpoint(endpoint); logger()->debug() << "Data:" << data.mid(index).toHex(); - if (endpoint == watchPHONE_CONTROL) { - if (data.length() >= 5) { - if (data.at(4) == callHANGUP) { - emit hangup(); - } - } - } + + emit messageDecoded(endpoint, datalen, data); } void WatchConnector::onReadSocket() @@ -199,7 +155,7 @@ void WatchConnector::sendData(const QByteArray &data) socket->write(data); } -void WatchConnector::sendMessage(unsigned int endpoint, QByteArray data) +void WatchConnector::sendMessage(uint endpoint, QByteArray data) { logger()->debug() << "Sending message"; QByteArray msg; @@ -229,7 +185,7 @@ void WatchConnector::buildData(QByteArray &res, QStringList data) } } -QByteArray WatchConnector::buildMessageData(unsigned int lead, QStringList data) +QByteArray WatchConnector::buildMessageData(uint lead, QStringList data) { QByteArray res; res.append(lead & 0xFF); @@ -238,7 +194,43 @@ QByteArray WatchConnector::buildMessageData(unsigned int lead, QStringList data) return res; } -void WatchConnector::ping(unsigned int val) +void WatchConnector::sendPhoneVersion() +{ + unsigned int sessionCap = sessionCapGAMMA_RAY; + unsigned int remoteCap = remoteCapTELEPHONY | remoteCapSMS | osANDROID; + QByteArray res; + + //Prefix + res.append(0x01); + res.append(0xff); + res.append(0xff); + res.append(0xff); + res.append(0xff); + + //Session Capabilities + res.append((char)((sessionCap >> 24) & 0xff)); + res.append((char)((sessionCap >> 16) & 0xff)); + res.append((char)((sessionCap >> 8) & 0xff)); + res.append((char)(sessionCap & 0xff)); + + //Remote Capabilities + res.append((char)((remoteCap >> 24) & 0xff)); + res.append((char)((remoteCap >> 16) & 0xff)); + res.append((char)((remoteCap >> 8) & 0xff)); + res.append((char)(remoteCap & 0xff)); + + //Version Magic + res.append((char)0x02); + + //Append Version + res.append((char)0x02); //Major + res.append((char)0x00); //Minor + res.append((char)0x00); //Bugfix + + sendMessage(watchPHONE_VERSION, res); +} + +void WatchConnector::ping(uint val) { QByteArray res; res.append((char)0); @@ -256,13 +248,13 @@ QString WatchConnector::timeStamp() return QString::number(QDateTime::currentMSecsSinceEpoch()); } -void WatchConnector::sendNotification(unsigned int lead, QString sender, QString data, QString subject) +void WatchConnector::sendNotification(uint lead, QString sender, QString data, QString subject) { QStringList tmp; tmp.append(sender); tmp.append(data); tmp.append(timeStamp()); - if (lead == 0) tmp.append(subject); + if (lead == leadEMAIL) tmp.append(subject); QByteArray res = buildMessageData(lead, tmp); @@ -271,15 +263,27 @@ void WatchConnector::sendNotification(unsigned int lead, QString sender, QString void WatchConnector::sendSMSNotification(QString sender, QString data) { - sendNotification(1, sender, data, ""); + sendNotification(leadSMS, sender, data, ""); } void WatchConnector::sendEmailNotification(QString sender, QString data, QString subject) { - sendNotification(0, sender, data, subject); + sendNotification(leadEMAIL, sender, data, subject); +} + +void WatchConnector::sendMusicNowPlaying(QString track, QString album, QString artist) +{ + QStringList tmp; + tmp.append(track.left(30)); + tmp.append(album.left(30)); + tmp.append(artist.left(30)); + + QByteArray res = buildMessageData(leadNOW_PLAYING_DATA, tmp); + + sendMessage(watchMUSIC_CONTROL, res); } -void WatchConnector::phoneControl(char act, unsigned int cookie, QStringList datas) +void WatchConnector::phoneControl(char act, uint cookie, QStringList datas) { QByteArray head; head.append((char)act); @@ -292,7 +296,7 @@ void WatchConnector::phoneControl(char act, unsigned int cookie, QStringList dat sendMessage(watchPHONE_CONTROL, head); } -void WatchConnector::ring(QString number, QString name, bool incoming, unsigned int cookie) +void WatchConnector::ring(QString number, QString name, bool incoming, uint cookie) { QStringList tmp; tmp.append(number); @@ -306,12 +310,12 @@ void WatchConnector::ring(QString number, QString name, bool incoming, unsigned phoneControl(act, cookie, tmp); } -void WatchConnector::startPhoneCall(unsigned int cookie) +void WatchConnector::startPhoneCall(uint cookie) { phoneControl(callSTART, cookie, QStringList()); } -void WatchConnector::endPhoneCall(unsigned int cookie) +void WatchConnector::endPhoneCall(uint cookie) { phoneControl(callEND, cookie, QStringList()); } diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index 36d0936..dafaa83 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -49,11 +49,13 @@ class WatchConnector : public QObject Q_OBJECT LOG4QT_DECLARE_QCLASS_LOGGER + Q_ENUMS(Endpoints) + Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString connected READ isConnected NOTIFY connectedChanged) public: - enum { + enum Endpoints { watchTIME = 11, watchVERSION = 16, watchPHONE_VERSION = 17, @@ -71,6 +73,7 @@ public: watchNOTIFICATION = 3000, watchRESOURCE = 4000, watchAPP_MANAGER = 6000, + watchDATA_LOGGING = 6778, watchSCREENSHOT = 8000, watchPUTBYTES = 48879 }; @@ -85,35 +88,76 @@ public: callSTART = 8, callEND = 9 }; + enum { + musicPLAY_PAUSE = 1, + musicPAUSE = 2, + musicPLAY = 3, + musicNEXT = 4, + musicPREVIOUS = 5, + musicVOLUME_UP = 6, + musicVOLUME_DOWN = 7, + musicGET_NOW_PLAYING = 8, + musicSEND_NOW_PLAYING = 9 + }; + enum { + leadEMAIL = 0, + leadSMS = 1, + leadNOW_PLAYING_DATA = 16 + }; + enum { + sessionCapGAMMA_RAY = 0x80000000 + }; + enum { + remoteCapTELEPHONY = 16, + remoteCapSMS = 32, + remoteCapGPS = 64, + remoteCapBTLE = 128, + remoteCapCAMERA_REAR = 256, + remoteCapACCEL = 512, + remoteCapGYRO = 1024, + remoteCapCOMPASS = 2048 + }; + enum { + osUNKNOWN = 0, + osIOS = 1, + osANDROID = 2, + osOSX = 3, + osLINUX = 4, + osWINDOWS = 5 + }; + + explicit WatchConnector(QObject *parent = 0); virtual ~WatchConnector(); bool isConnected() const { return is_connected; } - QString name() const { if (socket != nullptr) return socket->peerName(); return ""; } + QString name() const { return socket != nullptr ? socket->peerName() : ""; } QString timeStamp(); - QString decodeEndpoint(unsigned int val); + QString decodeEndpoint(uint val); signals: void messageReceived(QString peer, QString msg); + void messageDecoded(uint endpoint, uint datalen, QByteArray data); void nameChanged(); void connectedChanged(); - void hangup(); public slots: void sendData(const QByteArray &data); - void sendMessage(unsigned int endpoint, QByteArray data); - void ping(unsigned int val); - void sendNotification(unsigned int lead, QString sender, QString data, QString subject); + void sendMessage(uint endpoint, QByteArray data); + void ping(uint val); + void sendNotification(uint lead, QString sender, QString data, QString subject); void sendSMSNotification(QString sender, QString data); void sendEmailNotification(QString sender, QString data, QString subject); + void sendMusicNowPlaying(QString track, QString album, QString artist); + void sendPhoneVersion(); void buildData(QByteArray &res, QStringList data); - QByteArray buildMessageData(unsigned int lead, QStringList data); + QByteArray buildMessageData(uint lead, QStringList data); - void phoneControl(char act, unsigned int cookie, QStringList datas); - void ring(QString number, QString name, bool incoming=true, unsigned int cookie=0); - void startPhoneCall(unsigned int cookie=0); - void endPhoneCall(unsigned int cookie=0); + void phoneControl(char act, uint cookie, QStringList datas); + void ring(QString number, QString name, bool incoming=true, uint cookie=0); + void startPhoneCall(uint cookie=0); + void endPhoneCall(uint cookie=0); void deviceConnect(const QString &name, const QString &address); void disconnect(); diff --git a/rpm/pebble.spec b/rpm/pebble.spec new file mode 100644 index 0000000..2ccceb0 --- /dev/null +++ b/rpm/pebble.spec @@ -0,0 +1,88 @@ +# +# Do NOT Edit the Auto-generated Part! +# Generated by: spectacle version 0.27 +# + +Name: pebble + +# >> macros +# << macros + +%{!?qtc_qmake:%define qtc_qmake %qmake} +%{!?qtc_qmake5:%define qtc_qmake5 %qmake5} +%{!?qtc_make:%define qtc_make make} +%{?qtc_builddir:%define _builddir %qtc_builddir} +Summary: Support for Pebble watch in SailfishOS +Version: 0.5 +Release: 1 +Group: Qt/Qt +License: BSD +URL: http://getpebble.com/ +Source0: %{name}-%{version}.tar.xz +Source100: pebble.yaml +Requires: sailfishsilica-qt5 >= 0.10.9 +Requires: systemd-user-session-targets +BuildRequires: pkgconfig(Qt5DBus) +BuildRequires: pkgconfig(Qt5Bluetooth) +BuildRequires: pkgconfig(Qt5Contacts) +BuildRequires: pkgconfig(Qt5Quick) +BuildRequires: pkgconfig(Qt5Qml) +BuildRequires: pkgconfig(Qt5Core) +BuildRequires: pkgconfig(commhistory-qt5) +BuildRequires: pkgconfig(mlite5) +BuildRequires: pkgconfig(sailfishapp) >= 0.0.10 +BuildRequires: desktop-file-utils + +%description +Include support for Pebble watch to receive event from SailfishOS device. Communicates via Bluetooth, supporting the Pebble protocol. + + +%prep +%setup -q -n %{name}-%{version} + +# >> setup +# << setup + +%build +# >> build pre +# << build pre + +%qtc_qmake5 + +%qtc_make %{?_smp_mflags} + +# >> build post +# << build post + +%install +rm -rf %{buildroot} +# >> install pre +# << install pre +%qmake5_install + +# >> install post +mkdir -p %{buildroot}%{_libdir}/systemd/user/user-session.target.wants +ln -s ../pebbled.service %{buildroot}%{_libdir}/systemd/user/user-session.target.wants/ +# << install post + +desktop-file-install --delete-original \ + --dir %{buildroot}%{_datadir}/applications \ + %{buildroot}%{_datadir}/applications/*.desktop + +%files +%defattr(-,root,root,-) +%{_bindir} +%{_datadir}/%{name}/qml +%{_datadir}/applications/%{name}.desktop +%{_datadir}/icons/hicolor/86x86/apps/%{name}.png +%{_libdir}/systemd/user/%{name}d.service +%{_libdir}/systemd/user/user-session.target.wants/%{name}d.service +%{_datadir}/%{name}/log4qt.conf +%{_datadir}/%{name}/lib +# >> files +# << files + +%post +# >> post +systemctl --user daemon-reload +# << post diff --git a/rpm/pebble.yaml b/rpm/pebble.yaml index 30b9c84..16f3f2c 100644 --- a/rpm/pebble.yaml +++ b/rpm/pebble.yaml @@ -1,6 +1,6 @@ Name: pebble Summary: Support for Pebble watch in SailfishOS -Version: 0.4 +Version: 0.5 Release: 1 Group: Qt/Qt URL: http://getpebble.com/ |
