Rocky.JS is the first step in Pebble journey to run JavaScript directly on the watches (unlike Pebble.JS which runs on your phone). Previously I described how to convert a simple watchface from C to Rocky.js. But that was a static watchface with unchangeable settings.

Here I will show how to create a configurable watchface in Rocky.js similarly how classic SDK faces can be configured. You will be able to reuse your existing config page – and if it was set to work with Pebble emulator as well as real watch – you will reuse it without any changes at all.

First let’s review how classic Pebble SDK calls config page. In PKJS (JavaScript) portion of Pebble code usually there’s a piece like this:

Pebble.addEventListener("showConfiguration", function(e) { Pebble.openURL("http://my.cool.server/pebble/my_cool_config.html"); } );

If user requests config of face/app – this event fires and opens page with configurable options from specified URL. After user modifies settings usually “Save” button is clicked on that page and code similar to this executes:

$('#xbtnSave').click(function () { var location = (decodeURIComponent(getURLVariable('return_to')) || "pebblejs://close#") + encodeURIComponent(JSON.stringify(settings)); document.location = location; })

Here, first we determine which location to redirect config page to. If parameter "return_to ” is passed in query string (here custom function getURLVariable() is used to extract individual parameters – look it up), so if this parameter is passed – it means config page is called form the emulator and we use it for redirection. Otherwise we use standard "pebblejs://close#" URL to save settings into real watch. We also take settings object which has our collective options combined, convert it to string and add to the URL as a parameter. Page then is redirected to resulting URL and Pebble emulator or real watch takes care of processing parameters.

So, how can we (re)use it in a Rocky.js watchface?

Assuming you already have a running watchface similar to described in previous post let’s add a config button (it can be an image) – add it right after your canvas declaration:

<img src="configure.png" style="cursor:pointer" onclick="open_config();" />

And add actual function open_config() to your JS code:

function open_config() { window.open("http://my.cool.server/pebble/my_cool_config.html?return_to=" + encodeURIComponent(location.href + "?config=true&json="), "config", "width=350, height=600") }

Similarly to the original approach this code opens your config page in a separate window of predefined size, but in this case it explicitly adds "return_to" parameter which is current page’s own URL plus 2 additional parameters – “config=true” – to indicate that page is called to save config settings and “json=” which is placeholder for returned settings.

Let’s go back to HTML and add following code before your CANVAS declaration:

<script type="text/javascript"> if (getURLVariable('config') == 'true') { var json_string = getURLVariable('json'); if (json_string != '') opener.save_config(json_string); window.close(); } </script>

Using getURLVariable() function (did you look it up?) this code determines whether config=true parameter is passed in query string. If it isn’t – that means it’s a normal page load and we skip to the rest of watchface code. But if config=true is passed – that means we’re currently in a popup window and returning from the external config page. In that case we check if any payload was passed in the “json=” parameter (remember placeholder we added before?). If it’s empty that means user clicked “Cancel” on the config page and we simple close the popup window. Otherwise the popup calls save_settings() function of the main parent page, passes the received string with settings and then closes the window.

Let’s go back to your main JS code and add the save_settings() function:

var flag_showSeconds; var flag_invertColors function save_config(json_string) { //getting settings var settings = JSON.parse(decodeURIComponent(json_string)); //storing them flag_showSeconds = settings.showSeconds; localStorage.setItem("flag_showSeconds", flag_showSeconds); flag_invertColors = settings.invertColors; localStorage.setItem("flag_invertColors", flag_invertColors); rocky.mark_dirty(); }

Here we restoring objects with settings from its stringified version and store settings (in this case .showSeconds and .invertColors ) both into variables (for use in code) and localStorage for persistent save. Traditional C SDK uses Pebble persistent storage, but in JavaScript world we use localStorage. We then mark canvas a dirty so it can refresh with new settings

We’re done with retrieving and persisting settings. Now to use them. Before your main code runs – you need to load settings from localStorage, should saved settings already exist. Or fill them with default values otherwise:

flag_showSeconds = localStorage.getItem("flag_showSecondse") === null ? '1' : localStorage.getItem("flag_showSeconds"); flag_invertColors = localStorage.getItem("flag_invertColors") === null ? '1' : localStorage.getItem("flag_invertColors");

Now that you have your settings – either read from localStorage on initial run or returned from config page – you can use them:

rocky.update_proc = function (ctx, bounds) { //.... some important stuff if (flag_showSeconds == '1') { //.... run code to display seconds } //.... more important stuff if (flag_invertColors == '1') { //.... run code to invert colors } //.... some really important stuff }

See wasn’t that easy? To see this shenanigan in action take a look at Vortex watchface in at the beginning of this article. Tap or click gear icon next to it. Change any settings (besides battery and Bluetooth because – come on!) and hit “Save” button. Close and reopen the page. It’s a miracle.