Countdown timer on WatchFace out of sync with clock

Hi,

I have discovered some inconsistencies with keeping a countdown timer in sync with the clock on a watchface.

What I do is to display the clock time at the top of the watchface, and below that, the remaining days, hours and minutes to a specific date in the future (hard coded Moment at this stage)

So I get the now Moment, and then subtract it from the future event Moment to get a difference.
From this I calculate the remaining days, hours and minutes and update the label on the view.

But, I have found that testing this in the simulator works as expected. The clock and countdown timer's values match.
E.g. if the clock is 09:58

And the future event is at 11:00

The count down timer displays

0 days
1 hour
2 minutes

But when I run it on the actual watch itself, the count down timer always has the minutes out of sync and would display
0 days
1 hour
3 minutes

I have also found that when I set the watch time manually in order to test on the watch, it still seem to get the wrong clock time (not the manually entered time, but the GPS time).

I have read another post about the onUpdate and the seconds being forced to 00. I am wondering if my issue has got something to do with this?

It is really confusing.

H
  • You probably need to do this as a "watch-app" and not a "watch-face", as watch faces enter and leave "low power" mode all the time. In low power, updates only occur about every minute. And if you are setting your own timer in a "watch-face", it can get pretty weird on updates! That would also allow you to use a "number picker" to set the times. You can't do that in a watch face.

    If you do want to try to keep this a watchface, when you display a new timer value in onUpdate, calculate the difference between end time (in seconds) and now (in seconds) and use that result to generate the timer vale display. That way, every second (not in low power) or every minutes (in low power), what's displayed will be correct at the time of the update.
  • You probably need to do this as a "watch-app" and not a "watch-face", as watch faces enter and leave "low power" mode all the time. In low power, updates only occur about every minute. And if you are setting your own timer in a "watch-face", it can get pretty weird on updates!


    Keep in mind that I am not displaying seconds. I have already figured out that the update happens only once every minute.
    The update that I do is to update the clock (only hours and minutes) and the countdown display (only days, hours and minutes).

    Looking at my code I cannot see how it can still go out of sync.

    I am willing to share code so that you can have a look if interested.

    Regards,

    H
  • But updates don't occur on the clock minute, just every minute. Are you using the difference between endSeconds and nowSeconds to generate the "timer value" display?
  • But updates don't occur on the clock minute, just every minute. Are you using the difference between endSeconds and nowSeconds to generate the "timer value" display?


    Like I said, I am not using seconds.

    I do it as follows:

    var eventMoment = Calendar.moment({:year => 2015, :month => 5, :day => 31, :hour => 3, :minute => 30});
    var now = Time.now();
    if(eventMoment.greaterThan(now))
    {
    updateCountDownTimer(now);
    }

    function updateCountDownTimer(now) {

    var remaining = eventMoment.subtract(now);
    var remMins = remaining.value() / 3600;
    var remainingDays = remMins / 24;
    var remainingHours = (remMins) - (remainingDays * 24);
    var remainingMinutes = ((remaining.value() / 60) % 60);

    //UPDATE LABELS HERE WITH ABOVE VALUES
    }

    Setting the clock I use exactly what I found in the samples.

    H
  • just wondering if you have managed to get this straightened out.
    I'm getting confused over the moment thing.
    I'm too trying to make my own custom watch face/widget.
  • just wondering if you have managed to get this straightened out.
    I'm getting confused over the moment thing.
    I'm too trying to make my own custom watch face/widget.


    Unfortunately not. This is definitely a bug and Garmin Connect IQ team has not responded or taken notice of this. No acknowledgment of my report at all.

    What I did was to change my code in favour of the actual values being displayed on the watches. Unfortunately this means the simulator is out of sync, but that is OK. I guess it is more important to have real thing appearing correct on actual watches.
  • But updates don't occur on the clock minute, just every minute. Are you using the difference between endSeconds and nowSeconds to generate the "timer value" display?


    From my experience, onUpdate() method is called each whole clock minute (zero seconds), when in low power.
  • I tried out the code snippet u provided above and found that in my side - the delta is even greater than a couple of seconds. It's as far as 5-6 hours.

    I did another app - and for some reason - it works on the simulator but does not work on the watch itself.
    I'm gonna start trouble shooting it, but I think it would be similar to your problem (as in workaround)
  • I'm not seeing a problem.

    using Toybox.System as Sys;
    using Toybox.WatchUi as Ui;
    using Toybox.Time as Time;
    using Toybox.Time.Gregorian as Gregorian;
    using Toybox.Graphics as Gfx;

    class TestWatchFace extends Ui.WatchFace
    {
    hidden var lowPowerMode = true;
    hidden var eventMoment;

    function initialize() {
    lowPowerMode = true;

    // get a moment for midnight today
    eventMoment = Time.today();

    // advance 24 hours to midnight tomorrow
    eventMoment = eventMoment.add(new Time.Duration(Gregorian.SECONDS_PER_DAY));

    // i think the following should not be necessary and is compensating for
    // a bug with Time.today(). it should not affect your code.

    // convert from utc time to local time
    eventMoment = eventMoment.add(new Time.Duration(-Sys.getClockTime().timeZoneOffset));
    }

    function onLayout(dc) {
    }

    function onShow() {
    }

    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_BLACK);
    dc.clear();

    var cx = dc.getWidth() / 2;
    var cy = dc.getHeight() / 2;

    var format = "$1$:$2$";
    var color = Gfx.COLOR_GREEN;
    var font = Gfx.FONT_LARGE;

    var ty = dc.getFontHeight(font) / 2 + 10;
    cy -= ty;

    if (!lowPowerMode) {
    format = "$1$:$2$:$3$";
    color = Gfx.COLOR_RED;
    }

    dc.setColor(color, Gfx.COLOR_TRANSPARENT);

    if (1)
    {
    var time = Sys.getClockTime();
    time.hour = 1 + (time.hour + 11) % 12;

    var text = Lang.format(format, [
    time.hour,
    time.min.format("%02d"),
    time.sec.format("%02d")
    ]);

    dc.drawText(cx, cy, font, text, Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
    cy += ty;
    }

    if (1)
    {
    var now = Time.now();

    var time = Gregorian.info(now, Time.FORMAT_SHORT);
    time.hour = 1 + (time.hour + 11) % 12;

    var text = Lang.format(format, [
    time.hour,
    time.min.format("%02d"),
    time.sec.format("%02d")
    ]);

    dc.drawText(cx, cy, font, text, Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
    cy += ty;

    if (eventMoment.greaterThan(now)) {
    var remaining = eventMoment.subtract(now).value();

    // at 7:33:12am, the remaining time is 16:26:48. if we display 16:26 it will
    // look like we are off by one minute (which is tied up in the seconds). we
    // must account for that.
    if (lowPowerMode) {
    remaining += Gregorian.SECONDS_PER_MINUTE;
    }

    var sec = (remaining % Gregorian.SECONDS_PER_MINUTE);
    var min = (remaining % Gregorian.SECONDS_PER_HOUR) / Gregorian.SECONDS_PER_MINUTE;
    var hour = (remaining % Gregorian.SECONDS_PER_DAY) / Gregorian.SECONDS_PER_HOUR;

    text = Lang.format(format, [
    hour,
    min.format("%02d"),
    sec.format("%02d")
    ]);

    dc.drawText(cx, cy, font, text, Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
    }
    else {
    eventMoment = Time.today().add(new Time.Duration(Gregorian.SECONDS_PER_DAY));
    }
    }
    }

    function onHide() {
    }

    function onEnterSleep() {
    lowPowerMode = true;
    Ui.requestUpdate();
    }

    function onExitSleep() {
    lowPowerMode = false;
    Ui.requestUpdate();
    }
    }
  • I do see a minute error when in low power mode using the above code.