All software (including apps) in this repository is MIT Licensed - see LICENSE By submitting code to this repository you confirm that you are happy with it being MIT licensed, and that it is not licensed in another way that would make this impossible.
apps.json
apps/<id>
which is uploadedapps.json
, loads them, and sends them over Web Bluetooth.Check out:
Filenames in storage are limited to 28 characters. To easily distinguish between file types, we use the following:
stuff.info
is JSON that describes an app - this is auto-generated by the App Loaderstuff.img
is an imagestuff.app.js
is JS code for applicationsstuff.wid.js
is JS code for widgetsstuff.settings.js
is JS code for the settings menustuff.boot.js
is JS code that automatically gets run at boot timestuff.json
is used for JSON settings for an appSave on Send
in settings set to the default setting of To RAM
app.js
or widget.js
into the IDE and start developing.Upload
button will load your app to Bangle.js temporarily7chname
. 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.apps/<id>
, lets assume apps/7chname
apps/7chname/app.png
should be a 48px iconapps/7chname/app-icon.js
, using a 1 bit, 4 bit or 8 bit Web Palette "Image String"apps.json
as follows:{ "id": "7chname",
"name": "My app's human readable name",
"shortName" : "Short Name",
"icon": "app.png",
"description": "A detailed description of my great app",
"tags": "",
"storage": [
{"name":"7chname.app.js","url":"app.js"},
{"name":"7chname.img","url":"app-icon.js","evaluate":true}
],
},
This is the best way to test...
Bangle App Loader
at https://\<your-github-username>.github.io/BangleApps/index.html to load apps onto your deviceNote: 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.
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.
Using the 'Storage' icon in the Web IDE (4 discs), upload your files into the places described in your JSON:
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.
When you upload code this way, your app will even be uploaded to Bangle.js's menu
without you having to use the Bangle App Loader
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
.
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
.
If you're making a widget please start the name with wid
to make
it easy to find!
The app example is available in apps/_example_app
Apps are listed in the Bangle.js menu, accessible from a clock app via the middle button.
add_to_apps.json
- insert into apps.json
, describes the app to bootloader and loaderapp.png
- app icon - 48x48pxapp-icon.js
- JS version of the icon (made with http://www.espruino.com/Image+Converter) for use in Bangle.js's menuapp.js
- app codeapp-icon.js
The icon image and short description is used in Bangle.js's launcher.
Use the Espruino image converter and upload your app.png
file.
Follow this steps to create a readable icon as image string.
Replace this line with the image converter output:
require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="))
You can also use this converter for creating images you like to draw with g.drawImage()
with your app.
Apps that need widgets can call Bangle.loadWidgets()
once at startup to load
them, and then Bangle.drawWidgets()
to draw them onto the screen whenever the app
has call to completely clear the screen. Widgets themselves will update as and when needed.
The widget example is available in apps/_example_widget
add_to_apps.json
- insert into apps.json
, describes the widget to bootloader and loaderwidget.js
- widget codeWidgets are just small bits of code that run whenever an app that supports them
calls Bangle.loadWidgets()
. If they want to display something in the 24px high
widget bars at the top and bottom of the screen they can add themselves to
the global WIDGETS
array with:
WIDGETS["mywidget"]={
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
draw:draw // called to draw the widget
};
When the widget is to be drawn, x
and y
values are set up in WIDGETS["mywidget"]
and draw
can then use this.x
and this.y
to figure out where it needs to draw to.
app.info
formatThis 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.
{
"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
"data":"appid.data.json,appid.data?.json;appidStorageFile,appidStorageFile*"
// added by BangleApps loader on upload - lists files that
// the app might write, so they can be deleted on uninstall
// typically these files are not uploaded, but created by the app
// these can include '*' or '?' wildcards
}
apps.json
format{ "id": "appid", // 7 character app id
"name": "Readable name", // readable name
"shortName": "Short name", // short name for launcher
"icon": "icon.png", // icon in apps/
"description": "...", // long description
"type":"...", // optional(if app) - 'app'/'widget'/'launch'/'bootloader'
"tags": "", // comma separated tag list for searching
"readme": "README.md", // if supplied, a link to a markdown-style text file
// that contains more information about this app (usage, etc)
// A 'Read more...' link will be added under the app
"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
// see below for more info
"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
// see below for more info
"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
"storage": [ // list of files to add to storage
{"name":"appid.js", // filename to use in storage
"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)
},
]
"data": [ // list of files the app writes to
{"name":"appid.data.json", // filename used in storage
"storageFile":true // if supplied, file is treated as storageFile
},
{"wildcard":"appid.data.*" // wildcard of filenames used in storage
}, // this is mutually exclusive with using "name"
],
"sortorder" : 0, // optional - choose where in the list this goes.
// this should only really be used to put system
// stuff at the top
}
tool
, system
, clock
, game
, sound
, gps
, widget
, launcher
or empty.apps.json
: custom
elementApps 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="https://github.com/jeffmer/JeffsBangleAppsDev/blob/master/./../css/spectre.min.css">
</head>
<body>
<p><button id="upload" class="btn btn-primary">Upload</button></p>
<script src="https://github.com/jeffmer/JeffsBangleAppsDev/raw/master/./../lib/customize.js"></script>
<script>
document.getElementById("upload").addEventListener("click", function() {
sendCustomizedApp({
id : "7chname",
storage:[
{name:"7chname.app.js", content:app_source_code},
{name:"7chname.img", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true},
]
});
});
</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
elementApps 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="https://github.com/jeffmer/JeffsBangleAppsDev/blob/master/./../css/spectre.min.css">
</head>
<body>
<script src="https://github.com/jeffmer/JeffsBangleAppsDev/raw/master/./../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.
Apps (or widgets) can add their own settings to the "Settings" menu under "App/widget settings".
To do so, the app needs to include a settings.js
file, containing a single function
that handles configuring the app.
When the app settings are opened, this function is called with one
argument, back
: a callback to return to the settings menu.
Example settings.js
// make sure to enclose the function in parentheses
(function(back) {
let settings = require('Storage').readJSON('app.json',1)||{};
function save(key, value) {
settings[key] = value;
require('Storage').write('app.json',settings);
}
const appMenu = {
'': {'title': 'App Settings'},
'< Back': back,
'Monkeys': {
value: settings.monkeys||12,
onchange: (m) => {save('monkeys', m)}
}
};
E.showMenu(appMenu)
})
In this example the app needs to add app.settings.js
to storage
in apps.json
.
It should also add app.json
to data
, to make sure it is cleaned up when the app is uninstalled.
{ "id": "app",
...
"storage": [
...
{"name":"app.settings.js","url":"settings.js"},
],
"data": [
{"name":"app.json"}
]
},
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
chaining graphics methods, eg g.setColor(0xFD20).setFontAlign(0,0).setfont("6x8",3)
Need to save state? Use the E.on('kill',...)
event to save JSON to a file called 7chname.json
, then load it at startup.
'Alarm' apps define a file called alarm.js
which handles the actual alarm window.
Locale is handled by require("locale")
. An app may create a locale
file in Storage which is
a module that overwrites Bangle.js's default locale.
The screen is parted in a widget and app area for lcd mode direct
(default).
areas | as rectangle or point |
---|---|
Widget | (0,0,239,23) |
Apps | (0,24,239,239) |
BTN1 | (230, 55) |
BTN2 | (230, 140) |
BTN3 | (230, 210) |
BTN4 | (0,0,119, 239) |
BTN5 | (120,0,239,239) |
Use g.setFontAlign(0, 0, 3)
to draw rotated string to BTN1-BTN3 with g.drawString()
.
For BTN4-5 the touch area is named
You can use g.setColor(r,g,b)
OR g.setColor(16bitnumber)
- some common 16 bit colors are below:
color-name | color-value |
---|---|
Black | 0x0000 |
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 |
The testing
folder contains snippets of code that might be useful for your apps.
testing/colors.js
- 16 bit colors as name value pairstesting/gpstrack.js
- code to store a GPS track in Bangle.js storage and output it back to the consoletesting/map
- code for splitting an image into map tiles and then displaying themThe majority of icons used for these apps are from Icons8 - we have a commercial license but icons are also free for Open Source projects.