From 509732723437ccddfb351a07493c96b92315a987 Mon Sep 17 00:00:00 2001 From: uli Date: Sat, 28 May 2022 23:56:56 +0200 Subject: [PATCH] add local time service to PineTime, fixed standard ble format --- .../service/btle/BLETypeConversions.java | 35 +++++++++++++++---- .../service/btle/GattCharacteristic.java | 2 +- .../service/devices/huami/HuamiSupport.java | 21 ++++++----- .../devices/pinetime/PineTimeJFSupport.java | 8 ++--- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java index 7ac0c10cc..4083662a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java @@ -40,9 +40,8 @@ public class BLETypeConversions { * @return * @see GattCharacteristic#UUID_CHARACTERISTIC_CURRENT_TIME */ - public static byte[] calendarToRawBytes(Calendar timestamp) { - // MiBand2: - // year,year,month,dayofmonth,hour,minute,second,dayofweek,0,0,tz + public static byte[] calendarToCurrentTime(Calendar timestamp) { + // year,year,month,dayofmonth,hour,minute,second,dayofweek,fractions256,reason byte[] year = fromUint16(timestamp.get(Calendar.YEAR)); return new byte[] { @@ -54,9 +53,33 @@ public class BLETypeConversions { fromUint8(timestamp.get(Calendar.MINUTE)), fromUint8(timestamp.get(Calendar.SECOND)), dayOfWeekToRawBytes(timestamp), - 0, // fractions256 (not set) - // 0 (DST offset?) Mi2 - // k (tz) Mi2 + fromUint8((int) (timestamp.get(Calendar.MILLISECOND) / 1000. * 256)), + 0, // reason (not set) + }; + } + + /** + * Converts a timestamp to the byte sequence to be sent to the local time characteristic + * + * Values are expressed in quarters of hours + * Following the BLE specification, timezone is constant over dst changes + * + * @param timestamp + * @return + * @see GattCharacteristic#UUID_CHARACTERISTIC_LOCAL_TIME + */ + public static byte[] calendarToLocalTime(Calendar timestamp) { + TimeZone timeZone = timestamp.getTimeZone(); + int offsetMillisTimezone = timeZone.getRawOffset(); + int utcOffsetInQuarterHours = (offsetMillisTimezone / (1000 * 60 * 15)); + + int offsetMillisIncludingDST = timestamp.getTimeZone().getOffset(timestamp.getTimeInMillis()); + int dstOffsetMillis = offsetMillisIncludingDST - offsetMillisTimezone; + int dstOffsetInQuarterHours = (dstOffsetMillis / (1000 * 60 * 15)); + + return new byte[] { + fromUint8(utcOffsetInQuarterHours), + fromUint8(dstOffsetInQuarterHours), }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java index 3727597fc..1857b4812 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java @@ -47,7 +47,7 @@ public class GattCharacteristic { public static final UUID UUID_CHARACTERISTIC_EXACT_TIME_256 = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A0C"))); public static final UUID UUID_CHARACTERISTIC_DST_OFFSET = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A0D"))); public static final UUID UUID_CHARACTERISTIC_TIME_ZONE = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A0E"))); - public static final UUID UUID_CHARACTERISTIC_LOCAL_TIME_INFORMATION = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A0F"))); + public static final UUID UUID_CHARACTERISTIC_LOCAL_TIME = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A0F"))); public static final UUID UUID_CHARACTERISTIC_TIME_WITH_DST = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A11"))); public static final UUID UUID_CHARACTERISTIC_TIME_ACCURACY = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A12"))); public static final UUID UUID_CHARACTERISTIC_TIME_SOURCE = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A13"))); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java index c4cdffc1e..046968ca6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java @@ -404,15 +404,20 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements return weatherSpec.windSpeedAsBeaufort() + ""; // cast to string } - public byte[] getTimeBytes(final Calendar calendar, final TimeUnit precision) { - final byte[] bytes = BLETypeConversions.shortCalendarToRawBytes(calendar); - - if (precision != TimeUnit.MINUTES && precision != TimeUnit.SECONDS) { - throw new IllegalArgumentException("Unsupported precision, only MINUTES and SECONDS are supported"); + public byte[] getTimeBytes(Calendar calendar, TimeUnit precision) { + if (precision == TimeUnit.MINUTES) { + final byte[] bytes = BLETypeConversions.shortCalendarToRawBytes(calendar); + //add nonstandard extension + final byte[] tail = new byte[] { 0, BLETypeConversions.mapTimeZone(calendar, BLETypeConversions.TZ_FLAG_INCLUDE_DST_IN_TZ) }; + return BLETypeConversions.join(bytes, tail); + } else if (precision == TimeUnit.SECONDS) { + final byte[] bytes = BLETypeConversions.calendarToCurrentTime(calendar); + //add nonstandard extension, only one byte needed, as format is different from above + final byte[] tail = new byte[] { BLETypeConversions.mapTimeZone(calendar, BLETypeConversions.TZ_FLAG_INCLUDE_DST_IN_TZ) }; + return BLETypeConversions.join(bytes, tail); + } else { + throw new IllegalArgumentException("Unsupported precision, only MINUTES and SECONDS are supported till now"); } - final byte seconds = precision == TimeUnit.SECONDS ? fromUint8(calendar.get(Calendar.SECOND)) : 0; - final byte tz = BLETypeConversions.mapTimeZone(calendar, BLETypeConversions.TZ_FLAG_INCLUDE_DST_IN_TZ); - return BLETypeConversions.join(bytes, new byte[]{seconds, tz}); } public Calendar fromTimeBytes(byte[] bytes) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java index 58473fb37..7d9fb83d2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java @@ -316,12 +316,12 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL public void onSetTime() { // Since this is a standard we should generalize this in Gadgetbridge (properly) GregorianCalendar now = BLETypeConversions.createCalendar(); - byte[] bytes = BLETypeConversions.calendarToRawBytes(now); - byte[] tail = new byte[]{0, BLETypeConversions.mapTimeZone(now, BLETypeConversions.TZ_FLAG_INCLUDE_DST_IN_TZ)}; - byte[] all = BLETypeConversions.join(bytes, tail); + byte[] bytesCurrentTime = BLETypeConversions.calendarToCurrentTime(now); + byte[] bytesLocalTime = BLETypeConversions.calendarToLocalTime(now); TransactionBuilder builder = new TransactionBuilder("set time"); - builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME), all); + builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME), bytesCurrentTime); + builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_LOCAL_TIME), bytesLocalTime); builder.queue(getQueue()); }