Computed fields from fit file messages

You can now create custom activity and interval fields that process fit file messages with scripts. Tick the “Processes fit file messages” box:

Your script will be run when the file is first processed or re-processed. Here is an example script that extracts heart rate monitor details from the device_info messages:

{
  let hrm
  for (let di of icu.fit.device_info) {
    if (di.device_type?.value !== 120 /* HEART_RATE */) continue
    console.log("di " + di)
    let manufacturer = di.manufacturer?.valueName
    if (!manufacturer) continue
    let product = di.product?.value
    if (product) {
      if (manufacturer === "GARMIN") product = icu.fitSdk.enumValueName('GARMIN_PRODUCT', product)
      else if (manufacturer === "FAVERO") product = icu.fitSdk.enumValueName('FAVERO_PRODUCT', product)
    }
    let serial = di.serial_number?.value
    hrm = manufacturer + (product ? " " + product : "") + (serial ? " " + serial : "")
  }
  hrm
}

You need to poke around on fitfileviewer.com and in the FIT SDK to figure out what to look for.

There is documentation for the fit and fitSdk objects here.

I am going to be adding heart rate monitor related fields soon but its still a nice example of what can be done with this feature.

13 Likes

This is amazing! Thank you David :smiley:

1 Like

What an update, all the possibilities!

1 Like

Here is another example. This one prints out the first 1000 messages from the file with fields:

{
  let c = 0
  for (let m of icu.fit) {
    console.log("[" + c + "] " + m._name + " " + m._num + ": " + m.join(", "))
    if (++c >= 1000) break
  }
}
[0] file_id 0: serial_number=3332779479, time_created=1060332298, manufacturer=1, product=3558, type=4
[1] file_creator 49: software_version=300
[2] unknown 288: unknown=1060332300
[3] event 21: timestamp(s)=1060332300, data=0, event=0, event_type=0, event_group=0
[4] device_info 23: timestamp(s)=1060332300, serial_number=3332779479, manufacturer=1, product=3558, software_version=3.0, device_index=0, source_type=5
[5] device_info 23: timestamp(s)=1060332300, manufacturer=1, product=3558, software_version=3.0, device_index=1, device_type=4, source_type=5
[6] device_info 23: timestamp(s)=1060332300, manufacturer=1, product=2957, software_version=2.5, device_index=2, device_type=0, source_type=5
[7] device_info 23: timestamp(s)=1060332300, serial_number=3437995431, cum_operating_time(s)=507058, unknown=2, unknown=2977473959, manufacturer=1, product=3299, software_version=2.5, battery_voltage(V)=2.99609375, device_index=3, device_type=120, hardware_version=49, battery_status=3, ant_network=1, source_type=1
[8] device_info 23: timestamp(s)=1060332300, serial_number=1482690984, cum_operating_time(s)=123456, unknown=2, unknown=84629600, manufacturer=263, product=12, software_version=4.8, battery_voltage(V)=4.19921875, device_index=4, device_type=11, hardware_version=4, battery_status=2, ant_network=1, source_type=1
[9] device_info 23: timestamp(s)=1060332300, manufacturer=36853, product=10, software_version=126.1, device_index=5, device_type=8, source_type=5
[10] device_info 23: timestamp(s)=1060332300, manufacturer=1, product=3559, software_version=0.06, device_index=6, device_type=12, source_type=5
[11] unknown 22: unknown=1060332300, unknown=2, unknown=2, unknown=4, unknown=1, unknown=3, unknown=5, unknown=4
[12] unknown 141: unknown=1060332300, unknown=1060127982, unknown=1060451982, unknown=1
...

Awesome. Any chance we could use that to match that activity to a gear component to track it without mapping it to the gear.

It would be great if there was a library people can contribute scripts they create so others can use it too, similar to the charts library etc.

You can share custom activity fields with everyone. There are already a lot of them.

1 Like

Unfortunately not. Having components or multiple gear items per activity requires quite a lot of changes.

I did just add custom fields to the ones you can use for gear filters.

1 Like

They’re bound to still be gears what are not connected to the head unit and be written to the fit file.

This is awesome, thank you, David! I was able to figure out how to pull battery voltages and statuses for my power meter and HRMs which is so convenient! :smile:

1 Like

Can you share your script? :slight_smile: I will adjust it to my devices.

@uysek Sure, here they are:

HRM battery status:

{
  let status;

  for (let di of icu.fit.device_info) {
    if (di.device_type?.value !== 120) { // 'HEART_RATE'
      continue;
    }

    if (typeof di.battery_status?.value === 'number') {
      status = icu.fitSdk.enumValueName('BATTERY_STATUS', di.battery_status.value);
    }

    if (typeof voltage === 'string') {
      console.log('status=', status);
    }

    break;
  }

  status;
}

HRM battery voltage:

{
  let voltage;

  for (let di of icu.fit.device_info) {
    if (di.device_type?.value !== 120) { // HEART_RATE
      continue;
    }

    if (typeof di.battery_voltage?.value === 'number') {
      voltage = di.battery_voltage.value;
    }

    if (typeof voltage === 'number' && !Number.isNaN(voltage)) {
      console.log('voltage=', voltage);
    }

    break;
  }

  voltage;
}

Power meter battery status (Garmin Rally Series, in case that matters):

{
  let status;

  for (let di of icu.fit.device_info) {
    if (di.device_type?.value !== 11) { // 'BIKE_POWER'
      continue;
    }

    if (typeof di.battery_status?.value === 'number') {
      status = icu.fitSdk.enumValueName('BATTERY_STATUS', di.battery_status.value);
    }

    if (typeof voltage === 'string') {
      console.log('status=', status);
    }

    break;
  }

  status;
}

Power meter voltage:

{
  let voltage;

  for (let di of icu.fit.device_info) {
    if (di.device_type?.value !== 11) { // BIKE_POWER
      continue;
    }

    // console.log('di ' + di); // prints all fields in debug format
    // console.log(di.battery_voltage?.value);

    if (typeof di.battery_voltage?.value === 'number') {
      voltage = di.battery_voltage.value;
    }

    if (typeof voltage === 'number' && !Number.isNaN(voltage)) {
      console.log('voltage=', voltage);
    }

    break;
  }

  voltage;
}

Shimano di2 battery level:

{
  let level;

  for (let di of icu.fit.device_info) {
    if (di.manufacturer?.value !== 41) { // SHIMANO
      continue;
    }

    if (di.device_type?.value !== 1) { // assuming this is Di2
      continue;
    }

    // console.log('di ' + di); // prints all fields in debug format
    // console.log(di.battery_voltage?.value);

    if (typeof di.battery_level?.value === 'number') {
      level = di.battery_level.value;
    }

    if (typeof level === 'number' && !Number.isNaN(level)) {
      console.log('level=', level);
    }

    break;
  }

  level / 100;
}
1 Like

Thanks for these
Do you know what I need to do to correct this error? It’s a Garmin HRM Dual I’m looking for battery status for.

@nasatt You’re welcome! :smile:

This looks like the result when you don’t check the “Process .fit file” option on the configuration screen:

Make sure that box is checked…

2 Likes

Brilliant. Thanks again. Much appreciated

1 Like