Working with the PebbleKit JavaScript Framework

The PebbleKit JavaScript framework, introduced in Pebble SDK 2.0, enables you to expand the reach of your Pebble app with JavaScript logic running on the phone. With this gateway on the mobile phone, your app can access the Internet and the phone GPS, use the phone screen and keyboard to display a configuration interface, and persist data on the phone.

Key concepts

PebbleKit JavaScript is completely platform-independent. You don’t have to write separate, native code for iOS and Android devices, which greatly simplifies the development process, reducing the amount of code you have to write. It also makes your app much easier to install and use: your users do not need to download and run a separate mobile application.

  • The JavaScript code is part of the Pebble app and bundled in your pbw file. The phone extracts and installs this code when you install your app on Pebble.

  • Your JavaScript is executed in a sandbox inside the Pebble mobile app.

  • Your JavaScript is started by a request from the Pebble app. It will be killed when the user exits your application.

  • The Pebble mobile app will attempt to keep your app running as long as your Pebble app is running. However, under certain circumstances, the phone may kill the Pebble app and your app. If this occurs, the Pebble mobile app will be automatically restarted.

Overview

Pebble JavaScript sandbox provides you with a set of standard JavaScript APIs that let your app receive messages from the watch, make HTTP requests, and send new messages to the watch, among other capabilities.

On Pebble, the AppMessage APIs are used to send and receive data. This means that your Pebble watchapp can use the resources of the connected phone to fetch information from web services, for example, or send information to web APIs, store login credentials, and more. On the JavaScript side, you communicate with Pebble via a Pebble object exposed in the namespace.

Use cases

As a Pebble watchapp developer, you’ll find a host of new applications you can now write, using the PebbleKit JavaScript APIs available in Pebble SDK 2.0.

Some possible use cases:

  • Accessing information customized with the user location (for example, transit, weather, and so on)
  • Controlling and receiving information from all the devices that support web APIs
  • Getting news reports, sports, and other information
  • Using the phone screen and keyboard to configure your watchface or watchapp
  • Persisting information for your watchapp and watchface

Getting started with PebbleKit JavaScript

Working with the Pebble JavaScript API is relatively straightforward. You should be familiar with basic JavaScript concepts and programming, as well as with the JavaScript Object Notation (JSON) data format. The core JavaScript language defines a minimal API for working with text, arrays, dates, and regular expressions but does not include any input or output functionality.

If you’re not familiar with JavaScript or wish to refresh your knowledge, you’ll find a list of recommended books in the introductory section of the Pebble Developer Guide.

In this section, you’ll learn how to

  • write JavaScript-enabled watchapps
  • install and run your app
  • initialize your app
  • debug your app

Adding PebbleKit JavaScript to your watchapp

To create a new project with an empty JavaScript source file, you can use the pebble tool:

pebble new-project --javascript my_js_project

This automatically creates a js folder inside your src folder and a basic JavaScript file named pebble-js-app.js.

Tip: Upgrading an existing project to PebbleKit JS

To upgrade an existing project, you simply need to create a js sub-folder inside the src folder of your app and add a file named pebble-js-app.js in that folder.

Important: PebbleKit JS only supports one JavaScript file, which must be named pebble-js-app.js.

Installing and running JavaScript Apps

Installing PebbleKit JS-enabled apps is no different than installing any other Pebble apps. To perform the install, you can use the pebble install command line tool, or open the pbw file with the Pebble mobile app.

Once the app is installed on Pebble, you can open it similar to the way that you would open any Pebble app, that is, from the watchface carousel or the system menu.

Initializing your JavaScript app

When your JavaScript is loaded, the first event it receives is ready. You can use this event to perform any initialization required by your project. Remember that this will only be called when your Pebble app sends the first message.

Pebble.addEventListener("ready",
  function(e) {
    console.log("JavaScript app ready and running!");
  }
);

Important: You must invoke any initialization code in this event handler. Code that is not part of this event handler incurs the risk of being executed before the Pebble object is available and initialized.

Logging messages

As shown in the previous example, you may use the traditional console.log() statements in your JavaScript app. The messages will be sent through to the pebble tool. To observe the logs in real time, use the pebble logs command or pebble install --logs.

For more information on how to set up the pebble tool to communicate with your app, refer to Using Pebble tool to install and debug apps.

Exchanging messages with Pebble

A core functionality of PebbleKit JS is the capability of exchanging messages with Pebble. On the JavaScript side, you use methods, which are simply functions called on a particular object, in this case, the Pebble object.

On Pebble, you can use the app_message_* functions or the app_sync_* functions. Both work with PebbleKit JavaScript. If you haven’t done so already, you should read the App Message APIs documentation carefully.

AppMessage objects in JavaScript

The AppMessage API enforces strict limitations on the format of messages that can be exchanged. These are specified as follows:

  • Each message is a dictionary containing key-value pairs.
  • Each key must be an integer.
  • Values can be integers, strings or byte arrays. Values cannot be other dictionaries or arrays.
  • The size of messages is limited to 124 bytes once transformed in binary format.

The following conventions are used to convert messages coming from Pebble to JavaScript object. Likewise, the same conventions are expected when sending messages to Pebble:

  • A Pebble AppMessage is a JavaScript object (also known as a hash or dictionary: {}). It must conform to the JSON specifications.
  • Keys are strings of integers (JavaScript only allows key-valued strings)
  • Values are either integers, strings or byte arrays.
    • All integers will be transformed into 32 bits signed integers.
    • Byte arrays in JavaScript are represented as an array of integers (for example: [ 0, 1, 2, 3, 4, 5] is a 6 bytes long byte-array). Each byte is in the range 0..255.
    • When sending data, byte arrays can contain strings. For example: [ 0, 1, 2, “hello” , “01” ] is a 10 bytes long byte-array.

Sending messages to Pebble

To send messages to Pebble, you use the Pebble.sendAppMessage(data, ackHandler, nackHandler) method.

Parameters:

  • data: A JavaScript object representing the message to send
  • ackHandler: An optional callback that will be called when Pebble acknowledges that the message was delivered
  • nackHandler: An optional callback that will be called if an error occurs.

Return value: This function returns a unique transaction identifier.

Both callbacks take one parameter: an event object with a data.transactionId key set to the unique transaction identifier. The nackHandler will also provide a error.message key with an error explanation.

var transactionId = Pebble.sendAppMessage( { "0": 42, "1": "String value" },
  function(e) {
    console.log("Successfully delivered message with transactionId="
      + e.data.transactionId);
  },
  function(e) {
    console.log("Unable to deliver message with transactionId="
      + e.data.transactionId
      + " Error is: " + e.error.message);
  }
);

Receiving messages from Pebble

Your JavaScript code can process messages coming from Pebble through the appmessage event. The callback to this event takes one parameter: a JavaScript object with a key payload representing the message received.

Pebble.addEventListener("appmessage",
  function(e) {
    console.log("Received message: " + e.payload);
  }
);

Using named keys in PebbleKit JS messages

PebbleKit JavaScript provides a mechanism to use named keys instead of integer keys. This improves the readability of your JavaScript code and allows you to group in one place the definition of your App Message keys.

Named keys are configured through the appKeys object in the appinfo.json file. This object associates integers value to strings. Those values are used to convert the keys of outgoing and incoming messages.

"appKeys": {
  "firstKey":  0,
  "secondKey": 1
},

For each key of an incoming messages, PebbleKit JS looks for an element in the appKeys object that has the same integer value as the key. If it can find one, it replaces this key by this string in the JavaScript object. If it cannot find one, it creates a new string containing the integer value of the key.

For each key of an outgoing messages, PebbleKit JS looks for an element in the appKeys object that is equal to the key. If it finds one, it uses the integer value associated to this element as the integer representation of the key. If it cannot find one, it tries to convert the key into an integer. If this fails, an error is raised.

For example, given the appKeys configuration above, the following statements are equivalent:

Pebble.sendAppMessage({ "0":        "A value" });
Pebble.sendAppMessage({ "firstKey": "A value" });

Sending notifications to Pebble

Using the Pebble object, you can send a standard system notification to Pebble:

Pebble.showSimpleNotificationOnPebble(title, text);

The notification appears as a standard system notification on Pebble.

Account token

PebbleKit JavaScript provides a unique account token that is associated to the Pebble account of the current user.

console.log("Pebble Account Token: " + Pebble.getAccountToken());
  • The identifier is a string.
  • This identifier is guaranteed to be identical across devices if the user owns several Pebble or several mobile devices.
  • This identifier is unique to your app and cannot be used to track users across applications.
  • If the user is not logged in, this function will return an empty string ("").

Connecting to the Web with HTTP Requests

The PebbleKit JavaScript sandbox provides the standard XMLHttpRequest object to make HTTP calls.

Refer to XMLHTTPRequest specification on the W3C website for more information.

 var req = new XMLHttpRequest();
  req.open('GET', 'http://api.openweathermap.org/data/2.1/find/city?lat=37.830310&lon=-122.270831&cnt=1', true);
  req.onload = function(e) {
    if (req.readyState == 4 && req.status == 200) {
      if(req.status == 200) {
        var response = JSON.parse(req.responseText);
        var temperature = result.list[0].main.temp;
        var icon = result.list[0].main.icon;
        Pebble.sendAppMessage({ "icon":icon, "temperature":temperature + "\u00B0C"});
      } else { console.log("Error"); }
    }
  }
  req.send(null);

Using the geolocation API

PebbleKit JavaScript also provides access to the geolocation services provided by the phone through the navigator.geolocation object.

This object follows the W3C proposed standard for a Geolocation API.

To use the geolocation API, you must declare in appinfo.json that you will be using with this API. To do this, add the string location in the capabilities array:

{
  "uuid": "4846a572-4eb2-4eaa-a949-0dc4fea76eab",
  "longName": "Location Aware Pebble App",
  "capabilities": [ "location" ],
  /* ... */
}

In Pebble OS 2.0, location permission is given by the user to the Pebble application for all the Pebble apps. On iOS, the user may deny access to location information. In the future, the Pebble application may enforce per-app location permission.

Your app should gracefully handle the PERMISSION DENIED error and fallback to a default value or manual configuration.

Storing data

PebbleKit JavaScript provides a storage API that is persisted on the phone. This storage is available through the window.localStorage object which follows the W3C recommendation for Web Storage.

PebbleKit Storage is:

  • Associated to the application UUID and cannot be shared between apps
  • Persisted when the user uninstalls and then reinstalls an app
  • Persisted when the user upgrades an app. Note that this is an important difference from the Persistent Storage API, which is erased every time the app is updated.

Showing a configuration window

PebbleKit JavaScript allows you to show a configuration window on the phone to edit settings or authenticate the user with a web service.

Pebble apps that are configurable show a small gear icon next to their name in the list of applications on the phone. The user can click on this icon to open a configuration screen.

Informing a Pebble mobile app that you support configuration

To display the gear icon, your app needs to include the string configurable in the capabilities array of appinfo.json.

{
  "uuid": "4846a572-4eb2-4eaa-a949-0dc4fea76eab",
  "longName": "Configurable Pebble App",
  "capabilities": [ "configurable" ],
  /* ... */
}

Adding this string in the capabilities array enables Pebble mobile apps to show a configuration button to the user.

Showing a configuration window

When the user presses the configuration button for your app, the following sequence of events occur:

  • If your app was not running on the watch, it is started automatically and the ready event is fired.
  • The showConfiguration event is fired.
  • You should prepare an URL will be loaded in a webview and call Pebble.openURL() to start displaying this webview.

Important:

  • If your application includes the configurable capability, you must implement an event handler for the showConfiguration event.
  • The showConfiguration event must always call Pebble.openURL() to display a configuration window. If you do not call Pebble.openURL(), the Pebble Mobile Application will display an error message to the user.

Returning data from the configuration window

When the user is done with the configuration window, your JavaScript must call the special URL pebblejs://close. Any data added in the anchor of this URL, will be passed back to the PebbleJS app.

window.location.href = "pebblejs://close#success"

When the configuration window is closed, the event webviewclosed will be fired by Pebble. The event handler will receive one parameter with a key response, a string with the data passed in the anchor.

Pebble.addEventListener("webviewclosed",
  function(e) {
    console.log("Configuration window returned: " + e.response);
  }
);

Tip: Use JSON to transfer configuration information

Pebble recommends using the JSON data format to transfer configuration information from your configuration UI to your PebbleKit JavaScript code.

In your configuration page, do this:

 window.location.href = "pebblejs://close#" + encodeURIComponent(JSON.stringify(configuration));

In your Pebble app JavaScript code, do this:

Pebble.addEventListener("webviewclosed",
  function(e) {
    var configuration = JSON.parse(decodeURIComponent(e.response));
    console.log("Configuration window returned: ", JSON.stringify(configuration));
  }
);

Related documentation and sample code

To receive and send messages on the watch side, refer to the App Message APIs.

Pebble SDK ships with several examples:

  • PebbleSDK-2.0.2/PebbleSDK-2.x/Examples/pebblekit-js/quotes: Demonstrates how to use PebbleKit JS to fetch quote price from the web. It uses AppMessage on the C side.
  • PebbleSDK-2.0.2/PebbleSDK-2.x/Examples/pebblekit-js/weather: A PebbleKit JS version of the traditional weather-demo example. It uses AppSync on the C side.
  • js-configure-demo: Demonstrates how to integrate the JS configuration webview into a Pebble app