Simulator time control?

I would like to test how a watch face behaves at different times.
Is there a way to set the simulator time, or control the time programmatically (for example to go faster than real time)?

Thanks,

Lorenzo
  • If you want to see what a watch face shows at 9am, set the your system clock for 9am and run the simulator. If you want to just try out arbitrary time values without setting the system clock, you could implement something using the app settings system pretty easily.
  • If you're goal is to make sure the time always fits on the screen, one thing I'l done is dummy up the time in the code. So for example instead of using the hour and min you get from the system, start with hhmm=0;

    When the time is needed, calc the hour and min with something like:
    hour=hhmm/60;
    min=hhmm%60;
    hhmm++;
    hhmm=hhmm%(24*60);


    When running in low power mode, things change at normal speed (but always starts at 00:00/12:00am), but if you leave low power mode in the sim, it advances a minute per second (assuming you get the time in onUpdate() ) (so in 24 mins, you's gone though a whole day). If you want to go faster, instead of "hhmm++;" you can do something like "hhmm=hhmm+7;" It won't show every minute but you'll catch the combos of the minutes digits over time. Code for your variables for hour and min and all you have to do is comment out a few lines to go back to normal.

    In the sim, it doesn't do anything special when the system time hits midnight (it doesn't reset daily steps for example), and I've found the best way to check changes like that is to set the time manually on the watch to something like 23:50. This will however screw up your watches daily step count. So most times I just leave the time set normally and look at the results the next morning.
  • Thanks for the inputs!

    What I am really looking for is:
    - manually set one specific time, better without changing the system time to identify a specific situation
    - programmatically set a series of times (from the simulator or a unit test), typically to simulate a "fast forward" mode to see the watch face drawing.

    Changing the system clock does not seem very practical unless running inside a VM and only testing a couple a times. TRAVIS, can you maybe elaborate what you mean by "using the app settings system"?

    If I understand correctly jim_m_58 solution, I consist in taking advantage that the onUpdate() is called 60 time faster (every seconds rather than every minute) in Power Mode, and manage your own time there. I do not like having slightly different code when testing / deploying but I guess this is the best solution short of better simulator capabilities.

    I am new to ConnectIQ, but definitely appreciate that Garmin provides this SDK and that there is an active community around it. I just wish for a next release a simulator with a simulated time independent of the host computer time ;-)

    Lorenzo
  • TRAVIS, can you maybe elaborate what you mean by "using the app settings system"?

    The following example demonstrates a few things.

    • Writing debug code should not require littering your code with a bunch of debug stuff that you need to remove manually.
    • Using a virtual timer to demonstrate moving through time more quickly or slowly.
    • Using app settings to specify preset time values to be displayed. You set the value and it is displayed; time will not advance


    A few notes about this approach

    • I haven't found a way to exclude resources, so this isn't a perfect solution. If someone has an idea, I'm all ears.
    • If you want to test on a device, you'll need to upload the app to the store for testing. See this post for information on getting a test app that is not approved.
    • It is probably overkill for what you need, but it does demonstrate how to do what you want without waiting for the simulator to get updated.


    using Toybox.Application as App;
    using Toybox.WatchUi as Ui;
    using Toybox.Graphics as Gfx;
    using Toybox.System as Sys;
    using Toybox.Timer as Timer;

    //
    // for this code to work, you must ensure that one of the following compile
    // flags is used when building the code.
    //
    // 1) -x debug (used for release builds)
    // 2) -x release (used for debug builds)
    //
    // you can do this with a build setting or via a build exclusion resource.
    //
    // see Window > Preferences > ConnectIQ > Compiler > Compile time options in
    // Eclipse or the Developer's Guide for more information.
    //

    // this is an base class for all classes that provide time information. base
    // classes aren't really needed in MonkeyC, but by having this, I can avoid
    // defining empty functions in each of the derived classes.
    class Clock
    {
    function initialize() {
    }

    function getClockTime() {
    return null;
    }

    function onEnterSleep() {
    }

    function onExitSleep() {
    }

    function onSettingsChanged(app) {
    }
    }

    // provides a time based on the local system time
    class SystemClock extends Clock
    {
    function initialize() {
    Clock.initialize();
    }

    function getClockTime() {
    return Sys.getClockTime();
    }
    }

    // provides a time based on a time scale value, which is controlled in the app
    // settings. only available if app is compiled with '-x release'
    (:debug)
    class VirtualClock extends Clock
    {
    hidden var _M_scale;

    function initialize() {
    Clock.initialize();
    _M_scale = 1.0;
    }

    hidden var _M_timer;

    function onEnterSleep() {
    if (_M_timer != null) {
    _M_timer.stop();
    _M_timer = null;
    }
    }

    function onExitSleep() {
    if (_M_timer == null) {
    _M_timer = new Timer.Timer();
    _M_timer.start(self.method(:onTimer), (1000 / _M_scale).toNumber(), true);
    }
    }

    function onTimer() {
    Ui.requestUpdate();
    }

    function getClockTime() {
    var clockTime = Sys.getClockTime();

    var seconds = ((Sys.getTimer() * _M_scale) / 1000).toNumber();
    seconds %= 86400;

    clockTime.hour = (seconds / 60) / 60;
    clockTime.min = (seconds / 60) % 60;
    clockTime.sec = (seconds % 60);

    return clockTime;
    }

    function onSettingsChanged(app) {
    _M_scale = app.getProperty("Scale");
    if (_M_scale == null) {
    _M_scale = 1.0;
    }

    if (_M_timer != null) {
    _M_timer.stop();
    _M_timer.start(self.method(:onTimer), (1000 / _M_scale).toNumber(), true);
    }
    }
    }

    // provides a time that is determined by app settings; the value is fixed
    // until you change the setting again. only available if app is compiled
    // with '-x release'
    (:debug)
    class SettingClock extends Clock
    {
    function initialize() {
    Clock.initialize();
    }

    hidden var _M_hour;
    hidden var _M_min;
    hidden var _M_sec;

    function getClockTime() {
    var clockTime = Sys.getClockTime();

    clockTime.hour = _M_hour;
    clockTime.min = _M_min;
    clockTime.sec = _M_sec;

    return clockTime;
    }

    function onSettingsChanged(app) {
    _M_hour = app.getProperty("Hour");
    if (_M_hour == null) {
    _M_hour = 0;
    }

    _M_min = app.getProperty("Min");
    if (_M_min == null) {
    _M_min = 0;
    }

    _M_sec = app.getProperty("Sec");
    if (_M_sec == null) {
    _M_sec = 0;
    }
    }
    }


    // all of the logic for drawing the time goes in here. this class only knows
    // how to draw the time. it gets information about the current time from the
    // time source passed to it via `setClock'. there should be no special case
    // code in here for tinkering with time values.
    class MyWatchFace extends Ui.WatchFace
    {
    function initialize() {
    WatchFace.initialize();
    }

    hidden var _M_clock;

    function setClock(clock) {
    _M_clock = clock;
    }

    hidden var _M_sleeping;

    function onEnterSleep() {
    _M_sleeping = true;
    _M_clock.onEnterSleep();
    }

    function onExitSleep() {
    _M_clock.onExitSleep();
    _M_sleeping = false;
    }

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

    if (_M_clock != null) {

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

    // don't show the seconds if we are in low power mode
    var fmt;
    if (_M_sleeping) {
    fmt = "$1$:$2$";
    }
    else {
    fmt = "$1$:$2$:$3$";
    }

    var clockTime = _M_clock.getClockTime();

    dc.drawText(cx, cy, Gfx.FONT_LARGE,
    Lang.format(fmt, [
    clockTime.hour.format("%2d"),
    clockTime.min.format("%02d"),
    clockTime.sec.format("%02d")
    ]),
    Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
    }
    }
    }

    class ZZZApp extends App.AppBase
    {
    function initialize() {
    AppBase.initialize();
    }

    hidden var _M_view;

    function getInitialView() {
    _M_view = new MyWatchFace();

    // read the configuration and apply it
    onSettingsChanged();

    return [ _M_view ];
    }

    // this function is used if not compiled with '-x release'. it is the only
    // bit of code that knows about the various clock types outside of the
    // resource files that define the properties for them.
    (:debug)
    hidden function clockFactory() {
    var mode = getProperty("Mode");
    if (mode == 2) {
    return new SettingClock();
    }
    else if (mode == 1) {
    return new VirtualClock();
    }
    else {
    return new SystemClock();
    }
    }

    // this function is used if compiled with '-x debug'. the only clock type
    // available is the system clock.
    (:release)
    hidden function clockFactory() {
    return new SystemClock();
    }

    function onSettingsChanged() {
    var clock = clockFactory();
    clock.onSettingsChanged(self);

    _M_view.setClock(clock);
    }
    }


    Then you've got the resources that make all of this code configurable from outside the app.

    <!-- these resources are only needed when building in debug mode -->
    <resources>
    <strings>
    <string id="Mode">Mode</string>
    <string id="SystemClock">System Clock</string>
    <string id="VirtualClock">Virtual Clock</string>
    <string id="SettingClock">Setting Clock</string>

    <string id="Scale">Virtual Clock - Scale</string>

    <string id="Hour">Setting Clock - Hour</string>
    <string id="Min">Setting Clock - Minute</string>
    <string id="Sec">Setting Clock - Second</string>
    </strings>

    <properties>
    <property id="Mode" type="number">0</property>

    <property id="Scale" type="float">1.0</property>

    <property id="Hour" type="number">23</property>
    <property id="Min" type="number">59</property>
    <property id="Sec" type="number">59</property>
    </properties>

    <settings>
    <setting propertyKey="@Properties.Mode" title="@Strings.Mode">
    <settingConfig type="list">
    <listEntry value="0">@Strings.SystemClock</listEntry>
    <listEntry value="1">@Strings.VirtualClock</listEntry>
    <listEntry value="2">@Strings.SettingClock</listEntry>
    </settingConfig>
    </setting>

    <setting propertyKey="@Properties.Scale" title="@Strings.Scale">
    <settingConfig type="numeric" min="0" max="100" />
    </setting>

    <setting propertyKey="@Properties.Hour" title="@Strings.Hour">
    <settingConfig type="numeric" min="0" max="23" />
    </setting>

    <setting propertyKey="@Properties.Min" title="@Strings.Min">
    <settingConfig type="numeric" min="0" max="59" />
    </setting>

    <setting propertyKey="@Properties.Sec" title="@Strings.Sec">
    <settingConfig type="numeric" min="0" max="59" />
    </setting>
    </settings>
    </resources>


    I'm not sure if any of this is actually useful to you, but it seems like it would be (especially if someone could figure out how to exclude the property/setting/string resource definitions that are not needed in release mode.

    Travis