Number of devices in export greater than number of devices in manifest.

I encountered an unexpected result in my latest release: the build generated output for 166 devices, despite my manifest specifying only 90. Curious, I delved into the reason behind this discrepancy.

This might be well-known to experienced developers, but it was a new and interesting discovery for me, and I wanted to share my results.

It turns out the answer is tied to the structure of the Connect IQ device files. On macOS, these files reside in ~/Library/Application Support/Garmin/ConnectIQ/Devices, with each supported device (144 in total) having its own directory. Inside each directory, the compiler.json file, a substantial JSON object, guides the compiler and simulator.

This file contains device identifiers, including deviceID (matching the manifest.xml, e.g., venu3s), WorldWidePartNumber (e.g., "006-B2337-00"), and partNumbers. Each partNumber entry specifies a firmwareVersion (e.g., 500), which seems to determine font language support.

Therefore, for each device listed in the manifest, the build produces a separate bytecode file for every firmware version defined in compiler.json. The variation in firmware versions per device, from one to seven, is likely related to hardware differences.

  • TL;DR

    - CIQ device (e.g. fr955) = single hardware model (more or less)

    - part numbers = software variants for a given CIQ device

    Each partNumber entry specifies a firmwareVersion (e.g., 500)

    Each partNumber entry is uniquely identified by the value of the number key (e.g. "006-B2337-00") which is the part number itself, not firmwareVersion.

    In general, a partNumber entry is information about a specific part number, not a specific firmware version.

    Therefore, for each device listed in the manifest, the build produces a separate bytecode file for every firmware version defined in compiler.json. The variation in firmware versions per device, from one to seven, is likely related to hardware differences.

    It's about part numbers, not firmware versions. Each part number has its own firmware version number, but the firmware version number itself is not the determining factor in the essential differences between part numbers. (This can especially be seen with devices that have multiple part numbers, but some or all of the part numbers have the same software version number.)

    A part number is basically a software variant of a given hardware model. So it's not really about hardware differences at all: for example, most older devices have 2 part numbers - one for WW (worldwide) and one for APAC (Asia-Pacific), and the only real difference between the part numbers is the language support (and, usually, the firmware version).

    e.g. fr935 is one hardware model with two part numbers (software variants which only differ by language support and firmware version):

    - 006-B2691-00: WW

    - 006-B2833-00: APAC

    (see below for more information)

    Each partNumber entry specifies a firmwareVersion (e.g., 500), which seems to determine font language support.

    Not exactly. It's the part number itself which determines language support, not the firmware version number.

    For example, all 4 part numbers for venu2s are on firmwareVersion 1905 (19.05), yet half of them are WW (worldwide) / ROW (rest of world) variants and half of them are APAC. This can be guessed as follows:

    - WW-only part numbers tend to support European languages (other than English) and not Asian languages. And of course the part number specified by worldWidePartNumber is the sole (or default) world wide part number

    - APAC-only part numbers tend to support Asian languages and not European languages other than English.

    The most common scenario (for older devices), and the easiest one to guess, is the case where the device has 2 part numbers: one for WW and one for APAC.

    e.g. fr935:

    - 006-B2691-00: WW

    - 006-B2833-00: APAC

    Newer devices typically have a single part number for both WW and APAC (presumably because there's enough storage to fit all the languages on a single variant now).

    e.g. fr955:

    - 006-B4024-00 (presumably sold in both Asia and the rest of the world)

    To make matters even more interesting, some part numbers also indicate a unique variant of a CIQ device (beyond WW vs. APAC). For example, venu2s has 4 part numbers, which correspond to:

    - 006-B3704-00: "plain" Venu 2S WW

    - 006-B3949-00: "plain" Venu 2S APAC

    - 006-B4175-00: Mercedes-Benz Venu 2S WW

    - 006-B4181-00: Mercedes-Benz Venu 2S APAC

    Another example of a device which has both kinds of variants is fenix7x:

    - 006-B3907-00: Fenix 7X, Quatix 7X (WW) [it's clear this is WW-only from the presence of an APAC variant below)

    - 006-B3910-00: Fenix 7X, Quatix 7X (APAC) [it's clear this is an Asian/APAC model from the deviceTypes info below)

    - 006-B4135-00: Tactix 7

    - 006-B4341-00: Enduro 2

    And an example of a newer device which has no WW/APAC variants, but only other types of variants is fenix8solar51mm:

    - 006-B4533-00: Fenix 8 solar 51mm

    - 006-B4776-00: Tactix 8 solar 51mm

    Having said all that, you might wonder how it's possible to determine exactly which variant a given part number corresponds to (when it's not an obvious case of WW vs APAC).

    You can look at the response of the following Garmin API, which is loaded when you click on the Compatible Devices tab in the Connect IQ store website:

    [https://apps.garmin.com/api/appsLibraryExternalServices/api/asw/deviceTypes]

    For example, here's the entry for "006-B4135-00" (fenix7x):

    We can see this part number actually maps to Tactix 7. Additionally, its urlName is tactix7, which gives us the key to figuring out the CIQ store URL which shows apps only for that specific model:

    https://apps.garmin.com/devices/tactix7/apps

    (It's kind of easy to guess that one, but the urls for newer devices like fenix 8 are not so easy to guess, since they differ significantly from the device id)

  • The main thing to understand about the partNumbers configuration in compiler.json is that it determines whether a given part number is included in the build (assuming the device is included ofc), and whether a user can install your app for that part number. It also determines the minimum guaranteed firmware/CIQ version for each part number.

    - minApiVersion in manifest.xml determines which part numbers are included/excluded. For each device that you include the manifest, only part numbers for which connectIqApiVersion is greater or equal to minApiVersion are included. The rest are excluded

    This is significant because older devices have separate WW and APAC part numbers, and the APAC part number often has a lower connectIqApiVersion than the WW part number. This means that by specifying a certain minApiVersion, it's possible to include WW part numbers, but exclude APAC part numbers. (This comes up a lot in the forums).

    - language support in manifest.xml determines which part numbers are included/excluded. For example, if you select only fre (French) as a supported language, that will exclude all APAC-only part numbers (as they don't support French)

    - firmwareVersion for part numbers in compiler.json is used by the CIQ store to determine the minimum firmware required to install on a given device. For example, all the part numbers for venu2s currently have firmwareVersion 1905 (19.05). This means anyone who has a Venu 2S will be required to have at least firmware 19.05, or they won't be allowed to install your app (they'll be asked to update their firmware).

    - connectIqVersion for part numbers in compiler.json constitutes a guarantee to the developer that the given part number has a certain version of CIQ or higher.

    For example, all the part numbers for venu2s/compiler.json have connectIqVersion 5.0.0 (and firmwareVersion 1905). This means if you build an app *now* for venu2s, venu2s users can't install  your app unless they have at least firmware version 19.05, which in turn guarantees at least Connect IQ version 5.0.0. connectIqVersion is used to determine device functionality at both compile time and when the app runs in the simulator.

    Speaking of which, when you run an app in the simulator for a given device, the simulator always uses the first part number (which is always the worldwide part number, and never an APAC-only part number.) This can be an issue when the APAC part number has a lower CIQ version thtn the WW part number.

    For example, suppose I build an app for d2charlie, whose WW part number supports CIQ 3.0.0, but whose APAC part number only supports CIQ 2.4.1. If I use some API functionality which requires CIQ 3 and I don't use a has check nor do I specify minApiVersion in manifest.xml, then it would be possible for the app to crash on an APAC D2 Charlie, and I would not be able to catch this problem by running the app in the simulator.

  • Going back to the difference between Connect IQ devices such as fenix7x and the partNumbers in compiler.json for a given device, these part numbers are explicitly referred to as software part numbers in other contexts.

    So a CIQ device such as fenix7x (which has multiple part numbers) can be thought of as a single hardware model (indeed, each compiler.json has a single hardwarePartNumber), while each of the partNumbers can be thought as a different software variant (e.g. Fenix 7X WW, Fenix 7X APAC, Tactix 7, Enduro 2).

    Of course it's not to say that the hardware is 100% identical, only that it's close enough for Connect IQ's purposes. (There's obviously cosmetic differences between Fenix 7X and Tactix 7, and there's obviously multiple colour variants for various models which aren't captured by any of this. As another example, Enduro 2 is said to be equivalent to Fenix 7X, but with a bigger battery)

  • While Garmin doesn't officially provide device files as a development tool, I've integrated them into my workflow to manage device-specific variations within my application.

    A valuable enhancement would be the inclusion of device input specifications, such as the number of buttons and touch screen capability, directly within these files.

  • touch screen capability,

    Devices which support touch have isTouch equal to true in simulator.json

    number of buttons

    It's not exactly what you want, but the keys array in simulator.json shows the (logical) keys supported by the simulator. e.g. fr955, a 5-button Forerunner, looks like this:

        "keys": [
            {
                "behavior": "onSelect",
                "id": "enter",
                "location": {
                    "height": 47,
                    "width": 40,
                    "x": 342,
                    "y": 162
                }
            },
            {
                "behavior": "previousPage",
                "id": "up",
                "location": {
                    "height": 47,
                    "width": 28,
                    "x": 2,
                    "y": 258
                }
            },
            {
                "behavior": "onMenu",
                "id": "menu",
                "isHold": true,
                "location": {
                    "height": 47,
                    "width": 28,
                    "x": 2,
                    "y": 258
                }
            },
            {
                "behavior": "nextPage",
                "id": "down",
                "location": {
                    "height": 47,
                    "width": 39,
                    "x": 19,
                    "y": 348
                }
            },
            {
                "id": "clock",
                "isHold": true,
                "location": {
                    "height": 47,
                    "width": 39,
                    "x": 19,
                    "y": 348
                }
            },
            {
                "behavior": "onBack",
                "id": "esc",
                "location": {
                    "height": 47,
                    "width": 39,
                    "x": 342,
                    "y": 348
                }
            }
        ],

    Obviously this isn't what you want, as:

    - some physical keys are listed twice (e.g. different logical keys for short press of UP and long press of UP)

    - some physical keys are omitted (e.g. the LIGHT key)

    - the keys array includes a "clock" key (hold DOWN), except that doesn't seem like it can actually be detected in CIQ, on a real device (even if you disable the hold DOWN shortcut)

    All of this information is available in a reduced form at:

    [https://developer.garmin.com/connect-iq/reference-guides/devices-reference/]

  • the compiler.json file, a substantial JSON object, guides the compiler and simulator.

    There's a separate simulator.json file for the simulator

  • I think the simulator.json file will be a good resource to deduce the button configuration. If the device has more than three entries in the "keys" attribute then I can safely assume it's a 5-button device.

    And with the isTouch attribute I should be able to get the info I need. 

    Thanks for the heads-up. 

  • If the device has more than three entries in the "keys" attribute then I can safely assume it's a 5-button device.

    By "5-button device", I assume you mean a device which has buttons for scrolling, as opposed to a device which requires touch for scrolling. Meaning that you are looking for devices for which touch can actually be disabled (or ignored) and the device can still be fully used.

    There is at least one device which is an exception to your more than 3 entry "rule": fr630 (it is super old though).

    fr630 has 5 physical buttons: light/power, start, back, lap, and menu. (Yes, back, lap and menu are all separate dedicated buttons). As such, there are 4 entries in the keys array (all of the physical buttons except light/power). Scrolling is only possible via the touchscreen. (The menu "button" is actually a touch-sensitive area below the screen, but it's treated as a button by CIQ and the built-in softtware.)

    fr630 would be included by your rule, but I think you would want to exclude it.

    I think a better rule of thumb would be to simply look for devices with physical keys that have the behaviour you want (scrolling, and probably onSelect, onBack and onMenu).

    If it was me, I would look for all devices which have previousPage and nextPage behaviors in the keys array. I'm fairly certain that all devices which have those keys also have keys for onSelect, onMenu and onBack, but if I wasn't, I would just look for those behaviors too.

    Yes it's true that your rule probably applies to all modern devices as expected, but why make needless assumptions?

  • Thanks for the helpful suggestions. I've decided to exclude the FR630 due to its semi-round screen, focusing solely on round and rectangular devices.

    I'm currently facing a challenge with simulator.json. Unlike compiler.json, it lacks the deviceID, making it difficult to identify devices when slurping them into an array of objects.

    The closest identifier I've found is the image filename ("image": "fenix7.png"), which seems a bit dodgy.

  • I'm currently facing a challenge with simulator.json. Unlike compiler.json, it lacks the deviceID, making it difficult to identify devices when slurping them into an array of objects.

    Note that each device has its own folder, which is named after the device id itself.

    e.g.

    ConnectIQ/Devices/fr955/compiler.json
    ConnectIQ/Devices/fr955/simulator.json

    fr955/compiler.json:

    {
    //...
      "deviceId": "fr955",
    //...

    There's many other files in each device's folder, such as svg and png files which cannot reasonably be expected to contain the device ID at all.

    Even if the device folders were not named after the device ID, it seems like that would not be an insurmountable obstacle to associating the device ID with all information contained within a device folder, as long as at least one file in the device folder contains the device ID.

    To be clear, you have full control over the process of reading device information and writing it to your own data structures: it's up to you to write the device ID in places where it's necessary, or perhaps to structure your data so that all data associated with a given device / device ID is grouped together.

    Nobody forces you to:

    - ignore the device folder name when reading device data

    - apparently read compiler.json and simulator.json independently of each other, and write them to completely independent objects