2019-11-06 17:25:02 +00:00
Bangle.js App Loader (and Apps)
================================
2020-02-28 14:48:22 +00:00
[](https://travis-ci.org/espruino/BangleApps)
2019-11-07 20:37:46 +00:00
Try it live at [banglejs.com/apps ](https://banglejs.com/apps )
2019-11-26 10:27:31 +00:00
## How does it work?
2019-11-06 17:25:02 +00:00
* A list of apps is in `apps.json`
2019-11-17 22:42:32 +00:00
* Each element references an app in `apps/<id>` which is uploaded
2019-11-06 17:25:02 +00:00
* When it starts, BangleAppLoader checks the JSON and compares
it with the files it sees in the watch's storage.
* To upload an app, BangleAppLoader checks the files that are
listed in `apps.json` , loads them, and sends them over Web Bluetooth.
2019-11-26 10:27:31 +00:00
## What filenames are used
2019-11-06 17:25:02 +00:00
Filenames in storage are limited to 8 characters. To
easily distinguish between file types, we use the following:
2020-02-28 11:44:25 +00:00
* `stuff.info` is JSON that describes an app - this is auto-generated by the App Loader
* `stuff.img` is an image
* `stuff.app.js` is JS code
* `stuff.wid.js` is JS code for widgets
* `stuff.json` is used for JSON settings for an app
2019-11-06 17:25:02 +00:00
2019-11-26 10:27:31 +00:00
## Developing your own app
* Head over to [the Web IDE ](https://www.espruino.com/ide/ ) and ensure `Save on Send` in settings set to the *default setting* of `To RAM`
* We'd recommend that you start off using code from 'Example Applications' (below) to get started...
* Load [`app.js` ](apps/_example_app/app.js ) or [`widget.js` ](apps/_example_widget/widget.js ) into the IDE and start developing.
* The `Upload` button will load your app to Bangle.js temporarily
## Adding your app to the menu
2019-11-06 17:25:02 +00:00
2020-02-28 11:44:25 +00:00
* Come up with a unique (all lowercase, nu spaces) name, we'll assume `7chname` . Bangle.js
is limited to 28 char filenames and appends a file extension (eg `.js` ) so please
try and keep filenames short to avoid overflowing the buffer.
2019-11-26 10:27:31 +00:00
* Create a folder called `apps/<id>` , lets assume `apps/7chname`
* We'd recommend that you copy files from 'Example Applications' (below) as a base, or...
* `apps/7chname/app.png` should be a 48px icon
* Use http://www.espruino.com/Image+Converter to create `apps/7chname/app-icon.js` , using a 1 bit, 4 bit or 8 bit Web Palette "Image String"
2019-11-17 22:42:32 +00:00
* Create an entry in `apps.json` as follows:
2019-11-06 17:25:02 +00:00
```
{ "id": "7chname",
"name": "My app's human readable name",
2020-02-28 11:44:25 +00:00
"shortName" : "Short Name",
2019-11-26 10:27:31 +00:00
"icon": "app.png",
2019-11-06 17:25:02 +00:00
"description": "A detailed description of my great app",
"tags": "",
"storage": [
2020-02-28 11:44:25 +00:00
{"name":"7chname.app.js","url":"app.js"},
{"name":"7chname.img","url":"app-icon.js","evaluate":true}
2019-11-07 08:43:56 +00:00
],
2019-11-06 17:25:02 +00:00
},
```
2019-11-26 10:27:31 +00:00
## Testing
### Online
This is the best way to test...
* Fork the https://github.com/espruino/BangleApps git repository
* Add your files
* Go to GitHub Settings and activate GitHub Pages
* Run your personal `Bangle App Loader` at https://\<your-github-username\>.github.io/BangleApps/index.html to load apps onto your device
* Your apps should be inside it - if there are problems, check your web browser's 'developer console' for errors
2020-02-28 14:17:22 +00:00
**Note:** It's a great idea to get a local copy of the repository on your PC,
then run `bin/sanitycheck.js` - it'll run through a bunch of common issues
that there might be.
2019-11-26 10:27:31 +00:00
Be aware of the delay between commits and updates on github.io - it can take a few minutes (and a 'hard refresh' of your browser) for changes to take effect.
### Offline
2020-02-28 11:44:25 +00:00
Using the 'Storage' icon in [the Web IDE ](https://www.espruino.com/ide/ )
(4 discs), upload your files into the places described in your JSON:
2019-11-26 10:27:31 +00:00
2020-02-28 11:44:25 +00:00
* `app-icon.js` -> `7chname.img`
Now load `app.js` up in the editor, and click the down-arrow to the bottom
right of the `Send to Espruino` icon. Click `Storage` and then either choose
`7chname.app.js` (if you'd uploaded your app previously), or `New File`
and then enter `7chname.app.js` as the name.
Now, clicking the `Send to Espruino` icon will load the app directly into
Espruino **and** will automatically run it.
2019-11-26 10:27:31 +00:00
2020-02-28 11:44:25 +00:00
When you upload code this way, your app will even be uploaded to Bangle.js's menu
2019-11-26 10:27:31 +00:00
without you having to use the `Bangle App Loader`
2020-02-28 11:44:25 +00:00
**Note:** Widgets need to be run inside a clock or app, so if you're
developing a widget you need to go go `Settings` -> `Communications` -> `Load after saving`
and set it to `Load default application` .
2019-11-26 10:27:31 +00:00
## Example Applications
To make the process easier we've come up with some example applications that you can use as a base
when creating your own. Just come up with a unique 7 character name, copy `apps/_example_app`
or `apps/_example_widget` to `apps/7chname` , and add `apps/_example_X/add_to_apps.json` to
`apps.json` .
2020-02-28 11:44:25 +00:00
**If you're making a widget** please start the name with `wid` to make
it easy to find!
2019-11-26 10:27:31 +00:00
### App Example
The app example is available in [`apps/_example_app` ](apps/_example_app )
Apps are listed in the Bangle.js menu, accessible from a clock app via the middle button.
2020-02-28 11:44:25 +00:00
* `add_to_apps.json` - insert into `apps.json` , describes the app to bootloader and loader
2019-11-26 10:27:31 +00:00
* `app.png` - app icon - 48x48px
* `app-icon.js` - JS version of the icon (made with http://www.espruino.com/Image+Converter) for use in Bangle.js's menu
2019-12-03 17:07:15 +00:00
* `app.js` - app code
2019-11-26 10:27:31 +00:00
#### `app-icon.js`
2020-02-28 11:44:25 +00:00
The icon image and short description is used in the menu entry as selection possibility.
2019-11-26 10:27:31 +00:00
Use the Espruino [image converter ](https://www.espruino.com/Image+Converter ) and upload your `app.png` file.
Follow this steps to create a readable icon as image string.
1. upload a png file
2. set _X_ Use Compression
3. set _X_ Transparency (optional)
4. set Diffusion: _flat_
2019-12-03 17:07:15 +00:00
5. set Colours: _1 bit_ , _4 bit_ or _8 bit Web Palette_
2019-11-26 10:27:31 +00:00
6. set Output as: _Image String_
Replace this line with the image converter output:
2019-12-03 17:07:15 +00:00
2019-11-26 10:27:31 +00:00
```
require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="));
```
Keep in mind to use this converter for creating images you like to draw with `g.drawImage()` with your app.
### Widget Example
The widget example is available in [`apps/_example_widget` ](apps/_example_widget )
* `add_to_apps.json` - insert into `apps.json` , describes the widget to bootloader and loader
2019-12-03 17:07:15 +00:00
* `widget.js` - widget code
2019-11-26 10:27:31 +00:00
2020-02-28 11:44:25 +00:00
### `app.info` format
2019-12-05 14:48:56 +00:00
2020-02-28 11:44:25 +00:00
This is the file that's **auto-generated** and loaded onto Bangle.js by the App Loader,
and which gives information about the app for the Launcher.
2019-12-05 14:48:56 +00:00
```
{
"name":"Short Name", // for Bangle.js menu
"icon":"*7chname", // for Bangle.js menu
"src":"-7chname", // source file
"type":"widget/clock/app", // optional, default "app"
// if this is 'widget' then it's not displayed in the menu
// if it's 'clock' then it'll be loaded by default at boot time
"version":"1.23",
// added by BangleApps loader on upload based on apps.json
"files:"file1,file2,file3",
// added by BangleApps loader on upload - lists all files
// that belong to the app so it can be deleted
}
```
2019-11-26 10:27:31 +00:00
2019-12-05 14:48:56 +00:00
### `apps.json` format
2019-11-06 17:25:02 +00:00
```
{ "id": "appid", // 7 character app id
"name": "Readable name", // readable name
2020-02-28 11:44:25 +00:00
"shortName": "Short name", // short name for launcher
2019-11-06 17:25:02 +00:00
"icon": "icon.png", // icon in apps/
"description": "...", // long description
2020-02-28 11:44:25 +00:00
"type":"...", // optional(if app) - 'app'/'widget'/'launch'/'bootloader'
2019-11-06 17:25:02 +00:00
"tags": "", // comma separated tag list for searching
"custom": "custom.html", // if supplied, apps/custom.html is loaded in an
// iframe, and it must post back an 'app' structure
// like this one with 'storage','name' and 'id' set up
2020-02-10 13:49:36 +00:00
// see below for more info
2019-11-06 17:25:02 +00:00
2020-02-07 17:16:45 +00:00
"interface": "interface.html", // if supplied, apps/interface.html is loaded in an
// iframe, and it may interact with the connected Bangle
// to retrieve information from it
2020-02-10 13:49:36 +00:00
// see below for more info
2020-02-07 17:16:45 +00:00
2019-12-03 17:07:15 +00:00
"allow_emulator":true, // if 'app.js' will run in the emulator, set to true to
// add an icon to allow your app to be tested
2019-11-06 17:25:02 +00:00
"storage": [ // list of files to add to storage
2020-02-28 11:44:25 +00:00
{"name":"appid.js", // filename to use in storage
2019-11-06 17:25:02 +00:00
"url":"", // URL of file to load (currently relative to apps/)
"content":"..." // if supplied, this content is loaded directly
"evaluate":true // if supplied, data isn't quoted into a String before upload
// (eg it's evaluated as JS)
},
2019-11-07 08:43:56 +00:00
"sortorder" : 0, // optional - choose where in the list this goes.
// this should only really be used to put system
// stuff at the top
2019-11-06 17:25:02 +00:00
]
}
```
2019-11-26 10:27:31 +00:00
* name, icon and description present the app in the app loader.
2020-01-17 11:43:26 +00:00
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool` , `system` , `clock` , `game` , `sound` , `gps` , `widget` , `launcher` or empty.
2019-11-26 10:27:31 +00:00
* storage is used to identify the app files and how to handle them
2020-02-10 13:49:36 +00:00
### `apps.json`: `custom` element
Apps that can be customised need to define a `custom` element in `apps.json` ,
which names an HTML file in that app's folder.
When `custom` is defined, the 'upload' button is replaced by a customize
button, and when clicked it opens the HTML page specified in an iframe.
In that HTML file you're then responsible for handling a button
press and calling `sendCustomizedApp` with your own customised
version of what's in `apps.json` :
```
< html >
< head >
< link rel = "stylesheet" href = "../../css/spectre.min.css" >
< / head >
< body >
< p > < button id = "upload" class = "btn btn-primary" > Upload< / button > < / p >
< script src = "../../lib/customize.js" > < / script >
< script >
document.getElementById("upload").addEventListener("click", function() {
sendCustomizedApp({
id : "7chname",
storage:[
2020-02-28 11:44:25 +00:00
{name:"7chname.app.js", content:app_source_code},
{name:"7chname.img", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true},
2020-02-10 13:49:36 +00:00
]
});
});
< / script >
< / body >
< / html >
```
This'll then be loaded in to the watch. See [apps/qrcode/grcode.html ](the QR Code app )
for a clean example.
### `apps.json`: `interface` element
Apps that create data that can be read back can define a `interface` element in `apps.json` ,
which names an HTML file in that app's folder.
When `interface` is defined, a `Download from App` button is added to
the app's description, and when clicked it opens the HTML page specified
in an iframe.
```
< html >
< head >
< link rel = "stylesheet" href = "../../css/spectre.min.css" >
< / head >
< body >
< script src = "../../lib/interface.js" > < / script >
< div id = "t" > Loading...< / div >
< script >
function onInit() {
Puck.eval("E.getTemperature()", temp=> {
document.getElementById("t").innerHTML = temp;
});
}
< / script >
< / body >
< / html >
```
When the page is ready a function called `onInit` is called,
and in that you can call `Puck.write` and `Puck.eval` to get
the data you require from Bangle.js.
See [apps/gpsrec/interface.html ](the GPS Recorder ) for a full example.
2019-11-26 10:27:31 +00:00
## Coding hints
- use `g.setFont(.., size)` to multiply the font size, eg ("6x8",3) : "18x24"
- use `g.drawString(text,x,y,true)` to draw with background color to overwrite existing text
- use `g.clearRect()` to clear parts of the screen, instead of using `g.clear()`
- use `g.fillPoly()` or `g.drawImage()` for complex graphic elements
- using `g.clear()` can cause screen flicker
- using `g.setLCDBrightness()` can save you power during long periods with lcd on
2020-02-07 13:47:59 +00:00
- chaining graphics methods, eg `g.setColor(0xFD20).setFontAlign(0,0).setfont("6x8",3)`
2019-11-26 10:27:31 +00:00
2020-02-28 14:17:22 +00:00
### Misc Notes
- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `7chname.json` , then load it at startup.
- 'Welcome' apps define a file called `welcome.js` which the booloader picks up. This then chain-loads the welcome app itself.
2019-12-03 17:07:15 +00:00
### Graphic areas
2019-11-26 10:27:31 +00:00
The screen is parted in a widget and app area for lcd mode `direct` (default).
| areas | as rectangle or point |
2019-12-03 17:07:15 +00:00
| :-:| :-: |
| Widget | (0,0,239,23) |
2019-11-26 10:27:31 +00:00
| Apps | (0,24,239,239) |
| BTN1 | (230, 55) |
| BTN2 | (230, 140) |
| BTN3 | (230, 210) |
2019-12-03 17:07:15 +00:00
| BTN4 | (0,0,119, 239)|
| BTN5 | (120,0,239,239) |
2019-11-26 10:27:31 +00:00
2019-12-03 17:07:15 +00:00
- Use `g.setFontAlign(0, 0, 3)` to draw rotated string to BTN1-BTN3 with `g.drawString()` .
2019-11-26 10:27:31 +00:00
- For BTN4-5 the touch area is named
2019-12-03 17:07:15 +00:00
## Available colors
2019-11-26 10:27:31 +00:00
2019-11-26 16:51:23 +00:00
You can use `g.setColor(r,g,b)` OR `g.setColor(16bitnumber)` - some common 16 bit colors are below:
2019-11-26 10:27:31 +00:00
| color-name | color-value|
| :-: | :-: |
2019-12-03 17:07:15 +00:00
| Black | 0x0000 |
2019-11-26 10:27:31 +00:00
| Navy | 0x000F |
| DarkGreen | 0x03E0 |
| DarkCyan | 0x03EF |
| Maroon | 0x7800 |
| Purple | 0x780F |
| Olive | 0x7BE0
| LightGray | 0xC618
| DarkGrey | 0x7BEF
| Blue | 0x001F
| Green | 0x07E0 |
| Cyan | 0x07FF |
| RED | 0xF800 |
| Magenta | 0xF81F |
| Yellow | 0xFFE0 |
| White | 0xFFFF |
| Orange | 0xFD20 |
| GreenYellow | 0xAFE5 |
| Pink | 0xF81F |
## API Reference
[Reference ](http://www.espruino.com/Reference#software )
[Bangle Class ](https://banglejs.com/reference#Bangle )
[Graphics Class ](https://banglejs.com/reference#Graphics )
## 'Testing' folder
The [`testing` ](testing ) folder contains snippets of code that might be useful for your apps.
* `testing/colors.js` - 16 bit colors as name value pairs
2020-02-07 13:47:59 +00:00
* `testing/gpstrack.js` - code to store a GPS track in Bangle.js storage and output it back to the console
2019-11-26 10:27:31 +00:00
* `testing/map` - code for splitting an image into map tiles and then displaying them
## Credits
2019-11-06 17:25:02 +00:00
The majority of icons used for these apps are from [Icons8 ](https://icons8.com/ ) - we have a commercial license but icons are also free for Open Source projects.