BreathingView.mc:63:            bigText = Common.formatTime(targetHoldS);
BreathingView.mc:68:            bigText = Common.formatTime(AppSettings.breathingRestSeries[0]);
BreathingView.mc:73:            bigText = Common.formatTime(currentTime);
BreathingView.mc:78:            bigText = Common.formatTime(currentTime);
BreathingView.mc:83:            bigText = Common.formatTime(totalHoldTime);
Common.mc:14:        return m.format("%02d") + ":" + s.format("%02d");
DepthView.mc:76:            bigText = Common.formatTime(breathUpTime - currentTime);
DepthView.mc:82:            infoText = "SI " + Common.formatTime(surfaceIntervalTime) + "  HR " + hrString();
DepthView.mc:87:            infoText = "MAX " + depthString(maxDepthM) + "  " + Common.formatTime(diveTime) + "  HR " + hrString();
DepthView.mc:91:            bigText = Common.formatTime(recoveryTime - currentTime);
DepthView.mc:97:            infoText = "DIVE " + Common.formatTime(diveTime) + "  HR " + hrString();
DepthView.mc:113:        return d.format("%.1f") + "m";
DistanceView.mc:72:            bigText = Common.formatTime(breathUpTime - currentTime);
DistanceView.mc:77:            bigText = Common.formatTime(currentTime);
DistanceView.mc:82:            bigText = Common.formatTime(recoveryTime - currentTime);
DistanceView.mc:87:            bigText = Common.formatTime(maxHoldTime);
PoolLengthSettingsView.mc:37:            valueStr = customValue.format("%d") + "m";
PoolLengthSettingsView.mc:39:            valueStr = PRESET_VALUES[selectedIndex].format("%d") + "m";
StaticView.mc:62:            bigText = Common.formatTime(breathUpTime - currentTime);
StaticView.mc:67:            bigText = Common.formatTime(currentTime);
StaticView.mc:72:            bigText = Common.formatTime(recoveryTime - currentTime);
StaticView.mc:73:            infoText = "HOLD " + Common.formatTime(maxHoldTime) + "  HR " + hrString();
StaticView.mc:77:            bigText = Common.formatTime(maxHoldTime);


bash

cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n 'createField.*DATA_TYPE_STRING' *.mc


(no output)

Command exited with code 1


cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n "DATA_TYPE_STRING\|DATA_TYPE_UINT8\|DATA_TYPE_UINT16\|DATA_TYPE_FLOAT" *.mc


BreathingView.mc:181:            "discipline", 0, FitContributor.DATA_TYPE_STRING,
BreathingView.mc:186:            "hold_target_s", 15, FitContributor.DATA_TYPE_UINT16,
BreathingView.mc:191:            "cycles_completed", 13, FitContributor.DATA_TYPE_UINT8,
BreathingView.mc:196:            "total_hold_time_s", 14, FitContributor.DATA_TYPE_UINT16,
BreathingView.mc:201:            "last_hr_bpm", 12, FitContributor.DATA_TYPE_UINT8,
DepthView.mc:204:                "depth_m", 1, FitContributor.DATA_TYPE_FLOAT,
DepthView.mc:209:                "hr_bpm", 2, FitContributor.DATA_TYPE_UINT8,
DepthView.mc:214:                "discipline", 0, FitContributor.DATA_TYPE_STRING,
DepthView.mc:219:                "max_depth_m", 3, FitContributor.DATA_TYPE_FLOAT,
DepthView.mc:224:                "dive_time_s", 4, FitContributor.DATA_TYPE_UINT16,
DepthView.mc:229:                "surface_interval_s", 5, FitContributor.DATA_TYPE_UINT16,
DepthView.mc:234:                "breath_up_time_s", 10, FitContributor.DATA_TYPE_UINT16,
DepthView.mc:239:                "last_hr_bpm", 12, FitContributor.DATA_TYPE_UINT8,
DistanceView.mc:168:            "discipline", 0, FitContributor.DATA_TYPE_STRING,
DistanceView.mc:173:            "horizontal_distance_m", 6, FitContributor.DATA_TYPE_UINT16,
DistanceView.mc:178:            "length_count", 7, FitContributor.DATA_TYPE_UINT8,
DistanceView.mc:183:            "stroke_count", 8, FitContributor.DATA_TYPE_UINT8,
DistanceView.mc:188:            "contraction_count", 16, FitContributor.DATA_TYPE_UINT8,
DistanceView.mc:193:            "breathhold_time_s", 9, FitContributor.DATA_TYPE_UINT16,
DistanceView.mc:198:            "breath_up_time_s", 10, FitContributor.DATA_TYPE_UINT16,
DistanceView.mc:203:            "last_hr_bpm", 12, FitContributor.DATA_TYPE_UINT8,
StaticView.mc:148:            "discipline", 0, FitContributor.DATA_TYPE_STRING,
StaticView.mc:153:            "breathhold_time_s", 9, FitContributor.DATA_TYPE_UINT16,
StaticView.mc:158:            "breath_up_time_s", 10, FitContributor.DATA_TYPE_UINT16,
StaticView.mc:163:            "recovery_time_s", 11, FitContributor.DATA_TYPE_UINT16,
StaticView.mc:168:            "last_hr_bpm", 12, FitContributor.DATA_TYPE_UINT8,


cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n 'Timer.Timer\|Timer.start\|timer.stop\|new Timer' *.mc


BreathingDelegate.mc:44:                backTimer = new Timer.Timer();
BreathingDelegate.mc:45:                backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
BreathingView.mc:40:        timer = new Timer.Timer();
BreathingView.mc:45:        if (timer != null) { timer.stop(); }
BreathingView.mc:168:        timer.stop();
BreathingView.mc:214:        if (timer != null) { timer.stop(); }
DepthDelegate.mc:31:                backTimer = new Timer.Timer();
DepthDelegate.mc:32:                backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
DepthView.mc:51:        timer = new Timer.Timer();
DepthView.mc:57:            timer.stop();
DepthView.mc:190:        timer.stop();
DepthView.mc:254:            timer.stop();
DistanceDelegate.mc:40:                backTimer = new Timer.Timer();
DistanceDelegate.mc:41:                backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
DistanceView.mc:49:        timer = new Timer.Timer();
DistanceView.mc:54:        if (timer != null) { timer.stop(); }
DistanceView.mc:155:        timer.stop();
DistanceView.mc:216:        if (timer != null) { timer.stop(); }
MainMenuDelegate.mc:39:            backTimer = new Timer.Timer();
MainMenuDelegate.mc:40:            backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
StaticDelegate.mc:30:                backTimer = new Timer.Timer();
StaticDelegate.mc:31:                backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
StaticView.mc:39:        timer = new Timer.Timer();
StaticView.mc:44:        if (timer != null) { timer.stop(); }
StaticView.mc:135:        timer.stop();
StaticView.mc:181:        if (timer != null) { timer.stop(); }


        discipline = disc;
        breathUpTime = AppSettings.defaultBreathUpS;
        recoveryTime = AppSettings.defaultRecoveryS;
    }

    function onShow() {
        timer = new Timer.Timer();
        timer.start(method(:onTimer), 1000, true);
    }

    function onHide() {
        if (timer != null) {
            timer.stop();
        }
    }

[217 more lines in file. Use offset=60 to continue.]


edit

    function onShow() {
        timer = new Timer.Timer();
        timer.start(method(:onTimer), 1000, true);
    }

    function onHide() {
        if (timer != null) {
            timer.stop();
            timer = null;
        }
    }

    function onShow() {
        timer = new Timer.Timer();
        timer.start(method(:onTimer), 1000, true);
    }

    function onHide() {
        if (timer != null) {
            timer.stop();
        }
    }


Successfully replaced 1 block(s) in /home/liph/code/garmin-freedive-app/source_fenix5/DepthView.mc.

     ...
  54
  55     function onHide() {
  56         if (timer != null) {
  57             timer.stop();
+ 58             timer = null;
  58         }
  59     }
  60
  61     function onUpdate(dc) {
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/DepthView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/DepthView.mc
@@ -54,8 +54,9 @@

     function onHide() {
         if (timer != null) {
             timer.stop();
+            timer = null;
         }
     }

     function onUpdate(dc) {


    function finishSession() {
        if (session != null && session.isRecording()) {
            if (disciplineField != null) { disciplineField.setData(discipline); }
            if (maxDepthField != null) { maxDepthField.setData(maxDepthM); }
            if (diveTimeField != null) { diveTimeField.setData(diveTime); }
            if (surfaceIntervalField != null) { surfaceIntervalField.setData(surfaceIntervalTime); }
            if (breathUpField != null) { breathUpField.setData(breathUpTime); }
            if (lastHrField != null && lastHeartRate != null) { lastHrField.setData(lastHeartRate); }
            session.stop();
            session.save();
        }
        if (timer != null) {
            timer.stop();
            timer = null;
        }
        state = STATE_DONE;
        WatchUi.requestUpdate();
    }

    function finishSession() {
        if (session != null && session.isRecording()) {
            if (disciplineField != null) { disciplineField.setData(discipline); }
            if (maxDepthField != null) { maxDepthField.setData(maxDepthM); }
            if (diveTimeField != null) { diveTimeField.setData(diveTime); }
            if (surfaceIntervalField != null) { surfaceIntervalField.setData(surfaceIntervalTime); }
            if (breathUpField != null) { breathUpField.setData(breathUpTime); }
            if (lastHrField != null && lastHeartRate != null) { lastHrField.setData(lastHeartRate); }
            session.stop();
            session.save();
        }
        timer.stop();
        state = STATE_DONE;
        WatchUi.requestUpdate();
    }


     ...
 187             if (lastHrField != null && lastHeartRate != null) { lastHrField.setData(lastHeartRate); }
 188             session.stop();
 189             session.save();
 190         }
-191         timer.stop();
+191         if (timer != null) {
+192             timer.stop();
+193             timer = null;
+194         }
 192         state = STATE_DONE;
 193         WatchUi.requestUpdate();
 194     }
 195
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/DepthView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/DepthView.mc
@@ -187,9 +187,12 @@
             if (lastHrField != null && lastHeartRate != null) { lastHrField.setData(lastHeartRate); }
             session.stop();
             session.save();
         }
-        timer.stop();
+        if (timer != null) {
+            timer.stop();
+            timer = null;
+        }
         state = STATE_DONE;
         WatchUi.requestUpdate();
     }


        session = null;
        if (timer != null) {
            timer.stop();
            timer = null;
        }
        DepthModel.reset();
        WatchUi.switchToView(new MainMenuView(), new MainMenuDelegate(), WatchUi.SLIDE_RIGHT);

        session = null;
        if (timer != null) {
            timer.stop();
        }
        DepthModel.reset();
        WatchUi.switchToView(new MainMenuView(), new MainMenuDelegate(), WatchUi.SLIDE_RIGHT);


Could not find the exact text in /home/liph/code/garmin-freedive-app/source_fenix5/DepthView.mc. The old text must match exactly including all whitespace and newlines.


            );

            session.start();
        }
    }

    function discardSession() {
        if (session != null && session.isRecording()) {
            session.stop();
            session.discard();
        }
        session = null;
        if (timer != null) {
            timer.stop();
        }
        DepthModel.reset();
        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
    }

    function onStartButton() {

[16 more lines in file. Use offset=265 to continue.]


        session = null;
        if (timer != null) {
            timer.stop();
            timer = null;
        }
        DepthModel.reset();
        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);

        session = null;
        if (timer != null) {
            timer.stop();
        }
        DepthModel.reset();
        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);


     ...
 255         }
 256         session = null;
 257         if (timer != null) {
 258             timer.stop();
+259             timer = null;
 259         }
 260         DepthModel.reset();
 261         MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
 262     }
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/DepthView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/DepthView.mc
@@ -255,8 +255,9 @@
         }
         session = null;
         if (timer != null) {
             timer.stop();
+            timer = null;
         }
         DepthModel.reset();
         MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
     }


        recoveryTime = AppSettings.defaultRecoveryS;
    }

    function onShow() {
        timer = new Timer.Timer();
        timer.start(method(:onTimer), 1000, true);
    }

    function onHide() {
        if (timer != null) { timer.stop(); }
    }

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

[193 more lines in file. Use offset=60 to continue.]


    function onHide() {
        if (timer != null) { timer.stop(); timer = null; }
    }

    function onHide() {
        if (timer != null) { timer.stop(); }
    }


Successfully replaced 1 block(s) in /home/liph/code/garmin-freedive-app/source_fenix5/DistanceView.mc.

     ...
  50         timer.start(method(:onTimer), 1000, true);
  51     }
  52
  53     function onHide() {
- 54         if (timer != null) { timer.stop(); }
+ 54         if (timer != null) { timer.stop(); timer = null; }
  55     }
  56
  57     function onUpdate(dc) {
  58         dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/DistanceView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/DistanceView.mc
@@ -50,9 +50,9 @@
         timer.start(method(:onTimer), 1000, true);
     }

     function onHide() {
-        if (timer != null) { timer.stop(); }
+        if (timer != null) { timer.stop(); timer = null; }
     }

     function onUpdate(dc) {
         dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);


        timer.stop();
        timer = null;
        state = STATE_DONE;
        WatchUi.requestUpdate();
    }

        timer.stop();
        state = STATE_DONE;
        WatchUi.requestUpdate();
    }


     ...
 152             session.stop();
 153             session.save();
 154         }
 155         timer.stop();
+156         timer = null;
 156         state = STATE_DONE;
 157         WatchUi.requestUpdate();
 158     }
 159
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/DistanceView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/DistanceView.mc
@@ -152,8 +152,9 @@
             session.stop();
             session.save();
         }
         timer.stop();
+        timer = null;
         state = STATE_DONE;
         WatchUi.requestUpdate();
     }


        session = null;
        if (timer != null) { timer.stop(); timer = null; }
        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);

        session = null;
        if (timer != null) { timer.stop(); }
        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);


     ...
 213             session.stop();
 214             session.discard();
 215         }
 216         session = null;
-217         if (timer != null) { timer.stop(); }
+217         if (timer != null) { timer.stop(); timer = null; }
 218         MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
 219     }
 220
 221     function onStartButton() {
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/DistanceView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/DistanceView.mc
@@ -213,9 +213,9 @@
             session.stop();
             session.discard();
         }
         session = null;
-        if (timer != null) { timer.stop(); }
+        if (timer != null) { timer.stop(); timer = null; }
         MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
     }

     function onStartButton() {


Successfully replaced 1 block(s) in /home/liph/code/garmin-freedive-app/source_fenix5/StaticView.mc.

     ...
  40         timer.start(method(:onTimer), 1000, true);
  41     }
  42
  43     function onHide() {
- 44         if (timer != null) { timer.stop(); }
+ 44         if (timer != null) { timer.stop(); timer = null; }
  45     }
  46
  47     function onUpdate(dc) {
  48         dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/StaticView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/StaticView.mc
@@ -40,9 +40,9 @@
         timer.start(method(:onTimer), 1000, true);
     }

     function onHide() {
-        if (timer != null) { timer.stop(); }
+        if (timer != null) { timer.stop(); timer = null; }
     }

     function onUpdate(dc) {
         dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);


     ...
 132             session.stop();
 133             session.save();
 134         }
 135         timer.stop();
+136         timer = null;
 136         state = STATE_DONE;
 137         WatchUi.requestUpdate();
 138     }
 139
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/StaticView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/StaticView.mc
@@ -132,8 +132,9 @@
             session.stop();
             session.save();
         }
         timer.stop();
+        timer = null;
         state = STATE_DONE;
         WatchUi.requestUpdate();
     }


     ...
 178             session.stop();
 179             session.discard();
 180         }
 181         session = null;
-182         if (timer != null) { timer.stop(); }
+182         if (timer != null) { timer.stop(); timer = null; }
 183         MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
 184     }
 185
 186     function onStartButton() {
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/StaticView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/StaticView.mc
@@ -178,9 +178,9 @@
             session.stop();
             session.discard();
         }
         session = null;
-        if (timer != null) { timer.stop(); }
+        if (timer != null) { timer.stop(); timer = null; }
         MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
     }

     function onStartButton() {


Successfully replaced 1 block(s) in /home/liph/code/garmin-freedive-app/source_fenix5/BreathingView.mc.

     ...
  41         timer.start(method(:onTimer), 1000, true);
  42     }
  43
  44     function onHide() {
- 45         if (timer != null) { timer.stop(); }
+ 45         if (timer != null) { timer.stop(); timer = null; }
  46     }
  47
  48     function onUpdate(dc) {
  49         dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/BreathingView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/BreathingView.mc
@@ -41,9 +41,9 @@
         timer.start(method(:onTimer), 1000, true);
     }

     function onHide() {
-        if (timer != null) { timer.stop(); }
+        if (timer != null) { timer.stop(); timer = null; }
     }

     function onUpdate(dc) {
         dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);


     ...
 165             session.stop();
 166             session.save();
 167         }
 168         timer.stop();
+169         timer = null;
 169         state = STATE_DONE;
 170         WatchUi.requestUpdate();
 171     }
 172
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/BreathingView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/BreathingView.mc
@@ -165,8 +165,9 @@
             session.stop();
             session.save();
         }
         timer.stop();
+        timer = null;
         state = STATE_DONE;
         WatchUi.requestUpdate();
     }


     ...
 211             session.stop();
 212             session.discard();
 213         }
 214         session = null;
-215         if (timer != null) { timer.stop(); }
+215         if (timer != null) { timer.stop(); timer = null; }
 216         MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
 217     }
 218
 219     function onStartButton() {
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/BreathingView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/BreathingView.mc
@@ -211,9 +211,9 @@
             session.stop();
             session.discard();
         }
         session = null;
-        if (timer != null) { timer.stop(); }
+        if (timer != null) { timer.stop(); timer = null; }
         MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
     }

     function onStartButton() {


bash

cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n 'switchToMainMenu\|new MainMenuView()\|new MainMenuDelegate' *.mc


BreathingDelegate.mc:73:                    MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
BreathingView.mc:216:        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
BreathingView.mc:232:            MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DepthDelegate.mc:61:                    MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DepthView.mc:262:        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DepthView.mc:276:            MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DistanceDelegate.mc:69:                    MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DistanceView.mc:218:        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DistanceView.mc:229:            MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
Fenix5App.mc:24:        var view = new MainMenuView();
Fenix5App.mc:25:        var delegate = new MainMenuDelegate(view);
MainMenuView.mc:119:    static function switchToMainMenu(slideDir) {
MainMenuView.mc:120:        var view = new MainMenuView();
MainMenuView.mc:121:        WatchUi.switchToView(view, new MainMenuDelegate(view), slideDir);
StaticDelegate.mc:58:                    MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
StaticView.mc:183:        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
StaticView.mc:194:            MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);


cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n 'discipline.equals\|\.equals(' *.mc


MainMenuView.mc:75:        if (discipline.equals("CWT")) {


cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n 'method(:' *.mc


BreathingDelegate.mc:45:                backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
BreathingView.mc:41:        timer.start(method(:onTimer), 1000, true);
DepthDelegate.mc:32:                backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
DepthView.mc:52:        timer.start(method(:onTimer), 1000, true);
DistanceDelegate.mc:41:                backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
DistanceView.mc:50:        timer.start(method(:onTimer), 1000, true);
MainMenuDelegate.mc:40:            backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
MainMenuView.mc:55:            var subView = new SubMenuView("DEPTH TYPE", ["Constant Weight", "Free Immersion"], method(:onDepthType));
MainMenuView.mc:58:            var subView2 = new SubMenuView("DIST FIN TYPE", ["Bi-fins", "Monofin", "No-fins"], method(:onDistanceType));
MainMenuView.mc:76:            var subView = new SubMenuView("CWT FIN STYLE", ["Bi-fins", "Monofin", "No-fins"], method(:onCwtFinStyle));
StaticDelegate.mc:31:                backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
StaticView.mc:40:        timer.start(method(:onTimer), 1000, true);


            discipline = "CWT";
        } else {
            discipline = "FIM";
        }
        // If Constant Weight, ask for fin style before launching depth view.
        if (discipline.equals("CWT")) {
            var subView = new SubMenuView("CWT FIN STYLE", ["Bi-fins", "Monofin", "No-fins"], method(:onCwtFinStyle));
            WatchUi.pushView(subView, new SubMenuDelegate(subView), WatchUi.SLIDE_LEFT);
        } else {
            launchDepth(discipline);
        }
    }

    function onCwtFinStyle(index) {
        var discipline;

[40 more lines in file. Use offset=85 to continue.]


edit

        if (index == 0) {
            discipline = "CWT";
        } else {
            discipline = "FIM";
        }
        // If Constant Weight, ask for fin style before launching depth view.
        if (index == 0) {
            var subView = new SubMenuView("CWT FIN STYLE", ["Bi-fins", "Monofin", "No-fins"], method(:onCwtFinStyle));
            WatchUi.pushView(subView, new SubMenuDelegate(subView), WatchUi.SLIDE_LEFT);
        } else {
            launchDepth(discipline);
        }

        if (index == 0) {
            discipline = "CWT";
        } else {
            discipline = "FIM";
        }
        // If Constant Weight, ask for fin style before launching depth view.
        if (discipline.equals("CWT")) {
            var subView = new SubMenuView("CWT FIN STYLE", ["Bi-fins", "Monofin", "No-fins"], method(:onCwtFinStyle));
            WatchUi.pushView(subView, new SubMenuDelegate(subView), WatchUi.SLIDE_LEFT);
        } else {
            launchDepth(discipline);
        }


Successfully replaced 1 block(s) in /home/liph/code/garmin-freedive-app/source_fenix5/MainMenuView.mc.

     ...
  71         } else {
  72             discipline = "FIM";
  73         }
  74         // If Constant Weight, ask for fin style before launching depth view.
- 75         if (discipline.equals("CWT")) {
+ 75         if (index == 0) {
  76             var subView = new SubMenuView("CWT FIN STYLE", ["Bi-fins", "Monofin", "No-fins"], method(:onCwtFinStyle));
  77             WatchUi.pushView(subView, new SubMenuDelegate(subView), WatchUi.SLIDE_LEFT);
  78         } else {
  79             launchDepth(discipline);
     ...

--- /home/liph/code/garmin-freedive-app/source_fenix5/MainMenuView.mc
+++ /home/liph/code/garmin-freedive-app/source_fenix5/MainMenuView.mc
@@ -71,9 +71,9 @@
         } else {
             discipline = "FIM";
         }
         // If Constant Weight, ask for fin style before launching depth view.
-        if (discipline.equals("CWT")) {
+        if (index == 0) {
             var subView = new SubMenuView("CWT FIN STYLE", ["Bi-fins", "Monofin", "No-fins"], method(:onCwtFinStyle));
             WatchUi.pushView(subView, new SubMenuDelegate(subView), WatchUi.SLIDE_LEFT);
         } else {
             launchDepth(discipline);


bash

cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n 'Array\| instanceof Array' *.mc


AppSettings.mc:47:        if (series != null && series instanceof Array) {


cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n 'setProperty\|getProperty' AppSettings.mc


19:        v = app.getProperty("poolLengthM");
22:        v = app.getProperty("poolLengthIndex");
25:        v = app.getProperty("waterType");
28:        v = app.getProperty("defaultBreathUpS");
31:        v = app.getProperty("defaultRecoveryS");
34:        v = app.getProperty("distanceDownAction");
37:        v = app.getProperty("breathingHoldTargetS");
40:        v = app.getProperty("staticBreathUpS");
43:        v = app.getProperty("staticRecoveryS");
46:        var series = app.getProperty("breathingRestSeries");
54:        app.setProperty("poolLengthM", poolLengthM);
55:        app.setProperty("poolLengthIndex", poolLengthIndex);
56:        app.setProperty("waterType", waterType);
57:        app.setProperty("defaultBreathUpS", defaultBreathUpS);
58:        app.setProperty("defaultRecoveryS", defaultRecoveryS);
59:        app.setProperty("distanceDownAction", distanceDownAction);
60:        app.setProperty("breathingHoldTargetS", breathingHoldTargetS);
61:        app.setProperty("staticBreathUpS", staticBreathUpS);
62:        app.setProperty("staticRecoveryS", staticRecoveryS);
63:        app.setProperty("breathingRestSeries", breathingRestSeries);


cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n 'has :' *.mc


DepthModel.mc:14:        if (info != null && info has :rawAmbientPressure && info.rawAmbientPressure != null) {
DepthModel.mc:24:        if (info == null || !(info has :rawAmbientPressure) || info.rawAmbientPressure == null) {
Fenix5App.mc:14:        if (Sensor has :setEnabledSensors) {


cd /home/liph/code/garmin-freedive-app/source_fenix5 && grep -n 'class .* extends\|InputDelegate\|AppBase\|View' *.mc | head -40


BreathingDelegate.mc:5:class BreathingDelegate extends WatchUi.InputDelegate {
BreathingDelegate.mc:12:        InputDelegate.initialize();
BreathingDelegate.mc:73:                    MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
BreathingDelegate.mc:85:            var settingsView = new SettingsView();
BreathingDelegate.mc:86:            WatchUi.pushView(settingsView, new SettingsDelegate(settingsView), WatchUi.SLIDE_LEFT);
BreathingView.mc:9:class BreathingView extends WatchUi.View {
BreathingView.mc:35:        View.initialize();
BreathingView.mc:216:        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
BreathingView.mc:232:            MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DepthDelegate.mc:5:class DepthDelegate extends WatchUi.InputDelegate {
DepthDelegate.mc:12:        InputDelegate.initialize();
DepthDelegate.mc:61:                    MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DepthDelegate.mc:75:            var settingsView = new SettingsView();
DepthDelegate.mc:76:            WatchUi.pushView(settingsView, new SettingsDelegate(settingsView), WatchUi.SLIDE_LEFT);
DepthView.mc:9:class DepthView extends WatchUi.View {
DepthView.mc:44:        View.initialize();
DepthView.mc:262:        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DepthView.mc:276:            MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DistanceDelegate.mc:5:class DistanceDelegate extends WatchUi.InputDelegate {
DistanceDelegate.mc:12:        InputDelegate.initialize();
DistanceDelegate.mc:69:                    MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DistanceDelegate.mc:81:            var settingsView = new SettingsView();
DistanceDelegate.mc:82:            WatchUi.pushView(settingsView, new SettingsDelegate(settingsView), WatchUi.SLIDE_LEFT);
DistanceView.mc:9:class DistanceView extends WatchUi.View {
DistanceView.mc:42:        View.initialize();
DistanceView.mc:218:        MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DistanceView.mc:229:            MainMenuView.switchToMainMenu(WatchUi.SLIDE_RIGHT);
DownActionSettingsView.mc:5:class DownActionSettingsView extends WatchUi.View {
DownActionSettingsView.mc:13:        View.initialize();
DownActionSettingsView.mc:52:        WatchUi.popView(WatchUi.SLIDE_RIGHT);
Fenix5App.mc:5:class Fenix5App extends Application.AppBase {
Fenix5App.mc:8:        AppBase.initialize();
Fenix5App.mc:23:    function getInitialView() {
Fenix5App.mc:24:        var view = new MainMenuView();
MainMenuDelegate.mc:5:class MainMenuDelegate extends WatchUi.InputDelegate {
MainMenuDelegate.mc:12:        InputDelegate.initialize();
MainMenuDelegate.mc:74:        var settingsView = new SettingsView();
MainMenuDelegate.mc:75:        WatchUi.pushView(settingsView, new SettingsDelegate(settingsView), WatchUi.SLIDE_LEFT);
MainMenuView.mc:5:class MainMenuView extends WatchUi.View {
MainMenuView.mc:17:        View.initialize();


cd /home/liph/code/garmin-freedive-app/source_fenix5 && for f in *.mc; do echo "=== $f ==="; head -n 1 "$f"; done


=== AppSettings.mc ===
using Toybox.Application;
=== BreathingDelegate.mc ===
using Toybox.WatchUi;
=== BreathingView.mc ===
using Toybox.WatchUi;
=== Common.mc ===
using Toybox.System;
=== DepthDelegate.mc ===
using Toybox.WatchUi;
=== DepthModel.mc ===
using Toybox.Activity;
=== DepthView.mc ===
using Toybox.WatchUi;
=== DistanceDelegate.mc ===
using Toybox.WatchUi;
=== DistanceView.mc ===
using Toybox.WatchUi;
=== DownActionSettingsView.mc ===
using Toybox.WatchUi;
=== Fenix5App.mc ===
using Toybox.Application;
=== MainMenuDelegate.mc ===
using Toybox.WatchUi;
=== MainMenuView.mc ===
using Toybox.WatchUi;
=== PoolLengthSettingsView.mc ===
using Toybox.WatchUi;
=== SettingsDelegate.mc ===
using Toybox.WatchUi;
=== SettingsEditDelegate.mc ===
using Toybox.WatchUi;
=== SettingsView.mc ===
using Toybox.WatchUi;
=== StaticDelegate.mc ===
using Toybox.WatchUi;
=== StaticView.mc ===
using Toybox.WatchUi;
=== SubMenuDelegate.mc ===
using Toybox.WatchUi;
=== SubMenuView.mc ===
using Toybox.WatchUi;
=== WaterTypeSettingsView.mc ===
using Toybox.WatchUi;


Question: Garmin Connect IQ monkeyc command line build examples -d device -f jungle -o prg -y key


1. Starter Garmin Watch App
   URL: https://cardinalengineeringlog.com/explorations/garmin_watch_app
   Starter Garmin Watch App

# Cardinal

## Engineering Log

# Starter Garmin Watch App

Modern Garmin devices allow for custom applications to run on them. This ecosystem is known as Garmin Connect IQ.

In this exploration, we get an example application running using the Connect IQ SDK. Sadly, the SDK itself is non-free and requires an account to download. However, specific attention is paid to avoid to any other proprietry/non-free software. To this end, we won't be using VS code (and associated plugins), which is the usual way to develop on the platform.

Note that much of the below is summarised at https://developer.garmin.com/connect-iq/reference-guides/monkey-c-command-line-setup/, but this guide might still be of use to explain the end-to-end process of getting an app running.

### Install the SDK

Download the SDK from the Connect IQ website (https://developer.garmin.com/connect-iq/sdk/). In this case, we'll be using the Linux version. Open the installer and create an account/login. Continue as per Garmin's guide: https://developer.garmin.com/connect-iq/connect-iq-basics/getting-started/. Be sure to install the latest SDK, as well as whatever device you'd like to target.

Add the SDK to your PATH

```

export PATH=$PATH:`cat $HOME/.Garmin/ConnectIQ/current-sdk.cfg`/bin

```

Add the same above line to ~/.bash_profile to persist the change.

### Getting a sample application

Garmin provides sample applications. We'll be using one of these. Samples can be found at /home/username/.Garmin/ConnectIQ/Sdks/connectiq.../samples. Here we select SimpleDataField.

Copy an example to a directory (this directory will be our base directory).

### Generating a private developer key

Garmin apps need to be signed by a private key (RSA, 4096 bit). This key needs to be stored safely and permanently since you will need it any time you want to update your app (particularly if you end up sharing your app through the app store). Usually this key is generated using IDE plugins, but here we use common unix CLI tools.

In our base directory, alongside the sample application folder, run the following to generate an RSA key with OpenSSL:

```

openssl genrsa -out garmin_priv_rsa.pem 4096

```

The above generates a PEM 4096 bit key. Note that it must be 4096; if 2048 is used you will get an 'Signature check failed' error later on. PEM is the common plaintext key format. However, Garmin requires the key as a binary (DER format). To convert to DER:

```

openssl pkcs8 -topk8 -inform PEM -outform DER -in garmin_priv_rsa.pem -out garmin_priv_rsa.der -nocrypt

```

Now our base directory looks like this (note that the public cert is not required):

### Makefile

To build Connect IQ apps, a number of CLI tools are provided in the SDK. IDE plugins use these under the hood to build/run the app. However, a more IDE/environment agnostic method of calling these tools is to use a Makefile. In the sample application directory, we create a Makefile containing the following:

```

DEVICE = instinct2
PRIVATE_KEY = ../garmin_priv_rsa.der
APP_NAME = yourappname

build:
	monkeyc \
	--jungles ./monkey.jungle \
	--device $(DEVICE) \
	--output bin/$(APP_NAME).prg \
	--private-key $(PRIVATE_KEY) \
	--warn

run: build
	@monkeydo bin/$(APP_NAME).prg $(DEVICE)

```

Edit DEVICE to be one of the devices listed in your manifest.xml file (see info below).

### A note on devices and the manifest file

Within the sample app folder, you will find a manifest.xml file. This contains the list of devices that your app is compatible with (amongst other app details). Edit this accordingly. Note that for whichever device you want to support, you will need those device profiles installed via the SDK manager.

### Building, running on the simulator

In the sample directory, now run make build.

To run the app on the simulator, the simulator needs to be started first:

```

connectiq

```

Once the simulator is running, in another terminal, run make run in the sample app directory. The app will now be loaded into the simulator.

### Installing onto device

Simply copy the .prg file in /bin to the APPS folder on your device (connected in mass storage mode).

2. Johann Glaser: Garmin Connect IQ on Linux
   URL: http://johann-glaser.blogspot.com/2016/10/garmin-connect-iq-on-linux.html
   Johann Glaser: Garmin Connect IQ on Linux

# Introduction

Many Garmin devices like heart rate monitors or bike computers support the download and execution of apps. These enable custom watch faces, additional data fields during activities, and stand-alone apps and widgets. Garmin offers the Connect IQ SDK free of charge to develop your own apps. However, the SDK is only offered for Windows and Mac OS. Fortunately, it mostly relies on Java and two simple platform-dependent applications which are simple enough for Wine.

# More Information

# Installation

The shown installation instructions were used for Debian (sid) in October 2016 on an Intel Core i7 PC. If you have a different Linux distribution, your steps might differ.

## 1) Download the Connect IQ SDK

Use the version for Windows (!) from https://developer.garmin.com/connect-iq/sdk/. These instructions were tried with version 2.1.4.

## 2) As root, extract the package

```

su -
mkdir /opt/Garmin-ConnectIQ
cd /opt/Garmin-ConnectIQ
unzip /path/where/you/stored/connectiq-sdk-win-2.1.4.zip

```

This will result in an installation of 123M.

## 3) For Linux, a few minor adaptions are necessary

To start the two Windows`.exe` programs with Wine, the shell scripts "`shell`" and "`simulator`" are created. Further, all scripts are made executable and Unix line end convention is ensured.

```

cd bin/
echo -e '#!/bin/bash\n\nwine $(dirname "$0")/shell.exe "$@"' > shell
echo -e '#!/bin/bash\n\nwine $(dirname "$0")/simulator.exe "$@"' > simulator
chmod a+x monkeyc monkeydo monkeygraph connectiq connectiqpkg simulator shell
sed -i 's/\r//g' monkeygraph

```

## 4) Make the executables callable from everywhere

You have to add the`bin` directory to your path.

```

export PATH="$PATH:/opt/Garmin-ConnectIQ/bin"

```

Unfortunately, the scripts don't support to be symbol-linked from e.g.,`/usr/local/bin/`, because they use`$0` to determine their base directory.

## 5) Install Java and Wine

Debian Packages`wine32:i386` and`default-jre` and their dependencies work fine.

## 6) As user, create a developer key

See the explanation at https://developer.garmin.com/connect-iq/programmers-guide/getting-started/

```

mkdir ~/.ciq
openssl genrsa -out ~/.ciq/developer_key.pem 4096
openssl pkcs8 -topk8 -inform PEM -outform DER \
    -in ~/.ciq/developer_key.pem \
    -out ~/.ciq/developer_key.der -nocrypt

```

# Test

## 7) Test

Setup the path

```

export PATH="$PATH:/opt/Garmin-ConnectIQ/bin"

```

Copy and compile a sample program

```

mkdir /tmp/ciqtest
cd /tmp/ciqtest
cp -r /opt/Garmin-ConnectIQ/samples/ProgressBar/ .
cd ProgressBar/
monkeyc -y ~/.ciq/developer_key.der -o ProgressBar.prg \
    -m manifest.xml -z resources/resources.xml \
    -z resources/strings.xml -z resources/bitmaps.xml \
    source/ProgressBar.mc source/ProgressBarView.mc

```

Start the simulator in the background

```

connectiq &

```

Start your program

```

monkeydo ProgressBar.prg

```

This will upload the program and then execute it. You can stop the application using File → Kill App or by pressing the back button at the start screen of the app.

## 8) Another Test

Copy the source code of the example program

```

cd /tmp/ciqtest
cp -r /opt/Garmin-ConnectIQ/samples/Picker/ .
cd Picker/

```

Create a new file "`build.sh`" with the content:

```

#!/bin/bash

# result/output
OUT=Picker.prg

# developer key
DEVEL_KEY=~/.ciq/developer_key.der

# MonkeyC compiler
CC=monkeyc

# declare an array for all arguments
declare -a args

# developer key
args+=('-y' "$DEVEL_KEY")
# output
args+=('-o' "$OUT")
# manifest
args+=('-m' 'manifest.xml')
# resources
for i in resources*/*.xml resources*/*/*.xml ; do
  args+=('-z' "$i")
done
# sources
for i in $(find source -iname '*.mc') ; do
  args+=("$i")
done

# compile
$CC "${args[@]}"

```

Make it executeable and execute it

```

chmod a+x build.sh
./build.sh

```

Finally run the program and click on the buttons

```

monkeydo Picker.prg fr920xt

```

If you press the back-button in the intro screen, the program exits.

The color picker doesn't work but crash because it can't find the resource. I don't know if this is my fault or theirs.

# Additional Notes

- Attention: The simulator will always use the extension "`.prg`", regardless of what you specify with "`monkeydo`". If it can't find your executable, "`monkeydo`" will display a "System Error" (very informative!)
- The uploaded apps are placed in`.wine/drive_c/users/ /Temp/GARMIN/APPS/`
- The upload and the start are executed in two steps, i.e.,`shell.exe` is executed twice. See the script`monkeydo` for more details on the parameters of`shell.exe`.
- The tool "`connectiqpkg`" doesn't work because the Java file`pkgtool.jar` is missing.
- You can start "`monkeydo`" with a second parameter to specify the watch or watch type. Possible values are "`square_watch`", "`round_watch`", "`semi_round_watch`", "`tall_watch`", "`fr920xt`", "`fr735xt`", "`fr230`", ...
- For a complete list, see the "`id`" attribute of the "`device`" entries in`/opt/Garmin-ConnectIQ/bin/devices.xml`
- Attention: If you use an invalid watch type, nothing is displayed and "`monkeydo`" simply hangs. You have to kill "`shell.exe`".

3. VS Code Export Naming Query - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/314698/vs-code-export-naming-query
   VS Code Export Naming Query - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- Replies 23 replies
- Subscribers 18 subscribers
- Views 5354 views
- Users 0 members are here

Options

Related

# VS Code Export Naming Query

I have a VS Code Project with multiple sub projects as they all share common base class code and some resources. When I export the sub project they are always named with the name of the parent project. Is it possible to control this somehow so that the output file so it takes its name from the sub project name? Otherwise when I export all the projects they overwrite each other.

- Sign in to reply
- flocsy🤠 likes this" data-format="{count}" data-configuration="Format=%7Bcount%7D&IncludeTip=true">
- Cancel

I'm not sure CIQ supports sub projects. Can you run each of the subprojects in the sim and you can tell if you are running different ones?

For shared code, I use different projects along with barrels or common files (Which I typically have in the barrel project)

I use symlinks to share common code between projects, but it has a few issues:

- You have to change VS Code's search settings to follow symlinks

- Certain features like git integration don't work properly with linked files/folders (at least under Windows)

I have the multiple projects building and running. It is just the export feature to produce the prg file I am querying about. Is there a command line command which lets you specify the output filename?

Is there a command line command which lets you specify the output filename?

{SDK_PATH}\bin\monkeyc -e -j monkey.jungle -k {DEV_PRIVATE_KEY} -o {OUTPUT_FILE}

(Use {SDK_PATH}\bin\monkeyc -h to see all available options)

And: [https://developer.garmin.com/connect-iq/reference-guides/monkey-c-command-line-setup/]

Lovely, working through the other aspects of moving to VS Code with multi project sources. Thanks

I seem to have it working without symlinks (Not used these so not sure what they are?), by getting the dependencies sorted in the manifest and VS Code. Not finished testing but seems to be going well.

Moving on from this I have found my configuration in Eclipse is providing multiple jungle files to the build process. I would like to continue this strategy with VS Code. I can get this to work from the command line but do you know what the process is to drive this from within VS Code so it will pick up this dependency when running from within the IDE? I have a base class module with its own jungle file and a number of child projects with their own specific jungle file

If I'm not mistaken, VS Code and Eclipse just automatically pass all files named "*.jungle" in the project root to the compiler command line. I'm curious how you're getting this to work in Eclipse.

And I'm curious how you're doing "subprojects" in VS Code. VS Code doesn't support subprojects as far as I know, it supports a multi-root workspace with more than one project, but each project should work the same way as it would in a single-project workspace. e.g., exporting a project should work properly.

Anyway, since you've obviously got everything else working the way you want in VS Code, I suggest

1) CTRL/CMD-SHIFT-P > Preferences - Open Workspace Settings

2) If you are actually using a multi-root workspace with multiple project, click Folder in the list of tabs (User | Workspace | Folder) and select your project, otherwise select Workspace.

3) Search for "Monkey C"

4) Edit "Monkey C: Jungle Files" and add your additional shared jungle file(s)

--

I'll explain what I usually do:

In Eclipse I used project links (which obviously only work in Eclipse) and in VS Code I use Windows symlinks (symbolic links). A symlink in Windows or Linux is basically a kind of pointer in the file system from one file/folder to another. It's like a Windows Explorer shortcut, but it's a first-class member of the file system (it requires file system support, whereas a Window Explorer shortcut is just a special kind of file). Contrast this with a hard link, which is also a kind of pointer to a file, but it's indistinguishable from the file itself (this has advantages and disadvantages).

The nice thing about project links in Eclipse is that they worked more or less transparently (although ofc you have to do everything from within Eclipse.) Everything else about Eclipse is terrible tho.

Symlink support in Windows isn't great (you have to change a system setting to be able to create them without administrator privileges), but it works. And they're source-controllable by git.

So let's say I have two projects which share a jungle file, resources and source files:

shared/||-- source/|-- resources/|-- shared.jungle||project1/|| -- source/| -- resources/| -- monkey.jungle|project2/|| -- source/| -- resources/| -- monkey.jungle

What I do is use symlinks so that the shared files and folders appear to exist under each of the two projects. e.g.

project1/|| -- source/| -- resources/| -- monkey.jungle| -->>> shared.jungle| -- shared/ | |-->>> source/ |-->>> resources/|project2/|| -- source/| -- resources/| -- monkey.jungle| -->>> shared.jungle| -- shared/ | |-->>> source/ |-->>> resources/

Since I added a link to shared.jungle to the root of each project, the compiler automatically picks it up (or rather, the VS Code / Eclipse plugin automatically adds it to the compiler command line.)

Then I change my shared.jungle so the compiler picks up the shared source folders and resource folders, as well as the project-specific folders.

You will notice that it isn't really necessary to use symlinks for the shared resource and source folders, since I can just use the jungle file to specify whatever source and resource folders I want. It does allow me to open either project1/ or project2/ independently and still see all the files that I care about.

Only problems are:

1) I need to change the developer mode setting in Windows to allow creating symlinks without admin perms

2) I need to change the VS Code setting to follow symlinks when searching files

3) VS Code git source control integration doesn't work properly with linked files/folders. In this case I end up using the command line or other tools

If you always open all your projects in a one huge workspace, and your shared folders are always available, then this kind of thing probably isn't useful for you.

--

Some Windows symlink references:

If you use git, then another possibility would be to use git subprojects. Is that what you meant?

(This would also have the advantage of allowing different projects to use different versions of the shared "module(s)", if necessary)

Thanks I hadn't spotted that setting. All seems to be working now, well compiling with no errors, I need to go through and validate the functionality now. In order to get the shared code into the sub projects I add the following to the sub project manifest

# Source

base.sourcePath = $(base.sourcePath);..\Parent\source\

# Resources

base.resourcePath = $(base.resourcePath);..\Parent\resources\

where ..\Parent holds the parent\common code project.

These means there is no need to symlink the code into the sub projects. Now with the Parent jungle added to the preferences all looks to be compiling without any warnings.

Hold the presses, there seems to be an issue now with conflicting project.manifest = manifest.xml entries within the parent and the sub project manifests. More work needed!

4. Update to SDK 6.3 and NixOS 23.05
   URL: https://github.com/samueldr/nix-connectiq/pull/1
   # Update to SDK 6.3 and NixOS 23.05

- State: open
- Author: hoh
- Created: 2023-09-07T00:37:07Z
- Updated: 2024-10-04T19:47:27Z
- Repository: samueldr/nix-connectiq
- Number: #1
- +29 -25 in 3 files
- Draft: yes
- Merge commit: 77375c8fb1e0629f35ae427a1992e864a1e754bb

---

This is a work in progress regarding the support of the latest version of the Connect IQ SDK.

I updated the version of the SDK and fixed changes in Nix variables.

I am now facing an issue of `Read-only file system`, and don't know how to solve it yet.

```shell
$ nix-shell

$ monkeyc -y developer_key.der  -f /nix/store/rsgpkgyhsrz96wyd10ha4dhbm5rgygw1-connectiq-sdk-6.3.0-2023-08-29-fc81ed416/share/connectiq-sdk/samples/Attention/monkey.jungle  -o Attention.prg
ERROR: Unable to generate default.jungle: Read-only file system
usage: monkeyc [-a <arg>] [-b <arg>] [--build-stats <arg>] [-c <arg>] [-d <arg>]
(...)
```

## Timeline

- someone committed

**samueldr** commented on 2023-09-07T02:29:36Z:

> Thanks, I'd started looking into it and got something going on my end. But not updated to this point yet.
>
> Try copying the sample in a directory with write access. My first guess here is that it would be trying to make files (think `.o`-like files) in the Nix store directory.

**samueldr** commented on 2023-09-07T02:32:07Z:

> Or, another alternative, is to try to build it using the tooling within this repo:
>
> ```
> $ nix-build -I developer_key.der=../developer_key/developer_key.der ./samples.nix -A Attention
> ```
>
> From a 3.1.8 SDK found on archive.org, I got that sample built and going on my watch as a first attempt. I like verifying that things work *as they are* before trying to update.
>
> (To install on your watch, copy the `.prg` file to the `APPS` folder)
>
> * * *
>
> Just tested, same issue, looking into it.

**samueldr** commented on 2023-09-07T03:30:37Z:

> Some clues:
>
> ```
> openat(AT_FDCWD, "/nix/store/cn6n4i2h4r5ifxvkfvla667zg7lhb58r-connectiq-sdk-6.3.0-2023-08-29-fc81ed416/share/connectiq-sdk/bin/default.jungle", O_RDWR|O_CREAT|O_EXCL, 0666
> ```
>
> > The Connect IQ SDK includes a default Jungle file, which is always applied to projects even if no custom Jungle files are present
>
> - https://developer.garmin.com/connect-iq/reference-guides/jungle-reference/#thedefaultjunglefile
>
> It looks like while it includes it, it doesn't exist by default and is generated?
>
> * * *
>
> ***ugh*** I was apparently right
>
> ```
> ~/tmp/tmp/connectiq 1 $ grep -Ri 'default\.jungle' .
> grep: ./com/garmin/monkeybrains/jungle/DefaultJungleFile.class: binary file matches
> grep: ./com/garmin/monkeybrains/jungle/DefaultJungleGenerator.class: binary file matches
> ```

**samueldr** commented on 2023-09-07T04:11:29Z:

> Okay, I think I understand the issue, and it will require figuring out more details about their SDK "helper" app...
>
> ***It's pretty ugly***.
>
> We'll need to fetch whatever `.Garmin/ConnectIQ/Devices` comes from, as `default.jungle` ends-up referring to that (to this point) bogus path.
>
> That's because the whole thing seems to assume it will run from a directory in the user's home?
>
> See 6148db9f6c9adafc264ae02c285d550cb48b0a8b, where I worked around the `default.jungle` file missing issue.
>
> @hoh how did you end-up getting the SDK filename?

- hoh mentioned

- hoh subscribed

**samueldr** commented on 2023-09-07T04:43:00Z:

> Looking at how the SDK manager works, it's requiring to be logged-in and do OAuth to fetch devices information. That's rude and prevents building automated CI pipelines. Whew.

**hoh** commented on 2023-09-08T11:23:02Z:

> Thanks for these updates and the refactoring of the commits !
>
> > @hoh how did you end-up getting the SDK filename?
>
> By browsing the URLs of the Garmin website, I found a list of all the SDK versions and version codes. I don't remember which one however.
>
> I confirm that device information is fetched from another domain that requires authentication. That is pretty annoying to automate into developer pipelines, including CI pipelines... 😐

- hoh mentioned

- hoh subscribed

**samueldr** commented on 2023-09-08T22:49:16Z:

> Do you have that link for the SDK? maybe around there, there would be other hints to help out?
>
> I think working around the devices listing (and fonts listing) can be worked around in a legit way that respects their wishes with a helper script that would handle loading via a conscious action by the end-user.
>
> If you have the full SDK installed in any other form, could you gist a listing of the `.Garmin` folder it somehow manages?
>
> That would help me tie the loose ends together and make the mechanism work.

**hoh** commented on 2024-06-18T08:38:45Z:

> Found it again: [Searching for "site:developer.garmin.com 6.3.0"](https://duckduckgo.com/?t=ffab&q=site%3Adeveloper.garmin.com+6.3.0&ia=web) returns https://developer.garmin.com/downloads/connect-iq/sdks/sdks.json

**buhl** commented on 2024-09-24T08:33:26Z:

> Hello, I have been looking a little into this and have manages to get a semi working sdk-manager installed and running so I can log in and download all things to `.Garmin/ConnectIQ` by borrowing from the sdk overlay.
>
> in overlay/default.nix
> ```nix
> manager = self.callPackage ./manager {
> libpng = super.libpng12;
> libjpeg = self.libjpeg8;
> curl = super.curl.overrideAttrs (old: {
> configureFlags = old.configureFlags ++ ["--enable-versioned-symbols"];
> });
> glib-networking = super.glib-networking;
> version = "1.0.11";
> sha256 = "sha256-NMnVdUgT5Ec5c06/OxfmGJruO8pt1MM5s38C6XWuvP4=";
> };
> ```
>
> overlay/manager/default.nix
> ```nix
> { stdenv , lib , fetchurl , openjdk , unzip , runtimeShell , autoPatchelfHook , openssl
> # autoPatchelfHook libraries
> , libusb1 , zlib , webkitgtk , xorg , libjpeg , libpng, curl, glib-networking
> # Configuration
> , version , sha256 }:
> let
> binPath = lib.makeBinPath [
> ];
> # XXX
> # /build/.Garmin/ConnectIQ/Devices
> devicesDir = "/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-TODO";
> in
> stdenv.mkDerivation rec {
> pname = "connectiq-sdk-manager";
> inherit version;
>
> src = fetchurl {
> #https://developer.garmin.com/downloads/connect-iq/sdk-manager/sdk-manager.json
> url = "https://developer.garmin.com/downloads/connect-iq/sdk-manager/connectiq-sdk-manager-linux.zip";
> inherit sha256;
> };
>
> unpackPhase = ''
> (
> mkdir -p sdk-manager
> cd sdk-manager
> unzip -qq ${src}
> )
> '';
>
> sdkpath = "${placeholder "out"}/share/sdk-manager";
>
> nativeBuildInputs = [
> unzip
> autoPatchelfHook
> openssl
> ];
>
> buildInputs = [
> libusb1
> zlib
> webkitgtk
> xorg.libXxf86vm
> libjpeg
> libpng
> curl
> glib-networking
> openssl
> ];
>
> installPhase = ''
> mkdir -p $sdkpath
> rmdir $sdkpath
> mv sdk-manager $sdkpath
>
> mkdir -p $out/bin
> for f in $sdkpath/bin/*; do
> if test -x $f; then
> echo "Adding a shim for '$f'"
> cat > "$out/bin/$(basename $f)" <  #${runtimeShell}
> PATH="${binPath}:$PATH"
> export GIO_MODULE_DIR=${glib-networking}/lib/gio/modules/
> exec "$f" "\$@"
> EOF
> chmod +x "$out/bin/$(basename $f)"
> fi
> done
> '';
>
> meta = with lib; {
> description = "Connect IQ SDK";
> homepage = "https://developer.garmin.com/connect-iq/sdk/";
> license = {
> fullName = "Connect IQ License Agreement";
> url = "https://developer.garmin.com/connect-iq/license-agreement/";
> free = false;
> };
> platforms = platforms.linux;
> };
> }
> ```
>
> With this you can start sdkmanager and download the device info. The program is gui-only as stated above but I guess it's a start.
>
> Now having everything downloaded I still have the problem of the tools trying to write to a file in the nix store
>
> ```bash
> $ monkeyc -y ./developer_key.der -f $SDK_PATH/samples/Attention/monkey.jungle -o Attention.prg
> ERROR: Error occurred while processing application jungle file: /nix/store/XXX-connectiq-sdk-7.3.1-2024-09-23-df7b5816a/share/connectiq-sdk/bin/default.jungle (Read-only file system)
> $ cat /nix/store/XXX-connectiq-sdk-7.3.1-2024-09-23-df7b5816a/share/connectiq-sdk/bin/default.jungle
> # FOR REFERENCE ONLY
> # Default Jungle file
>
> devicesPath = "/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-TODO"
>
> # Set the base properties, base represents the root of the Monkey C Project
> base.sourcePath = .\**.mc
> base.resourcePath = resources
> base.personality = resources
>
> # Set the base language-specific string resources
> ```
>
> I have no experience with garmins sdk to its pretty difficult to trouble-shoot. I hope me sharing this information can help in some way. Could ``base.sourcePath = .\**.mc`` or the MB_HOME env variable in ``/nix/store/XXX-connectiq-sdk-7.3.1-2024-09-23-df7b5816a/share/connectiq-sdk/bin/monkeyc`` be causing problems?
>
> As a side-note:
> As you can see I updated to the latest sdk-version as well
> `https://developer.garmin.com/downloads/connect-iq/sdks/sdks.json`
> `https://developer.garmin.com/downloads/connect-iq/sdk-manager/sdk-manager.json`
>
> I just took a quick look at strings in sdkmanager and found the following interesting
> ```
> ...
> %s/ciq-product-onboarding/devices?sdkManagerVersion=%d.%d.%d
> %s/ciq-product-onboarding/fonts
> %s/ciq-product-onboarding/fonts/font?fontName=%s
> %s/ciq-product-onboarding/devices/%s/ciqInfo
> %s/downloads/connect-iq/sdk-manager/
> sdk-manager.json
> %s/downloads/connect-iq/sdks/sdks.json
> %s/downloads/connect-iq/sdks/%s
> ...
> https://api.gcs.garmin.com
> https://api.gcs.stage.garmin.com
> https://api.gcs.test.garmin.com
> https://services.garmin.com
> https://servicesstg.garmin.com
> https://servicestest.garmin.com
> https://developer.garmin.com
> https://monkeynet.garmin.com
> ```
> I does look like you have to be logged in to download the device files which pretty much ends CI. But if I could just get everything working (including building and simulating) on my machine I think the CI part would be solvable to some degree at a later state.

**dmadisetti** commented on 2024-10-01T16:27:32Z:

> Webkit won't render the login for me, which is annoying- but you can hit the endpoint manually and grab a service ticket:
>
> https://ssotest.garmin.com/sso/embed/?locale=en&socialEnabled=true&createAccountShown=true&clientId=ConnectIqSdkManager&consentTypeIds=GLOBAL_PRIVACY_POLICY&locati%20%20%20%20%20%20onPromptShown=true&reauth=true&rememberMeChecked=false
>
> to check and
>
> https://sso.garmin.com/sso/embed/?locale=en&socialEnabled=true&createAccountShown=true&clientId=ConnectIqSdkManager&consentTypeIds=GLOBAL_PRIVACY_POLICY&locati%20%20%20%20%20%20onPromptShown=true&reauth=true&rememberMeChecked=false
>
> For the login page.
>
> Next step would be applying the service ticket. Probably just dumped somewhere in `.Garmin` yeah?

**dmadisetti** commented on 2024-10-04T00:53:02Z:

> Got webkit working on sway, and tweaked the manager install script to hook in some curl debugging with gdb:
>
> ---
> ```nix
> installPhase = ''
> mkdir -p $out/bin
> mkdir -p $out/share
> mv sdkmanager $out/share
>
> gdbScript="$out/debug.gdb"
> cat > $gdbScript <  set environment GIO_MODULE_DIR=${glib-networking}/lib/gio/modules
>
> # Set a breakpoint at curl_easy_setopt to capture its address
> break curl_easy_setopt
> run
> # Only one call to curl_easy_setopt I think.
> # Step out and record as a good place to hook in.
> step
> set \$ip_address = \$rip
> delete 1
> break *\$ip_address
> commands
> silent
> # Enable CURLOPT_VERBOSE (0x29)
> call (int) curl_easy_setopt(\$rdi, 0x29, 1L)
> continue
> end
> continue
> EOF
>
> for f in $out/share/sdkmanager/bin/*; do
> cat > "$out/bin/$(basename $f)" <  #${runtimeShell}
> PATH="${binPath}:$PATH" \
> gdb --batch -x $gdbScript --args "$f" "\$@"
> EOF
> chmod +x "$out/bin/$(basename $f)"
> done
> '';
> ```
> ---
>
> Capture curl debug info with `sdkmanager 2> log.err`
>
> ---
>
> ## Findings:
>
> After login, manager hits `https://developer.garmin.com/downloads/connect-iq/sdks/sdks.json`
>
> Which is expected. It then hits `api.gcs.garmin.com/ciq-product-onboarding/devices?sdkManagerVersion=1.0.11`
> Here's the curl signature:
>
> ```txt
> * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
> * ALPN: server accepted h2
> * Server certificate:
> * subject: C=US; ST=California; L=San Francisco; O=Cloudflare, Inc.; CN=api.gcs.garmin.com
> * start date: Mar 28 00:00:00 2024 GMT
> * expire date: Dec 31 23:59:59 2024 GMT
> * subjectAltName: host "api.gcs.garmin.com" matched cert's "api.gcs.garmin.com"
> * issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
> * SSL certificate verify ok.
> * using HTTP/2
> * h2 [:method: GET]
> * h2 [:scheme: https]
> * h2 [:authority: api.gcs.garmin.com]
> * h2 [:path: /ciq-product-onboarding/devices?sdkManagerVersion=1.0.11]
> * h2 [accept: application/json]
> * h2 [authorization: Bearer]
> * Using Stream ID: 1
> > GET /ciq-product-onboarding/devices?sdkManagerVersion=1.0.11 HTTP/2^M
> Host: api.gcs.garmin.com^M
> Accept: application/json^M
> Authorization: Bearer ^M
> ```
>
>
> Which is easily replicated with just
>
> ```bash
> curl -X GET "https://api.gcs.garmin.com/ciq-product-onboarding/devices?sdkManagerVersion=1.0.11" \
> -H "Accept: application/json" \
> -H "Authorization: Bearer "
> ```
>
> I'd attached the devices dump
> [devices.json](https://github.com/user-attachments/files/17251459/devices.json)
>
> ---
>
> When downloading a device package (example Forerunner 645 below), the signature is:
>
> ```txt
> * Connection #0 to host api.gcs.garmin.com left intact
> * processing: https://api.gcs.garmin.com/ciq-product-onboarding/devices/006-B2886-00/ciqInfo
> * Trying 104.16.9.45:443...
> * Connected to api.gcs.garmin.com (104.16.9.45) port 443
> * ALPN: offers h2,http/1.1
> * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
> * ALPN: server accepted h2
> * Server certificate:
> * subject: C=US; ST=California; L=San Francisco; O=Cloudflare, Inc.; CN=api.gcs.garmin.com
> * start date: Mar 28 00:00:00 2024 GMT
> * expire date: Dec 31 23:59:59 2024 GMT
> * subjectAltName: host "api.gcs.garmin.com" matched cert's "api.gcs.garmin.com"
> * issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
> * SSL certificate verify ok.
> * using HTTP/2
> * h2 [:method: GET]
> * h2 [:scheme: https]
> * h2 [:authority: api.gcs.garmin.com]
> * h2 [:path: /ciq-product-onboarding/devices/006-B2886-00/ciqInfo]
> * h2 [accept: */*]
> * h2 [authorization: Bearer]
> * Using Stream ID: 1
> > GET /ciq-product-onboarding/devices/006-B2886-00/ciqInfo HTTP/2^M
> Host: api.gcs.garmin.com^M
> Accept: */*^M
> Authorization: Bearer ^M
> ```
>
> Which is a zip (curl works here too):
>
> ```
> Archive: dump.zip
> inflating: compiler.json
> inflating: fr645.png
> inflating: menu2_check_off_vegas.png
> inflating: menu2_check_on_vegas.png
> inflating: menu2_toggle_off_left_240x240.png
> inflating: menu2_toggle_off_right_240x240.png
> inflating: menu2_toggle_on_left_240x240.png
> inflating: menu2_toggle_on_right_240x240.png
> inflating: simulator.json
> inflating: fr645.bin
> inflating: fr645.api.debug.xml
> ```
>
> which are just the contents found in `.Garmin/ConnectIQ/Devices/fr645/`
> Font files are listed in simulator, and the path to download the fonts is: `https://api.gcs.garmin.com/ciq-product-onboarding/fonts/font?fontName= `
> Which should give you another zip.
>
> ---
>
> Not sure how to turn the service_ticket into a bearer token. Not sure whether there's a standard way or if I need to open up gdb again. Also not certain the lifetime of a bearer token, but my tokens haven't expired within the last few hours

**samueldr** commented on 2024-10-04T19:17:41Z:

> Thank you for taking a look at all that, and more importantly, sharing notes! Any note like these helps the next person who might have an idea!

**samueldr** commented on 2024-10-04T19:23:58Z:

> I'm not looking into that at this moment, but I had a thought: anyone else using one of those API endpoints?
>
> GitHub has a surprisingly good search engine with this one weird trick: search using regexes:
>
> - https://github.com/search?q=%2Fciq-product-onboarding%2F&type=code
> - https://github.com `/hatl/hasscontrol/pull/36/`
> - https://github.com `/scrapper/postrunner/issues/45`
>
> [links munged to prevent needless backlinking]
>
> I haven't looked at the content of the links much, but if it's not been solved on their ends either, at least it seems there might be more use than the SDK. Though one `hasscontrol` repo *sure* looks like it handles building Monkey C in CI :thinking:.

**samueldr** commented on 2024-10-04T19:31:47Z:

> Sorry for the repeated pings, but it looks like I *had* made some notes a while back. But never shared? Those notes do include tokens and misc wrong unfinished stuff.
>
> `https://services.garmin.com/api/oauth/token?client_id=CIQ_SDK_MANAGER`
>
> The following one gave me a `Bearer` token (at the time valid for 90 days).
>
> ```
> $ curl -i -H "Accept: application/json" -X POST -d \
> 'grant_type=service_ticket&client_id=CIQ_SDK_MANAGER&service_ticket=ST-XXXXXXXX-xxXXxxXXxxXXxxXX-cas&service_url=https://sso.garmin.com/sso/embed' \
> 'https://services.garmin.com/api/oauth/token'
> ```
>
> Looking at my notes, I hadn't yet tracked down a useful endpoint to use that with, but I'm pretty sure I just stopped early because I didn't really need any of that at that point in time.
>
> * * *
>
> And linked in that other PR, https://github.com/lindell/connect-iq-sdk-manager-cli

**dmadisetti** commented on 2024-10-04T19:47:25Z:

> Oh. Yeah. Good search. I think folding this in is probably the best bet: https://github.com/lindell/connect-iq-sdk-manager-cli
>
> But yes, I think the curl you posted also completes E2E if someone wanted to roll their own.
>
> A nice thing about the `devices.json` and `fonts.json` dump is that they include the md5 of the expected artifacts. So, a cache could be prepopulated, or nix could use fetchurl on the fly.
>
> Kinda want to build my app first, before getting bogged down in workflow- but I think this is a solid start for anyone who wants to pick it back up

5. Building clock with Barrel in CLI fails - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/221157/building-clock-with-barrel-in-cli-fails
   Building clock with Barrel in CLI fails - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- State Verified Answer
- Replies 5 replies
- Subscribers 18 subscribers
- Views 1325 views
- Users 0 members are here

Options

Related

# Building clock with Barrel in CLI fails

When I build a clock without barrels, it just builds in the CLI. When I build it with a barrel, it fails; while it does just build & run when I build it within Eclipse. It also runs my barrel code, so all should be fine.

I'm getting this error:

```
com.garmin.connectiq.common.manifest.ManifestException: [102] The Monkey Barrel dependency MyBarrel (version 0.0.0) could not be resolved. Please make sure the barrel path was linked to within the main application's jungle file.
at com.garmin.monkeybrains.ProjectBuilder.createBarrelContexts(ProjectBuilder.java:426)
at com.garmin.monkeybrains.Monkeybrains.runPRGCompiler(Monkeybrains.java:1234)
at com.garmin.monkeybrains.Monkeybrains.compileApplication(Monkeybrains.java:1024)
at com.garmin.monkeybrains.Monkeybrains.run(Monkeybrains.java:2317)
at com.garmin.monkeybrains.Monkeybrains.simpleMain(Monkeybrains.java:254)
at com.garmin.monkeybrains.Monkeybrains.simpleMain(Monkeybrains.java:236)
at com.garmin.monkeybrains.Monkeybrains.main(Monkeybrains.java:283)
```

I guess I'm missing something in the jungle file, but I have no clue what I should add. Can't find anything in the docs about it. Anyone that knows what I'm doing wrong here?

- Sign in to reply
- Cancel

## Top Replies

- Mine looks just the same.I've been trying some other stuff, and it appears I needed to do this:monkeyc -d venu -o bin/example.prg -f "monkey.jungle;barrels.jungle"Wrapping them in quotes made…

## All Replies

What is the command line you're using? Looking at the command line used in eclipse I see this where the path for both barrels.jungle and monkey.jungle are included:

-f C:\Users\James\workspace-prod\SimLeanWU\barrels.jungle;C:\Users\James\workspace-prod\SimLeanWU\monkey.jungle

Interesting! Didn't know I had to combine both files after the -f.However, still can't get it to work, not sure how I should combine these on Mac (zsh).

I'm no running this:monkeyc -d venu -f monkey.jungle -o bin/example.prg

Which works when I copy the lines from the barrels.jungle into the monkey.jungle file. But that isn't the right way I think.

When I run this, it fails as well. So I'll need a way to combine both jungle files.monkeyc -d venu -f monkey.jungle;barrels.jungle -o bin/example.prgIs there a way to inspect the commands Eclipse is using underneath?

Ok found that, should've looked harder before answering ;)

Eclipse is -f at the end. If I didn't do that, it could find the -o flag. But now, when I run "monkeyc -d venu -o bin/example.prg -f monkey.jungle;barrels.jungle" I get the following error: ./monkey.jungle: line 1: project.manifest: command not foundIt still seems my zsh doesn't combine the two files correctly and tries to run ./monkey.jungle as a separate command. Not sure why Eclipse is able to run it as one command.

Eclipse is showing the exact command it's using.what's the contents of your monkey.jungle file?

for mine that don't use my own jungles, it's just

project.manifest = manifest.xml

for the barrels.jungle (with 2 barrels) it's this:

```
# Do not hand edit this file. To make changes right click
# on the project and select "Configure Monkey Barrels".

BgWu = C:\Users\James\workspace-prod\BgWu\bin\BgWu.barrel
base.barrelPath = $(base.barrelPath);$(BgWu)

BgSettings = C:\Users\James\workspace-prod\BgSettings\bin\BgSettings.barrel
base.barrelPath = $(base.barrelPath);$(BgSettings)

```

Mine looks just the same.

I've been trying some other stuff, and it appears I needed to do this:monkeyc -d venu -o bin/example.prg -f "monkey.jungle;barrels.jungle"Wrapping them in quotes made it work. Strange that Eclipse doesn't need to do that while running the command. Anyway, I'm glad I've found the issue.Thank you so much for pointing me in the right direction!

Starter Garmin Watch App

Update to SDK 6.3 and NixOS 23.05


I still cannot execute the actual build because `monkeyc` is not installed in this environment and the SDK cannot be installed automatically here. However, I did a thorough static review and fixed several likely compile/runtime issues. Here is the result of **step 1** (build prep / static check):

## Static review fixes applied

| Issue | Fix |
|---|---|
| `Fenix5App` called `delegate.setView(view)` which no longer exists | Removed the extra call |
| `discipline.equals("CWT")` on a `Lang.String` may not compile cleanly | Replaced with `index == 0` check |
| `Graphics.FONT_NUMBER_HOT` may not exist in older SDK/device files | Replaced with `Graphics.FONT_NUMBER_MEDIUM` in `DepthView`, `StaticView`, `BreathingView` |
| Session timers not nulled after stopping, could restart old stopped timer | Set `timer = null` in `onHide`, `finishSession`, and `discardSession` for all four session views |
| View transition helper scattered across files | Centralized `MainMenuView.switchToMainMenu(slideDir)`; verified all return paths use it |

## What a real build needs

On a machine with the Connect IQ SDK installed, run:

```bash
cd ~/code/garmin-freedive-app
./build_fenix5.sh
```

This will produce:
- `bin/Freedive_fenix5.prg`
- `bin/Freedive_fenix5s.prg`
- `bin/Freedive_fenix5x.prg`
- `bin/Freedive_fenix5plus.prg`
- `bin/Freedive_fenix5splus.prg`
- `bin/Freedive_fenix5xplus.prg`

## What I expect the SDK to catch when you build

1. **Possible warnings** about unused imports like `Toybox.Lang` in views — harmless.
2. **Possible strict-type warnings** with `Method.invoke()` and method references. If you enable strict type checking, some may need explicit casts.
3. **Font fit on screen** — `FONT_NUMBER_MEDIUM` may look smaller; may need layout tuning after simulator/device testing.
4. **Depth accuracy** — needs real-device calibration test in pool/open water.

## What you need to do next

Please run `./build_fenix5.sh` on your own machine and paste back any compile errors or warnings. I will fix them immediately. If the build succeeds, run it in the simulator (`monkeydo`) or sideload to your fēnix 5 and report any runtime bugs.

Want me to also add a `Makefile` or VS Code launch config to make building easier?


❯ ./build_fenix5.sh
Connect IQ SDK not found.
Install the SDK Manager, download an SDK + devices, then try again.


The user ran `./build_fenix5.sh` and got the error "Connect IQ SDK not found." because `~/.Garmin/ConnectIQ/current-sdk.cfg` doesn't exist. The SDK Manager needs to be installed. I should guide them through installing it. Since context-mode is active, I should use ctx_execute for commands and ctx_fetch_and_index for web docs if needed.

The build script can't find the Connect IQ SDK because `~/.Garmin/ConnectIQ/current-sdk.cfg` doesn't exist yet. You need to install the SDK Manager, download an SDK, and install the fēnix 5 device files.

## Installation steps (Linux / your machine)

1. **Download the SDK Manager**
   - Go to: https://developer.garmin.com/connect-iq/sdk/
   - Download the **Connect IQ SDK Manager** for Linux.
   - Extract it and run `sdkmanager`.

2. **Log in with your Garmin developer account**
   - The SDK Manager requires a Garmin account to download SDKs and device files.

3. **Download the latest SDK**
   - In the SDK Manager, select the latest SDK (e.g., 6.x or 7.x) and install it.

4. **Install fēnix 5 device files**
   - In the SDK Manager, install device files for:
     - `fenix5`
     - `fenix5s`
     - `fenix5x`
     - `fenix5plus`
     - `fenix5splus`
     - `fenix5xplus`

5. **Verify `current-sdk.cfg` exists**
   After installation, run:
   ```bash
   cat ~/.Garmin/ConnectIQ/current-sdk.cfg
   ```
   It should print a path like:
   ```
   /home/yourname/.Garmin/ConnectIQ/Sdks/connectiq-sdk-x.x.x
   ```

6. **Re-run the build**
   ```bash
   cd ~/code/garmin-freedive-app
   ./build_fenix5.sh
   ```

I can also update the build script to provide a clearer error message and suggest these exact steps. Should I do that?


❯ connectiq-sdk-manager --help
00:42:20: Error: Unable to initialize GTK+, is DISPLAY set properly?

i installed via AUR on arch


The user is trying to run the Garmin Connect IQ SDK Manager on Arch Linux via AUR, but it's failing with a GTK display error. This is a headless environment issue. The SDK Manager is a GUI app that needs a display.

I need to help them work around this. Options:
1. Run it with a display (if they have a desktop environment, use the terminal from there, or set DISPLAY=:0 if running remotely via SSH)
2. Use the command-line SDK manager if available
3. Check if there's a CLI tool or download SDK manually
4. Use a VNC/remote desktop if headless server

Since context-mode is active, I should use ctx_execute for system investigation. Let me check what's installed and environment variables.

But first, I should give practical advice. The SDK Manager GUI needs a display. If they're SSH'd into a machine, they need X forwarding or run it locally on the desktop.

I should investigate what the AUR package installed and if there are CLI alternatives.

echo "DISPLAY=$DISPLAY"
echo "---"
echo "SDK Manager locations:"
which connectiq-sdk-manager 2>/dev/null || true
which sdkmanager 2>/dev/null || true
ls -la /usr/bin/*connectiq* 2>/dev/null || true
ls -la /opt/*connectiq* 2>/dev/null || true
ls -la ~/.Garmin/ConnectIQ/ 2>/dev/null || true
echo "---"
echo "AUR package files:"
pacman -Ql connectiq-sdk-manager 2>/dev/null | head -50 || true


```shell
echo "DISPLAY=$DISPLAY"
echo "---"
echo "SDK Manager locations:"
which connectiq-sdk-manager 2>/dev/null || true
which sdkmanager 2>/dev/null || true
ls -la /usr/bin/*connectiq* 2>/dev/null || true
ls -la /opt/*connectiq* 2>/dev/null || true
ls -la ~/.Garmin/ConnectIQ/ 2>/dev/null || true
echo "---"
echo "AUR package files:"
pacman -Ql connectiq-sdk-manager 2>/dev/null | head -50 || true
```

DISPLAY=
---
SDK Manager locations:
/usr/bin/connectiq-sdk-manager
sdkmanager not found
-rwxr-xr-x 1 root root 7732976 Jul 27 00:40 /usr/bin/connectiq-sdk-manager
---
AUR package files:
connectiq-sdk-manager /usr/
connectiq-sdk-manager /usr/bin/
connectiq-sdk-manager /usr/bin/connectiq-sdk-manager
connectiq-sdk-manager /usr/share/
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/acknowledgements.html
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/changes.html
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/connectiq-icon.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-available-updates.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-collapse-arrow.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-delete.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-download.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-expanded-arrow.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-future.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-new.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-processing-1.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-processing-2.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-processing-3.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-processing-4.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-processing-5.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-processing-6.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-processing-7.png
connectiq-sdk-manager /usr/share/connectiq-sdk-manager/icons8-processing-8.png
connectiq-sdk-manager /usr/share/licenses/
connectiq-sdk-manager /usr/share/licenses/connectiq-sdk-manager/
connectiq-sdk-manager /usr/share/licenses/connectiq-sdk-manager/LICENSE


The SDK Manager is a GUI app and DISPLAY is empty. The user is likely running in a headless SSH session or terminal without a display. I need to give them practical options:

1. If they have a desktop on the machine, run it from a graphical terminal or set DISPLAY=:0
2. If they're SSH'd in, use X11 forwarding: ssh -X
3. If truly headless, install a minimal display server or use a CLI alternative
4. Check if there's a CLI version of the SDK manager

I should also look for whether the AUR package includes any CLI tools or if there's a way to download SDKs without the GUI. I know from the web search earlier that there are community efforts like connect-iq-sdk-manager-cli. But the official way requires the GUI.

Actually, I should check if maybe the SDK Manager binary has CLI flags. Some versions support command-line download. Let me check --help output properly with a fake display or check strings.

Also, the AUR package might not have downloaded any SDK or devices yet - it just installed the manager binary.

Let me investigate the binary for CLI flags and also check if there's a headless way.

# Try to list help options by running with a fake display via xvfb if available
which xvfb-run 2>/dev/null || true

# Check if xorg-server-xvfb is installed
pacman -Q | grep -i xvfb || true

# Try xvfb-run if available
if command -v xvfb-run >/dev/null 2>&1; then
  echo "=== xvfb-run connectiq-sdk-manager --help ==="
  xvfb-run connectiq-sdk-manager --help 2>&1 | head -80 || true
fi

echo "=== strings check for CLI flags ==="
strings /usr/bin/connectiq-sdk-manager 2>/dev/null | grep -E "^--[a-z]|^-[a-z]" | sort -u | head -50 || true


```shell
# Try to list help options by running with a fake display via xvfb if available
which xvfb-run 2>/dev/null || true

# Check if xorg-server-xvfb is installed
pacman -Q | grep -i xvfb || true

# Try xvfb-run if available
if command -v xvfb-run >/dev/null 2>&1; then
  echo "=== xvfb-run connectiq-sdk-manager --help ==="
  xvfb-run connectiq-sdk-manager --help 2>&1 | head -80 || true
fi

echo "=== strings check for CLI flags ==="
strings /usr/bin/connectiq-sdk-manager 2>/dev/null | grep -E "^--[a-z]|^-[a-z]" | sort -u | head -50 || true
```

xvfb-run not found
=== strings check for CLI flags ===
-a.:/
-dark
-j;(
-uCH


The SDK Manager is a GUI-only Electron/Java app with no obvious CLI flags. The user has a few options:

1. Run it on a machine with a display (graphical terminal)
2. Use SSH X11 forwarding if their local machine has a display
3. Install xvfb-run (xorg-server-xvfb) on Arch and run it headless
4. Use a community CLI tool like connect-iq-sdk-manager-cli

Since the user is on Arch and installed via AUR, the simplest path is probably:
- If they're SSH'd in, use `ssh -X` or `ssh -Y` from a machine with a display
- Or install `xorg-server-xvfb` and run `xvfb-run connectiq-sdk-manager`

I should give clear instructions for each. Also mention that they need to log in to Garmin through the SDK Manager GUI, then download SDK + device files.

Actually, I should also verify whether the SDK Manager binary itself is just a wrapper or has any hidden options. But the strings output was minimal and not helpful.

Let me provide practical guidance.

The AUR package installed only the GUI SDK Manager binary. `DISPLAY=` means you’re running in a headless terminal (likely SSH). The SDK Manager needs a graphical display to launch.

## Quick fix options

### Option 1: SSH with X11 forwarding (easiest if you have a Linux/Mac desktop)
From your local machine with a display, reconnect with forwarding:

```bash
ssh -Y youruser@yourserver
```

Then in that SSH session:

```bash
connectiq-sdk-manager
```

The GUI should open on your local desktop.

### Option 2: Run it directly on a graphical session
If the Arch machine has a desktop (GNOME/KDE/etc.), open a terminal **inside the desktop** (not via SSH) and run:

```bash
connectiq-sdk-manager
```

### Option 3: Fake display with Xvfb (run headless on the server)

Install Xvfb:

```bash
sudo pacman -S xorg-server-xvfb
```

Then run:

```bash
xvfb-run connectiq-sdk-manager
```

It may still fail if it tries to open a browser for Garmin login, but worth trying.

### Option 4: Community CLI SDK manager
There is an unofficial command-line SDK manager:
https://github.com/lindell/connect-iq-sdk-manager-cli

This may let you download SDKs and device files without a GUI. I have not tested it myself.

## Once the SDK Manager opens

1. Log in with your Garmin account.
2. Download the **latest Connect IQ SDK**.
3. Download device files for **fenix5, fenix5s, fenix5x, fenix5plus, fenix5splus, fenix5xplus**.
4. Verify:
   ```bash
   cat ~/.Garmin/ConnectIQ/current-sdk.cfg
   ```
5. Re-run:
   ```bash
   cd ~/code/garmin-freedive-app
   ./build_fenix5.sh
   ```

Which option fits your setup? Are you SSH’d into a headless server, or does the Arch machine have a desktop?


need to compile on different computer since i tried on lxc container without  gtk. can you rewrite the steps i need to do write now


The user needs a clean checklist of steps to build the fēnix 5 app on a different computer (one with a GUI / proper Connect IQ SDK support). I should write concise, actionable steps. I can use normal tools; no need for context-mode here since the task is small. Let me also update the build script error message to be clearer about what to do, and maybe add a note about running on a machine with GUI.

Here is the exact checklist to build the fēnix 5 v2 app on a GUI-capable machine:

## Build checklist for a different computer

### 1. Copy the project to the build machine

```bash
# From the current machine
scp -r ~/code/garmin-freedive-app youruser@build-machine:~/garmin-freedive-app
```

Or use `rsync`, a USB stick, or Git.

### 2. On the build machine: install the Connect IQ SDK Manager

- Download the SDK Manager from: https://developer.garmin.com/connect-iq/sdk/
- Install it (Linux `.zip` has a runnable binary, or use the AUR package if on Arch).
- **Important:** the SDK Manager needs a graphical display. Run it on the desktop, not in a headless container/SSH without X11.

### 3. Open the SDK Manager and log in

```bash
connectiq-sdk-manager
```

Log in with your Garmin developer account.

### 4. Download required components

In the SDK Manager:

- **SDK:** install the latest Connect IQ SDK.
- **Devices:** install these device files:
  - `fenix5`
  - `fenix5s`
  - `fenix5x`
  - `fenix5plus`
  - `fenix5splus`
  - `fenix5xplus`

Wait for downloads to finish.

### 5. Verify the SDK is recognized

```bash
cat ~/.Garmin/ConnectIQ/current-sdk.cfg
```

You should see a path like:

```
/home/youruser/.Garmin/ConnectIQ/Sdks/connectiq-sdk-x.x.x
```

### 6. Build the app

```bash
cd ~/garmin-freedive-app
./build_fenix5.sh
```

If successful, you will see `.PRG` files in `bin/`:

```
bin/Freedive_fenix5.prg
bin/Freedive_fenix5s.prg
bin/Freedive_fenix5x.prg
bin/Freedive_fenix5plus.prg
bin/Freedive_fenix5splus.prg
bin/Freedive_fenix5xplus.prg
```

### 7. Sideload to your watch

1. Connect the fēnix 5 to the computer with USB.
2. On the watch, confirm **Mass Storage / USB mode**.
3. Copy the `.PRG` matching your watch model to `GARMIN/APPS/` on the watch drive.
4. Safely eject, disconnect, then launch the app on the watch.

### 8. (Optional) Test in simulator first

Start the simulator:

```bash
connectiq
```

Then run:

```bash
monkeydo bin/Freedive_fenix5.prg fenix5
```

---

I will also update `build_fenix5.sh` to print this clearer checklist when the SDK is missing.

edit

if [ ! -f "$SDK_CFG" ]; then
    echo "Connect IQ SDK not found at $SDK_CFG."
    echo ""
    echo "To build this app you need the Garmin Connect IQ SDK on a GUI-capable machine:"
    echo "  1. Download the SDK Manager from https://developer.garmin.com/connect-iq/sdk/"
    echo "  2. Run connectiq-sdk-manager and log in with your Garmin account."
    echo "  3. Install the latest SDK and the fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus device files."
    echo "  4. Verify ~/.Garmin/ConnectIQ/current-sdk.cfg exists and points to the SDK."
    echo "  5. Re-run ./build_fenix5.sh."
    echo ""
    echo "Note: the SDK Manager requires a graphical display; it will not run in a headless container without X11/Xvfb."
    exit 1
fi

if [ ! -f "$SDK_CFG" ]; then
    echo "Connect IQ SDK not found."
    echo "Install the SDK Manager, download an SDK + devices, then try again."
    exit 1
fi


Successfully replaced 1 block(s) in /home/liph/code/garmin-freedive-app/build_fenix5.sh.

    ...
 11
 12 SDK_CFG="$HOME/.Garmin/ConnectIQ/current-sdk.cfg"
 13
 14 if [ ! -f "$SDK_CFG" ]; then
-15     echo "Connect IQ SDK not found."
-16     echo "Install the SDK Manager, download an SDK + devices, then try again."
+15     echo "Connect IQ SDK not found at $SDK_CFG."
+16     echo ""
+17     echo "To build this app you need the Garmin Connect IQ SDK on a GUI-capable machine:"
+18     echo "  1. Download the SDK Manager from https://developer.garmin.com/connect-iq/sdk/"
+19     echo "  2. Run connectiq-sdk-manager and log in with your Garmin account."
+20     echo "  3. Install the latest SDK and the fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus device files."
+21     echo "  4. Verify ~/.Garmin/ConnectIQ/current-sdk.cfg exists and points to the SDK."
+22     echo "  5. Re-run ./build_fenix5.sh."
+23     echo ""
+24     echo "Note: the SDK Manager requires a graphical display; it will not run in a headless container without X11/Xvfb."
 17     exit 1
 18 fi
 19
 20 SDK=$(cat "$SDK_CFG")
    ...

--- /home/liph/code/garmin-freedive-app/build_fenix5.sh
+++ /home/liph/code/garmin-freedive-app/build_fenix5.sh
@@ -11,10 +11,18 @@

 SDK_CFG="$HOME/.Garmin/ConnectIQ/current-sdk.cfg"

 if [ ! -f "$SDK_CFG" ]; then
-    echo "Connect IQ SDK not found."
-    echo "Install the SDK Manager, download an SDK + devices, then try again."
+    echo "Connect IQ SDK not found at $SDK_CFG."
+    echo ""
+    echo "To build this app you need the Garmin Connect IQ SDK on a GUI-capable machine:"
+    echo "  1. Download the SDK Manager from https://developer.garmin.com/connect-iq/sdk/"
+    echo "  2. Run connectiq-sdk-manager and log in with your Garmin account."
+    echo "  3. Install the latest SDK and the fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus device files."
+    echo "  4. Verify ~/.Garmin/ConnectIQ/current-sdk.cfg exists and points to the SDK."
+    echo "  5. Re-run ./build_fenix5.sh."
+    echo ""
+    echo "Note: the SDK Manager requires a graphical display; it will not run in a headless container without X11/Xvfb."
     exit 1
 fi

 SDK=$(cat "$SDK_CFG")


Done — I updated `build_fenix5.sh` so the next time you run it without the SDK, it prints the exact steps above.

Let me know once you have the `.PRG` built (or paste any compile errors here), and I’ll continue with the next step.


❯ ./build_fenix5.sh
Generating a new developer key (developer_key.der)...
Building for fēnix 5...
ERROR: Invalid device id specified: 'fenix5'.
usage: monkeyc [-a <arg>] [-b <arg>] [--build-stats <arg>] [-d <arg>]
       [--debug-log-level <arg>] [--debug-log-output <arg>]
       [--disable-api-has-check-removal] [--disable-v2-opcodes] [-e]
       [--Eno-invalid-symbol] [-f <arg>] [-g] [-h] [-i <arg>] [-k] [-l <arg>]
       [-m <arg>] [--no-gen-styles] [-o <arg>] [-O <arg>] [-p <arg>] [-r] [-t]
       [-u <arg>] [-v] [-w] [-x <arg>] [-y <arg>] [-z <arg>]
-a,--apidb <arg>                  API import file
-b,--apimir <arg>                 API MIR file
   --build-stats <arg>            Print build stats [0=basic]
-d,--device <arg>                 Target device
   --debug-log-level <arg>        Debug logging verbosity [0=errors, 1=basic,
                                  2=intermediate, 3=verbose]
   --debug-log-output <arg>       Output log zip file
   --disable-api-has-check-removalDo not optimize out API has checks
   --disable-v2-opcodes           Do not use the v2 opcodes
-e,--package-app                  Create an application package.
   --Eno-invalid-symbol           Do not error when a symbol is found to be
                                  invalid
-f,--jungles <arg>                Jungle files
-g,--debug                        Print debug output
-h,--help                         Prints help information
-i,--import-dbg <arg>             Import api.debug.xml
-k,--profile                      Enable profiling support
-l,--typecheck <arg>              Type check [0=off, 1=gradual, 2=informative,
                                  3=strict]
-m,--manifest <arg>               Manifest file (deprecated)
   --no-gen-styles                Do not generate Rez.Styles module
-o,--output <arg>                 Output file to create
-O,--optimization <arg>           Optimization level [0=none, 1=basic, 2=fast
                                  optimizations, 3=slow optimizations]
                                  [p=optimize performance, z=optimize code
                                  space]
-p,--project-info <arg>           projectInfo.xml file to use when compiling
-r,--release                      Strip debug information
-t,--unit-test                    Enables compilation of unit tests
-u,--devices <arg>                devices.xml file to use when compiling
                                  (deprecated)
-v,--version                      Prints the compiler version
-w,--warn                         Show compiler warnings
-x,--excludes <arg>               Add annotations to the exclude list
                                  (deprecated)
-y,--private-key <arg>            Private key to sign builds with
-z,--rez <arg>                    Resource files (deprecated)

i believe i have the fenix 5 plus


The user has the fenix 5 Plus. The build failed because the SDK doesn't recognize `fenix5` as a device id. This is likely because the SDK device IDs are different. I need to check what device IDs are available in the SDK devices folder and update the build script and manifest accordingly. Also, the user said they have a fenix 5 Plus, so we can focus on that device first.

I should:
1. Check the SDK devices folder to find valid device IDs.
2. Update the manifest to only include fenix 5 Plus variants if that's what they have, or update the build script to use valid IDs.
3. The manifest already includes all fenix 5 variants, but the build script tries to build all of them. Since the user only has fenix 5 Plus, I should make the build script build only the relevant one(s), or at least update the device IDs to match SDK conventions.

Let me first check the devices directory.

# Find Garmin SDK devices folder and list fenix 5 device IDs
DEVICES_DIR="$HOME/.Garmin/ConnectIQ/Devices"
if [ -d "$DEVICES_DIR" ]; then
  echo "Devices found:"
  ls "$DEVICES_DIR" | grep -i fenix || true
else
  echo "No ~/.Garmin/ConnectIQ/Devices directory"
fi

# Also check SDK path from current-sdk.cfg
SDK_CFG="$HOME/.Garmin/ConnectIQ/current-sdk.cfg"
if [ -f "$SDK_CFG" ]; then
  SDK=$(cat "$SDK_CFG")
  echo "---"
  echo "SDK devices.xml entries with fenix:"
  grep -i 'fenix5' "$SDK/bin/devices.xml" 2>/dev/null | head -20 || true
fi


```shell
# Find Garmin SDK devices folder and list fenix 5 device IDs
DEVICES_DIR="$HOME/.Garmin/ConnectIQ/Devices"
if [ -d "$DEVICES_DIR" ]; then
  echo "Devices found:"
  ls "$DEVICES_DIR" | grep -i fenix || true
else
  echo "No ~/.Garmin/ConnectIQ/Devices directory"
fi

# Also check SDK path from current-sdk.cfg
SDK_CFG="$HOME/.Garmin/ConnectIQ/current-sdk.cfg"
if [ -f "$SDK_CFG" ]; then
  SDK=$(cat "$SDK_CFG")
  echo "---"
  echo "SDK devices.xml entries with fenix:"
  grep -i 'fenix5' "$SDK/bin/devices.xml" 2>/dev/null | head -20 || true
fi
```

No ~/.Garmin/ConnectIQ/Devices directory


The output shows no devices directory and no SDK devices.xml entries because the user is building on a different machine. But I can see the build is using monkeyc. The error says 'fenix5' is an invalid device id. This could be because:
1. The SDK installed is very new and uses different device IDs (e.g., fenix5 is deprecated/removed from devices.xml).
2. The user needs to download device files first.

Actually, looking at the error, the SDK was found, monkeyc ran, but it doesn't know fenix5. This is likely because the device files weren't downloaded/installed via SDK Manager. The SDK Manager downloads device files into ~/.Garmin/ConnectIQ/Devices/. Without those, monkeyc can't build for those devices.

Since the user has a fenix 5 Plus, I should:
1. Tell them to download the specific device files in SDK Manager.
2. Update the build script to build only fenix5plus (and maybe other variants if available), but first verify available device IDs.
3. Maybe create a simpler build script that builds only fenix5plus for now.

Let me also check if the manifest product IDs might need to be different. In the manifest, fenix 5 Plus is `fenix5plus`. But maybe the actual device ID in newer SDKs is different. Let me search for known device IDs.

Question: Garmin Connect IQ SDK device id fenix 5 Plus manifest product id monkeyc invalid device id


Reviewed 3 sources · synthesized answer with 3 findings

Core Topics

- [web_fetch] Community/practical context: source https://developer.garmin.com/connect-iq/core-topics/manifest-and-permissions/ sdk 9.1.0 fetched 2026-05-30 generated auto-converted from SDK doc/docs/Core_Topics/Manifest_an
- [web_fetch] Community/practical context: A collection of Connect IQ apps and libraries Purpose This repo is officially maintained by the Garmin Connect IQ team, containing a small set of reference examples and monkey barr
- [web_fetch] Community/practical context: OverviewCompatible DevicesAPI DocsGet the SDKSubmit an AppStay InformedConnect IQ BasicsMonkey CCore TopicsManifest and PermissionsApplication and System ModulesPersisting DataBack

Internal research: web_search ×1, web_fetch ×4, web_fetch_headless ×1

Findings
- [web_fetch] Community/practical context: source https://developer.garmin.com/connect-iq/core-topics/manifest-and-permissions/ sdk 9.1.0 fetched 2026-05-30 generated auto-converted from SDK doc/docs/Core_Topics/Manifest_an
- [web_fetch] Community/practical context: A collection of Connect IQ apps and libraries Purpose This repo is officially maintained by the Garmin Connect IQ team, containing a small set of reference examples and monkey barr
- [web_fetch] Community/practical context: OverviewCompatible DevicesAPI DocsGet the SDKSubmit an AppStay InformedConnect IQ BasicsMonkey CCore TopicsManifest and PermissionsApplication and System ModulesPersisting DataBack

Sources
- [web_fetch] garmin-dev-skills/skills/garmin-dev/references/connect-iq-docs/portal/core-topics/manifest-and-permissions.md at main · athitupakan/garmin-dev-skills: https://github.com/athitupakan/garmin-dev-skills/blob/main/skills/garmin-dev/references/connect-iq-docs/portal/core-topics/manifest-and-permissions.md
- [web_fetch] GitHub - garmin/connectiq-apps: A collection of Connect IQ apps.: https://github.com/garmin/connectiq-apps
- [web_fetch] Core Topics: https://developer.garmin.com/connect-iq/core-topics/manifest-and-permissions/

Internal tools
Internal research: web_search ×1, web_fetch ×4, web_fetch_headless ×1

Caveat
Evidence is partial: the strongest readable sources were mostly community/practical context.

Core Topics