#include #include #include #include "metawatch.h" #include "metawatchbletransport.h" MetaWatch::MetaWatch(const QString &btAddr, QObject *parent) : QObject(parent) { GatoPeripheral *peripheral = new GatoPeripheral(GatoAddress(btAddr), this); _transport = new MetaWatchBLETransport(peripheral, this); connect(_transport, &MetaWatchTransport::connected, this, &MetaWatch::connected); connect(_transport, &MetaWatchTransport::disconnected, this, &MetaWatch::disconnected); connect(_transport, &MetaWatchTransport::messageReceived, this, &MetaWatch::handleTransportMessage); } QList MetaWatch::availableClocks() { QList clocks; for (int i = 0; i < 6; i++) { clocks << QUrl("clock://" + QString::number(i)); } return clocks; } 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::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::connectDevice() { _transport->connectDevice(); } void MetaWatch::disconnectDevice() { _transport->disconnectDevice(); } int MetaWatch::clockUrlToClockId(const QUrl &url) { if (url.scheme() != "clock") return -1; QString host = url.host(); if (!host.startsWith("clock")) return -1; return host.mid(5).toInt(); } 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); } }