diff --git a/infinitime.go b/infinitime.go index 10e9f25..d1520f6 100644 --- a/infinitime.go +++ b/infinitime.go @@ -33,6 +33,7 @@ const ( MotionValChar = "00030002-78fc-48fe-8e23-433b3a1942d0" FirmwareVerChar = "00002a26-0000-1000-8000-00805f9b34fb" CurrentTimeChar = "00002a2b-0000-1000-8000-00805f9b34fb" + LocalTimeChar = "00002a0f-0000-1000-8000-00805f9b34fb" BatteryLvlChar = "00002a19-0000-1000-8000-00805f9b34fb" HeartRateChar = "00002a37-0000-1000-8000-00805f9b34fb" FSTransferChar = "adaf0200-4669-6c65-5472-616e73666572" @@ -47,6 +48,7 @@ var charNames = map[string]string{ MotionValChar: "Motion Values", FirmwareVerChar: "Firmware Version", CurrentTimeChar: "Current Time", + LocalTimeChar: "Local Time", BatteryLvlChar: "Battery Level", HeartRateChar: "Heart Rate", FSTransferChar: "Filesystem Transfer", @@ -66,6 +68,7 @@ type Device struct { motionValChar *gatt.GattCharacteristic1 fwVersionChar *gatt.GattCharacteristic1 currentTimeChar *gatt.GattCharacteristic1 + localTimeChar *gatt.GattCharacteristic1 battLevelChar *gatt.GattCharacteristic1 heartRateChar *gatt.GattCharacteristic1 fsVersionChar *gatt.GattCharacteristic1 @@ -421,6 +424,8 @@ func (i *Device) resolveChars() error { i.fwVersionChar = char case CurrentTimeChar: i.currentTimeChar = char + case LocalTimeChar: + i.localTimeChar = char case BatteryLvlChar: i.battLevelChar = char case HeartRateChar: @@ -708,7 +713,9 @@ func (i *Device) WatchMotion(ctx context.Context) (<-chan MotionValues, error) { return out, nil } -// SetTime sets the watch's time using the Current Time Service +// SetTime sets the watch's +// * time using the Current Time Service's current time characteristic +// * timezone information using the CTS's local time characteristic func (i *Device) SetTime(t time.Time) error { if err := i.checkStatus(i.currentTimeChar, CurrentTimeChar); err != nil { return err @@ -723,7 +730,38 @@ func (i *Device) SetTime(t time.Time) error { binary.Write(buf, binary.LittleEndian, uint8(t.Weekday())) binary.Write(buf, binary.LittleEndian, uint8((t.Nanosecond()/1000)/1e6*256)) binary.Write(buf, binary.LittleEndian, uint8(0b0001)) - return i.currentTimeChar.WriteValue(buf.Bytes(), nil) + if err := i.currentTimeChar.WriteValue(buf.Bytes(), nil); err != nil { + return err + } + + if err := i.checkStatus(i.localTimeChar, LocalTimeChar); err != nil { + // If the characteristic is unavailable, + // fail silently, as many people may be on + // older InfiniTime versions. A warning + // may be added later. + if _, ok := err.(ErrCharNotAvail); ok { + return nil + } else { + return err + } + } + _, offset := t.Zone() + dst := 0 + + // Local time expects two values: the timezone offset and the dst offset, both + // expressed in quarters of an hour. + // Timezone offset is to be constant over DST, with dst offset holding the offset != 0 + // when DST is in effect. + // As there is no standard way in go to get the actual dst offset, we assume it to be 1h + // when DST is in effect + if t.IsDST() { + dst = 3600 + offset -= 3600 + } + bufTz := &bytes.Buffer{} + binary.Write(bufTz, binary.LittleEndian, uint8(offset / 3600 * 4)) + binary.Write(bufTz, binary.LittleEndian, uint8(dst / 3600 * 4)) + return i.localTimeChar.WriteValue(bufTz.Bytes(), nil) } // Notify sends a notification to InfiniTime via