From 0fa52dd2e6a15f584d0cdebe78b5e6288d413877 Mon Sep 17 00:00:00 2001 From: Mohammad Abdul Sahil <127765312+abdulsaheel@users.noreply.github.com> Date: Sat, 3 Oct 2026 05:30:14 +0530 Subject: [PATCH] garmin: echo the time request's reference id instead of 0 --- lib/src/garmin.dart | 24 +++++++++++++++++------- test/garmin_test.dart | 17 +++++++++++++++-- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/src/garmin.dart b/lib/src/garmin.dart index 8889489..eb83f87 100644 --- a/lib/src/garmin.dart +++ b/lib/src/garmin.dart @@ -360,19 +360,29 @@ GarminStatusAck? garminParseStatusAck(GarminGfdiFrame f) { /// from. const int kGarminEpochOffset = 631065600; -/// Build the full answer to CURRENT_TIME_REQUEST (5052): a RESPONSE (5000) -/// frame shaped as `ref_msg_type:u16(5052) | status:i8(0) | reference_id:u32(0) -/// | garmin_timestamp:u32 | utc_offset_sec:i32 | dst_end:i32(0) | -/// dst_start:i32(0)`. DST transitions are left at 0 — this pass states the -/// current UTC offset and nothing about a future change to it. -Uint8List garminBuildTimeResponse({ +/// Build the full answer to a CURRENT_TIME_REQUEST (5052) [request]: a +/// RESPONSE (5000) frame shaped as `ref_msg_type:u16(5052) | status:i8(0) | +/// reference_id:u32 | garmin_timestamp:u32 | utc_offset_sec:i32 | +/// dst_end:i32(0) | dst_start:i32(0)`. `reference_id` echoes the request's +/// own payload u32 so the watch can match the answer to what it asked. Null +/// when [request] is not a 5052 frame carrying that id. DST transitions are +/// left at 0 — this pass states the current UTC offset and nothing about a +/// future change to it. +Uint8List? garminBuildTimeResponse( + GarminGfdiFrame request, { required int nowUnixSeconds, required int utcOffsetSeconds, }) { + if (request.type != kGarminMsgCurrentTimeRequest || + request.payload.length < 4) { + return null; + } + final referenceId = + ByteData.sublistView(request.payload).getUint32(0, Endian.little); final b = ByteData(23) ..setUint16(0, kGarminMsgCurrentTimeRequest, Endian.little) ..setInt8(2, 0) - ..setUint32(3, 0, Endian.little) + ..setUint32(3, referenceId, Endian.little) ..setUint32(7, nowUnixSeconds - kGarminEpochOffset, Endian.little) ..setInt32(11, utcOffsetSeconds, Endian.little) ..setInt32(15, 0, Endian.little) diff --git a/test/garmin_test.dart b/test/garmin_test.dart index 05fad1c..171604b 100644 --- a/test/garmin_test.dart +++ b/test/garmin_test.dart @@ -92,16 +92,29 @@ void main() { test('time response carries the Garmin-epoch timestamp and UTC offset', () { final nowUnix = 1735689600; // 2025-01-01T00:00:00Z - final frame = garminBuildTimeResponse( - nowUnixSeconds: nowUnix, utcOffsetSeconds: 3600); + final request = garminParseGfdiFrame(garminBuildGfdiFrame( + kGarminMsgCurrentTimeRequest, [0x2a, 0x01, 0x00, 0x00]))!; + final frame = garminBuildTimeResponse(request, + nowUnixSeconds: nowUnix, utcOffsetSeconds: 3600)!; final parsed = garminParseGfdiFrame(frame)!; expect(parsed.type, kGarminMsgResponse); final view = ByteData.sublistView(parsed.payload); expect(view.getUint16(0, Endian.little), kGarminMsgCurrentTimeRequest); + expect(view.getUint32(3, Endian.little), 0x012a, + reason: 'reference_id must echo the request'); expect(view.getUint32(7, Endian.little), nowUnix - kGarminEpochOffset); expect(view.getInt32(11, Endian.little), 3600); }); + + test('time response abstains without a request id to echo', () { + final short = garminParseGfdiFrame( + garminBuildGfdiFrame(kGarminMsgCurrentTimeRequest, [1, 2]))!; + expect( + garminBuildTimeResponse(short, + nowUnixSeconds: 1735689600, utcOffsetSeconds: 0), + isNull); + }); }); group('MLR', () {