summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--README.md27
-rw-r--r--app/pebbledinterface.cpp9
-rw-r--r--daemon/daemon.pro8
-rw-r--r--daemon/dbusconnector.cpp52
-rw-r--r--daemon/dbusconnector.h9
-rw-r--r--daemon/manager.cpp82
-rw-r--r--daemon/manager.h16
-rw-r--r--daemon/voicecallhandler.cpp85
-rw-r--r--daemon/voicecallhandler.h1
-rw-r--r--daemon/watchcommands.cpp37
-rw-r--r--daemon/watchcommands.h30
-rw-r--r--daemon/watchconnector.cpp128
-rw-r--r--daemon/watchconnector.h68
-rw-r--r--rpm/pebble.spec88
-rw-r--r--rpm/pebble.yaml2
16 files changed, 482 insertions, 161 deletions
diff --git a/.gitignore b/.gitignore
index fdabbee..897f22a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,5 +12,4 @@
*.la
*.a
-/rpm/*.spec
/*.pro.user
diff --git a/README.md b/README.md
index 2174bd9..bfd2f55 100644
--- a/README.md
+++ b/README.md
@@ -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/