#include #include #include #include #include #include "metawatch.h" #include "metawatchbttransport.h" #include "metawatchbletransport.h" MetaWatch::MetaWatch(const QString &btAddr, TransportType transport, QObject *parent) : QObject(parent) { switch (transport) { case TransportBluetooth: _transport = new MetaWatchBTTransport(QBluetoothAddress(btAddr), this); break; case TransportBluetoothLowEnergy: _transport = new MetaWatchBLETransport(GatoAddress(btAddr), this); break; } connect(_transport, &MetaWatchTransport::connected, this, &MetaWatch::connected); connect(_transport, &MetaWatchTransport::disconnected, this, &MetaWatch::disconnected); connect(_transport, &MetaWatchTransport::messageReceived, this, &MetaWatch::handleTransportMessage); } MetaWatch::~MetaWatch() { } bool MetaWatch::isDeviceConnected() const { return _transport && _transport->isDeviceConnected(); } void MetaWatch::setVibrateMode(bool enable, int on_duration, int off_duration, int cycles) { QByteArray data; QDataStream s(&data, QIODevice::WriteOnly); s.setByteOrder(QDataStream::LittleEndian); s << quint8(enable ? 1 : 0); s << quint16(on_duration) << quint16(off_duration) << quint8(cycles); _transport->sendMessage(MessageSetVibrateMode, 0, data); } void MetaWatch::setDateTime(const QDateTime &dt) { const QDate &date = dt.date(); const QTime &time = dt.time(); QByteArray data; QDataStream s(&data, QIODevice::WriteOnly); s.setByteOrder(QDataStream::BigEndian); s << quint16(date.year()) << quint8(date.month()) << quint8(date.day()); s << quint8(date.dayOfWeek() % 7); s << quint8(time.hour()) << quint8(time.minute()) << quint8(time.second()); _transport->sendMessage(MessageSetRealTimeClock, 0, data); } void MetaWatch::configure(WatchProperties props) { _transport->sendMessage(MessageWatchPropertyOperation, props | WatchPropertyOperationWrite, QByteArray()); } void MetaWatch::updateDeviceType() { // Also disables HFP/MAP connection. _transport->sendMessage(MessageGetDeviceType, 0xC0, QByteArray()); } void MetaWatch::updateBatteryStatus() { _transport->sendMessage(MessageGetBatteryStatus, 0, QByteArray()); } void MetaWatch::updateLcdDisplay() { _transport->sendMessage(MessageUpdateLcdDisplay, 1 << 7, QByteArray()); } void MetaWatch::updateLcdDisplayPage(int page) { _transport->sendMessage(MessageUpdateLcdDisplay, 1 << 7 | 1 << 5 | ((page << 2) & 0xC), QByteArray()); } void MetaWatch::updateLcdDisplayMode(WatchMode mode) { Q_ASSERT(mode != WatchModeIdle); // TODO _transport->sendMessage(MessageUpdateLcdDisplay, (mode & 0x3), QByteArray()); } void MetaWatch::updateWidgetList(const QList &widgets) { int num_widgets = 0; // Count valid widgets for (int w = 0; w < widgets.size(); w++) { const WidgetInfo &info = widgets[w]; if (info.valid()) num_widgets++; } const int max_widgets_in_one_msg = 7; const int num_messages = (num_widgets + (max_widgets_in_one_msg - 1)) / max_widgets_in_one_msg; int num_message = 0; QByteArray msg; Q_ASSERT(num_messages < 4); msg.reserve(max_widgets_in_one_msg * 2); qDebug() << "Sending widget list:" << num_widgets << "widgets," << num_messages << "messages"; if (num_widgets == 0) { msg.append(static_cast(0xFF)); msg.append(static_cast(0x00)); // Clear all widgets _transport->sendMessage(MessageSetWidgetList, 0x4, msg); return; } for (int i = 0; i < widgets.size(); i++) { const WidgetInfo &info = widgets[i]; if (!info.valid()) continue; // Skip disabled/empty widget quint8 id = i; quint8 options = ((info.page() << 4) & 0x30) | ((info.size() << 2) & 0xC) | ((info.position() << 2) & 0x3); int clockFace = info.builtinClockfaceId(); if (clockFace >= 0) { id |= (clockFace << 4) & 0xF0; options |= 0x80; } if (info.invert()) { options |= 0x40; } msg.append(id); msg.append(options); if (msg.size() >= max_widgets_in_one_msg * 2) { Q_ASSERT(num_message < num_messages); _transport->sendMessage(MessageSetWidgetList, ((num_messages << 2) & 0xC) | (num_message & 0x3), msg); msg.clear(); msg.reserve(max_widgets_in_one_msg * 2); num_message++; } } if (msg.size() > 0) { Q_ASSERT(num_message < num_messages); _transport->sendMessage(MessageSetWidgetList, ((num_messages << 2) & 0xC) | (num_message & 0x3), msg); } } void MetaWatch::clearModeImage(WatchMode mode, bool black) { _transport->sendMessage(MessageLoadTemplate, mode & 0x3, QByteArray(1, black ? 1 : 0)); } void MetaWatch::sendModeImage(WatchMode mode, const QImage &image) { const int bytesPerLine = image.bytesPerLine(); Q_ASSERT(image.width() == 96 && image.height() == 96); Q_ASSERT(bytesPerLine == 12); QByteArray data(1 + bytesPerLine, '\0'); for (int i = 0; i < image.height(); i++) { const uchar *scanline = image.scanLine(i); bool empty = true; for (int j = 0; j < bytesPerLine; j++) { empty &= scanline[j] == 0; } if (empty) { continue; } data[0] = i; memcpy(data.data() + 1, scanline, bytesPerLine); _transport->sendMessage(MessageWriteLcdBuffer, 0x10 | (mode & 0x3), data); } } void MetaWatch::connectDevice() { _transport->connectDevice(); } void MetaWatch::disconnectDevice() { _transport->disconnectDevice(); } void MetaWatch::handleTransportMessage(quint8 type, quint8 options, const QByteArray &payload) { switch (type) { case MessageGetDeviceTypeResponse: emit deviceType(static_cast(options & 0xF)); break; case MessageModeChangeIndication: qDebug() << "Got mode change indication"; if (payload.size() < 1) { qWarning() << "Invalid mode change indicator size"; } switch (payload.at(0)) { case 1: emit modeChange(WatchMode(options & 0xF), (options >> 4) & 0xF); break; default: qWarning() << "Unknown mode change indicator: " << payload.toHex(); break; } break; case MessageWatchPropertyOperationResponse: qDebug() << "Got watch property operation response"; break; case MessageReadBatteryStatusResponse: if (payload.size() < 6) { qWarning() << "Invalid battery status response size"; } emit batteryStatus(payload[1], payload[2]); break; case MessageConnectionChange: case MessageIntervalChanged: // No idea what to do with these. break; default: qWarning() << "Unknown message type received:" << QString::number(type, 16); } }