Announcing Fitbit OS SDK 4.0
chevron down

Announcing Fitbit OS SDK 4.0

Fitbit OS SDK 4.0

We're proud to announce the latest version of the software development kit for Fitbit OS. SDK 4.0 adds support for the new Fitbit Versa 2, a comprehensive update to the Cryptography API, multi-view SVG support, and much more.

SDK 4.0 support will require the new Fitbit OS 4.0 firmware update which has already begun the rollout process. You can use the updated Fitbit OS Simulator to begin development today if you're waiting for the new firmware.

With this release we deprecated SDK 3.0 as we previously described in our deprecation blog post. While existing published projects will continue to work for users, you should now switch to SDK 4.0 for updates, or new projects.

Versa 2 for Developers

Fitbit Versa™ 2 - A premium, modern designed smartwatch that builds on the popular Versa with advanced health & fitness and convenience features to maximize your day with the addition of Amazon Alexa Built-in, innovative in-app sleep features like daily Sleep Score, and Fitbit Pay™ on all models for wallet-free payments*.

Developers will be pleased to learn that the display resolution is the same as Fitbit Versa and Fitbit Versa Lite (300x300 pixels), meaning the majority of clocks and apps are already compatible without any changes. However, there are some hardware differences developers need to be aware of in order to adapt their apps and clock faces to support Versa 2.

Hardware Differences

The Versa 2 contains the following hardware differences when compared to the original Versa:

  • New AMOLED display
  • Added Microphone
  • Faster CPU
  • NFC (all editions)
  • Removal of the two physical buttons (right-hand side)
  • Removed Gyroscope and Orientation sensor

Note: Due to the change in display technology on Versa 2, the Display API has been updated to protect the device from screen burn-in. Additional details below.

What's New

The SDK 4.0 release contains the following new APIs and developer updates:

  • Cryptography API
  • Multi-view SVG
  • Activity History API
  • App Cluster Storage
  • API enhancements

In addition to the API changes, there has also been a complete rewrite of the communication stack on device and mobile. This new stack provides improved reliability & performance, true multiplexing, and Quality of Service (QoS).

We've updated the reference documentation to incorporate these updates, but let's take a look at each of the new features and enhancements in more detail.

Cryptography API

We have made some significant updates to the Device Crypto API to align it more closely with the W3 SubtleCypto API.

We've added cryptographic functions for verifying, signing, encrypting, decrypting data, and management functions to import and export cryptographic keys. This means that developers can now build secure apps and clock faces with end-to-end encrypted communications.

import * as Crypto from "crypto";

const buffer = new ArrayBuffer(16);

Crypto.subtle.digest("SHA-256", buffer).then(hash => {
  const h = new Uint8Array(hash);
});

const key = new ArrayBuffer(32);

for (let i = 0; i < 32; i++) {
  key[i] = i;
}

Crypto.subtle
  .importKey("raw", key, { name: "HMAC", hash: "SHA-256" }, false, [
    "sign",
    "verify"
  ])

  .then(secret => {
    const s = new Uint8Array(secret);
    console.log(`Secret is: ${s}`);

    Crypto.subtle
      .sign("HMAC", secret, buffer)
      .then(signature => {
        const sgn = new Uint8Array(signature);
        console.log(`HMAC signature is: ${sgn}`);

        Crypto.subtle
          .verify("HMAC", secret, signature, buffer)
          .then(success => {
            console.log(success);
            if (success) {
              console.log("Signature was successfully verified!");
            } else {
              console.log("Bad signature!");
            }
          });
      })
      .catch(e => {
        console.error(e);
      });
  })
  .catch(e => {
    console.error(e);
  });

For more information please refer to our reference documentation, or the MDN SubtleCrypto documentation.

Multi-view SVG

Developers can now utilize multiple SVG files to represent different user interface screens in their applications and clock faces. This significant upgrade will allow developers to build more complex on-device experiences, keep their codebase more organized, and reduce their overall memory footprint.

Historically, developers had to show and hide elements within a single SVG file to simulate navigation within their app. Now developers can create individual SVG files for each view, then use document.replaceSync() to dynamically load a different SVG file at runtime.

This new method of dynamically loading SVG files has some other intentional benefits:

  1. When an SVG file is replaced, all previous event handlers will be unregistered automatically. This means the resources are available for garbage collection, freeing up memory when they are no longer required.
  2. Launch performance can be improved significantly for larger apps by splitting SVG into multiple files, as only the default index.gui file will be parsed on application startup.
import document from "document";

document.replaceSync("./resources/another-view.gui");
console.log(document.location.pathname);
>> ./resources/another-view.gui

The SDK Toolchain has also been updated to support code splitting, so JavaScript files can be dynamically loaded at runtime too, saving even more memory and further reducing app launch times! Please note that there are some differences to the CommonJS specification in our implementation, particularly the lack of a module cache.

For more information please refer to the reference documentation , and our multi-view sample app.

Activity History API

Developers can now query historical user activity data that is stored locally on the device. The data is provided minute by minute for the past hour, and day by day for the past week. The data includes steps, distance, calories, elevationGain/floors (excluding Versa Lite), plus average and resting heart rate. This is a historical API, so the current minute, and current day are excluded from the returned data.

The amount of data on the device will vary per user, but developers can check the maxRecordCount to determine exactly how many records are available for the minute and day history. It is expected that individual records could be undefined for some periods, for example when the device is powered off, or after a significant clock time correction.

Let's take a look at the API in action:

import { me as appbit } from "appbit";
import { minuteHistory, dayHistory } from "user-activity";

if (appbit.permissions.granted("access_activity")) {
  // query the previous 5 minutes step data
  const minuteRecords = minuteHistory.query({ limit: 5 });

  minuteRecords.forEach((minute, index) => {
    console.log(`${minute.steps || 0} steps. ${index + 1} minute(s) ago.`);
  });

  // query all days history step data
  const dayRecords = dayHistory.query();

  dayRecords.forEach((day, index) => {
    console.log(`${day.steps || 0} steps. ${index + 1} day(s) ago.`);
  });
}

This would return the following results:

34 steps. 1 minute(s) ago.
29 steps. 2 minute(s) ago.
13 steps. 3 minute(s) ago.
37 steps. 4 minute(s) ago.
55 steps. 5 minute(s) ago.

9823 steps. 1 day(s) ago.
8342 steps. 2 day(s) ago.
9245 steps. 3 day(s) ago.
9324 steps. 4 day(s) ago.
8392 steps. 5 day(s) ago.
9936 steps. 6 day(s) ago.

For more information please refer to the reference documentation .

App Cluster Storage

A new Companion API has been added that allows a developer to persist data on the mobile phone and share it between all of their applications and clock faces. The App Cluster Storage API works in the same way as the existing Local Storage API, but the storage area is shared between all of the apps created by the same developer that specify the same cluster ID.

Note that changes to App Cluster Storage will not wake an inactive companion.

There are some common use cases where this will be used frequently, such as sharing oAuth tokens between an app and a clock face, so a user would only need to authenticate once, or clock bundle developers could provide an application to persist purchases when switching clocks. This type of storage is only deleted once all referencing apps are uninstalled, so if a user is switching clocks made by the same developer the storage would be persisted as new clocks are installed before the old clock is uninstalled.

In order to use the new API, you need to request the access_app_cluster_storage permission, and add two new keys to the fitbit section of the package.json file:

  • appClusterID - must be 1-64 characters, alphanumeric, and periods.

  • developerID - as displayed in GAM. When uploading your project file to GAM, we will verify that yourdeveloperID` is correct.

Let's take a look at the API in action:

import * as appClusterStorage from "app-cluster-storage";
import { me as appbit } from "companion";

const myCluster = appClusterStorage.get("my.cluster.id");
if (appbit.permissions.granted("access_app_cluster_storage")) {
  if (myCluster !== null) {
    myCluster.setItem("myKey", "myValue");
    console.log(myCluster.getItem("myKey"));
  } else {
    console.error("App Cluster Storage is unavailable.");
  }
}

Note: Side-loaded apps cannot share data with apps installed via the Gallery for security reasons.

For more information please refer to the reference documentation .

API Enhancements

In addition to the fantastic new Device and Companion APIs, there are also a number of smaller changes and additions to make developers' lives easier.

Device Display API

The following changes have been made to the Display API to protect the new AMOLED display used in Versa 2:

autoOff - Overriding this value is not supported on Versa 2. You can detect if the device supports disabling this by attempting to change the value and then reading it back afterwards.

brightnessOverride - is now an enum with predefined brightness levels (dim, normal, and max). Each setting uses the ambient light sensor to determine brightness levels, instead of using absolute brightness levels.

File System API

A new method has been added to the File System API to allow developers to easily determine if a file exists, without having to stat() the file and catch the exception.

import * as fs from "fs";

if (fs.existsSync("/private/data/my-file.txt")) {
  console.log("file exists!");
}

Sensor APIs

All Sensor APIs (accelerometer, barometer, gyroscope etc.) now allow the sensorOptions to be adjusted after initialization using the setOptions() method, allowing developers to easily adjust the sample rate or batch size on the fly.

import { Accelerometer } from "accelerometer";

if (Accelerometer) {
  // sampling at 1Hz
  const accel = new Accelerometer({ frequency: 1 });
  accel.addEventListener("reading", () => {
    console.log(
      `ts: ${accel.timestamp},
    x: ${accel.x},
    y: ${accel.y},
    z: ${accel.z}`
    );
  });

  accel.start();

  setTimeout(() => {
    // change sampling rate to 100Hz
    accel.setOptions({ frequency: 100 });
  }, 5000);
}

Device API

The bodyColor property of the Device API has been updated to include the new Versa 2 device colors: carbon, copper-rose, and mist-grey.

Scientific API

The variance() method in the Scientific API had been incorrectly named var() in previous SDK versions. This has been corrected to match the documentation now.

Build Targets

Versa 2 is the 4th device to be added as a build target for the Fitbit OS SDK. Each device has its own unique alias, modelId, and modelName.

modelName modelId alias
Fitbit Ionic 27 higgs
Fitbit Versa 32 meson
Fitbit Versa Lite 38 gemini
Fitbit Versa 2 35 mira

Note: You should always aim to write code based around the device capabilities, not the specific modelId, and definitely never using the modelName.

Updating to SDK 4.0

In order to be able to select Versa 2 (mira) as a build target you must first update your projects to SDK 4.0.

If you're using Fitbit Studio you just need to select SDK 4.0 in the package.json settings.

For the command line tools, change the @fitbit/sdk version to ~4.0.0, and change the @fitbit/sdk-cli to ^1.7.0, then install with npm install or yarn install.

If you're migrating from older versions such as SDK 1.x, 2.x, and 3.x, you'll want to check our handy migration guide to facilitate a smooth transition.

And Finally...

We already know what you're thinking, so let's take a moment to address the elephant in the room.

How do I utilize the Always on Display or microphone in my clock face or application?

Unfortunately the short answer is you can't at this time. The slightly longer answer is that we're still assessing the capabilities in a way that would make the most sense, so stay tuned for future updates. In the meantime, express your interest by submitting feature suggestions in the community forum!

Next Steps

If you've previously published an app or a clock face to the Fitbit App Gallery, make sure you submit an updated version with support for all platforms. Don't forget to share your screenshots on Twitter with the #Made4Fitbit hashtag. Until next time!

Amazon Alexa not available in all countries. See fitbit.com/voice. See Fitbit Pay availability here.