1. Device Reference
   URL: https://developer.garmin.com/connect-iq/device-reference/fenix5/
   Device Reference

- Approach® S50
- Approach® S60
- Approach® S62
- Approach® S70 42mm
- Approach® S70 47mm
- D2™ Air
- D2™ Air X10
- D2™ Bravo
- D2™ Bravo Titanium
- D2™ Charlie
- D2™ Delta
- D2™ Delta PX
- D2™ Delta S
- D2™ Mach 1
- D2™ Mach 2
- D2™ Mach 2 Pro
- Descent™ G1 / G1 Solar
- Descent™ G2
- Descent™ Mk1
- Descent™ Mk2 / Mk2i
- Descent™ Mk2 S
- Descent™ Mk3 43mm / Mk3i 43mm
- Descent™ Mk3i 51mm
- Edge® 1030
- Edge® 1030 / Bontrager
- Edge® 1030 Plus
- Edge® 1040 / 1040 Solar
- Edge® 1050
- Edge® 130
- Edge® 130 Plus
- Edge® 520 Plus
- Edge® 530
- Edge® 540 / 540 Solar
- Edge® 550
- Edge® 820 / Explore
- Edge® 830
- Edge® 840 / 840 Solar
- Edge® 850
- Edge® 1000 / Explore
- Edge® 520
- Edge® Explore
- Edge® Explore 2
- Edge® MTB
- Enduro™
- Enduro™ 3
- epix™
- epix™ (Gen 2) / quatix® 7 Sapphire
- epix™ Pro (Gen 2) 42mm
- epix™ Pro (Gen 2) 47mm / quatix® 7 Pro
- epix™ Pro (Gen 2) 51mm / D2™ Mach 1 Pro / tactix® 7 – AMOLED Edition
- eTrex® Touch
- fēnix® 3 / tactix® Bravo / quatix® 3
- fēnix® 3 HR
- fēnix® 5 / quatix® 5
- fēnix® 5 Plus
- fēnix® 5S
- fēnix® 5S Plus
- fēnix® 5X / tactix® Charlie
- fēnix® 5X Plus
- fēnix® 6 / 6 Solar / 6 Dual Power
- fēnix® 6 Pro / 6 Sapphire / 6 Pro Solar / 6 Pro Dual Power / quatix® 6
- fēnix® 6S / 6S Solar / 6S Dual Power
- fēnix® 6S Pro / 6S Sapphire / 6S Pro Solar / 6S Pro Dual Power
- fēnix® 6X Pro / 6X Sapphire / 6X Pro Solar / tactix® Delta Sapphire / Delta Solar / Delta Solar - Ballistics Edition / quatix® 6X / 6X Solar / 6X Dual Power
- fēnix® 7 / quatix® 7
- fēnix® 7 Pro
- fēnix® 7 Pro - Solar Edition (no Wi-Fi)
- fēnix® 7S
- fēnix® 7S Pro
- fēnix® 7X / tactix® 7 / quatix® 7X Solar / Enduro™ 2
- fēnix® 7X Pro
- fēnix® 7X Pro - Solar Edition (no Wi-Fi)
- fēnix® 8 43mm
- fēnix® 8 47mm / 51mm / tactix® 8 47mm / 51mm / quatix® 8 47mm / 51mm
- fēnix® 8 Pro 47mm / 51mm / MicroLED / quatix® 8 Pro 47mm / 51mm
- fēnix® 8 Solar 47mm
- fēnix® 8 Solar 51mm / tactix® 8 Solar 51mm
- fēnix® Chronos
- fēnix® E
- Forerunner® 165
- Forerunner® 165 Music
- Forerunner® 170
- Forerunner® 170 Music
- Forerunner® 230
- Forerunner® 235
- Forerunner® 245
- Forerunner® 245 Music
- Forerunner® 255
- Forerunner® 255 Music
- Forerunner® 255s
- Forerunner® 255s Music
- Forerunner® 265
- Forerunner® 265s
- Forerunner® 45
- Forerunner® 55
- Forerunner® 570 42mm
- Forerunner® 570 47mm
- Forerunner® 630
- Forerunner® 645
- Forerunner® 645 Music
- Forerunner® 70
- Forerunner® 735xt
- Forerunner® 745
- Forerunner® 920XT
- Forerunner® 935
- Forerunner® 945
- Forerunner® 945 LTE
- Forerunner® 955 / Solar
- Forerunner® 965
- Forerunner® 970
- Garmin Swim™ 2
- GPSMAP® 66s / 66i / 66sr / 66st
- GPSMAP® 67 / 67i
- GPSMAP® 86s / 86sc / 86i / 86sci
- GPSMAP® H1 / H1i Plus
- Instinct® 2 / Solar / Dual Power / dēzl Edition
- Instinct® 2S / Solar / Dual Power
- Instinct® 2X Solar
- Instinct® 3 AMOLED 45mm
- Instinct® 3 AMOLED 50mm
- Instinct® 3 Solar 45mm / 50mm
- Instinct® Crossover
- Instinct® Crossover AMOLED
- Instinct® E 40mm
- Instinct® E 45mm
- Captain Marvel
- First Avenger
- Darth Vader™
- Rey™
- MARQ® (Gen 2) Athlete / Adventurer / Captain / Golfer / Carbon Edition / Commander - Carbon Edition
- MARQ® (Gen 2) Aviator
- MARQ® Adventurer
- MARQ® Athlete
- MARQ® Aviator
- MARQ® Captain / MARQ® Captain: American Magic Edition
- MARQ® Commander
- MARQ® Driver
- MARQ® Expedition
- MARQ® Golfer
- Montana® 7 Series
- Oregon® 7 Series
- Rino® 7 Series
- Venu®
- Venu® 2
- Venu® 2 Plus
- Venu® 2S
- Venu® 3
- Venu® 3S
- Venu® 4 41mm
- Venu® 4 45mm / D2™ Air X15
- Venu® Mercedes-Benz® Collection
- Venu® Sq
- Venu® Sq 2
- Venu® Sq 2 Music
- Venu® Sq. Music Edition
- Venu® X1
- vívoactive®
- vívoactive® 3
- vívoactive® 3 Mercedes-Benz® Collection
- vívoactive® 3 Music
- vívoactive® 3 Music LTE
- vívoactive® 4
- vívoactive® 4S
- vívoactive® 5
- vívoactive® 6
- vívoactive® HR

MenuDevice Reference

2. Retrieve ambient pressure in foreground - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/288741/retrieve-ambient-pressure-in-foreground
   Retrieve ambient pressure in foreground - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- Replies 11 replies
- Subscribers 18 subscribers
- Views 1953 views
- Users 0 members are here

Options

Related

# Retrieve ambient pressure in foreground

Toybox.Sensor.getInfo().ambientPressure needs the permission Toybox.Sensor - I don't have added this permission yet because until now I used the pressure history.

I realised that ambientPressure is the value I want to display and not the one from the history and want to change this now.

1) it seems like ambientPressure value can't be retrieved from history, correct?

2) I realised that Toybox.Sensor also needs the background permission, does this mean I can only read the ambientPressure in the background?

Actually I don't want to add 2 permissions for this single data value to my watchface, is there no alternative way to get the current pressure (or last in one from history)?

- Sign in to reply
- Cancel

With a watch face, you can only access Sensor data in a background service.

Try Activity.getActivityInfo().ambientPressure in the foreground

Be sure to handle devices without a baro altimeter

In the sim I can access ambientPressure without an exception but it returns null (without adding the necessary permissions though)... But you say, the foreground access won't work on a real device anyway, that's the important information for me. Btw, adding background + sensor permission approx. halves the available memory for a watchface (ERROR: PRG generated exceeds the memory limit of 'background' applications for device id 'fenix6xpro': 63883 bytes) - is this something I have to accept or can I circumvent this side effect?

Can't I alternatively convert pressure to ambientPressure with the history data?

In the sim, are you simulating data? Try Activity.Info on a real device with a baro if you have doubts.

Once you add background permission, you want to make sure you use the (:background) annotation where needed, otherwise it will try to load the entire WF when the background runs, inclusing when onAppInstall() or onAppUpdate() are called.

Now that I simulate data it shows a value - thanks. Even without adding additional permissions.

Oh, now I remember that in need to add :background annotations where needed to avoid my whole WF is running in the background - thanks.

So actually you say something different than I though - that real devices with baro allow to access Activity.getActivityInfo().ambientPressure in the foreground? So I just have to add the permissions, make sure my WF is running in foreground and it will work on a real device? Or will it work without the additional permissions in the foreground on a real device (as this is what I see in the sim)?

hi, no permission is need for Activity.getActivityInfo().ambientPressure

when Jim said "Be sure to handle devices without a baro altimeter" I think he means to check if the device has a Baro with

if(Activity.getActivityInfo() has :ambientPressure){...}

if you use this method, you don't need :Background annotation or permission.

Thanks, that answers all open questions. The unsure phrase for me was "you can only access Sensor data in a background service"

yes the module Sensor. but Activity is available from foreground.

Don't forget to check null statment

thanks for clarifying that. Btw, isn't the null check enough for class variables? I actually though the :has must only be used for functions...

hi, no it is not enough, if you try for exemple Activity.getActivityInfo().ambientPressure on a F45, it will crash since FR45 does not have ABC sensor.

see the API doc, you have lots of exemple of :has

You want to do the has check too. Devices like the fr45, fr55, fr245 and venu sq don't have a baro altimeter.

3. Fenix 5 plus api version? - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/340738/fenix-5-plus-api-version
   Fenix 5 plus api version? - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- Cancel
- More
- German Forum
- Beta Program
- Developer
- Pilots
- Sports & Fitness
- Outdoor Recreation
- Apps & Software

- Users 0 members are here
- Views 460 views
- Subscribers 17 subscribers
- Replies 1 reply
- State Not Answered

Options

- Cancel
- More
- Share

Related

# Fenix 5 plus api version?

What API version does Phoenix 5 Plus have? The site says 3.3.0, and when programming, all the functionality is from 3.1.0

- Cancel
- Sign in to reply

What are you trying to do? When you look at the API doc, you'll see "Since API Level" but you also want to check "Supported devices". In the case of the fenix5+, It may be 3.3.3 , and Toybox.Weather is "Since API Level 3.2.0", but the fenix5+ isn't listed as a Supported Device for example. The Fenix5+ doesn't have the weather API...

4. Toybox.Weather.CurrentConditions.pressure clarification - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/409172/toybox-weather-currentconditions-pressure-clarification/1930864
   Toybox.Weather.CurrentConditions.pressure clarification - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- Replies 12 replies
- Subscribers 21 subscribers
- Views 2409 views
- Users 0 members are here

Options

Related

# Toybox.Weather.CurrentConditions.pressure clarification

Is the value from Toybox.Weather.CurrentConditions.pressure , the sea level air pressure or the ambient (ground level) pressure?

The API documentation says Connect IQ 5.1.0 supports it. My watch has CIQ 5.1.0. Reading the value seems to always return null (so far).

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

## Top Replies

- ...tested with my EDGE device:Now... clear as water?PS: Gumpoldskirchen is at 250 m above sea level.The difference between local and sea level pressure is about 1 hPa per 8 m.

## All Replies

Hello Garmin???

From a pilot (not Garmin)…

Short answer: Neither. It tells you how far from standard atmosphere you are.

Longer answer: It tells you how far from standard sea level you are by measuring the difference in the weight of the atmosphere on a standard day at sea level and the weight of the atmosphere of where you are. It’s an adjustment amount to tell someone how much more or less pressure the atmosphere is applying to a location compared to standard. I can’t pick a gooder way to say that right now.

Pilots adjust the aircraft’s altimeters from standard (29.92 in the US, 1019 most international places…and Garmin) so that we accurately measure where the ground is. For example, where I live the airport elevation is 856 feet. In order for that altitude to show on my altimeter right now, I need to dial in 29.56 inHG into the altimeter. If I didn’t make this adjustment and left the altimeter in standard, and I was flying without visual reference to the ground, I’d impact the ground while my altimeter was still indicating 1,216 feet (note: early morning math may be off a few feet). The lower pressure makes the altimeter feel higher than what it actually is.

(insert your favorite “One pilot says to the other pilot, 'What’s that goat doing in these clouds?'” joke here)

Clear as mud? Basic, but I hope this answers your question.

Thanks, clear as mud. I'm looking for more solid info on what this is from Garmin. Especially since there are so many places where the API documentation is just plain wrong or misleading.

Can't you decide it by looking at the data you get and the data from the same weather station in another place?

...tested with my EDGE device:

Now... clear as water?

PS: Gumpoldskirchen is at 250 m above sea level.The difference between local and sea level pressure is about 1 hPa per 8 m.

101586Pa ~= 1016hPa so it seems the Weather is at sea level?But also ActivityInfo almost matches: 98495Pa ~= 984hPa, and 989hPa is only 5 Pa higher, which means it's about 40m lower?But where is the 250m? that should be ~31hPa difference (BTW IMHO there's a typo: is it really 1hPa/8m or it's 1Pa/8m?)

250 m above sea level is the official altitude of Gumpoldskirchen. It is a hilly area near where I live. I don't know the exact altitude of the weather station in Gumpoldskirchen.

I live at 235 m above sea level - and this is where I took my Edge measurements.

(...and I'm sure none of this is mathematically accurate)

BTW IMHO there's a typo: is it really 1hPa/8m or it's 1Pa/8m?

From internet: (It seems to be ok… but is only valid for elevations below 500 m)Da der Luftdruck mit zunehmender Höhe abnimmt, musst du den relativen Luftdruck für jede 8 Meter Höhe um 1 hPa erhöhen.

English: As the air pressure decreases with increasing altitude, you must increase the relative air pressure by 1 hPa for every 8 meters of altitude.

985 hPa local plus 31 hPa from elevation (250/8) —> 1,016 hPa

…ok, these hPa figures are taken from my Edge, and I live at 235 + 6 m for 2nd floor = 241 m, and 241/8 makes only 30 hPa difference instead of 31…

Generally, in a weather report, the pressure is MSL, and if you are close to the weather station but at a different elevation, the pressure you observe will be different. In fact, if you are stating next to the weather station, what you observe can be different than what's reported for that weather station, due to it's elevation.

With my own weather station, one of the things you configure is the WS elevation, so when it displays pressure, it's adjusted to MSL.

Other things come into play with garmin weather, such as how old a reading is. For me and Garmin, the reading is always 51 minutes after the hour and sometimes a reading is missed so the value can be up to 2 hours old, where a storm rolled through and it's clear again, but I can see that on my own weather station that updates every few minutes.

Out of the 3 different sources of weather I use with CIQ, the most accurate is Weather Underground, where the data is right where I live. OWM isn't bad, as it's from a ranch not far away, and the worst is Garmin, where even the temperature with Garmin can be off by 10F. It's probably better in the flat lands of Kansas,

Thank you, and yes: this field of weather is very complicated in detail and interesting to solve.

But trudelta asked simply for clarification.Is the value from Toybox.Weather.CurrentConditions.pressure , the sea level air pressure or the ambient (ground level) pressure?

And the test above gives clarification: it‘s sea level pressure.

5. Device API levels - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/354168/device-api-levels
   Device API levels - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- State Not Answered
- Replies 9 replies
- Subscribers 20 subscribers
- Views 5007 views
- Users 0 members are here

- apilevel

Options

- Share
- More
- Cancel

Related

# Device API levels

Is there any way to know when and which version is available to devices?

The "Compatible Devices" page seems to be way behind? As I understand it

the API level shown there is the max level. E.g. the devices with the most recent API

(4.2) is still showing 4.2.0 there, when 4.2.1 was reported at least as early as June to

be working on a number of devices ...

Cheers

- Sign in to reply
- Cancel

## Top Replies

- TL;DR for any given device, compiler.json in the SDK device folder has the minimum required CIQ version for that device, which is also the maximum expected CIQ version for the app dev.--Open the SDK…

## All Replies

TL;DR for any given device, compiler.json in the SDK device folder has the minimum required CIQ version for that device, which is also the maximum expected CIQ version for the app dev.

--

Open the SDK's devices folder, and navigate to compiler.json for the device of interest.

On Windows, the devices folder is at %appdata%\garmin\connectiq\devices. This folder contains a separate folder for each device (e.g. fr265).

Each device folder has several files, including compiler.json, has a partNumbers array containing information about each part number (this is not the same part number that's shown on the box or listed on the website).

Newer devices may only have one part number, while older devices may have different part numbers for worldwide (WW) and Asia-Pacific (APAC) devices. The value for worldWidePartNumber will indicate which partNumber is WW (which means that its CIQ version will always be newer or equal to the APAC CIQ version.)

Each partNumbers element has the following fields:

connectIQVersion: e.g. "4.2.3"

firmwareVersion: e.g. 1616

number: the actual part number e.g. "006-B4257-00"

connectIqVersion and firmwareVersion are actually the minimum versions an app built with device file can expect to see when deployed to a device. This is enforced when the user tries to download the app - if the device has a lower CIQ or firmware version than the minimum, the user will be prompted to updated.

Since those are the minimum required versions for the user, they are also the maximum expected versions for the app dev. e.g. If we know every user has 4.2.3 or higher, then we can't assume they have anything higher than 4.2.3.

Thanks,

So ... do the compiler.json files represent what's been pushed out to actual devices?

It's the minimum api level that the device can have to install an app, not what may be on the device today.

Also note that you may see multiple part numbers in the same file, with the common case being APAC and Word Wide devices, where the APAC version may be lower than the WW version.

Look at using "has" to see if something is supported or not

No, and you're welcome to vote on this "feature" (documentation) request: forums.garmin.com/.../improve-documentation-add-api-levels-and-system-levels-to-device-reference

I'll add that any "available" information (documentation, compiler.json) only has information about what would be the max api level if a certain kind of device was upgraded to the latest available firmware. There's no information on the minimum possible. Even when you think you have that information it's not really that. When you look at a certain device family, and then split it by part numbers then you'll only know what would be the minimum api level of that device family if all the devices would be ugraded to the latest firmware version. The problem is that once there is a new FW version the json is updated to indicate the CURRENT minimum version, but if a device doesn't upgrade the FW then they still have an older CIQ with older api versions... And I am not aware of any official documentation that would tell what would be the lowest version of a part number if the FW wasn't upgraded since the 1st item that came out of the factory.

BTW I made some effort to collect the data, and it may be correct for devices that came out since I started the project (but not necessarily, because it depends on the json files in the devices directory, that might or might not be correct, and more importantly I am not checking every day if there are new versions of the devices in the sdk manager so I might have skipped/missed some) The idea is that I have a script that scans the device files and extracts the min, max CIQ version for the devices, and it ADDS the newly available versions to a list, but also KEEPS the older versions in the csv files: https://github.com/flocsy/garmin-dev-tools(look at the files under csv/)

Interesting, thanks.

So I am drawing the conclusion that when the ciq version is updated in the compiler.json, then it's also possible for users to update real devices (timing difference between sdk update and real device update, earlier or later)?

Devices can. and often do, get a CIQ version update before you see it in compiler.json. The last digit (the "3" in 4.2.3 for example) can just indicate a bug fix level. It might still show as 4.2.1 in compiler.json. as that's the minimum level a user needs to have to install your app. If the user has a level less that 4.2.1, they will be told they need a firmware update to install your app, but they don't need to have 4.2.3 -just 4.2.1 or later..

flocsy🤠 said:

I'll add that any "available" information (documentation, compiler.json) only has information about what would be the max api level if a certain kind of device was upgraded to the latest available firmware. There's no information on the minimum possible. Even when you think you have that information it's not really that.

I don't agree with this. Based on other threads and personal experience, if an app is built against a given device file, then the user will not be able to install it unless their device has the CIQ API version and firmware version specified in the device file, at a minimum. You will see that the exported .IQ file (zip archive) contains a manifest XML file which has the CIQ/firmware version for each device (and the naming scheme is something like "minApiVersion" which indeed suggests it's the minimum version.)

This means a device file does indeed specify the minimum possible CIQ version (*) for any given device, for apps that are built against that version of the device file. The only use case which isn't covered by looking at the latest device files is when you have an *old* app which hasn't been built again the latest device files. In this case, if you have a copy of the exported IQ file, you should be able to look at manifest to determine the minimum CIQ/firmware versions for that version of the app.

(* which is also the maximum CIQ version that you can safely *assume* is present)

Some anecdotal evidence: look at all the threads complaining that users can't install Spotify on their device because it has old firmware. There's also another thread that directly addresses this topic, but I don't have a link to it at the moment.

You can also test this for yourself by hand-editing a device file to increase the API version or firmware version to be greater than what's installed on your real device, uploading a beta app built against that device file, and observing what happens when you try to install it.

By the way, as far as what's in the manifest.xml file, this is what I pretty much always have:

minApiLevel="1.2.0"

if an app is only for devices with 3.x or higher, that is determined by what targets I add for the app. I just don't include any CIQ 1 or CIQ 2 devices.

The TL;DR is that the firmware version and CIQ version in a device's compiler.json are in fact the minimum versions your app will see on the device, when your app is built against that version of the device file; if the device doesn't have those versions, the store will prevent the user from installing the app.

This is the cause of the infamous complaints about being unable to install Spotify (for example) without a device update.

But yes, it is possible for a device to have a lower or higher version. If the version is lower, then they will be asked to update their app. If the version is higher, the app will install without any problems.

Device Reference


1. Switch between calls depending on device api level? (ActivityRecording vs Activity) - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/435955/switch-between-calls-depending-on-device-api-level-activityrecording-vs-activity/2030406
   Switch between calls depending on device api level? (ActivityRecording vs Activity) - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- Replies 2 replies
- Subscribers 19 subscribers
- Views 286 views
- Users 0 members are here

Options

Related

# Switch between calls depending on device api level? (ActivityRecording vs Activity)

I have an app where Fenix5 users reported crashes wereas my F5plus worked fine. I think I tracked it down to the ActivityRecording and enums of the sport/subsport.

_session = ActivityRecording.createSession({

:name => "Yada yada",

:sport => Activity.SPORT_RUNNING,

:subsport => Activity.SUB_SPORT_GENERIC

});

F5plus is on api level 3.3 and F5 is on api level 3.1. The issue is that the enum in Activity was introduced in 3.2. Before that the enum resides in ActivityRecording.

What is the proper way to handle this? Check for api level (how?) and use

ActivityRecording.SPORT_RUNNING

if its a F5 and use

Activity.SPORT_RUNNING

if it's a newer device?

- Sign in to reply
- Cancel

Hi,

Try the following, otherwise you can check the 'monkeyVersion' from Device Settings.

var at = Toybox.Activity has :SPORT_RUNNING ? Toybox.Activity.SPORT_RUNNING : Toybox.ActivityRecording.SPORT_RUNNING;

Or you can create your own const, as the only thing that matters are the numbers. BTW I think the best way is to turn on optimization to level 3. I use it in all apps, and as a side effect I don't have this problem, because it replaces the const with their value in compile time.

- - Cancel
- - Up 0 Down
- Sign in to reply
- Cancel
- - Cancel
- - Up 0 Down
- Sign in to reply
- Cancel

2. Fenix 5 plus api version? - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/340738/fenix-5-plus-api-version
   Fenix 5 plus api version? - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- Cancel
- More
- German Forum
- Beta Program
- Developer
- Pilots
- Sports & Fitness
- Outdoor Recreation
- Apps & Software

- Users 0 members are here
- Views 460 views
- Subscribers 17 subscribers
- Replies 1 reply
- State Not Answered

Options

- Cancel
- More
- Share

Related

# Fenix 5 plus api version?

What API version does Phoenix 5 Plus have? The site says 3.3.0, and when programming, all the functionality is from 3.1.0

- Cancel
- Sign in to reply

What are you trying to do? When you look at the API doc, you'll see "Since API Level" but you also want to check "Supported devices". In the case of the fenix5+, It may be 3.3.3 , and Toybox.Weather is "Since API Level 3.2.0", but the fenix5+ isn't listed as a Supported Device for example. The Fenix5+ doesn't have the weather API...

3. Toybox.ActivityRecording
   URL: https://developer.garmin.com/connect-iq/api-docs/Toybox/ActivityRecording.html
   Toybox.ActivityRecording

# Module: Toybox.ActivityRecording

## Overview

The Activity Recording module will allow Apps to access the FIT file recording capabilities of the device.

Apps can use this module to allow the user to start and stop recordings, create laps, and save recorded data. SPORT_* and SUB_SPORT_* constants are provided as enums.

The SPORT_* defines the type of activity, but is not guaranteed to change device behavior. This may cause different algorithms to be applied to the sensor data. For example, a device that does not natively support SPORT_MULTISPORT recording will not gain SPORT_MULTISPORT transition features. However, the FIT file can be defined as SPORT_MULTISPORT and MULTI_SPORT algorithms may be applied to the sensor data.

SUB_SPORT_* allows for clarification of sport when recording.

## See Also:

## Classes Under Namespace

## Constant Summary

### Sport1

This has been deprecated

This enum may be removed after System 8.

| Name | Value | Since | Description |
| --- | --- | --- | --- |
| SPORT_GENERIC | 0 | API Level 1.0.0 | Sport used for sports not on the list |
| SPORT_RUNNING | 1 | API Level 1.0.0 | Sport used for running |
| SPORT_CYCLING | 2 | API Level 1.0.0 | Sport used for multi-sport transitions |
| SPORT_TRANSITION | 3 | API Level 1.0.0 | |
| SPORT_FITNESS_EQUIPMENT | 4 | API Level 1.0.0 | Sport used for ANT enabled exercise equipment |
| SPORT_SWIMMING | 5 | API Level 1.0.0 | Sport used for swimming |
| SPORT_BASKETBALL | 6 | API Level 1.0.0 | Sport used for basketball |
| SPORT_SOCCER | 7 | API Level 1.0.0 | Sport used for soccer |
| SPORT_TENNIS | 8 | API Level 1.0.0 | Sport used for Tennis |
| SPORT_AMERICAN_FOOTBALL | 9 | API Level 1.0.0 | Sport used for American football |
| SPORT_TRAINING | 10 | API Level 1.0.0 | Sport used for activities such as strength training, cardio, etc |
| SPORT_WALKING | 11 | API Level 1.0.0 | Sport used for walking |
| SPORT_CROSS_COUNTRY_SKIING | 12 | API Level 1.0.0 | Sport used for cross-country skiing |
| SPORT_ALPINE_SKIING | 13 | API Level 1.0.0 | Sport used for alpine skiing |
| SPORT_SNOWBOARDING | 14 | API Level 1.0.0 | Sport used for snowboarding |
| SPORT_ROWING | 15 | API Level 1.0.0 | Sport used for rowing |
| SPORT_MOUNTAINEERING | 16 | API Level 1.0.0 | Sport used for mountaineering |
| SPORT_HIKING | 17 | API Level 1.0.0 | Sport used for hiking |
| SPORT_MULTISPORT | 18 | API Level 1.0.0 | Sport used for multi-sport events |
| SPORT_PADDLING | 19 | API Level 1.0.0 | Sport used for paddling |

### Sport2

This has been deprecated

This enum may be removed after System 8.

| Name | Value | Since | Description |
| --- | --- | --- | --- |
| SPORT_FLYING | 20 | API Level 3.0.10 | Sport used for flying |
| SPORT_E_BIKING | 21 | API Level 3.0.10 | Sport used for riding an e-bike |
| SPORT_MOTORCYCLING | 22 | API Level 3.0.10 | Sport used for motorcycling |
| SPORT_BOATING | 23 | API Level 3.0.10 | Sport used for boating |
| SPORT_DRIVING | 24 | API Level 3.0.10 | Sport used for driving |
| SPORT_GOLF | 25 | API Level 3.0.10 | Sport used for golfing |
| SPORT_HANG_GLIDING | 26 | API Level 3.0.10 | Sport used for hang gliding |
| SPORT_HORSEBACK_RIDING | 27 | API Level 3.0.10 | Sport used for horseback riding |
| SPORT_HUNTING | 28 | API Level 3.0.10 | Sport used for hunting |
| SPORT_FISHING | 29 | API Level 3.0.10 | Sport used for fishing |
| SPORT_INLINE_SKATING | 30 | API Level 3.0.10 | Sport used for inline skating |
| SPORT_ROCK_CLIMBING | 31 | API Level 3.0.10 | Sport used for rock climbing |
| SPORT_SAILING | 32 | API Level 3.0.10 | Sport used for sailing |
| SPORT_ICE_SKATING | 33 | API Level 3.0.10 | Sport used for ice skating |
| SPORT_SKY_DIVING | 34 | API Level 3.0.10 | Sport used for sky diving |
| SPORT_SNOWSHOEING | 35 | API Level 3.0.10 | Sport used for showshoeing |
| SPORT_SNOWMOBILING | 36 | API Level 3.0.10 | Sport used for snowmobiling |
| SPORT_STAND_UP_PADDLEBOARDING | 37 | API Level 3.0.10 | Sport used for paddle boarding |
| SPORT_SURFING | 38 | API Level 3.0.10 | Sport used for surfing |
| SPORT_WAKEBOARDING | 39 | API Level 3.0.10 | Sport used for wakeboarding |
| SPORT_WATER_SKIING | 40 | API Level 3.0.10 | Sport used for water skiing |
| SPORT_KAYAKING | 41 | API Level 3.0.10 | Sport used for kayaking |
| SPORT_RAFTING | 42 | API Level 3.0.10 | Sport used for rafting |
| SPORT_WINDSURFING | 43 | API Level 3.0.10 | Sport used for windsurfing |
| SPORT_KITESURFING | 44 | API Level 3.0.10 | Sport used for kite surfing |
| SPORT_TACTICAL | 45 | API Level 3.0.10 | Sport used for tactical |
| SPORT_JUMPMASTER | 46 | API Level 3.0.10 | Sport used for jumpmaster |
| SPORT_BOXING | 47 | API Level 3.0.10 | Sport used for boxing |
| SPORT_FLOOR_CLIMBING | 48 | API Level 3.0.10 | Sport used for climbing |
| SPORT_BASEBALL | 49 | API Level 3.0.10 | Sport used for baseball |
| SPORT_SOFTBALL_FAST_PITCH | 50 | API Level 3.0.10 | Sport used for fast pitch softball |
| SPORT_SOFTBALL_SLOW_PITCH | 51 | API Level 3.0.10 | Sport used for slow pitch softball |
| SPORT_SHOOTING | 56 | API Level 3.0.10 | Sport used for shooting |
| SPORT_AUTO_RACING | 57 | API Level 3.0.10 | Sport used for auto racing |

### SubSport

This has been deprecated

This enum may be removed after System 8.

| Name | Value | Since | Description |
| --- | --- | --- | --- |
| SUB_SPORT_GENERIC | 0 | API Level 1.0.0 | Sub-sport used if no other sub-sport is applicable |
| SUB_SPORT_TREADMILL | 1 | API Level 1.0.0 | Sub-sport for Running and Fitness Equipment |
| SUB_SPORT_STREET | 2 | API Level 1.0.0 | Sub-sport for Running |
| SUB_SPORT_TRAIL | 3 | API Level 1.0.0 | Sub-sport for Running |
| SUB_SPORT_TRACK | 4 | API Level 1.0.0 | Sub-sport for Running |
| SUB_SPORT_SPIN | 5 | API Level 1.0.0 | Sub-sport for Cycling |
| SUB_SPORT_INDOOR_CYCLING | 6 | API Level 1.0.0 | Sub-sport for Cycling and Fitness Equipment |
| SUB_SPORT_ROAD | 7 | API Level 1.0.0 | Sub-sport for Cycling |
| SUB_SPORT_MOUNTAIN | 8 | API Level 1.0.0 | Sub-sport for Cycling |
| SUB_SPORT_DOWNHILL | 9 | API Level 1.0.0 | Sub-sport for Cycling |
| SUB_SPORT_RECUMBENT | 10 | API Level 1.0.0 | Sub-sport for Cycling |
| SUB_SPORT_CYCLOCROSS | 11 | API Level 1.0.0 | Sub-sport for Cycling |
| SUB_SPORT_HAND_CYCLING | 12 | API Level 1.0.0 | Sub-sport for Cycling |
| SUB_SPORT_TRACK_CYCLING | 13 | API Level 1.0.0 | Sub-sport for Cycling |
| SUB_SPORT_INDOOR_ROWING | 14 | API Level 1.0.0 | Sub-sport for Rowing and Fitness Equipment |
| SUB_SPORT_ELLIPTICAL | 15 | API Level 1.0.0 | Sub-sport for Fitness Equipment |
| SUB_SPORT_STAIR_CLIMBING | 16 | API Level 1.0.0 | Sub-sport for Fitness Equipment |
| SUB_SPORT_LAP_SWIMMING | 17 | API Level 1.0.0 | Sub-sport for Swimming |
| SUB_SPORT_OPEN_WATER | 18 | API Level 1.0.0 | Sub-sport for Swimming |
| SUB_SPORT_FLEXIBILITY_TRAINING | 19 | API Level 1.0.0 | Sub-sport for Training |
| SUB_SPORT_STRENGTH_TRAINING | 20 | API Level 1.0.0 | Sub-sport for Training |
| SUB_SPORT_WARM_UP | 21 | API Level 1.0.0 | Sub-sport for Activity Warm-up |
| SUB_SPORT_MATCH | 22 | API Level 1.0.0 | Sub-sport for Sports with Matches (e.g. Tennis) |
| SUB_SPORT_EXERCISE | 23 | API Level 1.0.0 | Sub-sport for Exercise |
| SUB_SPORT_CHALLENGE | 24 | API Level 1.0.0 | Sub-sport for a Sport Challenge |
| SUB_SPORT_INDOOR_SKIING | 25 | API Level 1.0.0 | Sub-sport for Fitness Equipment |
| SUB_SPORT_CARDIO_TRAINING | 26 | API Level 1.0.0 | Sub-sport for Training |

### TimerEventType

| Name | Value | Since | Description |
| --- | --- | --- | --- |
| TIMER_EVENT_START | 0 | API Level 3.0.10 | Timer start event |
| TIMER_EVENT_STOP | 1 | API Level 3.0.10 | Timer stop event |
| TIMER_EVENT_PAUSE | 2 | API Level 3.0.10 | Timer pause event |
| TIMER_EVENT_RESUME | 3 | API Level 3.0.10 | Timer resume event |
| TIMER_EVENT_LAP | 4 | API Level 3.0.10 | Timer lap event |
| TIMER_EVENT_RESET | 5 | API Level 3.0.10 | Timer reset |
| TIMER_EVENT_WORKOUT_STEP_COMPLETE | 6 | API Level 3.0.10 | Workout step complete |
| TIMER_EVENT_NEXT_MULTISPORT_LEG | 7 | API Level 3.0.10 | Multisport leg started |

## Typedef Summary collapse

- Sport as ActivityRecording.Sport1 or ActivityRecording.Sport2

## Instance Method Summary collapse

createSession(options as { :sport as ActivityRecording.Sport or Activity.Sport, :subSport as ActivityRecording.SubSport or Activity.SubSport, :name as Lang.String, :poolLength as Lang.Float, :sensorLogger as SensorLogging.SensorLogger, :autoLap as { :type as Lang.Symbol, :entry as [Position.Location, Position.Location], :exit as [Position.Location, Position.Location] } }) as ActivityRecording.Session

Create a Session object with options determined by the caller.

## Typedef Details

### Sport as ActivityRecording.Sport1 or ActivityRecording.Sport2

## Instance Method Details

### createSession(options as { :sport as ActivityRecording.Sport or Activity.Sport, :subSport as ActivityRecording.SubSport or Activity.SubSport, :name as Lang.String, :poolLength as Lang.Float, :sensorLogger as SensorLogging.SensorLogger, :autoLap as { :type as Lang.Symbol, :entry as [ Position.Location, Position.Location ], :exit as [ Position.Location, Position.Location ] } }) as ActivityRecording.Session

Create a Session object with options determined by the caller.

Only one Session object is allowed to exist at a time. If there is an existing object that has not been closed using the save() or discard() methods, this method will return that object instead of creating a new one. On some products running the 1.x virtual machine, creating a Session object requires a large memory allocation. To free this memory, the Session must first be successfully saved or discarded, and then app references to the Session object should be set to`null`.

---

Generated Jun 8, 2026, 3:42:16 PM

4. Toybox.FitContributor
   URL: https://developer.garmin.com/connect-iq/api-docs/Toybox/FitContributor.html
   Toybox.FitContributor

# Module: Toybox.FitContributor

## Overview

The FitContributor module allows Applications and Data Fields to record Field data into FIT files on the device's file system during an activity. This is useful for recording data that is not already calculated by the device, which can be synced to a service like Garmin Connect.

There are three FitContributor message types available:

MESG_TYPE_SESSION

Session data is written once per recording session at the end of the recording, and is used for data that pertains to the entire session (e.g. average speed).

MESG_TYPE_LAP

Lap data is written once for every lap in the session, and used for data that pertains to each lap (e.g. average lap speed).

MESG_TYPE_RECORD

Depending on the device, record data is written once per second or when new data is available (Smart Recording), but is never written faster than once per second. This message type is used for instantaneous values (e.g. current speed).

Data type constants are also available for use with the createField() method.

## See Also:

Learn more about the FIT format

## Classes Under Namespace

## Constant Summary

### MessageType

API Level 1.3.0

The message type for session messages.

API Level 1.3.0

The message type for lap messages.

API Level 1.3.0

The message type for record messages.

| Name | Value | Since | Description |
| --- | --- | --- | --- |
| MESG_TYPE_SESSION | 18 |
| MESG_TYPE_LAP | 19 |
| MESG_TYPE_RECORD | 20 |

### DataType

API Level 1.3.0

API Level 1.3.0

API Level 1.3.0

API Level 1.3.0

API Level 1.3.0

API Level 1.3.0

API Level 1.3.0

API Level 1.3.0

API Level 1.3.0

| Name | Value | Since | Description |
| --- | --- | --- | --- |
| DATA_TYPE_SINT8 | 1 |
| DATA_TYPE_UINT8 | 2 |
| DATA_TYPE_SINT16 | 3 |
| DATA_TYPE_UINT16 | 4 |
| DATA_TYPE_SINT32 | 5 |
| DATA_TYPE_UINT32 | 6 |
| DATA_TYPE_STRING | 7 |
| DATA_TYPE_FLOAT | 8 |
| DATA_TYPE_DOUBLE | 9 |

---

Generated Jun 8, 2026, 3:42:16 PM

5. csv/sdk-class-min-api-levels.csv at main · flocsy/garmin-dev-tools
   URL: https://github.com/flocsy/garmin-dev-tools/blob/main/csv/sdk-class-min-api-levels.csv
   # File: flocsy/garmin-dev-tools/csv/sdk-class-min-api-levels.csv

- Repository: flocsy/garmin-dev-tools | Tools for Garmin CIQ developers | 20 stars | Python
- Branch: main

```csv
Activity.Info:1.0.0
Activity.ProfileInfo:3.2.0
Activity.SplitInfo:5.2.2
Activity.WorkoutIntervalStep:3.2.0
Activity.WorkoutStep:3.2.0
Activity.WorkoutStepInfo:3.2.0
Activity:1.0.0
ActivityMonitor.ActiveMinutes:2.1.0
ActivityMonitor.HeartRateIterator:1.2.2
ActivityMonitor.HeartRateSample:1.2.2
ActivityMonitor.History:1.0.0
ActivityMonitor.Info:1.0.0
ActivityMonitor:1.0.0
ActivityPrompts.ActivityPrompt:5.2.0
ActivityPrompts.ActivityPromptDelegate:5.2.0
ActivityPrompts:5.2.0
ActivityRecording.Session:1.0.0
ActivityRecording:1.0.0
Ant.BurstListener:2.2.0
Ant.BurstPayload:2.2.0
Ant.BurstPayloadIterator:2.2.0
Ant.ChannelAssignment:1.0.0
Ant.CryptoConfig:2.3.0
Ant.DeviceConfig:1.0.0
Ant.EncryptionInvalidSettingsException:2.3.0
Ant.GenericChannel:1.0.0
Ant.Message:1.0.0
Ant.UnableToAcquireChannelException:1.2.0
Ant.UnableToAcquireEncryptedChannelException:2.3.0
Ant:1.0.0
AntPlus.AntPlusNotAllowedException:3.1.0
AntPlus.BatteryStatus:2.2.0
AntPlus.BikeCadence:3.0.0
AntPlus.BikeCadenceInfo:3.0.0
AntPlus.BikeCadenceListener:3.0.0
AntPlus.BikeLight:2.2.0
AntPlus.BikePower:2.2.0
AntPlus.BikePowerListener:2.2.0
AntPlus.BikeRadar:3.0.0
AntPlus.BikeRadarListener:3.0.0
AntPlus.BikeSpeed:3.0.0
AntPlus.BikeSpeedCadence:3.0.0
AntPlus.BikeSpeedCadenceInfo:3.0.0
AntPlus.BikeSpeedCadenceListener:3.0.0
AntPlus.BikeSpeedInfo:3.0.0
AntPlus.BikeSpeedListener:3.0.0
AntPlus.CalculatedCadence:2.2.0
AntPlus.CalculatedPower:2.2.0
AntPlus.CalculatedWheelDistance:2.2.0
AntPlus.CalculatedWheelSpeed:2.2.0
AntPlus.CommonData:2.2.0
AntPlus.DerailleurStatus:3.1.0
AntPlus.Device:2.2.0
AntPlus.DeviceListener:2.2.0
AntPlus.DeviceState:2.2.0
AntPlus.FitnessEquipment:2.4.0
AntPlus.FitnessEquipmentData:2.4.0
AntPlus.FitnessEquipmentListener:2.4.0
AntPlus.FitnessEquipmentMode:2.4.0
AntPlus.LightNetwork:2.2.0
AntPlus.LightNetworkListener:2.2.0
AntPlus.ManufacturerInfo:2.2.0
AntPlus.PedalPowerBalance:2.2.0
AntPlus.ProductInfo:2.2.0
AntPlus.RadarTarget:3.0.0
AntPlus.ResistanceSettings:2.4.0
AntPlus.RunningDynamics:2.4.0
AntPlus.RunningDynamicsData:2.4.0
AntPlus.RunningDynamicsListener:2.4.0
AntPlus.SensorPosition:2.4.0
AntPlus.Shifting:3.1.0
AntPlus.ShiftingListener:3.1.0
AntPlus.ShiftingStatus:3.1.0
AntPlus.SimulationSettings:2.4.0
AntPlus.TargetPowerSettings:2.4.0
AntPlus.TorqueEffectivenessPedalSmoothness:2.2.0
AntPlus.UserSettings:2.4.0
AntPlus:2.2.0
Application.AppBase:1.0.0
Application.AudioContentProviderApp:3.0.0
Application.ObjectStoreAccessException:2.3.0
Application.Properties.InvalidKeyException:2.4.0
Application.Properties:2.4.0
Application.Storage:2.4.0
Application.WatchFaceConfig.Color:5.1.0
Application.WatchFaceConfig.ComplicationRef:5.1.0
Application.WatchFaceConfig.Id:5.1.0
Application.WatchFaceConfig.Settings:5.1.0
Application.WatchFaceConfig:5.1.0
Application:1.0.0
Attention.BacklightOnTooLongException:3.2.1
Attention.ToneProfile:3.1.0
Attention.VibeProfile:1.0.0
Attention:1.0.0
Authentication.Message:3.3.0
Authentication.OAuthMessage:3.3.0
Authentication:3.3.0
Background.ExitDataSizeLimitException:2.3.0
Background.InvalidBackgroundTimeException:2.3.0
Background.MessageSizeLimitException:2.3.0
Background:2.3.0
BluetoothLowEnergy.BleDelegate:3.1.0
BluetoothLowEnergy.Characteristic:3.1.0
BluetoothLowEnergy.Descriptor:3.1.0
BluetoothLowEnergy.Device:3.1.0
BluetoothLowEnergy.DevicePairException:3.1.0
BluetoothLowEnergy.InvalidRequestException:3.1.0
BluetoothLowEnergy.Iterator:3.1.0
BluetoothLowEnergy.ProfileRegistrationException:3.1.0
BluetoothLowEnergy.ScanResult:3.1.0
BluetoothLowEnergy.Service:3.1.0
BluetoothLowEnergy.Uuid:3.1.0
BluetoothLowEnergy.UuidFormatException:3.1.0
BluetoothLowEnergy:3.1.0
Communications.ConnectionListener:1.0.0
Communications.MailboxIterator:1.0.0
Communications.Message:1.3.0
Communications.OAuthMessage:1.3.0
Communications.PhoneAppMessage:1.4.0
Communications.SyncDelegate:3.1.0
Communications:1.0.0
Complications.Complication:4.2.0
Complications.ComplicationNotFoundException:4.2.0
Complications.Id:4.2.0
Complications.Iterator:4.2.0
Complications:4.2.0
Cryptography.Cipher:3.0.0
Cryptography.CipherBasedMessageAuthenticationCode:3.0.0
Cryptography.Hash:3.0.0
Cryptography.HashBasedMessageAuthenticationCode:3.0.0
Cryptography.InvalidBlockSizeException:3.0.0
Cryptography.Key:3.0.0
Cryptography.KeyAgreement:3.0.0
Cryptography.KeyPair:3.0.0
Cryptography:3.0.0
FitContributor.Field:1.3.0
FitContributor:1.3.0
Graphics.AffineTransform:4.2.0
Graphics.BitmapReference:4.0.0
Graphics.BitmapTexture:4.0.0
Graphics.BoundingBox:3.2.7
Graphics.BufferedBitmap:2.3.0
Graphics.BufferedBitmapReference:4.0.0
Graphics.Dc:1.0.0
Graphics.FontReference:4.0.0
Graphics.InvalidBitmapResourceException:2.3.0
Graphics.InvalidPaletteException:2.3.0
Graphics.OutOfGraphicsMemoryException:5.0.0
Graphics.ResourceReference:4.0.0
Graphics.VectorFont:4.2.1
Graphics:1.0.0
Lang.Array:1.0.0
Lang.Boolean:1.0.0
Lang.ByteArray:3.0.0
Lang.Char:1.3.0
Lang.Dictionary:1.0.0
Lang.Double:1.0.0
Lang.Exception:1.0.0
Lang.Float:1.0.0
Lang.InvalidOptionsException:2.3.0
Lang.InvalidValueException:3.0.0
Lang.Long:1.0.0
Lang.Method:1.0.0
Lang.Number:1.0.0
Lang.Object:1.0.0
Lang.OperationNotAllowedException:3.1.0
Lang.ResourceId:1.0.0
Lang.SerializationException:2.4.2
Lang.StorageFullException:2.4.0
Lang.String:1.0.0
Lang.Symbol:1.0.0
Lang.SymbolNotAllowedException:2.2.0
Lang.UnexpectedTypeException:1.2.0
Lang.ValueOutOfBoundsException:3.0.0
Lang.WeakReference:1.2.0
Lang:1.0.0
Math.Filter:2.3.0
Math.FirFilter:2.3.0
Math.IirFilter:2.3.0
Math:1.0.0
Media.ActiveContent:3.0.0
Media.AlbumArt:3.0.0
Media.AudioFormat:3.0.0
Media.CacheStatistics:3.0.0
Media.Content:3.0.0
Media.ContentDelegate:3.0.0
Media.ContentIterator:3.0.0
Media.ContentMetadata:3.0.0
Media.ContentRef:3.0.0
Media.ContentRefIterator:3.0.0
Media.CustomButton:3.0.3
Media.PlaybackProfile:3.0.0
Media.PlayerColors:3.0.3
Media.ProviderIconInfo:3.0.0
Media.SyncDelegate:3.0.0
Media.SystemButton:3.0.3
Media:3.0.0
Notifications.NotificationMessage:5.1.0
Notifications:5.1.0
PersistedContent.Course:2.2.0
PersistedContent.Iterator:2.2.0
PersistedContent.Route:2.2.0
PersistedContent.Track:2.2.0
PersistedContent.Waypoint:2.2.0
PersistedContent.Workout:2.2.0
PersistedContent:2.2.0
PersistedLocations:1.0.2
Position.Info:1.0.0
Position.Location:1.0.0
Position:1.0.0
ScanCode:6.0.0
Sensor.AccelerometerData:2.3.0
Sensor.GyroscopeData:3.3.0
Sensor.HeartRateData:3.0.0
Sensor.Info:1.0.0
Sensor.MagnetometerData:3.3.0
Sensor.SensorData:2.3.0
Sensor.SensorDelegate:5.1.0
Sensor.SensorInfo:3.2.0
Sensor.SensorInfoIterator:3.2.0
Sensor.TooManySensorDataListenersException:2.3.0
Sensor:1.0.0
SensorHistory.SensorHistoryIterator:2.1.0
SensorHistory.SensorSample:2.1.0
SensorHistory:2.1.0
SensorLogging.SensorLogger:2.3.0
SensorLogging.SensorLoggingStats:2.3.0
SensorLogging:2.3.0
StringUtil.InvalidHexStringException:3.0.0
StringUtil:1.3.0
System.AppNotInstalledException:2.2.0
System.ClockTime:1.0.0
System.ConnectionInfo:3.0.0
System.DeviceSettings:1.0.0
System.Intent:2.2.0
System.PreviousOperationNotCompleteException:2.2.0
System.ServiceDelegate:2.3.0
System.Stats:1.0.0
System.UnexpectedAppTypeException:2.2.0
System:1.0.0
Test.AssertException:2.1.0
Test.Logger:2.1.0
Test:2.1.0
Time.Duration:1.0.0
Time.Gregorian.Info:1.0.0
Time.Gregorian:1.0.0
Time.LocalMoment:3.3.0
Time.Moment:1.0.0
Time.RealTimeClockNotValidException:3.0.11
Time:1.0.0
Timer.Timer:1.0.0
Timer:1.0.0
UserProfile.Profile:1.0.0
UserProfile.UserActivity:3.3.0
UserProfile.UserActivityHistoryIterator:3.3.0
UserProfile:1.0.0
WatchUi.ActionMenu:3.4.0
WatchUi.ActionMenuDelegate:3.4.0
WatchUi.ActionMenuItem:3.4.0
WatchUi.AnimationDelegate:3.1.0
WatchUi.AnimationLayer:3.1.0
WatchUi.AnimationResource:3.1.0
WatchUi.BehaviorDelegate:1.0.0
WatchUi.Bitmap:1.0.0
WatchUi.BitmapResource:1.0.0
WatchUi.Button:2.1.0
WatchUi.CheckboxMenu:3.0.0
WatchUi.CheckboxMenuItem:3.0.0
WatchUi.ClickEvent:1.0.0
WatchUi.ComplicationDrawableRef:5.1.0
WatchUi.Confirmation:1.0.0
WatchUi.ConfirmationDelegate:1.0.0
WatchUi.CustomMenu:3.0.0
WatchUi.CustomMenuItem:3.0.0
WatchUi.DataField:1.0.0
WatchUi.DataFieldAlert:3.2.0
WatchUi.DragEvent:3.3.0
WatchUi.Drawable:1.0.0
WatchUi.FlickEvent:3.3.0
WatchUi.FontResource:1.0.0
WatchUi.GlanceView:3.1.0
WatchUi.GlanceViewDelegate:3.1.0
WatchUi.IconMenuItem:3.0.0
WatchUi.InputDelegate:1.0.0
WatchUi.InvalidMenuItemTypeException:3.0.0
WatchUi.InvalidPointException:3.0.0
WatchUi.InvalidSelectableStateException:2.1.0
WatchUi.KeyEvent:1.0.0
WatchUi.Layer:3.1.0
WatchUi.MapMarker:3.0.0
WatchUi.MapPolyline:3.0.0
WatchUi.MapTrackView:3.0.0
WatchUi.MapView:3.0.0
WatchUi.Menu2:3.0.0
WatchUi.Menu2InputDelegate:3.0.0
WatchUi.Menu:1.0.0
WatchUi.MenuInputDelegate:1.0.0
WatchUi.MenuItem:3.0.0
WatchUi.NumberPicker:1.0.0
WatchUi.NumberPickerDelegate:1.0.0
WatchUi.Picker:1.2.0
WatchUi.PickerDelegate:1.2.0
WatchUi.PickerFactory:1.2.0
WatchUi.ProgressBar:1.0.0
WatchUi.ReviewResponseToken:3.4.2
WatchUi.Selectable:2.1.0
WatchUi.SelectableEvent:2.1.0
WatchUi.SimpleDataField:1.0.0
WatchUi.SwipeEvent:1.0.0
WatchUi.Text:1.0.0
WatchUi.TextArea:3.1.0
WatchUi.TextPicker:1.1.0
WatchUi.TextPickerDelegate:1.1.0
WatchUi.ToggleMenuItem:3.0.0
WatchUi.TransparentProgressBar:3.0.3
WatchUi.View:1.0.0
WatchUi.ViewLoop:3.4.0
WatchUi.ViewLoopDelegate:3.4.0
WatchUi.ViewLoopFactory:3.4.0
WatchUi.WatchFace:1.0.0
WatchUi.WatchFaceDelegate:2.3.0
WatchUi.WatchFacePowerInfo:2.3.0
WatchUi:1.0.0
Weather.CurrentConditions:3.2.0
Weather.DailyForecast:3.2.0
Weather.HourlyForecast:3.2.0
Weather:3.2.0

```

Toybox.ActivityRecording

Toybox.FitContributor


I have enough context to draft a fēnix 5 port outline. The user wants an outline/implementation plan for porting the app to fēnix 5, which supports `Activity.Info.rawAmbientPressure` and has a higher API level (3.1.x or 3.2/3.3 depending on base vs Plus). I should create a new plan document or section, likely `PORT_FENIX5.md` or add to IMPLEMENTATION_PLAN.md. The user said "outling for the fenix 5 since i believe this would be the best choice". I should outline the port, covering:

- Why fēnix 5 is the best choice (rawAmbientPressure, 10 ATM, ANT+ HRM-Tri, physical buttons, no reported cap unlike 6/7/8/Tactix).
- Target models: fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus.
- API level gating: minApiLevel likely 2.4.0 (when rawAmbientPressure introduced) or 3.1.0 to cover fēnix 5 family. Need to discuss using `Toybox.Activity` vs `Toybox.ActivityRecording` for sport/subsport enums since `Activity.SPORT_*` was introduced in API 3.2; fēnix 5 base is 3.1, so use `ActivityRecording.SPORT_*` or has check.
- Depth calculation with rawAmbientPressure.
- What the fēnix 5 port would record: depth profile on each RECORD message, max depth, surface interval, etc.
- Keep breath-hold / pool-length horizontal distance features from fēnix 3 app as optional/fallback? The user may want both: freediving depth app with breath-hold timer and maybe pool-length distance. I should outline how to merge: keep prep/hold/recovery, add depth tracking during dive/hold, keep HR logging, keep manual pool length if desired (or drop if not needed). Since fēnix 5 has rawAmbientPressure, depth becomes the main feature. The user asked for "best choice" for depth measurement.
- UI/UX changes for dive: show current depth, max depth, dive time, surface interval. Add fresh/salt water toggle that actually affects depth calc.
- FIT fields: depth on RECORD (depth graph), max depth session field, etc. Keep existing hold_time_s, last_hr_bpm, horizontal_distance_m, length_count.
- New file structure: maybe create `DepthModel.mc`, update `BreathHoldView.mc` to `DiveView.mc` or keep same view with depth overlay. Use conditional compilation with `:has` checks for API gating? Because if you want a single app targeting both fēnix 3 and fēnix 5, you can't use rawAmbientPressure on fēnix 3, so use compile-time or runtime `has` checks. But fēnix 3 is API 1.4 and doesn't have `Activity.Info.rawAmbientPressure` symbol, so referencing it at compile time for fēnix 3 would fail. Use runtime `has` check: `if (info has :rawAmbientPressure)`. However, with compiler optimization, `has` checks may be removed. Need to handle with care. But if separate build variants or separate manifest products, easier: create a `fenix5` product with minApiLevel 3.1 or 2.4. But since `rawAmbientPressure` introduced API 2.4, we can set minApiLevel 2.4 and include fēnix 3? No, fēnix 3 doesn't support 2.4. So two separate builds or use conditional source via `monkey.jungle` with `(:typecheck)`? Better to create a separate branch/build variant, e.g., `source/fenix5/DepthModel.mc` included only for fēnix 5 product using build configurations.

I should produce a clear outline. The user asked "yes please outling for the fenix 5 since i believe this would be the best choice". So I will create a new file `PORT_FENIX5.md` in the project with a structured implementation plan. Also maybe add a short section to README. I should keep it concise but thorough.

Before writing, I need to verify which fēnix 5 models have which API levels. From web search: fēnix 5 / 5S / 5X = API 3.1.x (or 3.1.0). fēnix 5 Plus / 5S Plus / 5X Plus = API 3.2.x/3.3.x. rawAmbientPressure is available since API 2.4.0. So all fēnix 5 variants support it. Activity.SPORT_* moved to Activity module at API 3.2, but ActivityRecording still has them deprecated; on fēnix 5 base (3.1), use ActivityRecording.SPORT_* or has check. Since we want a single build for all fēnix 5 variants, use `Activity has :SPORT_GENERIC ? Activity.SPORT_GENERIC : ActivityRecording.SPORT_GENERIC` or just `ActivityRecording.SPORT_GENERIC` (works on all). Use `FitContributor.MESG_TYPE_*` for FIT. Use `using Toybox.Activity;` for `Activity.getActivityInfo()`.

Need to mention depth formula. rawAmbientPressure returns Pascals? The API docs: Activity.Info.rawAmbientPressure is Number (Pa). Yes. Use deltaP / (rho * g). For pressure underwater, rawAmbientPressure absolute. At surface ~101325 Pa. At 10 m fresh water, pressure ~201325 Pa (delta ~100000 Pa). rho*g ≈ 997*9.80665 ≈ 9777.6 Pa/m; deltaP/rho*g ≈ 10.23 m. Salt: 1023*9.80665 ≈ 10031.2 Pa/m; ≈ 9.97 m. The formula is fine.

Need to mention calibrating surface pressure at start of dive/hold. Important to set surfacePressure when user is floating at surface. Could auto-capture at startSession when depth ~0, or manual START to calibrate. Since wave/splash affects, average over a few seconds or require user to be still. Could provide "calibrate" via START in idle.

Need to mention no GPS underwater, no horizontal distance. The fēnix 5 app measures vertical depth only. If the user wants horizontal distance in pool, they still need manual length counting. So we can keep the pool-length feature.

Need to mention water type toggle: fresh vs salt; use in depth calc. Provide in settings.

Need to mention memory/performance: fēnix 5 has more memory than fēnix 3, depth recording per second as RECORD field.

Need to mention testing/sideloading: build for fenix5/fenix5s/fenix5x/fenix5plus/fenix5splus/fenix5xplus, sideload PRG.

Need to mention that fēnix 5 is not dive-rated, use at own risk, 10 ATM pressure equivalent but surface swimming only per Garmin.

Also mention HRM-Tri works via ANT+ on fēnix 5.

I think the best deliverable is a new markdown file `PORT_FENIX5.md` with the outline. I will also add a brief note to README.md linking to it. The user asked for outline, not implementation, but I can include code snippets.

Let's write the file. Use Write tool. Then edit README to mention it.

I need to ensure file paths are correct. I'll write `/home/liph/code/garmin-freedive-app/PORT_FENIX5.md`.

Then update README: add under "Which fēnix for depth?" a link to the new port plan, maybe mention it's outlined.

Also maybe update IMPLEMENTATION_PLAN.md to reference it. But not necessary; the user asked for outline. I'll create the doc and update README.

Let's compose the content.

Structure:
# fēnix 5 Freedive Depth Port Outline
## 1. Why fēnix 5 is the recommended target
Table of supported models, API level, rawAmbientPressure, ANT+, water rating, button controls.
## 2. What changes vs fēnix 3 app
- Add depth measurement using `Activity.Info.rawAmbientPressure`.
- Water-type toggle now used for density.
- UI shows depth, max depth, dive time, surface interval.
- FIT depth graph on RECORD messages, plus session summary fields.
- Keep breath-hold/pool-length/HR features as optional.
## 3. API / compatibility notes
- minApiLevel: 2.4.0? Actually fēnix 5 base is 3.1, but rawAmbientPressure since 2.4. To include all fēnix 5 variants, set minApiLevel 2.4.0? But product list determines devices; fēnix 5 product will require its own min API. Better set minApiLevel to 2.4.0 and add fēnix 5 products. Since fēnix 5 is 3.1, no issue. However, if we also include fēnix 3 in same manifest, can't because minApiLevel is max of all? Actually minApiLevel in manifest is the minimum required for the app; if you target fēnix 3 (1.4) and fēnix 5 (2.4+), you can't set minApiLevel higher than 1.4 or fēnix 3 won't install. But fēnix 5 devices have API 3.1+, so they meet 1.4. The problem is using rawAmbientPressure symbol in code compiled for fēnix 3: with `has` check, the symbol lookup is guarded, but optimization may remove it. In practice, many devs use `if (info has :rawAmbientPressure)`; at runtime on fēnix 3 this returns false, but if optimizer constant-folds it to false? Actually if `Activity.Info has :rawAmbientPressure` is false on fēnix 3, optimizer may remove the branch entirely and not include the symbol reference. But at strict optimization, it can be problematic. Simpler: separate build variants. The outline should recommend separate build variant for fēnix 5 (different manifest or monkey.jungle). We can create `manifest_fenix5.xml` and `monkey.jungle` profiles.

Let's decide: The outline should present two options:
A. Separate fēnix 5 build (cleanest): `manifest_fenix5.xml`, `monkey_fenix5.jungle`, source files `DepthModel.mc`, `DiveView.mc` etc.
B. Single unified app with runtime `has` checks and `(:typecheck)` guards (more complex, risk of optimizer issues).

Given user is personal sideload, separate build is easier. I'll recommend Option A, with notes on how to keep common code.

## 4. Suggested file changes
- `manifest_fenix5.xml`: products fenix5, fenix5s, fenix5x, fenix5plus, fenix5splus, fenix5xplus; minApiLevel 2.4.0; permissions Sensor, ActivityRecording, FitContributor.
- `monkey_fenix5.jungle`: project.manifest = manifest_fenix5.xml, base.sourcePath = source, plus maybe `fenix5.sourcePath = source/fenix5` for variant-specific files.
- `source/DepthModel.mc`: constants, surface pressure calibration, depth computation, water type.
- `source/DiveView.mc` or modify `BreathHoldView.mc`: add depth display, states for surface/dive/surface interval, max depth tracking.
- `source/DiveDelegate.mc` or modify `BreathHoldDelegate.mc`: keep long-press BACK for pool length, add START for surface pressure calibrate? Use UP/DOWN for length/contraction or maybe depth alarm? Keep same mapping to avoid confusion.
- `source/FitFields.mc` maybe helper to create FIT fields.

## 5. Depth calculation snippet
Provide code.

## 6. FIT field plan
Session: max_depth_m, best_hold_time_s, last_hr_bpm, horizontal_distance_m, length_count, surface_interval_s maybe.
Record: depth_m (FLOAT), maybe temp/HR if available.

## 7. UI layout ideas for fēnix 5 (240×240 MIP)
Display:
Top line: state (SURFACE / DIVE / SURF INT / HOLD / RECOVERY)
Big number: current depth or hold time
Middle: max depth / dive time
Bottom: HR, length/distance, contraction count
Add fresh/salt indicator.

## 8. Build & sideload
Use SDK, targets, copy PRG to APPS.

## 9. Risks
- Pressure cap on newer fēnix; 5 is reportedly uncapped, but firmware updates could change? Mention risk.
- Watch not dive-rated, warranty.
- Raw pressure may need surface calibration and temperature compensation.
- Depth accuracy ~0.1 m vs dive computer, but trust at own risk.
- Memory: recording depth each second may be fine.

## 10. Decision needed from user
- Do you want to keep the breath-hold prep/recovery timer and pool-length distance in the fēnix 5 app, or focus purely on depth diving?
- Separate app or unified build?
- Button mapping for dive-specific actions (e.g., mark surface, calibrate)?

I think that's a solid outline. I'll write the file. Also update README and maybe IMPLEMENTATION_PLAN.

Let's do it.

write

# fēnix 5 Freedive Depth Port — Implementation Outline

This document outlines how to port the existing fēnix 3 breath-hold app into a **depth-capable freediving app** for the **Garmin fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family.

> **Why fēnix 5?** It is the best fēnix choice for a personal freedive depth logger: it exposes `Activity.Info.rawAmbientPressure` (API Level 2.4.0+), supports the user’s ANT+ HRM‑Tri chest strap, has physical buttons that work underwater, and forum/developer reports indicate it does **not** artificially cap underwater pressure readings. Newer fēnix 6 / 7 / 8 / Tactix models reportedly cap pressure around 3 m underwater, making them unsuitable for freediving depth despite also having the API.

---

## 1. Recommended Target Devices & API

| Device | CIQ API level | `rawAmbientPressure` | ANT+ HR | Water rating | Notes |
|--------|---------------|----------------------|---------|--------------|-------|
| fēnix 5 / 5S / 5X | ~3.1.x | ✅ | ✅ | 10 ATM | Base 5-series; use `ActivityRecording.SPORT_*` enums for safety |
| fēnix 5 Plus / 5S Plus / 5X Plus | ~3.2.x–3.3.x | ✅ | ✅ | 10 ATM | Plus models moved some enums to `Toybox.Activity` |
| fēnix 3 / 3 HR | 1.4.0 | ❌ | ✅ | 10 ATM | Keep as separate breath-hold build (no depth) |
| fēnix 6 / 7 / 8 / Tactix | 3.3+ / 4.x / 5.x | ✅ (capped ~3 m) | ✅ | 10 ATM | **Not recommended** for depth |

**Manifest choice for fēnix 5-only build:**

```xml
<iq:application entry="DiveApp" id="…" minApiLevel="2.4.0" type="watch-app">
    <iq:products>
        <iq:product id="fenix5"/>
        <iq:product id="fenix5s"/>
        <iq:product id="fenix5x"/>
        <iq:product id="fenix5plus"/>
        <iq:product id="fenix5splus"/>
        <iq:product id="fenix5xplus"/>
    </iq:products>
    <iq:permissions>
        <iq:permission id="Sensor"/>
        <iq:permission id="ActivityRecording"/>
        <iq:permission id="FitContributor"/>
    </iq:permissions>
</iq:application>
```

> **Note:** For a single app that also runs on fēnix 3, you must guard `rawAmbientPressure` with `has` checks and be careful with compiler optimization. For a personal sideload, the cleanest approach is a **separate fēnix 5 build** (`manifest_fenix5.xml` + a second `.jungle` file), which is what this outline assumes.

---

## 2. What the fēnix 5 App Would Do

Keep the working breath-hold / surface features from the fēnix 3 app and add **true depth logging**:

- **Surface calibration** before the first dive.
- **Depth tracking** during the dive (current depth, max depth, dive time).
- **Surface interval** timer between dives.
- **Breath-hold prep / hold / recovery** timer (optional, useful for static sessions at the surface).
- **Contraction counter** and **manual pool-length counter** (optional, since horizontal distance still has no sensor).
- **Heart-rate logging** from HRM‑Tri or wrist sensor.
- **FIT recording** with a depth graph plus session summary fields.

> **What “underwater distance” means on fēnix 5:** The watch measures **vertical depth**, not horizontal/3-D travel distance. GPS does not work underwater, and accelerometer dead-reckoning drifts too fast. If you also want horizontal distance in a pool, keep the manual `lengths × pool length` feature.

---

## 3. New / Changed Files

| File | Purpose |
|------|---------|
| `manifest_fenix5.xml` | fēnix 5-only manifest (products + `minApiLevel="2.4.0"`) |
| `monkey_fenix5.jungle` | Points to `manifest_fenix5.xml` and optionally a `source/fenix5/` overlay |
| `source/DepthModel.mc` | Constants, surface-pressure calibration, depth calculation, water-type density |
| `source/DiveView.mc` | Main view: shows depth, dive time, max depth, HR, lengths, contractions |
| `source/DiveDelegate.mc` | Button handling; adds depth-related actions if desired |
| `source/Settings.mc` | Add `isSaltWater` to existing pool-length settings |
| `source/PoolLengthView.mc` | Add a fresh/salt-water toggle screen or toggle in the same picker |
| `source/FitFields.mc` *(optional)* | Helper that creates all `FitContributor` fields |
| `README.md` | Add fēnix 5 build/sideload instructions |

---

## 4. Depth Calculation

`Activity.Info.rawAmbientPressure` is the **absolute ambient pressure in Pascals** (uncalibrated). Depth is computed from the pressure difference versus a calibrated surface pressure.

```monkey-c
using Toybox.Activity;
using Toybox.System;

const G = 9.80665;
const RHO_FRESH = 997.0;  // kg/m³
const RHO_SALT  = 1023.0; // kg/m³

class DepthModel {
    static var surfacePressure = null;
    static var isSaltWater = false;

    // Call while the watch is floating calmly at the surface.
    static function calibrate() {
        var info = Activity.getActivityInfo();
        if (info != null && info has :rawAmbientPressure && info.rawAmbientPressure != null) {
            surfacePressure = info.rawAmbientPressure;
        }
    }

    static function depthM() {
        if (surfacePressure == null) { return null; }
        var info = Activity.getActivityInfo();
        if (info == null || !(info has :rawAmbientPressure) || info.rawAmbientPressure == null) {
            return null;
        }
        var rho = isSaltWater ? RHO_SALT : RHO_FRESH;
        var deltaP = info.rawAmbientPressure - surfacePressure;
        var d = deltaP / (rho * G);
        return (d < 0.0) ? 0.0 : d; // clamp negative splash noise to 0
    }
}
```

### Calibration strategy

1. **Auto-calibrate on first START** while the user is on the surface.
2. **Manual re-calibrate** via long-press START or a menu item if the surface pressure drifts.
3. For best accuracy, take a short average (3–5 samples) during calibration rather than a single reading.

---

## 5. FIT Field Plan

### Per-`RECORD` (depth graph)

| Field name | ID | Type | Units | Notes |
|------------|----|------|-------|-------|
| `depth_m` | 0 | `FLOAT` | m | Written every second during the dive |
| `hr_bpm` *(optional)* | 1 | `UINT8` | bpm | Redundant with native HR but convenient |

### Per-`SESSION` (summary)

| Field name | ID | Type | Units | Notes |
|------------|----|------|-------|-------|
| `max_depth_m` | 2 | `UINT16` | m | Deepest point of the session |
| `best_hold_time_s` | 3 | `UINT16` | s | Same as existing `hold_time_s` |
| `last_hr_bpm` | 4 | `UINT8` | bpm | Last sampled heart rate |
| `surface_interval_s` | 5 | `UINT16` | s | Time since last dive ended |
| `horizontal_distance_m` | 6 | `UINT16` | m | From manual pool-length counting |
| `length_count` | 7 | `UINT8` | laps | Number of manually counted lengths |

### Why not write the standard Garmin `distance` field?

Because the watch cannot measure true underwater travel distance. Writing Garmin’s standard `distance` field would produce a misleading graph. Use custom developer fields only.

---

## 6. Suggested UI Layout (fēnix 5 240 × 240 MIP)

```
+------------------+
|  SURFACE  SALT   |  <- state + water type
|                  |
|      0.0 m       |  <- current depth (big)
|                  |
|  MAX 12.3m  1:23 |  <- max depth / dive or hold time
|  HR 58  L3 D75m  |  <- HR + manual length/distance
|  UP=LAP DOWN=CONT|  <- hint
+------------------+
```

States:

- `IDLE` — show last session max depth, selected pool length, water type. START begins prep/calibration.
- `SURFACE` — waiting at surface; show current depth (0.0 m), surface interval. START marks “begin dive / begin hold.”
- `DIVE` / `HOLD` — show current depth prominently, dive time below it, max depth.
- `RECOVERY` / `SURFACE_INTERVAL` — show surface interval timer, last max depth, HR.
- `DONE` — summary screen.

Button mapping (kept close to fēnix 3 version):

| Button | Action |
|--------|--------|
| **START** | Begin session / start dive / end dive / finish recovery |
| **UP** | Count a pool length (manual horizontal distance) |
| **DOWN** | Count a contraction |
| **BACK short** | Exit (idle) / discard (active) |
| **BACK long** | Open settings (pool length + fresh/salt) |

Optional extra: **long-press START** from surface to force a surface-pressure re-calibration.

---

## 7. Build & Sideload

1. Install the Connect IQ SDK and the fēnix 5 / 5S / 5X / 5 Plus device files.
2. Build using the fēnix 5 manifest:
   ```bash
   monkeyc -d fenix5 -f monkey_fenix5.jungle -o bin/FreediveDepth_fenix5.prg -y developer_key.der
   ```
3. Sideload the `.PRG` to `GARMIN/APPS/` over USB mass-storage mode.
4. Test first in a pool, comparing depth against a known reference (pool floor depth, dive line, or a dedicated dive computer).

---

## 8. Risks & Open Questions

| Risk | Impact | Mitigation |
|------|--------|------------|
| fēnix 5 is **not dive-rated** by Garmin | Warranty / water-damage risk outside stated use | Use at own risk; rinse after salt water; inspect seals |
| Future firmware may cap pressure like fēnix 6/7/8 | Depth readings stop below ~3 m | Pin CIQ/firmware version if possible; test after any watch update |
| Surface pressure drift (waves, weather, temperature) | Depth offset | Re-calibrate before each dive; average several samples |
| Raw pressure sensor noise / temperature hysteresis | ±0.1–0.3 m jitter | Light filtering; clamp negative values to 0 |
| Memory / recording limits on long sessions | App crash or lost data | Keep depth as `FLOAT` record; test 5+ min sessions |
| HRM‑Tri optical/chest strap underwater | HR dropouts at depth | Accept as log-only; not a safety instrument |

---

## 9. Decisions Needed Before Coding

1. **Unified app or separate fēnix 5 build?**
   *Recommended:* separate `manifest_fenix5.xml` / `monkey_fenix5.jungle` to avoid `has`-check optimizer surprises.
2. **Keep breath-hold prep/recovery and pool-length features?**
   *Recommended:* keep them — they are useful for static training and pool swims.
3. **Auto vs manual surface-pressure calibration?**
   *Recommended:* auto-calibrate on first START, with a manual re-calibrate option.
4. **Add depth alarms?**
   (e.g., vibrate at target depth) — nice-to-have, but adds complexity and battery use.
5. **Target model order?**
   Build for `fenix5` first (most common), then `fenix5s` / `fenix5x` / `fenix5plus` variants.

---

## 10. Summary of the “Best fēnix” Choice

For a personal, sideloaded freediving app, the **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family is the sweet spot:

- Has the raw ambient pressure API and **reportedly uncapped** underwater readings.
- Works with the user’s existing **HRM‑Tri** ANT+ chest strap.
- Physical buttons work underwater.
- Cheaper and easier to source used than a Garmin Descent.

The main caveats are Garmin’s “not dive-rated” disclaimer and the fact that horizontal/3-D underwater distance is still impossible on any wrist device.


Successfully wrote 10722 bytes to /home/liph/code/garmin-freedive-app/PORT_FENIX5.md


edit

A depth value can then be written to a `FitContributor` field on each `RECORD` message to produce a depth graph in Garmin Connect.

---

## fēnix 5 depth port

A full implementation outline for porting this app to the depth-capable **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family is in [`PORT_FENIX5.md`](PORT_FENIX5.md). It covers the `rawAmbientPressure` API, depth calculation, water-type density, FIT field plan, UI layout, and build/sideload steps.

---


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

     ...
  99 A depth value can then be written to a `FitContributor` field on each `RECORD` message to produce a depth graph in Garmin Connect.
 100
 101 ---
 102
+103 ## fēnix 5 depth port
+104
+105 A full implementation outline for porting this app to the depth-capable **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family is in [`PORT_FENIX5.md`](PORT_FENIX5.md). It covers the `rawAmbientPressure` API, depth calculation, water-type density, FIT field plan, UI layout, and build/sideload steps.
+106
+107 ---
+108
 103 ## Build prerequisites
 104
 105 1. A modern computer with the **Connect IQ SDK** installed.
 106    - Download the SDK Manager from: https://developer.garmin.com/connect-iq/sdk/
     ...

--- /home/liph/code/garmin-freedive-app/README.md
+++ /home/liph/code/garmin-freedive-app/README.md
@@ -99,8 +99,14 @@
 A depth value can then be written to a `FitContributor` field on each `RECORD` message to produce a depth graph in Garmin Connect.

 ---

+## fēnix 5 depth port
+
+A full implementation outline for porting this app to the depth-capable **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family is in [`PORT_FENIX5.md`](PORT_FENIX5.md). It covers the `rawAmbientPressure` API, depth calculation, water-type density, FIT field plan, UI layout, and build/sideload steps.
+
+---
+
 ## Build prerequisites

 1. A modern computer with the **Connect IQ SDK** installed.
    - Download the SDK Manager from: https://developer.garmin.com/connect-iq/sdk/


So a freediving app on the fēnix 5 records **depth profile** (max depth, current depth, dive time, surface interval) rather than horizontal distance.

### Depth calculation on a fēnix 5 (future port)

```monkey-c
using Toybox.Activity;

const G = 9.80665;
const RHO_FRESH = 997.0;  // kg/m³
const RHO_SALT  = 1023.0; // kg/m³

var surfacePressure = null;
var isSaltWater = false;

function calibrateSurface() {
    var info = Activity.getActivityInfo();
    if (info != null && info has :rawAmbientPressure && info.rawAmbientPressure != null) {
        surfacePressure = info.rawAmbientPressure;
    }
}

function computeDepth() {
    if (surfacePressure == null) { return null; }
    var info = Activity.getActivityInfo();

[155 more lines in file. Use offset=90 to continue.]


    if (surfacePressure == null) { return null; }
    var info = Activity.getActivityInfo();
    if (info == null || !(info has :rawAmbientPressure) || info.rawAmbientPressure == null) {
        return null;
    }
    var rho = isSaltWater ? RHO_SALT : RHO_FRESH;
    var deltaP = info.rawAmbientPressure - surfacePressure;
    return deltaP / (rho * G);
}
```

A depth value can then be written to a `FitContributor` field on each `RECORD` message to produce a depth graph in Garmin Connect.

---

## fēnix 5 depth port

A full implementation outline for porting this app to the depth-capable **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family is in [`PORT_FENIX5.md`](PORT_FENIX5.md). It covers the `rawAmbientPressure` API, depth calculation, water-type density, FIT field plan, UI layout, and build/sideload steps.

---

## Build prerequisites

1. A modern computer with the **Connect IQ SDK** installed.
   - Download the SDK Manager from: https://developer.garmin.com/connect-iq/sdk/

[132 more lines in file. Use offset=113 to continue.]


Created a full fēnix 5 depth port outline at:

**`~/code/garmin-freedive-app/PORT_FENIX5.md`**

and linked it from `README.md`.

## Highlights of the outline

- **Recommended targets:** fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus — all support `Activity.Info.rawAmbientPressure` (API 2.4.0+) and reportedly do **not** cap underwater pressure, unlike fēnix 6/7/8/Tactix.
- **Build strategy:** create a separate `manifest_fenix5.xml` + `monkey_fenix5.jungle` rather than trying to merge fēnix 3 and fēnix 5 into one binary, because `rawAmbientPressure` is not present on fēnix 3 and `has`-check optimization can be tricky.
- **Depth model:** `DepthModel.mc` with surface-pressure calibration, fresh/salt density, and `depthM()` using `deltaP / (rho * g)`.
- **FIT fields:**
  - `depth_m` on every `RECORD` message → depth graph in Garmin Connect.
  - `max_depth_m`, `surface_interval_s`, plus the existing `best_hold_time_s`, `last_hr_bpm`, `horizontal_distance_m`, `length_count` on `SESSION`.
- **UI:** shows current depth as the big number, plus max depth, dive/hold time, HR, and manual length/distance.
- **Button mapping:** same as the fēnix 3 app (START, UP=length, DOWN=contraction, BACK long-press=settings), with optional long-press START to re-calibrate surface pressure.
- **Risks:** fēnix 5 is not dive-rated by Garmin; future firmware could theoretically cap pressure; horizontal/3-D underwater distance remains impossible.

I also listed the **decisions you need to make before I start coding** the fēnix 5 port in that document — mainly whether you want a separate build and whether to keep the breath-hold/pool-length features. Want me to start implementing the fēnix 5 variant next?


the distance should be on a different screen or even function since this a completely different meassurement. with dive time, distance, hr. would also be nice to have another screen or function. would be nice to have a menu at the beginning to choose between:
Depth type: constant weight (different menu for bifin, monofin, nofin), *something* immersions(check online how it is called), strokes count, speed?, depth, hr, breathhold time, menu for braething break or braeth up function normally 2min or 2ith button press.
Distance: (submenu: bifin, monofin, nofin), strokes count, speed?, distance, hr, breahold time, menu for breathing break or breath up function normally 2min or with button press.
Static apnoe test: breath up timer. braethhold time, hr, depth.
Breathing trainer (on land): predefined beathhold time manual entry. the breaks will get shorter. starting with 3min, 2.5min, 2min, 1.5min, 1min


The user is asking for a major feature expansion of the Garmin freediving app. They want:

1. Distance should be on a separate screen/function (completely separate measurement)
2. A main menu at the beginning to choose between:
   - Depth type:
     - Constant weight (submenu: bifin, monofin, nofin)
     - Something immersions (they want me to check online what this is called)
     - Strokes count, speed?, depth, hr, breathhold time
     - Menu for breathing break or breath up function (normally 2min or with button press)
   - Distance:
     - Submenu: bifin, monofin, nofin
     - Strokes count, speed?, distance, hr, breathhold time
     - Menu for breathing break or breath up function (normally 2min or with button press)
   - Static apnoe test:
     - breath up timer, breathhold time, hr, depth
   - Breathing trainer (on land):
     - predefined breathhold time manual entry
     - breaks get shorter: 3min, 2.5min, 2min, 1.5min, 1min

This is a substantial redesign. I need to:
1. Research what "immersions" in freediving is called (likely "free immersion" or FIM)
2. Create a new implementation plan
3. Possibly restructure the code significantly

Given the complexity, I should:
- Use web search to confirm freediving discipline terminology
- Create a new structured plan document
- Possibly ask clarifying questions but the user seems clear enough about what they want
- Start with the plan rather than jumping into code

I should use context-mode tools where appropriate, especially for web research. The user wants me to check online how "immersions" is called. So I need web_explore or ctx_fetch_and_index + ctx_search.

Let me first research the freediving disciplines terminology, then create a comprehensive plan.

Freediving disciplines:
- CWT = Constant Weight (with fins)
- CNF = Constant No-Fins
- CWTB = Constant Weight Bi-fins
- CWT-M = Constant Weight Monofin
- FIM = Free Immersion (pulling down/up the line)
- DYN = Dynamic Apnea (horizontal distance)
- DNF = Dynamic No-Fins
- STA = Static Apnea
- NLT = No-Limits

The user said "*something* immersions" - this is likely "Free Immersion" or FIM.

I'll research this and also think about how to structure the app.

Given the repeated message (3 times), the user is emphasizing this. I should take it seriously and create a good plan.

I should probably:
1. Use web_explore to confirm freediving discipline names
2. Read the current source files
3. Create a new plan document (maybe APP_MENU_PLAN.md or restructure IMPLEMENTATION_PLAN.md)
4. Present the plan to the user for approval before implementing

This is a big enough change that I shouldn't just start coding. The user has given me a lot of requirements but some things are still ambiguous (e.g., "speed?" with question marks means they're unsure if it's possible/feasible). I should note what's feasible and what's not.

Let me do the research first using context-mode/web tools.

Question: freediving disciplines constant weight bifin monofin nofin free immersion FIM CWT CNF official names


Reviewed 4 sources · synthesized answer with 5 findings

- [web_fetch] Community/practical context: Freediving pushes the boundaries of human capability in the aquatic world. It involves divers striving to achieve impressive depths or durations on a breath hold while adhering to
- [web_fetch] Community/practical context: Freediving has eight competitive disciplines recognized by AIDA International: four pool disciplines (STA, DYN, DYNB, DNF) and four depth disciplines (CWT, CWTB, CNF, FIM). Each te
- [web_fetch] Community/practical context: 🔒403 This area is restricted — like the deepest ocean zones. You don't have permission to access this page. Go to Homepage aidainternational.org
- [web_fetch] Community/practical context: Freediving has its own alphabet — STA, DYN, DNF, CWT, CWTB, CNF, FIM, VWT, NLT. If you're new to the sport, this looks like gibberish. But each abbreviation represents a specific d
- [web_explore] Community/practical context: Competitive FreedivingRecognized by the International Olympic Committee, CMAS serves as the official international federation for freediving.The organization is responsible for gov

Internal research: web_search ×4, web_fetch ×16, web_fetch_headless ×0

Findings
- [web_fetch] Community/practical context: Freediving pushes the boundaries of human capability in the aquatic world. It involves divers striving to achieve impressive depths or durations on a breath hold while adhering to
- [web_fetch] Community/practical context: Freediving has eight competitive disciplines recognized by AIDA International: four pool disciplines (STA, DYN, DYNB, DNF) and four depth disciplines (CWT, CWTB, CNF, FIM). Each te
- [web_fetch] Community/practical context: 🔒403 This area is restricted — like the deepest ocean zones. You don't have permission to access this page. Go to Homepage aidainternational.org
- [web_fetch] Community/practical context: Freediving has its own alphabet — STA, DYN, DNF, CWT, CWTB, CNF, FIM, VWT, NLT. If you're new to the sport, this looks like gibberish. But each abbreviation represents a specific d
- [web_explore] Community/practical context: Competitive FreedivingRecognized by the International Olympic Committee, CMAS serves as the official international federation for freediving.The organization is responsible for gov

Sources
- [web_fetch] Types of Freediving: The Different Disciplines Explained: https://blog.padi.com/types-of-freediving-different-disciplines-explained/
- [web_fetch] Freediving Disciplines Explained: Pool, Depth & Everything In Between: https://www.freedivingforall.com/blog/freediving-disciplines-explained
- [web_fetch] 403 – Access Forbidden | AIDA International: https://www.aidainternational.org/Competitive
- [web_fetch] Freediving Disciplines Explained: STA, DYN, CWT, FIM and More: https://www.lajollafreediveclub.com/blog/freediving-disciplines-sta-dyn-cwt-explained

Internal tools
Internal research: web_search ×4, web_fetch ×16, web_fetch_headless ×0

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


Good. Now I have the terminology. Let me read the current source files to understand the structure before planning. I should also probably ask a few clarifying questions because the request is complex, but I can also just create a plan and present it. Given the instructions say "Use ask_user_question whenever the user's request is underspecified" - this is somewhat specified but there are ambiguities. However, the user seems to know what they want and has given a lot of detail. I think creating a structured plan and presenting it is the right move, with options for them to confirm.

Let me read the current implementation plan and source files to base the new plan on them.


using Toybox.WatchUi;
using Toybox.Graphics;
using Toybox.System;
using Toybox.Lang;
using Toybox.Timer;
using Toybox.ActivityRecording;
using Toybox.FitContributor;
using Toybox.Sensor;

class BreathHoldView extends WatchUi.View {

    // App states
    const STATE_IDLE     = 0;
    const STATE_PREP     = 1;
    const STATE_HOLD     = 2;
    const STATE_RECOVERY = 3;
    const STATE_DONE     = 4;

    var state = STATE_IDLE;
    var timer = null;

    // FIT session
    var session = null;
    var holdField = null;
    var hrField = null;
    var distanceField = null;
    var lengthField = null;

    // Breath-hold configuration (seconds)
    var prepTime = 120;
    var recoveryTime = 120;

    // Runtime counters
    var currentTime = 0;
    var maxHoldTime = 0;
    var contractionCount = 0;
    var lengthCount = 0;
    var horizontalDistanceM = 0;
    var lastHeartRate = null;

    function initialize() {
        View.initialize();
        lengthCount = 0;
        horizontalDistanceM = 0;
    }

    function onShow() {
        timer = new Timer.Timer();
    }

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

    function onUpdate(dc) {
        // Clear screen
        dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);
        dc.clear();
        dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);

        var cx = dc.getWidth() / 2;
        var y = 16;

        var statusText = "";
        var timeText = "";
        var infoText = "";

        if (state == STATE_IDLE) {
            statusText = "FREEDIVE";
            timeText = "Press START";
            infoText = "POOL " + Settings.poolLengthM + "m  HOLD BACK";
        } else if (state == STATE_PREP) {
            statusText = "PREP BREATHE";
            timeText = formatTime(prepTime - currentTime);
            infoText = "START = skip to hold";
        } else if (state == STATE_HOLD) {
            statusText = "HOLD";
            timeText = formatTime(currentTime);
            infoText = "UP=LAP " + lengthCount + "  DOWN=CONT " + contractionCount;
        } else if (state == STATE_RECOVERY) {
            statusText = "RECOVERY";
            timeText = formatTime(recoveryTime - currentTime);
            infoText = "L" + lengthCount + " D" + horizontalDistanceM + "m  START=finish";
        } else if (state == STATE_DONE) {
            statusText = "SAVED";
            timeText = formatTime(maxHoldTime);
            infoText = "L" + lengthCount + " D" + horizontalDistanceM + "m";
        }

        // Title
        dc.drawText(cx, y, Graphics.FONT_SMALL, statusText, Graphics.TEXT_JUSTIFY_CENTER);

        // Main timer
        dc.drawText(cx, y + 28, Graphics.FONT_NUMBER_MEDIUM, timeText, Graphics.TEXT_JUSTIFY_CENTER);

        // Heart rate
        var hrStr = "--";
        if (lastHeartRate != null) {
            hrStr = lastHeartRate.toString();
        }
        dc.drawText(cx, y + 88, Graphics.FONT_SMALL, "HR " + hrStr, Graphics.TEXT_JUSTIFY_CENTER);

        // Info / instruction line
        dc.drawText(cx, y + 115, Graphics.FONT_TINY, infoText, Graphics.TEXT_JUSTIFY_CENTER);

        // Pool distance hint on idle and recovery/done screens
        if (state == STATE_IDLE) {
            dc.drawText(cx, y + 140, Graphics.FONT_TINY, "HOLD BACK = settings", Graphics.TEXT_JUSTIFY_CENTER);
        }
    }

    function formatTime(seconds) {
        if (seconds < 0) {
            seconds = 0;
        }
        var m = seconds / 60;
        var s = seconds % 60;
        return m.format("%02d") + ":" + s.format("%02d");
    }

    // Called once per second by the Timer
    function onTimer() {
        if (state == STATE_PREP) {
            currentTime++;
            if (currentTime >= prepTime) {
                state = STATE_HOLD;
                currentTime = 0;
            }
        } else if (state == STATE_HOLD) {
            currentTime++;
        } else if (state == STATE_RECOVERY) {
            currentTime++;
            if (currentTime >= recoveryTime) {
                finishSession();
                return;
            }
        }

        updateHeartRate();
        WatchUi.requestUpdate();
    }

    function updateHeartRate() {
        var info = Sensor.getInfo();
        if (info != null && info.heartRate != null) {
            lastHeartRate = info.heartRate;
        }
    }

    function onLengthButton() {
        if (state == STATE_HOLD) {
            lengthCount++;
            horizontalDistanceM = lengthCount * Settings.poolLengthM;
            WatchUi.requestUpdate();
        }
    }

    function startSession() {
        if (session == null || !session.isRecording()) {
            session = ActivityRecording.createSession({
                :name => "BreathHold",
                :sport => ActivityRecording.SPORT_GENERIC,
                :subSport => ActivityRecording.SUB_SPORT_GENERIC
            });

            holdField = session.createField(
                "hold_time_s",
                0,
                FitContributor.DATA_TYPE_UINT16,
                {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => "s"}
            );

            hrField = session.createField(
                "last_hr_bpm",
                1,
                FitContributor.DATA_TYPE_UINT8,
                {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => "bpm"}
            );

            distanceField = session.createField(
                "horizontal_distance_m",
                2,
                FitContributor.DATA_TYPE_UINT16,
                {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => "m"}
            );

            lengthField = session.createField(
                "length_count",
                3,
                FitContributor.DATA_TYPE_UINT8,
                {:mesgType => FitContributor.MESG_TYPE_SESSION, :units => "laps"}
            );

            session.start();
        }
    }

    function finishSession() {
        if (state == STATE_HOLD) {
            maxHoldTime = currentTime;
        }

        if (session != null && session.isRecording()) {
            if (holdField != null) {
                holdField.setData(maxHoldTime);
            }
            if (hrField != null && lastHeartRate != null) {
                hrField.setData(lastHeartRate);
            }
            if (distanceField != null) {
                distanceField.setData(horizontalDistanceM);
            }
            if (lengthField != null) {
                lengthField.setData(lengthCount);
            }
            session.stop();
            session.save();
        }

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

        state = STATE_DONE;
        WatchUi.requestUpdate();
    }

    function discardSession() {
        if (session != null && session.isRecording()) {
            session.stop();
            session.discard();
        }
        session = null;
        if (timer != null) {
            timer.stop();
        }
        resetRunState();
    }

    function resetRunState() {
        state = STATE_IDLE;
        currentTime = 0;
        maxHoldTime = 0;
        contractionCount = 0;
        lengthCount = 0;
        horizontalDistanceM = 0;
        lastHeartRate = null;
    }

    // Button-driven state machine. The delegate calls this on START.
    function onStartButton() {
        if (state == STATE_IDLE) {
            startSession();
            state = STATE_PREP;
            currentTime = 0;
            contractionCount = 0;
            lengthCount = 0;
            horizontalDistanceM = 0;
            timer.start(method(:onTimer), 1000, true);
        } else if (state == STATE_PREP) {
            // Skip remaining prep and start the breath-hold.
            state = STATE_HOLD;
            currentTime = 0;
        } else if (state == STATE_HOLD) {
            // End the hold, move to recovery.
            maxHoldTime = currentTime;
            state = STATE_RECOVERY;
            currentTime = 0;
        } else if (state == STATE_RECOVERY) {
            // Finish early and save.
            finishSession();
            return;
        } else if (state == STATE_DONE) {
            // Reset and return to idle.
            resetRunState();
        }
        WatchUi.requestUpdate();
    }

    // DOWN button: count contractions during the hold.
    function onContractionButton() {
        if (state == STATE_HOLD) {
            contractionCount++;
            WatchUi.requestUpdate();
        }
    }
}


using Toybox.WatchUi;
using Toybox.System;
using Toybox.Timer;

class BreathHoldDelegate extends WatchUi.InputDelegate {

    var view;
    var backTimer = null;
    const LONG_PRESS_MS = 800;

    function initialize(v) {
        InputDelegate.initialize();
        view = v;
    }

    function onKeyPressed(keyEvent) {
        var key = keyEvent.getKey();

        if (key == WatchUi.KEY_START || key == WatchUi.KEY_ENTER) {
            view.onStartButton();
            return true;
        }

        if (key == WatchUi.KEY_UP) {
            // UP counts a completed pool length during the hold.
            if (view.state == view.STATE_HOLD) {
                view.onLengthButton();
            }
            return true;
        }

        if (key == WatchUi.KEY_DOWN) {
            // DOWN counts a contraction during the hold.
            if (view.state == view.STATE_HOLD) {
                view.onContractionButton();
            }
            return true;
        }

        if (key == WatchUi.KEY_ESC || key == WatchUi.KEY_LAP) {
            if (view.state == view.STATE_IDLE) {
                // Start long-press detection; if released early we exit the app.
                backTimer = new Timer.Timer();
                backTimer.start(method(:onBackLongPress), LONG_PRESS_MS, false);
            } else {
                view.discardSession();
                view.state = view.STATE_IDLE;
                WatchUi.requestUpdate();
            }
            return true;
        }

        return false;
    }

    function onKey(keyEvent) {
        // Suppress the default press-and-release handler for keys we manage
        // in onKeyPressed/onKeyReleased so the system doesn't also exit the app
        // or navigate back on a short BACK press.
        var key = keyEvent.getKey();
        if (key == WatchUi.KEY_START || key == WatchUi.KEY_ENTER ||
            key == WatchUi.KEY_UP || key == WatchUi.KEY_DOWN ||
            key == WatchUi.KEY_ESC || key == WatchUi.KEY_LAP) {
            return true;
        }
        return false;
    }

    function onKeyReleased(keyEvent) {
        var key = keyEvent.getKey();

        if (key == WatchUi.KEY_ESC || key == WatchUi.KEY_LAP) {
            if (backTimer != null) {
                // Released before the long-press threshold: short press, exit the app.
                backTimer.stop();
                backTimer = null;
                System.exit();
            }
            return true;
        }

        return false;
    }

    function onBackLongPress() {
        backTimer = null;
        if (view.state == view.STATE_IDLE) {
            var pickerView = new PoolLengthView();
            WatchUi.pushView(pickerView, new PoolLengthDelegate(pickerView), WatchUi.SLIDE_LEFT);
        }
    }
}