Skip to content

Commit

Permalink
MASSIVE REFACTOR
Browse files Browse the repository at this point in the history
New Bangle.js firmwares remove 8 char name restriction so we're ditching the first char -> file type and using normal file extensions
Took the opportunity to remove code for older Bangle.js (since the new stuff won't work anyway)
Also removed the need for an 'app.json' - it's now renamed 'app.info' on the watch, and we just auto-generate it
Renamed a few apps so widgets all start with 'wid'
  • Loading branch information
gfwilliams committed Feb 28, 2020
1 parent 95a4258 commit 50e3c85
Show file tree
Hide file tree
Showing 106 changed files with 289 additions and 743 deletions.
101 changes: 41 additions & 60 deletions README.md
Expand Up @@ -17,11 +17,11 @@ listed in `apps.json`, loads them, and sends them over Web Bluetooth.
Filenames in storage are limited to 8 characters. To
easily distinguish between file types, we use the following:

* `+stuff` is JSON for an app
* `*stuff` is an image
* `-stuff` is JS code
* `=stuff` is JS code for stuff that is run at boot time - eg. handling settings or creating widgets on the clock screen
* `@stuff` is used for JSON settings for an app
* `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

## Developing your own app

Expand All @@ -32,35 +32,25 @@ easily distinguish between file types, we use the following:

## Adding your app to the menu

* Come up with a unique 7 character name, we'll assume `7chname`
* 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.
* 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"
* Create an entry in `apps/7chname/app.json` as follows:

```
{
"name":"Short Name",
"icon":"*7chname",
"src":"-7chname"
}
```

See `app.json / widget.json` below for more info on the correct format.

* Create an entry in `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","url":"app.json"},
{"name":"-7chname","url":"app.js"},
{"name":"*7chname","url":"app-icon.js","evaluate":true}
{"name":"7chname.app.js","url":"app.js"},
{"name":"7chname.img","url":"app-icon.js","evaluate":true}
],
},
```
Expand All @@ -81,54 +71,50 @@ Be aware of the delay between commits and updates on github.io - it can take a f

### Offline

You can add the following to the Espruino Web IDE:
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:

```
// replace with your 7chname app name
var appname = "mygreat";
require("Storage").write('*'+appname,
// place app-icon.js contents here
);
//
require("Storage").write("+"+appname,{
"name":"My Great App","type":"",
"icon":"*"+appname,
"src":"-"+appname,
});
require("Storage").write("-"+appname,`
// place contents of app.js here
// be aware of double-quoting templated strings
`
```
* `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 be uploaded to Bangle.js's menu
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`.

## 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`.

**If you're making a widget** please start the name with `wid` to make
it easy to find!

### 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.

* `add_to_apps.json` - insert into `apps.json`, describes the widget to bootloader and loader
* `add_to_apps.json` - insert into `apps.json`, describes the app to bootloader and loader
* `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
* `app.json` - short app name for Bangle.js menu and storage filenames
* `app.js` - app code

#### `app-icon.js`

The icon image and short description is used in the menu entry as selection posibility.
The icon image and short description is used in the menu entry as selection possibility.

Use the Espruino [image converter](https://www.espruino.com/Image+Converter) and upload your `app.png` file.

Expand All @@ -155,13 +141,12 @@ Keep in mind to use this converter for creating images you like to draw with `g.
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
* `widget.json` - short widget name and storage names
* `widget.js` - widget code

### `app.json` / `widget.json` format
### `app.info` format

This is the file that's loaded onto Bangle.js, which gives information
about the app.
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.

```
{
Expand All @@ -184,9 +169,10 @@ about the app.
```
{ "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'
"type":"...", // optional(if app) - 'app'/'widget'/'launch'/'bootloader'
"tags": "", // comma separated tag list for searching
"custom": "custom.html", // if supplied, apps/custom.html is loaded in an
Expand All @@ -203,7 +189,7 @@ about the app.
// add an icon to allow your app to be tested
"storage": [ // list of files to add to storage
{"name":"-appid", // filename to use in 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
Expand Down Expand Up @@ -245,13 +231,8 @@ version of what's in `apps.json`:
sendCustomizedApp({
id : "7chname",
storage:[
{name:"-7chname", content:app_source_code},
{name:"+7chname", content:JSON.stringify({
name:"My app's name",
icon:"*7chname",
src:"-7chname"
})},
{name:"*7chname", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true},
{name:"7chname.app.js", content:app_source_code},
{name:"7chname.img", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true},
]
});
});
Expand Down Expand Up @@ -299,7 +280,7 @@ See [apps/gpsrec/interface.html](the GPS Recorder) for a full example.

## Coding hints

- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `@7chname`, then load it at startup.
- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `7chname.json`, then load it at startup.

- use `g.setFont(.., size)` to multiply the font size, eg ("6x8",3) : "18x24"

Expand Down
46 changes: 33 additions & 13 deletions appinfo.js
Expand Up @@ -20,21 +20,12 @@ var AppInfo = {
})).then(fileContents => { // now we just have a list of files + contents...
// filter out empty files
fileContents = fileContents.filter(x=>x!==undefined);
// What about minification?
// Add app's info JSON
return AppInfo.createAppJSON(app, fileContents);
}).then(fileContents => {
// then map each file to a command to load into storage
fileContents.forEach(storageFile => {
// check if this is the JSON file
if (storageFile.name[0]=="+") {
storageFile.evaluate = true;
var json = {};
try {
json = JSON.parse(storageFile.content);
} catch (e) {
reject(storageFile.name+" is not valid JSON");
}
if (app.version) json.version = app.version;
json.files = fileContents.map(storageFile=>storageFile.name).join(",");
storageFile.content = JSON.stringify(json);
}
// format ready for Espruino
var js;
if (storageFile.evaluate) {
Expand All @@ -49,6 +40,35 @@ var AppInfo = {
}).catch(err => reject(err));
});
},
createAppJSON : (app, fileContents) => {
return new Promise((resolve,reject) => {
var appJSONName = app.id+".info";
// Check we don't already have a JSON file!
var appJSONFile = fileContents.find(f=>f.name==appJSONName);
if (appJSONFile) reject("App JSON file explicitly specified!");
// Now actually create the app JSON
var json = {
id : app.id
};
if (app.shortName) json.name = app.shortName;
else json.name = app.name;
if (app.type && app.type!="app") json.type = app.type;
if (fileContents.find(f=>f.name==app.id+".app.js"))
json.src = app.id+".app.js";
if (fileContents.find(f=>f.name==app.id+".img"))
json.icon = app.id+".img";
if (app.sortorder) json.sortorder = app.sortorder;
if (app.version) json.version = app.version;
var fileList = fileContents.map(storageFile=>storageFile.name);
fileList.unshift(appJSONName); // do we want this? makes life easier!
json.files = fileList.join(",");
fileContents.push({
name : appJSONName,
content : JSON.stringify(json)
});
resolve(fileContents);
});
}
};

if ("undefined"!=typeof module)
Expand Down

0 comments on commit 50e3c85

Please sign in to comment.