Activity.getActivityInfo() on FR45 mostly not working as expected

I tested Activity.getActivityInfo() in the simulator and on my fr45.
It appears that only altitude and currentLocationAccuracy are recognized by my device.
The simulator guesses that Calories works but not LocationAccuracy.
//Capital letters means it's not null.  Probably a bad way to check, but it's simple sanity check.
var info = Activity.getActivityInfo();
// checks += info.ambientPressure ? "P" : "p"; //error API Level 2.4.0
checks += info.altitude ? "A" : "a";
checks += info.averageHeartRate ? "A" : "a";
// checks += info.averagePower ? "P" : "p"; //  Error: Symbol Not Found Error API Level 1.0.0
checks += info.calories ? "C" : "c";
checks += info.currentHeading ? "C" : "c";
checks += info.currentHeartRate ? "H" : "h";
checks += info.currentLocationAccuracy ? "9" : "l";
// checks += info.currentOxygenSaturation ? "O" : "o"; // API Level 3.2.0
// checks += info.currentPower ? "P" : "p"; // Error: Symbol Not Found Error, API Level 1.0.0
checks += info.elapsedDistance ? "D" : "d";
// checks += info.energyExpenditure ? "E" : "e"; // Error: Symbol Not Found Error, API Level 1.2.0
View.findDrawableById("Readings").setText( checks );
Using 4.0.7 on Linux to build and 4.1.0B1 to simulate
Using the following to compile and run: monkeyc -d fr45 -f monkey.jungle -o dw.prg -y ../devkey/developer_key && monkeydo dw.prg fr45
Using the following to build with vscode 1.63.2: ctrl + shift + P <Monkey C: Export Project>
On the device I get: Aacch9d
In the simulator I get: AaCchld

I tried going to the widgets and into activities to see if any of those might activate any of these readings.

On a side note, how do I iterate through this object?

In python I'd do something like this: [ if info[i] then 1 else 0 for i in info ]

Pretty printing an object and seeing the properties would be nice too (instead of "Obj: 177")

Top Replies

All Replies

  • // checks += info.currentPower ? "P" : "p"; // Error: Symbol Not Found Error, API Level 1.0.0

    Power isn't supported by FR45 -- it's only supported by "multisport"/outdoor watches (FR7xx, FR9xx, Fenix) and Edge devices, so it's totally expected that trying to access power on an FR45 would return symbol not found.

    //Capital letters means it's not null.  Probably a bad way to check, but it's simple sanity check.
    checks += info.calories ? "C" : "c";

    This doesn't check whether info.calories is non-null or null, it's checks whether it's "truthy" or not. Meaning that either 0 or null will cause "c" to be printed out for the 3rd character of your output string. If you really want to see what's non-null, you should check for that explicitly, perhaps using a helper function to make it easier to read/maintain.

    On a side note, how do I iterate through this object?

    In python I'd do something like this: [ if info[i] then 1 else 0 for i in info ]

    Pretty printing an object and seeing the properties would be nice too (instead of "Obj: 177")

    Yeah, sadly you can't do that in Monkey C.

    Many of these things that seem like they should work, don't.

    I tried going to the widgets and into activities to see if any of those might activate any of these readings.

    Well, anything that returns Symbol Not Found is definitely not going to work (for that device) no matter what you do. Some things which are currently zero or null in your testing are probably that way because you haven't started an activity.

    You didn't mention what kind of app this is -- a device app, data field, widget or watchface? This also affects your ability to test this stuff in the sim. If it's a data field, for example, then you can simulate an activity/FIT data and see some of this data.

    Same goes for testing it on the real device: If you build a data field, add it to an activity, wear the watch outdoors, get a GPS fix, and start the activity, you should see pretty much all the data that's available, such as (average) heart rate.

    If it's a device app, then you'll have to explicitly enable GPS in your code (see Position.enableLocationEvents()). As far as something like HR goes, IIRC, wrist HR is automatically enabled, but not external HR sensors.

    As a side note, last time I checked, elapsed distance is null (and not 0) when you haven't started an activity yet. Perhaps this is to give the dev a way to distinguish between an activity which hasn't started, and an activity which has started but hasn't accumulated any distance yet (although there's other ways to do this.)

  • Only a couple of things in Activity.Info can be used in a watch face, and even those can vary by specific device (not on a Fenix5x for example)

    currentHeartRate and currentLocation are the two that are commonly used, and for location, you need to have the positioning permission set.

    Many of the things you are using aren't available unless an activity is being record, and things like pressure are only there on devices with a baro altimeter (the fr45 doesn't have one)

  • Hey FlowState (cool username btw), thanks for those insights, I mistakenly made a connection to earlier API levels to all devices and didn't check the individual ones.  Is power device power or human power (perhaps some calculation based off of body weight and temperature difference)?

    You're right about truthy (wasn't sure about the terminology in Monkey C), but figured it'd update after an activity.  However, apparently some of these like currentHeartRate are supposed to be accessible outside of an activity?  My impression from reading the documentation was that none of these were usable without an activity currently being recorded and that if you were recording an activity you wouldn't be in the watch face mode so it was pointless to try to access it.  jim_m_58 mentioned that currentHeartRate still works outside of an activity, so this was me hoping I'd have access to other stuff too.  For good measure, I also tried checks += null != info.whatever ? ... and didn't get a change in the output.

    It's a watchface app (the only one that fr45 has access to according to https://developer.garmin.com/connect-iq/compatible-devices/) and I didn't need to enable Position.enableLocationEvents() but it might have generated some code that did this.

    Thanks for mentioning the bit about elapsedDistance.

    As for the python list comprehension is there no way to iterate through the object properties? Using a more traditional for loop would be fine.

    Looks like I needed the fallback with ActivityMonitor.getHeartRateHistory(1, true).next().heartRate (as jim_m_58 shared here in a comment https://forums.garmin.com/developer/connect-iq/f/discussion/281641/fr45-preinstalled-watch-face-code/1352663#1352663 )

  • One thing to note is currentLocation won't actually be the current one, but it's the last location from when an activity with GPS was recorded, but is still useful to show things like sunrise/sunset.

  • Thanks for helping to clear that up and specifically mentioning that the fr45 doesn't have a barometer.  That's an important distinction for currentLocation, thanks for that too.  And, for helping me get the heartrate recognized on the device (via the other thread).  I think some of this information would be stuff I could get via the phone connection (perhaps with ANT or something else)?  I see that there's a preinstalled weather widget that works, but weather isn't available in the API until 3.2.0.  That has me a bit confused.

  • Is power device power or human power (perhaps some calculation based off of body weight and temperature difference)?

    It's power from a paired bike power sensor. Note that the Stryd running pod also uses the bike power ANT+ protocol, so if it's paired natively, you can get power from Stryd too. (The Garmin RD pod and HRM-RUN chest strap do not use this protocol).

    Note that all devices which support pairing a bike power sensor natively also support power in activity info. All of the devices which don't have power in activity info also lack native support for pairing a bike power sensor.

    You're right about truthy (wasn't sure about the terminology in Monkey C),

    Yeah, I don't think that's Monkey C terminology actually, but it's the most appropriate word I can think of for "a value that is considered true in a boolean context." I stole it from JavaScript, of course. But even C, one could say that any non-zero value is truthy. (And of course in C, NULL is 0.)

    Note that Monkey C is not exactly like js here.

    if (a)

    will run without error when a is null. (null is falsey).

    But

    if (a && b)

    will crash with a run-time type error if a is null. There's just some little inconsistencies in the language implementation (imo).

    (The above example is trivial to rewrite properly, but you lose a tiny bit of memory for the extra code, which can make a difference for apps which have tight memory limits, like data fields on a non-music device.)

    It's a watchface app (the only one that fr45 has access to according to https://developer.garmin.com/connect-iq/compatible-devices/)

    Oh right. my bad.

    and I didn't need to enable Position.enableLocationEvents() but it might have generated some code that did this.

    Nah, that would only apply to device apps and widgets.

    As for the python list comprehension is there no way to iterate through the object properties?

    I'm fairly sure there's no way to iterate through object properties in Monkey C, sadly, although I'd love to be proven wrong on this. You can't even convert a symbol to it's human-readable source code label at runtime, IIRC, so even if you could iterate through object properties, you wouldn't be able to get meaningful names for the "keys". (Object methods and member variables are all represented by symbols, so you could put a bunch of symbols in an array and iterate through that. But of course you would have to know the symbols in advance, at compile-time.)

    I see that there's a preinstalled weather widget that works, but weather isn't available in the API until 3.2.0.  That has me a bit confused.

    Native apps don't have the same constraints as CIQ apps. (You'll see this come up many times if you dig into the CIQ ecosystem.)

  • Thanks for the elaborations, clarifications, explanations, and the surprising if(truthy) example (normally null is falsey).  I think since things compile to byte code, someone might be able to make an app that's not a watchface by writing in bytecode directly.  It'd be in the reverse engineering territory, but it seems plausible even if I really would not want to bother doing that. 

  • Thanks for the elaborations, clarifications, explanations, and the surprising if(truthy) example (normally null is falsey). 

    Sorry, to be clear, null is falsey. I just mean that if (a) works as expected, without crashing, when a is null. if (a && b) will crash with a run-time type error when a is null, because null && x is not allowed in Monkey C.

  •   I think since things compile to byte code, someone might be able to make an app that's not a watchface by writing in bytecode directly.  I

    I don't think so, as you'd have to upload the app to the store, and the store would block you from uploading app types which are not allowed.

    Furthermore, even for sideloads, the FR45 probably doesn't even have the on-device infrastructure to support CIQ widgets, data fields and device apps. (That's extra code on the firmware side which doesn't seem like it would need to be included if it would never be used.)

  • It would be far easier to trade in  your fr45 for an fr55.  The 55 is ciq 3.x, and allows all app types.