From d2af3468f085effb1d75ed937711f43da0bc2df6 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Wed, 6 Apr 2016 21:48:16 +0200 Subject: [PATCH] Add support for new datalog message added in pebble firmware 3.11 This adds support for storing deep sleep data. --- CHANGELOG.md | 3 + .../pebble/DatalogSessionHealthSleep.java | 93 +++++++++++++++++-- .../devices/pebble/PebbleProtocol.java | 2 +- app/src/main/res/xml/changelog_master.xml | 5 + 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c691eebf0..13335a92a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version (next) +* Pebble: support pebble health datalog messages of firmware 3.11 (this adds the support for deep sleep!) + ####Version 0.9.3 * Pebble: Fix Pebble Health activation (was not available in the App Manager) * Simplify connection state display (only connecting->connected) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 8528287fd..aaa67ec39 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -21,22 +21,33 @@ class DatalogSessionHealthSleep extends DatalogSession { public DatalogSessionHealthSleep(byte id, UUID uuid, int tag, byte item_type, short item_size) { super(id, uuid, tag, item_type, item_size); - taginfo = "(health - sleep)"; + taginfo = "(health - sleep " + tag + " )"; } @Override public boolean handleMessage(ByteBuffer datalogMessage, int length) { LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length)); + switch (this.tag) { + case 83: + return handleMessage83(datalogMessage, length); + case 84: + return handleMessage84(datalogMessage, length); + default: + return false; + } + } + private boolean handleMessage84(ByteBuffer datalogMessage, int length) { int initialPosition = datalogMessage.position(); int beginOfRecordPosition; short recordVersion; //probably + short recordType; //probably: 1=sleep, 2=deep sleep if (0 != (length % itemSize)) return false;//malformed message? int recordCount = length / itemSize; - SleepRecord[] sleepRecords = new SleepRecord[recordCount]; + SleepRecord84[] sleepRecords = new SleepRecord84[recordCount]; for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) { beginOfRecordPosition = initialPosition + recordIdx * itemSize; @@ -45,23 +56,73 @@ class DatalogSessionHealthSleep extends DatalogSession { if (recordVersion != 1) return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it - sleepRecords[recordIdx] = new SleepRecord(datalogMessage.getInt(), + datalogMessage.getShort();//throwaway, unknown + recordType = datalogMessage.getShort(); + + sleepRecords[recordIdx] = new SleepRecord84(recordType, datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt()); + } + + return store84(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again. + } + + private boolean store84(SleepRecord84[] sleepRecords) { + DBHandler dbHandler = null; + SampleProvider sampleProvider = new HealthSampleProvider(); + try { + dbHandler = GBApplication.acquireDB(); + int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider); + for (SleepRecord84 sleepRecord : sleepRecords) { + if (latestTimestamp < (sleepRecord.timestampStart + sleepRecord.durationSeconds)) + return false; + int activityType = sleepRecord.type == 2 ? sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP) : sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP); + + dbHandler.changeStoredSamplesType(sleepRecord.timestampStart, (sleepRecord.timestampStart + sleepRecord.durationSeconds), activityType, sampleProvider); + } + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } finally { + if (dbHandler != null) { + dbHandler.release(); + } + } + return true; + } + + private boolean handleMessage83(ByteBuffer datalogMessage, int length) { + int initialPosition = datalogMessage.position(); + int beginOfRecordPosition; + short recordVersion; //probably + + if (0 != (length % itemSize)) + return false;//malformed message? + + int recordCount = length / itemSize; + SleepRecord83[] sleepRecords = new SleepRecord83[recordCount]; + + for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) { + beginOfRecordPosition = initialPosition + recordIdx * itemSize; + datalogMessage.position(beginOfRecordPosition);//we may not consume all the bytes of a record + recordVersion = datalogMessage.getShort(); + if (recordVersion != 1) + return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it + + sleepRecords[recordIdx] = new SleepRecord83(datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt()); } - return store(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again. + return store83(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again. } - private boolean store(SleepRecord[] sleepRecords) { + private boolean store83(SleepRecord83[] sleepRecords) { DBHandler dbHandler = null; SampleProvider sampleProvider = new HealthSampleProvider(); - GB.toast("We don't know how to store deep sleep from the pebble yet.", Toast.LENGTH_LONG, GB.INFO); + GB.toast("Deep sleep is supported only from firmware 3.11 onwards.", Toast.LENGTH_LONG, GB.INFO); try { dbHandler = GBApplication.acquireDB(); int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider); - for (SleepRecord sleepRecord : sleepRecords) { + for (SleepRecord83 sleepRecord : sleepRecords) { if (latestTimestamp < sleepRecord.bedTimeEnd) return false; dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider); @@ -76,17 +137,31 @@ class DatalogSessionHealthSleep extends DatalogSession { return true; } - private class SleepRecord { + private class SleepRecord83 { int offsetUTC; //probably int bedTimeStart; int bedTimeEnd; int deepSleepSeconds; - public SleepRecord(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) { + public SleepRecord83(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) { this.offsetUTC = offsetUTC; this.bedTimeStart = bedTimeStart; this.bedTimeEnd = bedTimeEnd; this.deepSleepSeconds = deepSleepSeconds; } } + + private class SleepRecord84 { + int type; //1=sleep, 2=deep sleep + int offsetUTC; //probably + int timestampStart; + int durationSeconds; + + public SleepRecord84(int type, int offsetUTC, int timestampStart, int durationSeconds) { + this.type = type; + this.offsetUTC = offsetUTC; + this.timestampStart = timestampStart; + this.durationSeconds = durationSeconds; + } + } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index d79f3d9f0..5d261e6a0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -1881,7 +1881,7 @@ public class PebbleProtocol extends GBDeviceProtocol { if (!mDatalogSessions.containsKey(id)) { if (uuid.equals(UUID_ZERO) && log_tag == 81) { mDatalogSessions.put(id, new DatalogSessionHealthSteps(id, uuid, log_tag, item_type, item_size)); - } else if (uuid.equals(UUID_ZERO) && log_tag == 83) { + } else if (uuid.equals(UUID_ZERO) && (log_tag == 83 || log_tag == 84)) { mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size)); } else { mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 0c25965e3..8527cad61 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,10 @@ + + Pebble: support pebble health datalog messages of firmware 3.11 (this adds the + support for deep sleep!) + + Pebble: Fix Pebble Health activation (was not available in the App Manager) Simplify connection state display (only connecting->connected)