mirror of
https://github.com/google/pebble.git
synced 2025-03-22 11:42:19 +00:00
380 lines
11 KiB
Markdown
380 lines
11 KiB
Markdown
---
|
||
# Copyright 2025 Google LLC
|
||
#
|
||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
# you may not use this file except in compliance with the License.
|
||
# You may obtain a copy of the License at
|
||
#
|
||
# http://www.apache.org/licenses/LICENSE-2.0
|
||
#
|
||
# Unless required by applicable law or agreed to in writing, software
|
||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
# See the License for the specific language governing permissions and
|
||
# limitations under the License.
|
||
|
||
layout: tutorials/tutorial
|
||
tutorial: js-watchface
|
||
tutorial_part: 2
|
||
|
||
title: Adding Web Content to a Rocky.js JavaScript Watchface
|
||
description: A guide to adding web content to a JavaScript watchface
|
||
permalink: /tutorials/js-watchface-tutorial/part2/
|
||
menu_section: tutorials
|
||
generate_toc: true
|
||
platform_choice: true
|
||
---
|
||
|
||
{% include tutorials/rocky-js-warning.html %}
|
||
|
||
In the [previous tutorial](/tutorials/js-watchface-tutorial/part1), we looked
|
||
at the process of creating a basic watchface using Pebble's new JavaScript API.
|
||
|
||
In this tutorial, we'll extend the example to add weather conditions from the
|
||
Internet to our watchface.
|
||
|
||

|
||
|
||
We'll be using the JavaScript component `pkjs`, which runs on the user's mobile
|
||
device using [PebbleKit JS](/docs/pebblekit-js). This `pkjs` component can be
|
||
used to access information from the Internet and process it on the phone. This
|
||
`pkjs` environment does not have the same the hardware and memory constraints of
|
||
the Pebble.
|
||
|
||
## First Steps
|
||
|
||
^CP^ The first thing we'll need to do is add a new JavaScript file to the
|
||
project we created in [Part 1](/tutorials/js-watchface-tutorial/part1). Click
|
||
'Add New' in the left menu, set the filename to `index.js` and the 'TARGET' to
|
||
'PebbleKit JS'.
|
||
|
||
^LC^ The first thing we'll need to do is edit a file from the project we
|
||
created in [Part 1](/tutorials/js-watchface-tutorial/part1). The file is
|
||
called `/src/pkjs/index.js` and it is the entry point for the `pkjs` portion
|
||
of the application.
|
||
|
||
This `pkjs` component of our application is capable of sending and receiving
|
||
messages with the smartwatch, accessing the user's location, making web
|
||
requests, and an assortment of other tasks that are all documented in the
|
||
[PebbleKit JS](/docs/pebblekit-js) documentation.
|
||
|
||
> Although Rocky.js (watch) and `pkjs` (phone) both use JavaScript, they
|
||
> have separate APIs and purposes. It is important to understand the differences
|
||
> and not attempt to run your code within the wrong component.
|
||
|
||
## Sending and Receiving Messages
|
||
|
||
Before we get onto the example, it's important to understand how to send and
|
||
receive messages between the Rocky.js component on the smartwatch, and the
|
||
`pkjs` component on the mobile device.
|
||
|
||
### Sending Messages
|
||
|
||
To send a message from the smartwatch to the mobile device, use the
|
||
``rocky.postMessage`` method, which allows you to send an arbitrary JSON
|
||
object:
|
||
|
||
```js
|
||
// rocky index.js
|
||
var rocky = require('rocky');
|
||
|
||
// Send a message from the smartwatch
|
||
rocky.postMessage({'test': 'hello from smartwatch'});
|
||
```
|
||
|
||
To send a message from the mobile device to the smartwatch, use the
|
||
``Pebble.postMessage`` method:
|
||
|
||
```js
|
||
// pkjs index.js
|
||
|
||
// Send a message from the mobile device
|
||
Pebble.postMessage({'test': 'hello from mobile device'});
|
||
```
|
||
|
||
### Message Listeners
|
||
|
||
We can create a message listener in our smartwatch code using the ``rocky.on``
|
||
method:
|
||
|
||
```js
|
||
// rocky index.js
|
||
|
||
// On the smartwatch, begin listening for a message from the mobile device
|
||
rocky.on('message', function(event) {
|
||
// Get the message that was passed
|
||
console.log(JSON.stringify(event.data));
|
||
});
|
||
```
|
||
|
||
We can also create a message listener in our `pkjs` code using the ``Pebble.on``
|
||
method:
|
||
|
||
```js
|
||
// pkjs index.js
|
||
|
||
// On the phone, begin listening for a message from the smartwatch
|
||
Pebble.on('message', function(event) {
|
||
// Get the message that was passed
|
||
console.log(JSON.stringify(event.data));
|
||
});
|
||
```
|
||
|
||
## Requesting Location
|
||
|
||
Our `pkjs` component can access to the location of the user's smartphone. The
|
||
Rocky.js component cannot access location information directly, it must request
|
||
it from `pkjs`.
|
||
|
||
^CP^ In order to use this functionality, you must change your project settings
|
||
in CloudPebble. Click 'SETTINGS' in the left menu, then tick 'USES LOCATION'.
|
||
|
||
<div class="platform-specific" data-sdk-platform="local">
|
||
{% markdown {} %}
|
||
In order to use this functionality, your application must include the
|
||
`location` flag in the
|
||
[`pebble.capabilities`](/guides/tools-and-resources/app-metadata/)
|
||
array of your `package.json` file.
|
||
|
||
|
||
```js
|
||
// file: package.json
|
||
// ...
|
||
"pebble": {
|
||
"capabilities": ["location"]
|
||
}
|
||
// ...
|
||
```
|
||
{% endmarkdown %}
|
||
</div>
|
||
|
||
Once we've added the `location` flag, we can access GPS coordinates using the
|
||
[Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation).
|
||
In this example, we're going to request the user's location when we receive the
|
||
"fetch" message from the smartwatch.
|
||
|
||
```js
|
||
// pkjs index.js
|
||
|
||
Pebble.on('message', function(event) {
|
||
// Get the message that was passed
|
||
var message = event.data;
|
||
|
||
if (message.fetch) {
|
||
navigator.geolocation.getCurrentPosition(function(pos) {
|
||
// TODO: fetch weather
|
||
}, function(err) {
|
||
console.error('Error getting location');
|
||
},
|
||
{ timeout: 15000, maximumAge: 60000 });
|
||
}
|
||
});
|
||
```
|
||
|
||
## Web Service Calls
|
||
|
||
The `pkjs` side of our application can also access the
|
||
[XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
|
||
object. Using this object, developers are able to interact with external web
|
||
services.
|
||
|
||
In this tutorial, we will interface with
|
||
[Open Weather Map](http://openweathermap.org/) – a common weather API used by
|
||
the [Pebble Developer Community](https://forums.pebble.com/c/development).
|
||
|
||
The `XMLHttpRequest` object is quite powerful, but can be intimidating to get
|
||
started with. To make things a bit simpler, we'll wrap the object with a helper
|
||
function which makes the request, then raises a callback:
|
||
|
||
```js
|
||
// pkjs index.js
|
||
|
||
function request(url, type, callback) {
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.onload = function () {
|
||
callback(this.responseText);
|
||
};
|
||
xhr.open(type, url);
|
||
xhr.send();
|
||
}
|
||
```
|
||
|
||
The three arguments we have to provide when calling our `request()` method are
|
||
the URL, the type of request (`GET` or `POST`) and a callback for when the
|
||
response is received.
|
||
|
||
### Fetching Weather Data
|
||
|
||
The URL is specified on the
|
||
[OpenWeatherMap API page](http://openweathermap.org/current), and contains the
|
||
coordinates supplied by `getCurrentPosition()` (latitude and longitude),
|
||
followed by the API key:
|
||
|
||
{% include guides/owm-api-key-notice.html %}
|
||
|
||
```js
|
||
var myAPIKey = '1234567';
|
||
var url = 'http://api.openweathermap.org/data/2.5/weather' +
|
||
'?lat=' + pos.coords.latitude +
|
||
'&lon=' + pos.coords.longitude +
|
||
'&appid=' + myAPIKey;
|
||
```
|
||
|
||
All together, our message handler should now look like the following:
|
||
|
||
```js
|
||
// pkjs index.js
|
||
|
||
var myAPIKey = '1234567';
|
||
|
||
Pebble.on('message', function(event) {
|
||
// Get the message that was passed
|
||
var message = event.data;
|
||
|
||
if (message.fetch) {
|
||
navigator.geolocation.getCurrentPosition(function(pos) {
|
||
var url = 'http://api.openweathermap.org/data/2.5/weather' +
|
||
'?lat=' + pos.coords.latitude +
|
||
'&lon=' + pos.coords.longitude +
|
||
'&appid=' + myAPIKey;
|
||
|
||
request(url, 'GET', function(respText) {
|
||
var weatherData = JSON.parse(respText);
|
||
//TODO: Send weather to smartwatch
|
||
});
|
||
}, function(err) {
|
||
console.error('Error getting location');
|
||
},
|
||
{ timeout: 15000, maximumAge: 60000 });
|
||
}
|
||
});
|
||
```
|
||
|
||
## Finishing Up
|
||
|
||
Once we receive the weather data from OpenWeatherMap, we need to send it to the
|
||
smartwatch using ``Pebble.postMessage``:
|
||
|
||
```js
|
||
// pkjs index.js
|
||
|
||
// ...
|
||
request(url, 'GET', function(respText) {
|
||
var weatherData = JSON.parse(respText);
|
||
|
||
Pebble.postMessage({
|
||
'weather': {
|
||
// Convert from Kelvin
|
||
'celcius': Math.round(weatherData.main.temp - 273.15),
|
||
'fahrenheit': Math.round((weatherData.main.temp - 273.15) * 9 / 5 + 32),
|
||
'desc': weatherData.weather[0].main
|
||
}
|
||
});
|
||
});
|
||
```
|
||
|
||
On the smartwatch, we'll need to create a message handler to listen for a
|
||
`weather` message, and store the information so it can be drawn on screen.
|
||
|
||
```js
|
||
// rocky index.js
|
||
var rocky = require('rocky');
|
||
|
||
// Global object to store weather data
|
||
var weather;
|
||
|
||
rocky.on('message', function(event) {
|
||
// Receive a message from the mobile device (pkjs)
|
||
var message = event.data;
|
||
|
||
if (message.weather) {
|
||
// Save the weather data
|
||
weather = message.weather;
|
||
|
||
// Request a redraw so we see the information
|
||
rocky.requestDraw();
|
||
}
|
||
});
|
||
```
|
||
|
||
We also need to send the 'fetch' command from the smartwatch to ask for weather
|
||
data when the application starts, then every hour:
|
||
|
||
```js
|
||
// rocky index.js
|
||
|
||
// ...
|
||
|
||
rocky.on('hourchange', function(event) {
|
||
// Send a message to fetch the weather information (on startup and every hour)
|
||
rocky.postMessage({'fetch': true});
|
||
});
|
||
```
|
||
|
||
Finally, we'll need some new code in our Rocky `draw` handler to display the
|
||
temperature and conditions:
|
||
|
||
```js
|
||
// rocky index.js
|
||
var rocky = require('rocky');
|
||
|
||
// ...
|
||
|
||
function drawWeather(ctx, weather) {
|
||
// Create a string describing the weather
|
||
//var weatherString = weather.celcius + 'ºC, ' + weather.desc;
|
||
var weatherString = weather.fahrenheit + 'ºF, ' + weather.desc;
|
||
|
||
// Draw the text, top center
|
||
ctx.fillStyle = 'lightgray';
|
||
ctx.textAlign = 'center';
|
||
ctx.font = '14px Gothic';
|
||
ctx.fillText(weatherString, ctx.canvas.unobstructedWidth / 2, 2);
|
||
}
|
||
|
||
rocky.on('draw', function(event) {
|
||
var ctx = event.context;
|
||
var d = new Date();
|
||
|
||
// Clear the screen
|
||
ctx.clearRect(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight);
|
||
|
||
// Draw the conditions (before clock hands, so it's drawn underneath them)
|
||
if (weather) {
|
||
drawWeather(ctx, weather);
|
||
}
|
||
|
||
// ...
|
||
});
|
||
```
|
||
|
||
## Conclusion
|
||
|
||
So there we have it, we successfully added web content to our JavaScript
|
||
watchface! To do this we:
|
||
|
||
1. Enabled `location` in our `package.json`.
|
||
2. Added a `Pebble.on('message', function() {...});` listener in `pkjs`.
|
||
3. Retrieved the users current GPS coordinates in `pkjs`.
|
||
4. Used `XMLHttpRequest` to query OpenWeatherMap API.
|
||
5. Sent the current weather conditions from the mobile device, to the
|
||
smartwatch, using `Pebble.postMessage()`.
|
||
6. On the smartwatch, we created a `rocky.on('message', function() {...});`
|
||
listener to receive the weather data from `pkjs`.
|
||
7. We subscribed to the `hourchange` event, to send a message to `pkjs` to
|
||
request the weather data when the application starts and every hour.
|
||
8. Then finally we drew the weather conditions on the screen as text.
|
||
|
||
If you have problems with your code, check it against the sample source code
|
||
provided using the button below.
|
||
|
||
[View Source Code >{center,bg-lightblue,fg-white}](https://github.com/pebble-examples/rocky-watchface-tutorial-part2)
|
||
|
||
## What's Next
|
||
|
||
We hope you enjoyed this tutorial and that it inspires you to make something
|
||
awesome!
|
||
|
||
Why not let us know what you've created by tweeting
|
||
[@pebbledev](https://twitter.com/pebbledev), or join our epic developer
|
||
community on [Discord]({{ site.links.discord_invite }}).
|