summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier S. Pedro <maemo@javispedro.com>2013-05-08 20:35:10 +0200
committerJavier S. Pedro <maemo@javispedro.com>2013-05-08 20:35:10 +0200
commitf5c26f02c047d2659d43a2d7e04e0542152e67c7 (patch)
treee3dcf1a7397fb0c404c0b2107cf2c3ad5d9df0d4
parent13042ef2cbf0f7e83146161d05a77cad57ea8ebf (diff)
downloadsowatch-f5c26f02c047d2659d43a2d7e04e0542152e67c7.tar.gz
sowatch-f5c26f02c047d2659d43a2d7e04e0542152e67c7.zip
connects and keeps connection to liveview
-rw-r--r--liveview/liveview.cpp194
-rw-r--r--liveview/liveview.h75
2 files changed, 226 insertions, 43 deletions
diff --git a/liveview/liveview.cpp b/liveview/liveview.cpp
index bd0cdbe..f4b7434 100644
--- a/liveview/liveview.cpp
+++ b/liveview/liveview.cpp
@@ -1,13 +1,19 @@
+#include <QtEndian>
+
#include "liveview.h"
using namespace sowatch;
QTM_USE_NAMESPACE
+#define PROTOCOL_DEBUG 1
+
LiveView::LiveView(ConfigKey* settings, QObject* parent) :
BluetoothWatch(QBluetoothAddress(settings->value("address").toString()), parent),
- _settings(settings->getSubkey(QString(), this))
+ _settings(settings->getSubkey(QString(), this)),
+ _sendTimer(new QTimer(this))
{
-
+ _sendTimer->setInterval(DelayBetweenMessages);
+ connect(_sendTimer, SIGNAL(timeout()), SLOT(handleSendTimerTick()));
}
LiveView::~LiveView()
@@ -35,11 +41,6 @@ QStringList LiveView::buttons() const
return QStringList();
}
-bool LiveView::isConnected() const
-{
- return false;
-}
-
bool LiveView::busy() const
{
return false; // TODO
@@ -47,15 +48,20 @@ bool LiveView::busy() const
void LiveView::setDateTime(const QDateTime& dateTime)
{
-
+ // It seems LiveView _requests_ the current date rather than expecting
+ // the phone to be sending it.
+ // Wonder what will happen during DST changes?
+ // Do nothing here.
}
+
void LiveView::queryDateTime()
{
-
+ // LiveView does not support this.
}
+
QDateTime LiveView::dateTime() const
{
- return QDateTime::currentDateTime(); // TODO
+ return QDateTime::currentDateTime();
}
void LiveView::queryBatteryLevel()
@@ -99,10 +105,176 @@ void LiveView::vibrate(int msecs)
void LiveView::setupBluetoothWatch()
{
-
+ connect(_socket, SIGNAL(readyRead()), SLOT(handleDataReceived()));
+ updateDisplayProperties();
}
void LiveView::desetupBluetoothWatch()
{
}
+
+void LiveView::send(const Message &msg)
+{
+ _sendingMsgs.enqueue(msg);
+ if (!_sendTimer->isActive()) {
+ _sendTimer->start();
+ }
+}
+
+void LiveView::updateDisplayProperties()
+{
+ static const char *software_version = "0.0.3";
+
+ send(Message(GetDisplayProperties,
+ QByteArray(software_version, strlen(software_version) + 1)));
+}
+
+void LiveView::updateSoftwareVersion()
+{
+ send(Message(GetSoftwareVersion, QByteArray(1, 0)));
+}
+
+void LiveView::enableLed()
+{
+ QByteArray data;
+ data.append(char(0xFF));
+ data.append(char(0xFF));
+ data.append(char(0x00));
+ data.append(char(0x64));
+ data.append(char(0x00));
+ data.append(char(0xFA));
+
+ send(Message(EnableLed, data));
+}
+
+void LiveView::handleMessage(const Message &msg)
+{
+ send(Message(Ack, QByteArray(1, msg.type)));
+ switch (msg.type) {
+ case GetDisplayPropertiesResponse:
+ handleDisplayProperties(msg);
+ break;
+ }
+}
+
+void LiveView::handleDisplayProperties(const Message &msg)
+{
+ updateSoftwareVersion();
+}
+
+void LiveView::handleDataReceived()
+{
+#pragma pack(push)
+#pragma pack(1)
+ static const int HEADER_SIZE = 6;
+ union header_t {
+ char c[HEADER_SIZE];
+ struct header_fields_t {
+ quint8 msg_type;
+ quint8 header_len;
+ quint32 data_len;
+ } h;
+ } header;
+#pragma pack(pop)
+
+ Q_ASSERT(sizeof(header) == HEADER_SIZE);
+
+ do {
+ qint64 dataRead;
+
+ qDebug() << "received" << _socket->bytesAvailable() << "bytes";
+
+ if (_receivingMsg.type == NoMessage) {
+ /* Still not received even the packet type */
+ /* Receive the full header. */
+ if (_socket->bytesAvailable() < HEADER_SIZE) {
+ /* Still not enough data available. */
+ return; /* Wait for more, if non blocking. */
+ }
+
+ dataRead = _socket->read(header.c, HEADER_SIZE);
+#if PROTOCOL_DEBUG
+ qDebug() << "received header" << QByteArray(header.c, HEADER_SIZE).toHex();
+#endif
+ if (dataRead < HEADER_SIZE) {
+ qWarning() << "Short read";
+ return;
+ }
+
+ _receivingMsg.type = static_cast<MessageType>(header.h.msg_type);
+ if (header.h.header_len != HEADER_SIZE - 2) {
+ qWarning() << "Unexpected header length:" << header.h.header_len;
+ }
+
+ unsigned long data_size = qFromBigEndian(header.h.data_len);
+ if (data_size > 1048576) {
+ // If input packet is > 1 MiB, consider a protocol error.
+ qWarning() << "Too large data size: " << data_size;
+ data_size = 0;
+ }
+ _receivingMsg.data.resize(data_size);
+
+ qDebug() << "got header (type=" << _receivingMsg.type <<
+ "size=" << data_size << ")";
+ }
+
+ /* We have the header; now, try to get the complete packet. */
+ if (_socket->bytesAvailable() < _receivingMsg.data.size()) {
+#if PROTOCOL_DEBUG
+ qDebug() << "Waiting for more data" << _socket->bytesAvailable() << "/" << _receivingMsg.data.size();
+#endif
+ return; /* Wait for more. */
+ }
+
+ dataRead = _socket->read(_receivingMsg.data.data(), _receivingMsg.data.size());
+ if (dataRead < _receivingMsg.data.size()) {
+ qWarning() << "Short read";
+ return;
+ }
+
+#if PROTOCOL_DEBUG
+ qDebug() << "received" << _receivingMsg.type << _receivingMsg.data.toHex();
+#endif
+ handleMessage(_receivingMsg);
+
+ // Prepare for the next packet
+ _receivingMsg.data.clear();
+ _receivingMsg.type = NoMessage;
+ } while (_socket->bytesAvailable() > 0);
+}
+
+void LiveView::handleSendTimerTick()
+{
+ static const int HEADER_SIZE = 6;
+ qDebug() << "Send tick";
+ // If there are packets to be sent...
+ if (!_sendingMsgs.empty()) {
+ // Send a message to the watch
+ Message msg = _sendingMsgs.dequeue();
+ const quint32 data_size = msg.data.size();
+ QByteArray packet;
+
+ Q_ASSERT(_connected && _socket);
+
+ packet.resize(HEADER_SIZE + data_size);
+ packet[0] = msg.type;
+ packet[1] = HEADER_SIZE - 2;
+ packet[2] = (data_size & 0xFF000000U) >> 24;
+ packet[3] = (data_size & 0x00FF0000U) >> 16;
+ packet[4] = (data_size & 0x0000FF00U) >> 8;
+ packet[5] = (data_size & 0x000000FFU);
+ packet.replace(HEADER_SIZE, data_size, msg.data);
+
+#if PROTOCOL_DEBUG
+ qDebug() << "sending" << packet.toHex();
+#endif
+
+ _socket->write(packet);
+ }
+ // If we just finished sending all packets...
+ if (_sendingMsgs.empty()) {
+ // Stop the send timer to save battery
+ _sendTimer->stop();
+ }
+}
diff --git a/liveview/liveview.h b/liveview/liveview.h
index 03c5499..69016b5 100644
--- a/liveview/liveview.h
+++ b/liveview/liveview.h
@@ -1,34 +1,3 @@
-/**
-
-This file is partially based on the work from Andrew de Quincey:
-
- Copyright (c) 2011, Andrew de Quincey
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the organization nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-*/
-
#ifndef LIVEVIEW_H
#define LIVEVIEW_H
@@ -51,7 +20,7 @@ public:
QString model() const;
QStringList buttons() const;
- bool isConnected() const;
+
bool busy() const;
void setDateTime(const QDateTime& dateTime);
@@ -71,11 +40,53 @@ public:
void vibrate(int msecs);
protected:
+ static const int DelayBetweenMessages = 5;
+
+ enum MessageType {
+ NoMessage = 0,
+ GetDisplayProperties = 1,
+ GetDisplayPropertiesResponse = 2,
+ StandBy = 7,
+ StandByResponse = 8,
+ EnableLed = 40,
+ EnableLedResponse = 41,
+ Ack = 44,
+ GetSoftwareVersion = 68,
+ GetSoftwareVersionResponse = 69
+ };
+
+ struct Message {
+ MessageType type;
+ QByteArray data;
+ Message(MessageType ntype = NoMessage, QByteArray ndata = QByteArray()) :
+ type(ntype), data(ndata)
+ { }
+ };
+
void setupBluetoothWatch();
void desetupBluetoothWatch();
+ void send(const Message& msg);
+
+ void updateDisplayProperties();
+ void updateSoftwareVersion();
+ void enableLed();
+
+ void handleMessage(const Message& msg);
+ void handleDisplayProperties(const Message& msg);
+
+private slots:
+ void handleDataReceived();
+ void handleSendTimerTick();
+
private:
ConfigKey *_settings;
+
+ /** Message outbox queue. */
+ QQueue<Message> _sendingMsgs;
+ QTimer* _sendTimer;
+ /** Incomplete message that is being received. */
+ Message _receivingMsg;
};
}