mirror of https://github.com/espruino/BangleApps
Merge remote-tracking branch 'upstream/master'
commit
c825c8ec86
|
@ -9,3 +9,4 @@ appdates.csv
|
|||
_config.yml
|
||||
tests/Layout/bin/tmp.*
|
||||
tests/Layout/testresult.bmp
|
||||
apps.json
|
||||
|
|
47
README.md
47
README.md
|
@ -12,7 +12,7 @@ and that it is not licensed in another way that would make this impossible.
|
|||
|
||||
## How does it work?
|
||||
|
||||
* A list of apps is in `apps.json`
|
||||
* A list of apps is in `apps.json` (this is auto-generated from all the `apps/yourapp/metadata.json` using Jekyll or `bin/create_apps_json.sh`)
|
||||
* Each element references an app in `apps/<id>` which is uploaded
|
||||
* When it starts, BangleAppLoader checks the JSON and compares
|
||||
it with the files it sees in the watch's storage.
|
||||
|
@ -53,10 +53,10 @@ easily distinguish between file types, we use the following:
|
|||
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/myappid`
|
||||
* We'd recommend that you copy files from 'Example Applications' (below) as a base, or...
|
||||
* We'd recommend that you copy files from one of the Examples in `apps/_example_*` (see below), or...
|
||||
* `apps/myappid/app.png` should be a 48px icon
|
||||
* Use http://www.espruino.com/Image+Converter to create `apps/myappid/app-icon.js`, using a 1 bit, 4 bit or 8 bit Web Palette "Image String"
|
||||
* Create an entry in `apps.json` as follows:
|
||||
* Create/modify `apps/myappid/metadata.json` as follows:
|
||||
|
||||
```
|
||||
{ "id": "myappid",
|
||||
|
@ -116,8 +116,7 @@ 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 name (ideally lowercase, under 20 chars), copy `apps/_example_app`
|
||||
or `apps/_example_widget` to `apps/myappid`, and add `apps/_example_X/add_to_apps.json` to
|
||||
`apps.json`.
|
||||
or `apps/_example_widget` to `apps/myappid`, and edit `apps/myappid/metadata.json` accordingly.
|
||||
|
||||
**Note:** the max filename length is 28 chars, so we suggest an app ID of under
|
||||
20 so that when `.app.js`/etc gets added to the end the filename isn't cropped.
|
||||
|
@ -131,7 +130,7 @@ 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 app to bootloader and loader
|
||||
* `metadata.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.js` - app code
|
||||
|
@ -144,11 +143,11 @@ Use the Espruino [image converter](https://www.espruino.com/Image+Converter) and
|
|||
|
||||
Follow this steps to create a readable icon as image string.
|
||||
|
||||
1. upload a png file
|
||||
1. upload a 48x48 png file - THE IMAGE SHOULD BE 48x48 OR LESS
|
||||
2. set _X_ Use Compression
|
||||
3. set _X_ Transparency (optional)
|
||||
4. set Diffusion: _flat_
|
||||
5. set Colours: _1 bit_, _4 bit_ or _8 bit Web Palette_
|
||||
5. set Colours: _1 bit_, any of the Optimised options, or _8 bit Web Palette_ are best
|
||||
6. set Output as: _Image String_
|
||||
|
||||
Replace this line with the image converter output:
|
||||
|
@ -157,6 +156,8 @@ Replace this line with the image converter output:
|
|||
require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="))
|
||||
```
|
||||
|
||||
**Do not add a trailing semicolon**
|
||||
|
||||
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
|
||||
|
@ -167,17 +168,18 @@ has call to completely clear the screen. Widgets themselves will update as and w
|
|||
|
||||
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
|
||||
* `metadata.json` - describes the widget to bootloader and loader
|
||||
* `widget.js` - widget code
|
||||
|
||||
Widgets 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:
|
||||
widget bar at the top 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)
|
||||
area:"tl", // tl (top left), tr (top right)
|
||||
sortorder:0, // (Optional) determines order of widgets in the same corner
|
||||
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
|
||||
};
|
||||
|
@ -202,7 +204,7 @@ and which gives information about the app for the Launcher.
|
|||
// if it's 'clock' then it'll be loaded by default at boot time
|
||||
// if this is 'bootloader' then it's code that is run at boot time, but is not in a menu
|
||||
"version":"1.23",
|
||||
// added by BangleApps loader on upload based on apps.json
|
||||
// added by BangleApps loader on upload based on metadata.json
|
||||
"files:"file1,file2,file3",
|
||||
// added by BangleApps loader on upload - lists all files
|
||||
// that belong to the app so it can be deleted
|
||||
|
@ -214,7 +216,7 @@ and which gives information about the app for the Launcher.
|
|||
}
|
||||
```
|
||||
|
||||
### `apps.json` format
|
||||
### `metadata.json` format
|
||||
|
||||
```
|
||||
{ "id": "appid", // 7 character app id
|
||||
|
@ -293,9 +295,9 @@ and which gives information about the app for the Launcher.
|
|||
* storage is used to identify the app files and how to handle them
|
||||
* data is used to clean up files when the app is uninstalled
|
||||
|
||||
### `apps.json`: `custom` element
|
||||
### `metadata.json`: `custom` element
|
||||
|
||||
Apps that can be customised need to define a `custom` element in `apps.json`,
|
||||
Apps that can be customised need to define a `custom` element in `metadata.json`,
|
||||
which names an HTML file in that app's folder.
|
||||
|
||||
When `custom` is defined, the 'upload' button is replaced by a customize
|
||||
|
@ -303,7 +305,7 @@ 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`:
|
||||
version of what's in `metadata.json`:
|
||||
|
||||
```
|
||||
<html>
|
||||
|
@ -335,9 +337,9 @@ for a clean example.
|
|||
and will never be loaded. This is so the app loader can tell if it's a JavaScript
|
||||
file based on the extension, and if so it can minify and pretokenise it.
|
||||
|
||||
### `apps.json`: `interface` element
|
||||
### `metadata.json`: `interface` element
|
||||
|
||||
Apps that create data that can be read back can define a `interface` element in `apps.json`,
|
||||
Apps that create data that can be read back can define a `interface` element in `metadata.json`,
|
||||
which names an HTML file in that app's folder.
|
||||
|
||||
When `interface` is defined, a `Download from App` button is added to
|
||||
|
@ -401,7 +403,7 @@ Example `settings.js`
|
|||
E.showMenu(appMenu)
|
||||
})
|
||||
```
|
||||
In this example the app needs to add `myappid.settings.js` to `storage` in `apps.json`.
|
||||
In this example the app needs to add `myappid.settings.js` to `storage` in `metadata.json`.
|
||||
It should also add `myappid.json` to `data`, to make sure it is cleaned up when the app is uninstalled.
|
||||
```json
|
||||
{ "id": "myappid",
|
||||
|
@ -461,16 +463,13 @@ 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) |
|
||||
| Widget bottom bar (optional) | (0,216,239,239) |
|
||||
| Apps | (0,24,239,239) (see below) |
|
||||
| Apps | (0,24,239,239) |
|
||||
| BTN1 | (230, 55) |
|
||||
| BTN2 | (230, 140) |
|
||||
| BTN3 | (230, 210) |
|
||||
| BTN4 | (0,0,119, 239)|
|
||||
| BTN5 | (120,0,239,239) |
|
||||
|
||||
- If there are widgets at the bottom of the screen, apps should actually keep the bottom 24px free, so should keep to the area (0,24,239,215)
|
||||
|
||||
- Use `g.setFontAlign(0, 0, 3)` to draw rotated string to BTN1-BTN3 with `g.drawString()`.
|
||||
|
||||
- For BTN4-5 the touch area is named
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "1button",
|
||||
"name": "One-Button-Tracker",
|
||||
"version": "0.01",
|
||||
"description": "A widget that turns BTN1 into a tracker, records time of button press/release.",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
"tags": "tool,quantifiedself,widget",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"interface": "interface.html",
|
||||
"storage": [
|
||||
{"name":"1button.wid.js","url":"widget.js"}
|
||||
],
|
||||
"data": [{"name":"one_button_presses.csv","storageFile":true}]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{ "id": "93dub",
|
||||
"name": "93 Dub",
|
||||
"shortName":"93 Dub",
|
||||
"icon": "93dub.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"version":"0.06",
|
||||
"description": "Fan recreation of orviwan's 91 Dub app for the Pebble smartwatch. Uses assets from his 91-Dub-v2.0 repo",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
"supports":["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"93dub.app.js","url":"app.js"},
|
||||
{"name":"93dub.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "BLEcontroller",
|
||||
"name": "BLE Customisable Controller with Joystick",
|
||||
"shortName": "BLE Controller",
|
||||
"version": "0.01",
|
||||
"description": "A configurable controller for BLE devices and robots, with a basic four direction joystick. Designed to be easy to customise so you can add your own menus.",
|
||||
"icon": "BLEcontroller.png",
|
||||
"tags": "tool,bluetooth",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": false,
|
||||
"storage": [
|
||||
{"name":"BLEcontroller.app.js","url":"app.js"},
|
||||
{"name":"BLEcontroller.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "HRV",
|
||||
"name": "Heart Rate Variability monitor",
|
||||
"shortName": "HRV monitor",
|
||||
"version": "0.04",
|
||||
"description": "Heart Rate Variability monitor, see Readme for more info",
|
||||
"icon": "hrv.png",
|
||||
"tags": "",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"HRV.app.js","url":"app.js"},
|
||||
{"name":"HRV.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "UI4swatch",
|
||||
"name": "UI 4 swatch",
|
||||
"shortName": "UI 4 swatch",
|
||||
"version": "0.01",
|
||||
"description": "A UI/UX for espruino smartwatches, displays dinamically calc. x,y coordinates.",
|
||||
"icon": "app.png",
|
||||
"tags": "Color,input,buttons,touch,UI",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"screenshots": [{"url":"UI4swatch_icon.png"},{"url":"UI4swatch_s1.png"}],
|
||||
"storage": [
|
||||
{"name":"UI4swatch.app.js","url":"app.js"},
|
||||
{"name":"UI4swatch.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
// Create an entry in apps.json as follows:
|
||||
{ "id": "7chname",
|
||||
"name": "My app's human readable name",
|
||||
"shortName":"Short Name",
|
|
@ -1,4 +1,3 @@
|
|||
// Create an entry in apps.json as follows:
|
||||
{ "id": "7chname",
|
||||
"name": "My widget's human readable name",
|
||||
"shortName":"Short Name",
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "a_clock_timer",
|
||||
"name": "A Clock with Timer",
|
||||
"version": "0.01",
|
||||
"description": "A Clock with Timer, Map and Time Zones",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"a_clock_timer.app.js","url":"app.js"},
|
||||
{"name":"a_clock_timer.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id":"a_speech_timer",
|
||||
"name":"Speech Timer",
|
||||
"icon": "app.png",
|
||||
"version":"1.01",
|
||||
"description": "A timer designed to help keeping your speeches and presentations to time.",
|
||||
"tags": "tool,timer",
|
||||
"readme":"README.md",
|
||||
"supports":["BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"a_speech_timer.app.js","url":"app.js"},
|
||||
{"name":"a_speech_timer.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "about",
|
||||
"name": "About",
|
||||
"version": "0.12",
|
||||
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"screenshots": [{"url":"bangle1-about-screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"about.app.js","url":"app-bangle1.js","supports": ["BANGLEJS"]},
|
||||
{"name":"about.app.js","url":"app-bangle2.js","supports": ["BANGLEJS2"]},
|
||||
{"name":"about.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"sortorder": -4
|
||||
}
|
|
@ -101,8 +101,8 @@
|
|||
<script>
|
||||
$(function () {
|
||||
let ClockSize, ClockSizeURL
|
||||
let ClockFace, ClockFaceNumerals, ClockFaceDots, ClockFaceURL
|
||||
let ClockHands, SecondHand, ClockHandsURL, FillColor
|
||||
let ClockFace, ClockFaceURL, ClockFaceNumerals, ClockFaceDots
|
||||
let ClockHands, ClockHandsURL, SecondHand, FillColor
|
||||
let ComplicationTL, ComplicationTLURL
|
||||
let ComplicationT, ComplicationTURL
|
||||
let ComplicationTR, ComplicationTRURL
|
||||
|
@ -118,8 +118,8 @@
|
|||
function backupConfiguration () {
|
||||
let Configuration = {
|
||||
ClockSize, ClockSizeURL,
|
||||
ClockFace, ClockFaceNumerals, ClockFaceDots, ClockFaceURL,
|
||||
ClockHands, SecondHand, ClockHandsURL, FillColor,
|
||||
ClockFace, ClockFaceURL, ClockFaceNumerals, ClockFaceDots,
|
||||
ClockHands, ClockHandsURL, SecondHand, FillColor,
|
||||
ComplicationTL, ComplicationTLURL,
|
||||
ComplicationT, ComplicationTURL,
|
||||
ComplicationTR, ComplicationTRURL,
|
||||
|
@ -130,7 +130,7 @@
|
|||
ComplicationBR, ComplicationBRURL,
|
||||
Foreground, Background
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
localStorage.setItem('ac_ac',JSON.stringify(Configuration))
|
||||
} catch (Signal) {
|
||||
|
@ -311,11 +311,11 @@
|
|||
|
||||
function chosenClockFace () {
|
||||
switch (ClockFace) {
|
||||
case 'none': return "undefined"
|
||||
case 'four-fold': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-four-fold-clock-face/main/ClockFace.js')"
|
||||
case 'twelve-fold': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-twelve-fold-clock-face/main/ClockFace.js')"
|
||||
case 'rainbow': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-rainbow-clock-face/main/ClockFace.js')"
|
||||
case 'custom': return "require('" + ClockFaceURL + "')"
|
||||
case 'none': return "undefined"
|
||||
case 'four-numbered': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-four-numbered-clock-face/main/ClockFace.js')"
|
||||
case 'twelve-numbered': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-twelve-numbered-clock-face/main/ClockFace.js')"
|
||||
case 'rainbow': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-rainbow-clock-face/main/ClockFace.js')"
|
||||
case 'custom': return "require('" + ClockFaceURL + "')"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -412,7 +412,7 @@ console.log(AppSource)
|
|||
}
|
||||
|
||||
$('input[type="radio"]').on('change',retrieveAndValidateInputs)
|
||||
$('input[type="url"]'). on('change',retrieveAndValidateInputs)
|
||||
$('input[type="url"]'). on('input', retrieveAndValidateInputs)
|
||||
$('select'). on('change',retrieveAndValidateInputs)
|
||||
$('#UploadButton').on('click',createAndUploadApp)
|
||||
})
|
||||
|
@ -485,23 +485,23 @@ console.log(AppSource)
|
|||
<input type="radio" name="clock-face" value="none" checked>
|
||||
<img src="none.png"/>
|
||||
</label><br>
|
||||
none
|
||||
(none)
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-face" value="four-fold">
|
||||
<img src="fourfoldClockFace.png"/>
|
||||
<input type="radio" name="clock-face" value="four-numbered">
|
||||
<img src="fournumberedClockFace.png"/>
|
||||
</label><br>
|
||||
four-fold
|
||||
four-numbered
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-face" value="twelve-fold">
|
||||
<img src="twelvefoldClockFace.png"/>
|
||||
<input type="radio" name="clock-face" value="twelve-numbered">
|
||||
<img src="twelvenumberedClockFace.png"/>
|
||||
</label><br>
|
||||
twelve-fold
|
||||
twelve-numbered
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
@ -521,25 +521,25 @@ console.log(AppSource)
|
|||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</p><p>
|
||||
Clock faces are drawn in the configured foreground and background colors
|
||||
(you may select them at the end of this form)
|
||||
</p><p>
|
||||
"Four-fold" clock faces may draw indian-arabic or roman numerals. Which do you prefer?
|
||||
</p><p>
|
||||
<input type="radio" name="clock-face-numerals" value="indian" checked> indian-arabic (3, 6, 9, 12)<br>
|
||||
<input type="radio" name="clock-face-numerals" value="roman"> roman (III, VI, IX, XII)
|
||||
</p><p>
|
||||
The "twelve-fold" and "rainbow"-colored faces may be drawn with or without
|
||||
dots marking the position of every minute. Which variant do you prefer?
|
||||
</p><p>
|
||||
<input type="radio" name="clock-face-dots" value="without-dots" checked> without dots <br>
|
||||
<input type="radio" name="clock-face-dots" value="with-dots"> with dots
|
||||
</p><p>
|
||||
If you prefer a "custom" clock face, please enter the URL
|
||||
of its JavaScript module below:
|
||||
</p><p>
|
||||
custom URL: <input type="url" id="clock-face-custom-url" size="50">
|
||||
</p><p>
|
||||
Clock faces are drawn in the configured foreground and background colors
|
||||
(you may select them at the end of this form)
|
||||
</p><p>
|
||||
"Four-numbered" clock faces may draw indian-arabic or roman numerals. Which do you prefer?
|
||||
</p><p>
|
||||
<input type="radio" name="clock-face-numerals" value="indian" checked> indian-arabic (3, 6, 9, 12)<br>
|
||||
<input type="radio" name="clock-face-numerals" value="roman"> roman (III, VI, IX, XII)
|
||||
</p><p>
|
||||
The "twelve-numbered" and "rainbow"-colored faces may be drawn with or without
|
||||
dots marking the position of every minute. Which variant do you prefer?
|
||||
</p><p>
|
||||
<input type="radio" name="clock-face-dots" value="without-dots" checked> without dots <br>
|
||||
<input type="radio" name="clock-face-dots" value="with-dots"> with dots
|
||||
</p>
|
||||
|
||||
<h3>Clock Hands</h3>
|
||||
|
@ -582,6 +582,11 @@ console.log(AppSource)
|
|||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</p><p>
|
||||
If you prefer "custom" clock hands, please enter the URL
|
||||
of their JavaScript module below:
|
||||
</p><p>
|
||||
custom URL: <input type="url" id="clock-hands-custom-url" size="50">
|
||||
</p><p>
|
||||
Clock hands are drawn in the configured foreground and background colors
|
||||
(you may select them at the end of this form)
|
||||
|
@ -631,11 +636,6 @@ console.log(AppSource)
|
|||
<input type="radio" name="second-hand" value="#FF00FF" class="ColorPatch" style="background:#FF00FF"/>
|
||||
<input type="radio" name="second-hand" value="#00FFFF" class="ColorPatch" style="background:#00FFFF"/>
|
||||
<input type="radio" name="second-hand" value="#FFFFFF" class="ColorPatch" style="background:#FFFFFF"/>
|
||||
</p><p>
|
||||
If you prefer "custom" clock hands, please enter the URL
|
||||
of their JavaScript module below:
|
||||
</p><p>
|
||||
custom URL: <input type="url" id="clock-hands-custom-url" size="50">
|
||||
</p>
|
||||
|
||||
<h3>Complications</h3>
|
||||
|
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,18 @@
|
|||
{ "id": "ac_ac",
|
||||
"name": "A Configurable Analog Clock",
|
||||
"shortName":"Configurable Clock",
|
||||
"version":"0.03",
|
||||
"description": "AC-AC, a highly customizable analog clock with several clock faces, hands and complications to choose from",
|
||||
"icon": "app-icon.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"allow_emulator": false,
|
||||
"screenshots": [{"url":"app-screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"custom": "Customizer.html",
|
||||
"storage": [
|
||||
{"name":"ac_ac.app.js","url":"app.js"},
|
||||
{"name":"ac_ac.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,14 @@
|
|||
{ "id": "accelgraph",
|
||||
"name": "Accelerometer Graph",
|
||||
"shortName":"Accel Graph",
|
||||
"version":"0.01",
|
||||
"description": "A simple app to draw a graph of data from the accelerometer on the screen",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,debug",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"accelgraph.app.js","url":"app.js"},
|
||||
{"name":"accelgraph.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "accellog",
|
||||
"name": "Acceleration Logger",
|
||||
"shortName": "Accel Log",
|
||||
"version": "0.03",
|
||||
"description": "Logs XYZ acceleration data to a CSV file that can be downloaded to your PC",
|
||||
"icon": "app.png",
|
||||
"tags": "outdoor",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"interface": "interface.html",
|
||||
"storage": [
|
||||
{"name":"accellog.app.js","url":"app.js"},
|
||||
{"name":"accellog.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"wildcard":"accellog.?.csv"}]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "accelrec",
|
||||
"name": "Acceleration Recorder",
|
||||
"shortName": "Accel Rec",
|
||||
"version": "0.02",
|
||||
"description": "This app puts the Bangle's accelerometer into 100Hz mode and reads 2 seconds worth of data after movement starts. The data can then be exported back to the PC.",
|
||||
"icon": "app.png",
|
||||
"tags": "",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"interface": "interface.html",
|
||||
"storage": [
|
||||
{"name":"accelrec.app.js","url":"app.js"},
|
||||
{"name":"accelrec.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"wildcard":"accelrec.?.csv"}]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "aclock",
|
||||
"name": "Analog Clock",
|
||||
"version": "0.15",
|
||||
"description": "An Analog Clock",
|
||||
"icon": "clock-analog.png",
|
||||
"screenshots": [{"url":"screenshot_analog.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"aclock.app.js","url":"clock-analog.js"},
|
||||
{"name":"aclock.img","url":"clock-analog-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Faster maze generation
|
||||
|
|
|
@ -35,21 +35,56 @@ function Maze(n) {
|
|||
this.walls[cell] = WALL_RIGHT|WALL_DOWN;
|
||||
this.groups[cell] = cell;
|
||||
}
|
||||
// Candidates of walls to break when digging the maze.
|
||||
// If candidate failed (breaking it would create a loop),
|
||||
// it would never succeed, so no need to retry it.
|
||||
let candidates_down = [],
|
||||
candidates_right = [];
|
||||
for (let r=0 ; r<n; r++) {
|
||||
for (let c=0; c<n; c++) {
|
||||
let cell = n*r+c;
|
||||
if (r<(n-1)) { // Don't break wall down for bottom row.
|
||||
candidates_down.push(cell);
|
||||
}
|
||||
if (c<(n-1)) { // Don't break wall right for rightmost column.
|
||||
candidates_right.push(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
let from_group, to_group;
|
||||
let ngroups = n*n;
|
||||
while (--ngroups) {
|
||||
// Abort if BTN1 pressed [grace period for menu]
|
||||
// (for some reason setWatch() fails inside constructor)
|
||||
if (ngroups<n*n-4 && digitalRead(BTN1)) {
|
||||
if (ngroups<n*n-16 && digitalRead(BTN1)) {
|
||||
aborting = true;
|
||||
return;
|
||||
}
|
||||
from_group = to_group = -1;
|
||||
while (from_group<0) {
|
||||
if (Math.random()<0.5) { // try to break a wall right
|
||||
let r = Math.floor(Math.random()*n);
|
||||
let c = Math.floor(Math.random()*(n-1));
|
||||
let cell = r*n+c;
|
||||
let trying_down = false;
|
||||
if (Math.random()<0.5 && candidates_down.length || !candidates_right.length) {
|
||||
trying_down = true;
|
||||
}
|
||||
let candidates = trying_down ? candidates_down : candidates_right;
|
||||
candidate_index = Math.floor(Math.random()*candidates.length),
|
||||
cell = candidates.splice(candidate_index, 1)[0],
|
||||
r = Math.floor(cell/n),
|
||||
c = cell%n;
|
||||
if (trying_down) { // try to break a wall down
|
||||
if (this.groups[cell]!=this.groups[cell+n]) {
|
||||
this.walls[cell] &= ~WALL_DOWN;
|
||||
g.clearRect(
|
||||
this.margin+c*this.wall_length+1,
|
||||
this.margin+(r+1)*this.wall_length,
|
||||
this.margin+(c+1)*this.wall_length-1,
|
||||
this.margin+(r+1)*this.wall_length
|
||||
);
|
||||
g.flip(); // show progress.
|
||||
from_group = this.groups[cell];
|
||||
to_group = this.groups[cell+n];
|
||||
}
|
||||
} else { // try to break a wall right
|
||||
if (this.groups[cell]!=this.groups[cell+1]) {
|
||||
this.walls[cell] &= ~WALL_RIGHT;
|
||||
g.clearRect(
|
||||
|
@ -62,21 +97,6 @@ function Maze(n) {
|
|||
from_group = this.groups[cell];
|
||||
to_group = this.groups[cell+1];
|
||||
}
|
||||
} else { // try to break a wall down
|
||||
let r = Math.floor(Math.random()*(n-1));
|
||||
let c = Math.floor(Math.random()*n);
|
||||
let cell = r*n+c;
|
||||
if (this.groups[cell]!=this.groups[cell+n]) {
|
||||
this.walls[cell] &= ~WALL_DOWN;
|
||||
g.clearRect(
|
||||
this.margin+c*this.wall_length+1,
|
||||
this.margin+(r+1)*this.wall_length,
|
||||
this.margin+(c+1)*this.wall_length-1,
|
||||
this.margin+(r+1)*this.wall_length
|
||||
);
|
||||
from_group = this.groups[cell];
|
||||
to_group = this.groups[cell+n];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let cell = 0; cell<n*n; cell++) {
|
||||
|
@ -253,7 +273,6 @@ let maze_interval = setInterval(
|
|||
function() {
|
||||
if (maze) {
|
||||
if (digitalRead(BTN1) || maze.status==STATUS_ABORTED) {
|
||||
console.log(`aborting ${start_time}`);
|
||||
maze = null;
|
||||
start_time = duration = 0;
|
||||
aborting = false;
|
||||
|
@ -270,7 +289,7 @@ let maze_interval = setInterval(
|
|||
duration = Date.now()-start_time;
|
||||
g.setFontAlign(0,0).setColor(g.theme.fg);
|
||||
g.setFont("Vector",18);
|
||||
g.drawString(`Solved in\n ${timeToText(duration)} \nClick to play again`, g.getWidth()/2, g.getHeight()/2, true);
|
||||
g.drawString(`Solved ${maze.n}X${maze.n} in\n ${timeToText(duration)} \nClick to play again`, g.getWidth()/2, g.getHeight()/2, true);
|
||||
}
|
||||
}
|
||||
}, 25);
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{ "id": "acmaze",
|
||||
"name": "AccelaMaze",
|
||||
"shortName":"AccelaMaze",
|
||||
"version":"0.02",
|
||||
"description": "Tilt the watch to roll a ball through a maze.",
|
||||
"icon": "app.png",
|
||||
"tags": "game",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"acmaze.app.js","url":"app.js"},
|
||||
{"name":"acmaze.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "activepedom",
|
||||
"name": "Active Pedometer",
|
||||
"shortName": "Active Pedometer",
|
||||
"version": "0.09",
|
||||
"description": "Pedometer that filters out arm movement and displays a step goal progress. Steps are saved to a daily file and can be viewed as graph.",
|
||||
"icon": "app.png",
|
||||
"tags": "outdoors,widget",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"screenshots": [{"url":"600.png"},{"url":"10600.png"},{"url":"1600.png"}],
|
||||
"storage": [
|
||||
{"name":"activepedom.wid.js","url":"widget.js"},
|
||||
{"name":"activepedom.settings.js","url":"settings.js"},
|
||||
{"name":"activepedom.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"activepedom.app.js","url":"app.js"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "alarm",
|
||||
"name": "Default Alarm & Timer",
|
||||
"shortName": "Alarms",
|
||||
"version": "0.14",
|
||||
"description": "Set and respond to alarms and timers",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm,widget",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"alarm.app.js","url":"app.js"},
|
||||
{"name":"alarm.boot.js","url":"boot.js"},
|
||||
{"name":"alarm.js","url":"alarm.js"},
|
||||
{"name":"alarm.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"alarm.wid.js","url":"widget.js"}
|
||||
],
|
||||
"data": [{"name":"alarm.json"}]
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"id": "alpinenav",
|
||||
"name": "Alpine Nav",
|
||||
"version": "0.01",
|
||||
"description": "App that performs GPS monitoring to track and display position relative to a given origin in realtime",
|
||||
"icon": "app-icon.png",
|
||||
"tags": "outdoors,gps",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"alpinenav.app.js","url":"app.js"},
|
||||
{"name":"alpinenav.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "analogimgclk",
|
||||
"name": "Analog Clock (Image background)",
|
||||
"shortName": "Analog Clock",
|
||||
"version": "0.03",
|
||||
"description": "An analog clock with an image background",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"analogimgclk.app.js","url":"app.js"},
|
||||
{"name":"analogimgclk.bg.img","url":"bg.img"},
|
||||
{"name":"analogimgclk.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{ "id": "andark",
|
||||
"name": "Analog Dark",
|
||||
"shortName":"AnDark",
|
||||
"version":"0.04",
|
||||
"description": "analog clock face without disturbing widgets",
|
||||
"icon": "andark_icon.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"andark.app.js","url":"app.js"},
|
||||
{"name":"andark.img","url":"app_icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"id": "android",
|
||||
"name": "Android Integration",
|
||||
"shortName": "Android",
|
||||
"version": "0.06",
|
||||
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,messages,notifications,gadgetbridge",
|
||||
"dependencies": {"messages":"app"},
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"android.app.js","url":"app.js"},
|
||||
{"name":"android.settings.js","url":"settings.js"},
|
||||
{"name":"android.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"android.boot.js","url":"boot.js"}
|
||||
],
|
||||
"data": [{"name":"android.settings.json"}],
|
||||
"sortorder": -8
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"id": "animals",
|
||||
"name": "Animals Game",
|
||||
"version": "0.01",
|
||||
"description": "Simple toddler's game - displays a different number of animals each time the screen is pressed",
|
||||
"icon": "animals.png",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"animals.app.js","url":"animals.js"},
|
||||
{"name":"animals.img","url":"animals-icon.js","evaluate":true},
|
||||
{"name":"animals-snake.img","url":"animals-snake.js","evaluate":true},
|
||||
{"name":"animals-duck.img","url":"animals-duck.js","evaluate":true},
|
||||
{"name":"animals-swan.img","url":"animals-swan.js","evaluate":true},
|
||||
{"name":"animals-fox.img","url":"animals-fox.js","evaluate":true},
|
||||
{"name":"animals-camel.img","url":"animals-camel.js","evaluate":true},
|
||||
{"name":"animals-pig.img","url":"animals-pig.js","evaluate":true},
|
||||
{"name":"animals-sheep.img","url":"animals-sheep.js","evaluate":true},
|
||||
{"name":"animals-mouse.img","url":"animals-mouse.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "animclk",
|
||||
"name": "Animated Clock",
|
||||
"shortName": "Anim Clock",
|
||||
"version": "0.03",
|
||||
"description": "An animated clock face using Mark Ferrari's amazing 8 bit game art and palette cycling: http://www.markferrari.com/art/8bit-game-art",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock,animated",
|
||||
"supports": ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"animclk.app.js","url":"app.js"},
|
||||
{"name":"animclk.pixels1","url":"animclk.pixels1"},
|
||||
{"name":"animclk.pixels2","url":"animclk.pixels2"},
|
||||
{"name":"animclk.pal","url":"animclk.pal"},
|
||||
{"name":"animclk.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": "antonclk",
|
||||
"name": "Anton Clock",
|
||||
"version": "0.06",
|
||||
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
||||
"readme":"README.md",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"antonclk.app.js","url":"app.js"},
|
||||
{"name":"antonclk.settings.js","url":"settings.js"},
|
||||
{"name":"antonclk.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"antonclk.json"}]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "arrow",
|
||||
"name": "Arrow Compass",
|
||||
"version": "0.05",
|
||||
"description": "Moving arrow compass that points North, shows heading, with tilt correction. Based on jeffmer's Navigation Compass",
|
||||
"icon": "arrow.png",
|
||||
"type": "app",
|
||||
"tags": "tool,outdoors",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"arrow.app.js","url":"app.js"},
|
||||
{"name":"arrow.img","url":"icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"id": "assistedgps",
|
||||
"name": "Assisted GPS Update (AGPS)",
|
||||
"version": "0.03",
|
||||
"description": "Downloads assisted GPS (AGPS) data to Bangle.js 1 or 2 for faster GPS startup and more accurate fixes. **No app will be installed**, this just uploads new data to the GPS chip.",
|
||||
"icon": "app.png",
|
||||
"type": "RAM",
|
||||
"tags": "tool,outdoors,agps",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"custom": "custom.html",
|
||||
"customConnect": true,
|
||||
"storage": []
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "astral",
|
||||
"name": "Astral Clock",
|
||||
"version": "0.03",
|
||||
"description": "Clock that calculates and displays Alt Az positions of all planets, Sun as well as several other astronomy targets (customizable) and current Moon phase. Coordinates are calculated by GPS & time and onscreen compass assists orienting. See Readme before using.",
|
||||
"icon": "app-icon.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"astral.app.js","url":"app.js"},
|
||||
{"name":"astral.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"id": "astrocalc",
|
||||
"name": "Astrocalc",
|
||||
"version": "0.02",
|
||||
"description": "Calculates interesting information on the sun and moon cycles for the current day based on your location.",
|
||||
"icon": "astrocalc.png",
|
||||
"tags": "app,sun,moon,cycles,tool,outdoors",
|
||||
"supports": ["BANGLEJS"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"astrocalc.app.js","url":"astrocalc-app.js"},
|
||||
{"name":"suncalc.js","url":"suncalc.js"},
|
||||
{"name":"astrocalc.img","url":"astrocalc-icon.js","evaluate":true},
|
||||
{"name":"first-quarter.img","url":"first-quarter-icon.js","evaluate":true},
|
||||
{"name":"last-quarter.img","url":"last-quarter-icon.js","evaluate":true},
|
||||
{"name":"waning-crescent.img","url":"waning-crescent-icon.js","evaluate":true},
|
||||
{"name":"waning-gibbous.img","url":"waning-gibbous-icon.js","evaluate":true},
|
||||
{"name":"full.img","url":"full-icon.js","evaluate":true},
|
||||
{"name":"new.img","url":"new-icon.js","evaluate":true},
|
||||
{"name":"waxing-gibbous.img","url":"waxing-gibbous-icon.js","evaluate":true},
|
||||
{"name":"waxing-crescent.img","url":"waxing-crescent-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "astroid",
|
||||
"name": "Asteroids!",
|
||||
"version": "0.03",
|
||||
"description": "Retro asteroids game",
|
||||
"icon": "asteroids.png",
|
||||
"screenshots": [{"url":"screenshot_asteroids.png"}],
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"astroid.app.js","url":"asteroids.js"},
|
||||
{"name":"astroid.img","url":"asteroids-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": "authentiwatch",
|
||||
"name": "2FA Authenticator",
|
||||
"shortName": "AuthWatch",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"version": "0.04",
|
||||
"description": "Google Authenticator compatible tool.",
|
||||
"tags": "tool",
|
||||
"interface": "interface.html",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"authentiwatch.app.js","url":"app.js"},
|
||||
{"name":"authentiwatch.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"authentiwatch.json"}]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id":"awairmonitor",
|
||||
"name":"Awair Monitor",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"version":"0.03",
|
||||
"description": "Displays the level of CO2, VOC, PM 2.5, Humidity and Temperature, from your Awair device.",
|
||||
"type": "clock",
|
||||
"tags": "clock,tool,health",
|
||||
"readme":"README.md",
|
||||
"supports":["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"awairmonitor.app.js","url":"app.js"},
|
||||
{"name":"awairmonitor.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "ballmaze",
|
||||
"name": "Ball Maze",
|
||||
"version": "0.02",
|
||||
"description": "Navigate a ball through a maze by tilting your watch.",
|
||||
"icon": "icon.png",
|
||||
"type": "app",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"ballmaze.app.js","url":"app.js"},
|
||||
{"name":"ballmaze.img","url":"icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"ballmaze.json"}]
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"id": "balltastic",
|
||||
"name": "Balltastic",
|
||||
"version": "0.02",
|
||||
"description": "Simple but fun ball eats dots game.",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "game,fun",
|
||||
"supports": ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"balltastic.app.js","url":"app.js"},
|
||||
{"name":"balltastic.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "banglebridge",
|
||||
"name": "BangleBridge",
|
||||
"shortName": "BangleBridge",
|
||||
"version": "0.01",
|
||||
"description": "Widget that allows Bangle Js to record pair and end data using Bluetooth Low Energy in combination with the BangleBridge Android App",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
"tags": "widget",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"banglebridge.wid.js","url":"widget.js"},
|
||||
{"name":"banglebridge.watch.img","url":"watch.img"},
|
||||
{"name":"banglebridge.heart.img","url":"heart.img"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "banglerun",
|
||||
"name": "BangleRun",
|
||||
"shortName": "BangleRun",
|
||||
"version": "0.10",
|
||||
"description": "An app for running sessions. Displays info and logs your run for later viewing.",
|
||||
"icon": "banglerun.png",
|
||||
"tags": "run,running,fitness,outdoors",
|
||||
"supports": ["BANGLEJS"],
|
||||
"interface": "interface.html",
|
||||
"allow_emulator": false,
|
||||
"storage": [
|
||||
{"name":"banglerun.app.js","url":"app.js"},
|
||||
{"name":"banglerun.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{ "id": "banglexercise",
|
||||
"name": "BanglExercise",
|
||||
"shortName":"BanglExercise",
|
||||
"version":"0.02",
|
||||
"description": "Can automatically track exercises while wearing the Bangle.js watch.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "app",
|
||||
"tags": "sport",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"allow_emulator":true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"banglexercise.app.js","url":"app.js"},
|
||||
{"name":"banglexercise.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"banglexercise.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"banglexercise.json"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "barclock",
|
||||
"name": "Bar Clock",
|
||||
"version": "0.09",
|
||||
"description": "A simple digital clock showing seconds as a bar",
|
||||
"icon": "clock-bar.png",
|
||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"barclock.app.js","url":"clock-bar.js"},
|
||||
{"name":"barclock.img","url":"clock-bar-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "batchart",
|
||||
"name": "Battery Chart",
|
||||
"shortName": "Battery Chart",
|
||||
"version": "0.10",
|
||||
"description": "A widget and an app for recording and visualizing battery percentage over time.",
|
||||
"icon": "app.png",
|
||||
"tags": "app,widget,battery,time,record,chart,tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"batchart.wid.js","url":"widget.js"},
|
||||
{"name":"batchart.app.js","url":"app.js"},
|
||||
{"name":"batchart.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "batclock",
|
||||
"name": "Bat Clock",
|
||||
"shortName": "Bat Clock",
|
||||
"version": "0.02",
|
||||
"description": "Morphing Clock, with an awesome \"The Dark Knight\" themed logo.",
|
||||
"icon": "bat-clock.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"batclock.app.js","url":"bat-clock.app.js"},
|
||||
{"name":"batclock.img","url":"bat-clock.icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "battleship",
|
||||
"name": "Battleship",
|
||||
"version": "0.01",
|
||||
"description": "The classic game of battleship",
|
||||
"icon": "battleship-icon.png",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS"],
|
||||
"screenshots": [{"url":"bangle1-battle-ship-screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"battleship.app.js","url":"battleship.js"},
|
||||
{"name":"battleship.img","url":"battleship-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "bclock",
|
||||
"name": "Binary Clock",
|
||||
"version": "0.03",
|
||||
"description": "A simple binary clock watch face",
|
||||
"icon": "clock-binary.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"bangle1-binary-clock-screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"bclock.app.js","url":"clock-binary.js"},
|
||||
{"name":"bclock.img","url":"clock-binary-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "beebclock",
|
||||
"name": "Beeb Clock",
|
||||
"version": "0.05",
|
||||
"description": "Clock face that may be coincidentally familiar to BBC viewers",
|
||||
"icon": "beebclock.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"screenshots": [{"url":"bangle1-beeb-clock-screenshot.png"}],
|
||||
"supports": ["BANGLEJS"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"beebclock.app.js","url":"beebclock.js"},
|
||||
{"name":"beebclock.img","url":"beebclock-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"id": "beer",
|
||||
"name": "Beer Compass",
|
||||
"version": "0.01",
|
||||
"description": "Uploads all the pubs in an area onto your watch, so it can always point you at the nearest one",
|
||||
"icon": "app.png",
|
||||
"tags": "",
|
||||
"supports": ["BANGLEJS"],
|
||||
"custom": "custom.html",
|
||||
"storage": [
|
||||
{"name":"beer.app.js"},
|
||||
{"name":"beer.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "berlinc",
|
||||
"name": "Berlin Clock",
|
||||
"version": "0.05",
|
||||
"description": "Berlin Clock (see https://en.wikipedia.org/wiki/Mengenlehreuhr)",
|
||||
"icon": "berlin-clock.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"berlin-clock-screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"berlinc.app.js","url":"berlin-clock.js"},
|
||||
{"name":"berlinc.img","url":"berlin-clock-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "binclock",
|
||||
"name": "Binary Clock",
|
||||
"shortName": "Binary Clock",
|
||||
"version": "0.03",
|
||||
"description": "A binary clock with hours and minutes. BTN1 toggles a digital clock.",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock,binary",
|
||||
"supports": ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"binclock.app.js","url":"app.js"},
|
||||
{"name":"binclock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{ "id": "binwatch",
|
||||
"name": "Binary Watch",
|
||||
"shortName":"BinWatch",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"version":"0.04",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator":true,
|
||||
"description": "Famous binary watch",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
"storage": [
|
||||
{"name":"binwatch.app.js","url":"app.js"},
|
||||
{"name":"binwatch.bg176.img","url":"Background176_center.img"},
|
||||
{"name":"binwatch.bg240.img","url":"Background240_center.img"},
|
||||
{"name":"binwatch.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "blackjack",
|
||||
"name": "Black Jack game",
|
||||
"shortName": "Black Jack game",
|
||||
"version": "0.02",
|
||||
"description": "Simple implementation of card game Black Jack",
|
||||
"icon": "blackjack.png",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS"],
|
||||
"screenshots": [{"url":"bangle1-black-jack-game-screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"blackjack.app.js","url":"blackjack.app.js"},
|
||||
{"name":"blackjack.img","url":"blackjack-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "bledetect",
|
||||
"name": "BLE Detector",
|
||||
"shortName": "BLE Detector",
|
||||
"version": "0.03",
|
||||
"description": "Detect BLE devices and show some informations.",
|
||||
"icon": "bledetect.png",
|
||||
"tags": "app,bluetooth,tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"bledetect.app.js","url":"bledetect.js"},
|
||||
{"name":"bledetect.img","url":"bledetect-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"id": "blescan",
|
||||
"name": "BLE Scanner",
|
||||
"version": "0.01",
|
||||
"description": "Scan for advertising BLE devices",
|
||||
"icon": "blescan.png",
|
||||
"tags": "bluetooth",
|
||||
"supports": ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"blescan.app.js","url":"blescan.js"},
|
||||
{"name":"blescan.img","url":"blescan-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "blobclk",
|
||||
"name": "Large Digit Blob Clock",
|
||||
"shortName": "Blob Clock",
|
||||
"version": "0.06",
|
||||
"description": "A clock with big digits",
|
||||
"icon": "clock-blob.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"bangle2-large-digit-blob-clock-screenshot.png"},{"url":"bangle1-large-digit-blob-clock-screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"blobclk.app.js","url":"clock-blob.js"},
|
||||
{"name":"blobclk.img","url":"clock-blob-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "bluetoothdock",
|
||||
"name": "Bluetooth Dock",
|
||||
"shortName": "Dock",
|
||||
"version": "0.01",
|
||||
"description": "When charging shows the time, scans Bluetooth for known devices (eg temperature) and shows them on the screen",
|
||||
"icon": "app.png",
|
||||
"tags": "bluetooth",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"bluetoothdock.app.js","url":"app.js"},
|
||||
{"name":"bluetoothdock.boot.js","url":"boot.js"},
|
||||
{"name":"bluetoothdock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "boldclk",
|
||||
"name": "Bold Clock",
|
||||
"version": "0.05",
|
||||
"description": "Simple, readable and practical clock",
|
||||
"icon": "bold_clock.png",
|
||||
"screenshots": [{"url":"screenshot_bold.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"boldclk.app.js","url":"bold_clock.js"},
|
||||
{"name":"boldclk.img","url":"bold_clock-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "boot",
|
||||
"name": "Bootloader",
|
||||
"version": "0.41",
|
||||
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
||||
"icon": "bootloader.png",
|
||||
"type": "bootloader",
|
||||
"tags": "tool,system",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":".boot0","url":"boot0.js"},
|
||||
{"name":".bootcde","url":"bootloader.js"},
|
||||
{"name":"bootupdate.js","url":"bootupdate.js"}
|
||||
],
|
||||
"sortorder": -10
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "bootgattbat",
|
||||
"name": "BLE GATT Battery Service",
|
||||
"shortName": "BLE Battery Service",
|
||||
"version": "0.01",
|
||||
"description": "Adds the GATT Battery Service to advertise the percentage of battery currently remaining over Bluetooth.\n",
|
||||
"icon": "bluetooth.png",
|
||||
"type": "bootloader",
|
||||
"tags": "battery,ble,bluetooth,gatt",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"gattbat.boot.js","url":"boot.js"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "breath",
|
||||
"name": "Breathing App",
|
||||
"shortName": "Breathing App",
|
||||
"version": "0.01",
|
||||
"description": "app to aid relaxation and train breath syncronicity using haptics and visualisation, also displays HR",
|
||||
"icon": "app-icon.png",
|
||||
"tags": "tools,health",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"breath.app.js","url":"app.js"},
|
||||
{"name":"breath.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"breath.settings.json","url":"settings.json"}]
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": "bthrm",
|
||||
"name": "Bluetooth Heart Rate Monitor",
|
||||
"shortName": "BT HRM",
|
||||
"version": "0.03",
|
||||
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "health,bluetooth",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"bthrm.app.js","url":"bthrm.js"},
|
||||
{"name":"bthrm.recorder.js","url":"recorder.js"},
|
||||
{"name":"bthrm.boot.js","url":"boot.js"},
|
||||
{"name":"bthrm.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"bthrm.settings.js","url":"settings.js"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"id": "buffgym",
|
||||
"name": "BuffGym",
|
||||
"version": "0.02",
|
||||
"description": "BuffGym is the famous 5x5 workout program for the BangleJS",
|
||||
"icon": "buffgym.png",
|
||||
"type": "app",
|
||||
"tags": "tool,outdoors,gym,exercise",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"interface": "buffgym.html",
|
||||
"allow_emulator": false,
|
||||
"storage": [
|
||||
{"name":"buffgym.app.js","url":"buffgym.app.js"},
|
||||
{"name":"buffgym-set.js","url":"buffgym-set.js"},
|
||||
{"name":"buffgym-exercise.js","url":"buffgym-exercise.js"},
|
||||
{"name":"buffgym-workout.js","url":"buffgym-workout.js"},
|
||||
{"name":"buffgym-workout-a.json","url":"buffgym-workout-a.json"},
|
||||
{"name":"buffgym-workout-b.json","url":"buffgym-workout-b.json"},
|
||||
{"name":"buffgym-workout-index.json","url":"buffgym-workout-index.json"},
|
||||
{"name":"buffgym.img","url":"buffgym-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "calculator",
|
||||
"name": "Calculator",
|
||||
"shortName": "Calculator",
|
||||
"version": "0.05",
|
||||
"description": "Basic calculator reminiscent of MacOs's one. Handy for small calculus.",
|
||||
"icon": "calculator.png",
|
||||
"screenshots": [{"url":"screenshot_calculator.png"}],
|
||||
"tags": "app,tool",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"calculator.app.js","url":"app.js"},
|
||||
{"name":"calculator.img","url":"calculator-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.06",
|
||||
"description": "Simple calendar",
|
||||
"icon": "calendar.png",
|
||||
"screenshots": [{"url":"screenshot_calendar.png"}],
|
||||
"tags": "calendar",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"calendar.app.js","url":"calendar.js"},
|
||||
{"name":"calendar.settings.js","url":"settings.js"},
|
||||
{"name":"calendar.img","url":"calendar-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"calendar.json"}]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "carcrazy",
|
||||
"name": "Car Crazy",
|
||||
"shortName": "Car Crazy",
|
||||
"version": "0.03",
|
||||
"description": "A simple car game where you try to avoid the other cars by tilting your wrist left and right. Hold down button 2 to start.",
|
||||
"icon": "carcrash.png",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"carcrazy.app.js","url":"app.js"},
|
||||
{"name":"carcrazy.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"carcrazy.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [{"name":"CarCrazy.csv"}]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "chargeanim",
|
||||
"name": "Charge Animation",
|
||||
"version": "0.02",
|
||||
"description": "When charging, show a sideways charging animation and keep the screen on. When removed from the charger load the clock again.",
|
||||
"icon": "icon.png",
|
||||
"tags": "battery",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"bangle2-charge-animation-screenshot.png"},{"url":"bangle-charge-animation-screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"chargeanim.app.js","url":"app.js"},
|
||||
{"name":"chargeanim.boot.js","url":"boot.js"},
|
||||
{"name":"chargeanim.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "choozi",
|
||||
"name": "Choozi",
|
||||
"version": "0.01",
|
||||
"description": "Choose people or things at random using Bangle.js.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"bangle1-choozi-screenshot1.png"},{"url":"bangle1-choozi-screenshot2.png"}],
|
||||
"storage": [
|
||||
{"name":"choozi.app.js","url":"app.js"},
|
||||
{"name":"choozi.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"id": "chrono",
|
||||
"name": "Chrono",
|
||||
"shortName": "Chrono",
|
||||
"version": "0.01",
|
||||
"description": "Single click BTN1 to add 5 minutes. Single click BTN2 to add 30 seconds. Single click BTN3 to add 5 seconds. Tap to pause or play to timer. Double click BTN1 to reset. When timer finishes the watch vibrates.",
|
||||
"icon": "chrono.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"chrono.app.js","url":"chrono.js"},
|
||||
{"name":"chrono.img","url":"chrono-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "chronowid",
|
||||
"name": "Chrono Widget",
|
||||
"shortName": "Chrono Widget",
|
||||
"version": "0.05",
|
||||
"description": "Chronometer (timer) which runs as widget.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,widget",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"chronowid.wid.js","url":"widget.js"},
|
||||
{"name":"chronowid.app.js","url":"app.js"},
|
||||
{"name":"chronowid.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -7,3 +7,7 @@
|
|||
Make circles and text slightly bigger
|
||||
0.05: Show correct percentage values in circles
|
||||
Show humidity as weather circle data
|
||||
0.06: Allow settings empty circles
|
||||
Support to choose between humidity and wind speed for weather circle progress
|
||||
Support to show time and progress until next sunrise or sunset
|
||||
Load daily steps from Bangle health if available
|
||||
|
|
|
@ -5,23 +5,20 @@ A clock with circles for different data at the bottom in a probably familiar sty
|
|||
By default the time, date and day of week is shown.
|
||||
|
||||
It can show the following information (this can be configured):
|
||||
* Steps (requires [pedometer widget](https://banglejs.com/apps/#pedometer))
|
||||
* Steps distance (depending on steps)
|
||||
* Steps
|
||||
* Steps distance
|
||||
* Heart rate (automatically updates when screen is on and unlocked)
|
||||
* Battery (including charging status and battery low warning)
|
||||
* Weather (requires [weather app](https://banglejs.com/apps/#weather))
|
||||
* Humidity as circle progress
|
||||
* Humidity or wind speed as circle progress
|
||||
* Temperature inside circle
|
||||
* Condition as icon below circle
|
||||
* Time and progress until next sunrise or sunset (requires [my location app](https://banglejs.com/apps/#mylocation))
|
||||
|
||||
## Screenshots
|
||||

|
||||

|
||||
|
||||
# TODO
|
||||
* Add sunrise and sunset
|
||||
* Display moon instead of sun during night on weather circle
|
||||
|
||||
## Creator
|
||||
Marco ([myxor](https://github.com/myxor))
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const locale = require("locale");
|
||||
const heatshrink = require("heatshrink");
|
||||
const storage = require("Storage");
|
||||
const SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
|
||||
|
||||
const shoesIcon = heatshrink.decompress(atob("h0OwYJGgmAAgUBkgECgVJB4cSoAUDyEBkARDpADBhMAyQRBgVAkgmDhIUDAAuQAgY1DAAYA="));
|
||||
const shoesIconGreen = heatshrink.decompress(atob("h0OwYJGhIEDgVIAgUEyQKDkmACgcggVACIeQAYMSgIRCgmApIbDiQUDAAkBkAFDGoYAD"));
|
||||
|
@ -11,6 +12,7 @@ const powerIconRed = heatshrink.decompress(atob("h0OwYQNoAEDyAEDkgEDpIFDiVJBweSA
|
|||
|
||||
const weatherCloudy = heatshrink.decompress(atob("iEQwYWTgP//+AAoMPAoPwAoN/AocfAgP//0AAgQAB/AFEABgdDAAMDDohMRA"));
|
||||
const weatherSunny = heatshrink.decompress(atob("iEQwYLIg3AAgVgAQMMAo8Am3YAgUB23bAoUNAoIUBjYFCsOwBYoFDDpFgHYI1JI4gFGAAYA="));
|
||||
const weatherMoon = heatshrink.decompress(atob("iEQwIFCgOAh/wj/4n/8AId//wBBBIoRBCoIZBDoI"));
|
||||
const weatherPartlyCloudy = heatshrink.decompress(atob("iEQwYQNv0AjgGDn4EDh///gFChwREC4MfxwIBv0//+AC4X4j4FCv/AgfwgED/wIBuAaBBwgFDgP4gf/AAXABwIEBDQQAEA=="));
|
||||
const weatherRainy = heatshrink.decompress(atob("iEQwYLIg/gAgUB///wAFBh/AgfwgED/wIBuEAj4OCv0AjgaCh/4AocAnAFBFIU4EAM//gRBEAIOBhw1C/AmDAosAC4JNIAAg"));
|
||||
const weatherPartlyRainy = heatshrink.decompress(atob("h0OwYJGjkAnAFCj+AAgU//4FCuEA8EAg8ch/4gEB4////AAoIIBCIMD/wgCg4bBg/8BwMD+AgBh4ZBDQf/FIIABh4IBgAA=="));
|
||||
|
@ -18,6 +20,9 @@ const weatherSnowy = heatshrink.decompress(atob("iEQwYROn/8AocH8AECuAFBh0Agf+CIN
|
|||
const weatherFoggy = heatshrink.decompress(atob("iEQwYROn/8AgUB/EfwAFBh/AgfwgED/wIBuEABwd/4EcDQgFDgE4Fosf///8f//A/Lj/xCQIRNA="));
|
||||
const weatherStormy = heatshrink.decompress(atob("iEQwYLIg/gAgUB///wAFBh/AgfwgED/wIBuEAj4OCv0AjgaCh/4AoX8gE4AoQpBnAdBF4IRBDQMH/kOHgY7DAo4AOA=="));
|
||||
|
||||
const sunSetDown = heatshrink.decompress(atob("iEQwIHEgOAAocT5EGtEEkF//wLDg1ggfACoo"));
|
||||
const sunSetUp = heatshrink.decompress(atob("iEQwIHEgOAAocT5EGtEEkF//wRFgfAg1gBIY"));
|
||||
|
||||
let settings;
|
||||
|
||||
function loadSettings() {
|
||||
|
@ -29,6 +34,7 @@ function loadSettings() {
|
|||
'stepLength': 0.8,
|
||||
'batteryWarn': 30,
|
||||
'showWidgets': false,
|
||||
'weatherCircleData': 'humidity',
|
||||
'circle1': 'hr',
|
||||
'circle2': 'steps',
|
||||
'circle3': 'battery'
|
||||
|
@ -40,9 +46,21 @@ function loadSettings() {
|
|||
}
|
||||
}
|
||||
loadSettings();
|
||||
|
||||
|
||||
/*
|
||||
* Read location from myLocation app
|
||||
*/
|
||||
function getLocation() {
|
||||
return storage.readJSON("mylocation.json", 1) || undefined;
|
||||
}
|
||||
let location = getLocation();
|
||||
|
||||
const showWidgets = settings.showWidgets || false;
|
||||
|
||||
let hrtValue;
|
||||
let now = Math.round(new Date().getTime() / 1000);
|
||||
|
||||
|
||||
// layout values:
|
||||
const colorFg = g.theme.dark ? '#fff' : '#000';
|
||||
|
@ -64,7 +82,6 @@ const radiusOuter = 25;
|
|||
const radiusInner = 20;
|
||||
const circleFont = "Vector:15";
|
||||
const circleFontBig = "Vector:16";
|
||||
const circleFontSmall = "Vector:13";
|
||||
|
||||
function draw() {
|
||||
g.clear(true);
|
||||
|
@ -93,6 +110,7 @@ function draw() {
|
|||
g.setFontAlign(0, -1);
|
||||
g.setColor(colorFg);
|
||||
g.drawString(locale.time(new Date(), 1), w / 2, h1 + 8);
|
||||
now = Math.round(new Date().getTime() / 1000);
|
||||
|
||||
// date & dow
|
||||
g.setFont("Vector:21");
|
||||
|
@ -127,19 +145,42 @@ function drawCircle(index) {
|
|||
case "weather":
|
||||
drawWeather(w);
|
||||
break;
|
||||
case "sunprogress":
|
||||
drawSunProgress(w);
|
||||
break;
|
||||
case "empty":
|
||||
// we draw nothing here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// serves as cache for quicker lookup of circle positions
|
||||
let circlePositionsCache = [];
|
||||
/*
|
||||
* Looks in the following order if a circle with the given type is somewhere visible/configured
|
||||
* 1. circlePositionsCache
|
||||
* 2. settings
|
||||
* 3. defaultCircleTypes
|
||||
*
|
||||
* In case 2 and 3 the circlePositionsCache will be updated
|
||||
*/
|
||||
function getCirclePosition(type) {
|
||||
if (circlePositionsCache[type] >= 0) {
|
||||
return circlePosX[circlePositionsCache[type]];
|
||||
}
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
const setting = settings['circle' + i];
|
||||
if (setting == type) return circlePosX[i - 1];
|
||||
if (setting == type) {
|
||||
circlePositionsCache[type] = i - 1;
|
||||
return circlePosX[i - 1];
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < defaultCircleTypes.length; i++) {
|
||||
if (type == defaultCircleTypes[i] && (!settings || settings['circle' + (i + 1)] == undefined)) {
|
||||
return circlePosX[i];
|
||||
}
|
||||
}
|
||||
if (type == defaultCircleTypes[i] && (!settings || settings['circle' + (i + 1)] == undefined)) {
|
||||
circlePositionsCache[type] = i;
|
||||
return circlePosX[i];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -147,16 +188,12 @@ function isCircleEnabled(type) {
|
|||
return getCirclePosition(type) != undefined;
|
||||
}
|
||||
|
||||
|
||||
function drawSteps(w) {
|
||||
if (!w) w = getCirclePosition("steps");
|
||||
const steps = getSteps();
|
||||
|
||||
// Draw rectangle background:
|
||||
g.setColor(colorBg);
|
||||
g.fillRect(w - radiusOuter - 3, h3 - radiusOuter - 3, w + radiusOuter + 3, h3 + radiusOuter + 3);
|
||||
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w, h3, radiusOuter);
|
||||
drawCircleBackground(w);
|
||||
|
||||
const stepGoal = settings.stepGoal || 10000;
|
||||
if (stepGoal > 0) {
|
||||
|
@ -165,15 +202,9 @@ function drawSteps(w) {
|
|||
drawGauge(w, h3, percent, colorBlue);
|
||||
}
|
||||
|
||||
g.setColor(colorBg);
|
||||
g.fillCircle(w, h3, radiusInner);
|
||||
drawInnerCircleAndTriangle(w);
|
||||
|
||||
g.fillPoly([w, h3, w - 15, h3 + radiusOuter + 5, w + 15, h3 + radiusOuter + 5]);
|
||||
|
||||
g.setFont(circleFont);
|
||||
g.setFontAlign(0, 0);
|
||||
g.setColor(colorFg);
|
||||
g.drawString(shortValue(steps), w + 2, h3);
|
||||
writeCircleText(w, shortValue(steps));
|
||||
|
||||
g.drawImage(shoesIcon, w - 6, h3 + radiusOuter - 6);
|
||||
}
|
||||
|
@ -184,12 +215,7 @@ function drawStepsDistance(w) {
|
|||
const stepDistance = settings.stepLength || 0.8;
|
||||
const stepsDistance = Math.round(steps * stepDistance);
|
||||
|
||||
// Draw rectangle background:
|
||||
g.setColor(colorBg);
|
||||
g.fillRect(w - radiusOuter - 3, h3 - radiusOuter - 3, w + radiusOuter + 3, h3 + radiusOuter + 3);
|
||||
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w, h3, radiusOuter);
|
||||
drawCircleBackground(w);
|
||||
|
||||
const stepDistanceGoal = settings.stepDistanceGoal || 8000;
|
||||
if (stepDistanceGoal > 0) {
|
||||
|
@ -198,15 +224,9 @@ function drawStepsDistance(w) {
|
|||
drawGauge(w, h3, percent, colorGreen);
|
||||
}
|
||||
|
||||
g.setColor(colorBg);
|
||||
g.fillCircle(w, h3, radiusInner);
|
||||
drawInnerCircleAndTriangle(w);
|
||||
|
||||
g.fillPoly([w, h3, w - 15, h3 + radiusOuter + 5, w + 15, h3 + radiusOuter + 5]);
|
||||
|
||||
g.setFont(circleFont);
|
||||
g.setFontAlign(0, 0);
|
||||
g.setColor(colorFg);
|
||||
g.drawString(shortValue(stepsDistance), w + 2, h3);
|
||||
writeCircleText(w, shortValue(stepsDistance));
|
||||
|
||||
g.drawImage(shoesIconGreen, w - 6, h3 + radiusOuter - 6);
|
||||
}
|
||||
|
@ -214,28 +234,18 @@ function drawStepsDistance(w) {
|
|||
function drawHeartRate(w) {
|
||||
if (!w) w = getCirclePosition("hr");
|
||||
|
||||
// Draw rectangle background:
|
||||
g.setColor(colorBg);
|
||||
g.fillRect(w - radiusOuter - 3, h3 - radiusOuter - 3, w + radiusOuter + 3, h3 + radiusOuter + 3);
|
||||
drawCircleBackground(w);
|
||||
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w, h3, radiusOuter);
|
||||
|
||||
if (hrtValue != undefined && hrtValue > 0) {
|
||||
if (hrtValue != undefined) {
|
||||
const minHR = settings.minHR || 40;
|
||||
const percent = (hrtValue - minHR) / (settings.maxHR - minHR);
|
||||
const maxHR = settings.maxHR || 200;
|
||||
const percent = (hrtValue - minHR) / (maxHR - minHR);
|
||||
drawGauge(w, h3, percent, colorRed);
|
||||
}
|
||||
|
||||
g.setColor(colorBg);
|
||||
g.fillCircle(w, h3, radiusInner);
|
||||
drawInnerCircleAndTriangle(w);
|
||||
|
||||
g.fillPoly([w, h3, w - 15, h3 + radiusOuter + 5, w + 15, h3 + radiusOuter + 5]);
|
||||
|
||||
g.setFont(circleFontBig);
|
||||
g.setFontAlign(0, 0);
|
||||
g.setColor(colorFg);
|
||||
g.drawString(hrtValue != undefined ? hrtValue : "-", w, h3);
|
||||
writeCircleText(w, hrtValue != undefined ? hrtValue : "-");
|
||||
|
||||
g.drawImage(heartIcon, w - 6, h3 + radiusOuter - 6);
|
||||
}
|
||||
|
@ -244,25 +254,14 @@ function drawBattery(w) {
|
|||
if (!w) w = getCirclePosition("battery");
|
||||
const battery = E.getBattery();
|
||||
|
||||
// Draw rectangle background:
|
||||
g.setColor(colorBg);
|
||||
g.fillRect(w - radiusOuter - 3, h3 - radiusOuter - 3, w + radiusOuter + 3, h3 + radiusOuter + 3);
|
||||
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w, h3, radiusOuter);
|
||||
drawCircleBackground(w);
|
||||
|
||||
if (battery > 0) {
|
||||
const percent = battery / 100;
|
||||
drawGauge(w, h3, percent, colorYellow);
|
||||
}
|
||||
|
||||
g.setColor(colorBg);
|
||||
g.fillCircle(w, h3, radiusInner);
|
||||
|
||||
g.fillPoly([w, h3, w - 15, h3 + radiusOuter + 5, w + 15, h3 + radiusOuter + 5]);
|
||||
|
||||
g.setFont(circleFont);
|
||||
g.setFontAlign(0, 0);
|
||||
drawInnerCircleAndTriangle(w);
|
||||
|
||||
let icon = powerIcon;
|
||||
let color = colorFg;
|
||||
|
@ -275,8 +274,7 @@ function drawBattery(w) {
|
|||
icon = powerIconRed;
|
||||
}
|
||||
}
|
||||
g.setColor(color);
|
||||
g.drawString(battery + '%', w, h3);
|
||||
writeCircleText(w, battery + '%');
|
||||
|
||||
g.drawImage(icon, w - 6, h3 + radiusOuter - 6);
|
||||
}
|
||||
|
@ -285,30 +283,37 @@ function drawWeather(w) {
|
|||
if (!w) w = getCirclePosition("weather");
|
||||
const weather = getWeather();
|
||||
const tempString = weather ? locale.temp(weather.temp - 273.15) : undefined;
|
||||
const humidity = weather ? weather.hum : undefined;
|
||||
const code = weather ? weather.code : -1;
|
||||
|
||||
// Draw rectangle background:
|
||||
g.setColor(colorBg);
|
||||
g.fillRect(w - radiusOuter - 3, h3 - radiusOuter - 3, w + radiusOuter + 3, h3 + radiusOuter + 3);
|
||||
drawCircleBackground(w);
|
||||
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w, h3, radiusOuter);
|
||||
|
||||
if (humidity >= 0) {
|
||||
drawGauge(w, h3, humidity / 100, colorYellow);
|
||||
const data = settings.weatherCircleData || "humidity";
|
||||
switch (data) {
|
||||
case "humidity":
|
||||
const humidity = weather ? weather.hum : undefined;
|
||||
if (humidity >= 0) {
|
||||
drawGauge(w, h3, humidity / 100, colorYellow);
|
||||
}
|
||||
break;
|
||||
case "wind":
|
||||
if (weather) {
|
||||
const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
|
||||
if (wind[1] >= 0) {
|
||||
if (wind[2] == "kmh") {
|
||||
wind[1] = windAsBeaufort(wind[1]);
|
||||
}
|
||||
// wind goes from 0 to 12 (see https://en.wikipedia.org/wiki/Beaufort_scale)
|
||||
drawGauge(w, h3, wind[1] / 12, colorYellow);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "empty":
|
||||
break;
|
||||
}
|
||||
|
||||
g.setColor(colorBg);
|
||||
g.fillCircle(w, h3, radiusInner);
|
||||
drawInnerCircleAndTriangle(w);
|
||||
|
||||
g.fillPoly([w, h3, w - 25, h3 + radiusOuter + 5, w + 25, h3 + radiusOuter + 5]);
|
||||
|
||||
const content = tempString ? tempString : "?";
|
||||
g.setFont(content.length < 4 ? circleFont : circleFontSmall);
|
||||
g.setFontAlign(0, 0);
|
||||
g.setColor(colorFg);
|
||||
g.drawString(content, w, h3);
|
||||
writeCircleText(w, tempString ? tempString : "?");
|
||||
|
||||
if (code > 0) {
|
||||
const icon = getWeatherIconByCode(code);
|
||||
|
@ -316,6 +321,69 @@ function drawWeather(w) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function drawSunProgress(w) {
|
||||
if (!w) w = getCirclePosition("sunprogress");
|
||||
const percent = getSunProgress();
|
||||
|
||||
drawCircleBackground(w);
|
||||
|
||||
drawGauge(w, h3, percent, colorYellow);
|
||||
|
||||
drawInnerCircleAndTriangle(w);
|
||||
|
||||
let icon = powerIcon;
|
||||
let color = colorFg;
|
||||
if (isDay()) {
|
||||
// day
|
||||
color = colorFg;
|
||||
icon = sunSetDown;
|
||||
} else {
|
||||
// night
|
||||
color = colorGrey;
|
||||
icon = sunSetUp;
|
||||
}
|
||||
g.setColor(color);
|
||||
|
||||
let text = "?";
|
||||
const times = getSunData();
|
||||
if (times != undefined) {
|
||||
const sunRise = Math.round(times.sunrise.getTime() / 1000);
|
||||
const sunSet = Math.round(times.sunset.getTime() / 1000);
|
||||
if (!isDay()) {
|
||||
// night
|
||||
if (now > sunRise) {
|
||||
// after sunRise
|
||||
const upcomingSunRise = sunRise + 60 * 60 * 24;
|
||||
text = formatSeconds(upcomingSunRise - now);
|
||||
} else {
|
||||
text = formatSeconds(sunRise - now);
|
||||
}
|
||||
} else {
|
||||
// day, approx sunrise tomorrow:
|
||||
text = formatSeconds(sunSet - now);
|
||||
}
|
||||
}
|
||||
|
||||
writeCircleText(w, text);
|
||||
|
||||
g.drawImage(icon, w - 6, h3 + radiusOuter - 6);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* wind goes from 0 to 12 (see https://en.wikipedia.org/wiki/Beaufort_scale)
|
||||
*/
|
||||
function windAsBeaufort(windInKmh) {
|
||||
const beaufort = [2, 6, 12, 20, 29, 39, 50, 62, 75, 89, 103, 118];
|
||||
let l = 0;
|
||||
while (l < beaufort.length && beaufort[l] < windInKmh) {
|
||||
l++;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Choose weather icon to display based on weather conditition code
|
||||
* https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
|
||||
|
@ -350,7 +418,7 @@ function getWeatherIconByCode(code) {
|
|||
case 8:
|
||||
switch (code) {
|
||||
case 800:
|
||||
return weatherSunny;
|
||||
return isDay() ? weatherSunny : weatherMoon;
|
||||
case 801:
|
||||
return weatherPartlyCloudy;
|
||||
case 802:
|
||||
|
@ -365,32 +433,122 @@ function getWeatherIconByCode(code) {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
function isDay() {
|
||||
const times = getSunData();
|
||||
if (times == undefined) return true;
|
||||
const sunRise = Math.round(times.sunrise.getTime() / 1000);
|
||||
const sunSet = Math.round(times.sunset.getTime() / 1000);
|
||||
|
||||
return (now > sunRise && now < sunSet);
|
||||
}
|
||||
|
||||
function formatSeconds(s) {
|
||||
if (s > 60 * 60) { // hours
|
||||
return Math.round(s / (60 * 60)) + "h";
|
||||
}
|
||||
if (s > 60) { // minutes
|
||||
return Math.round(s / 60) + "m";
|
||||
}
|
||||
return "<1m";
|
||||
}
|
||||
|
||||
function getSunData() {
|
||||
if (location != undefined && location.lat != undefined) {
|
||||
// get today's sunlight times for lat/lon
|
||||
return SunCalc.getTimes(new Date(), location.lat, location.lon);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculated progress of the sun between sunrise and sunset in percent
|
||||
*
|
||||
* Taken from rebble app and modified
|
||||
*/
|
||||
function getSunProgress() {
|
||||
const times = getSunData();
|
||||
if (times == undefined) return 0;
|
||||
const sunRise = Math.round(times.sunrise.getTime() / 1000);
|
||||
const sunSet = Math.round(times.sunset.getTime() / 1000);
|
||||
|
||||
if (isDay()) {
|
||||
// during day
|
||||
const dayLength = sunSet - sunRise;
|
||||
if (now > sunRise) {
|
||||
return (now - sunRise) / dayLength;
|
||||
} else {
|
||||
return (sunRise - now) / dayLength;
|
||||
}
|
||||
} else {
|
||||
// during night
|
||||
if (sunSet < sunRise) {
|
||||
const upcomingSunRise = sunRise + 60 * 60 * 24;
|
||||
return 1 - (upcomingSunRise - now) / (upcomingSunRise - sunSet);
|
||||
} else {
|
||||
const lastSunSet = sunSet - 60 * 60 * 24;
|
||||
return (now - lastSunSet) / (sunRise - lastSunSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Draws the background and the grey circle
|
||||
*/
|
||||
function drawCircleBackground(w) {
|
||||
// Draw rectangle background:
|
||||
g.setColor(colorBg);
|
||||
g.fillRect(w - radiusOuter - 3, h3 - radiusOuter - 3, w + radiusOuter + 3, h3 + radiusOuter + 3);
|
||||
// Draw grey background circle:
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w, h3, radiusOuter);
|
||||
}
|
||||
|
||||
function drawInnerCircleAndTriangle(w) {
|
||||
// Draw inner circle
|
||||
g.setColor(colorBg);
|
||||
g.fillCircle(w, h3, radiusInner);
|
||||
// Draw triangle which covers the bottom of the circle
|
||||
g.fillPoly([w, h3, w - 15, h3 + radiusOuter + 5, w + 15, h3 + radiusOuter + 5]);
|
||||
}
|
||||
|
||||
function radians(a) {
|
||||
return a * Math.PI / 180;
|
||||
}
|
||||
|
||||
/*
|
||||
* This draws the actual gauge consisting out of lots of little filled circles
|
||||
*/
|
||||
function drawGauge(cx, cy, percent, color) {
|
||||
const offset = 15;
|
||||
const end = 345;
|
||||
const r = radiusInner + 3;
|
||||
const radius = radiusInner + 3;
|
||||
const size = radiusOuter - radiusInner - 2;
|
||||
|
||||
if (percent <= 0) return;
|
||||
if (percent > 1) percent = 1;
|
||||
|
||||
const startrot = -offset;
|
||||
const endrot = startrot - ((end - offset) * percent);
|
||||
const startRotation = -offset;
|
||||
const endRotation = startRotation - ((end - offset) * percent);
|
||||
|
||||
g.setColor(color);
|
||||
|
||||
const size = radiusOuter - radiusInner - 2;
|
||||
// draw gauge
|
||||
for (let i = startrot; i > endrot - size; i -= size) {
|
||||
x = cx + r * Math.sin(radians(i));
|
||||
y = cy + r * Math.cos(radians(i));
|
||||
for (let i = startRotation; i > endRotation - size; i -= size) {
|
||||
x = cx + radius * Math.sin(radians(i));
|
||||
y = cy + radius * Math.cos(radians(i));
|
||||
g.fillCircle(x, y, size);
|
||||
}
|
||||
}
|
||||
|
||||
function writeCircleText(w, content) {
|
||||
if (content == undefined) return;
|
||||
g.setFont(content.length < 4 ? circleFontBig : circleFont);
|
||||
|
||||
g.setFontAlign(0, 0);
|
||||
g.setColor(colorFg);
|
||||
g.drawString(content, w, h3);
|
||||
}
|
||||
|
||||
function shortValue(v) {
|
||||
if (isNaN(v)) return '-';
|
||||
if (v <= 999) return v;
|
||||
|
@ -405,6 +563,9 @@ function shortValue(v) {
|
|||
}
|
||||
|
||||
function getSteps() {
|
||||
if (Bangle.getHealthStatus) {
|
||||
return Bangle.getHealthStatus("day").steps;
|
||||
}
|
||||
if (WIDGETS && WIDGETS.wpedom !== undefined) {
|
||||
return WIDGETS.wpedom.getSteps();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
{ "id": "circlesclock",
|
||||
"name": "Circles clock",
|
||||
"shortName":"Circles clock",
|
||||
"version":"0.06",
|
||||
"description": "A clock with circles for different data at the bottom in a probably familiar style",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"allow_emulator":true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"circlesclock.app.js","url":"app.js"},
|
||||
{"name":"circlesclock.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"circlesclock.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"circlesclock.json"}
|
||||
]
|
||||
}
|
|
@ -6,8 +6,12 @@
|
|||
settings[key] = value;
|
||||
storage.write(SETTINGS_FILE, settings);
|
||||
}
|
||||
var valuesCircleTypes = ["steps", "stepsDist", "hr", "battery", "weather"];
|
||||
var namesCircleTypes = ["steps", "distance", "heart", "battery", "weather"];
|
||||
|
||||
const valuesCircleTypes = ["steps", "stepsDist", "hr", "battery", "weather", "sunprogress", "empty"];
|
||||
const namesCircleTypes = ["steps", "distance", "heart", "battery", "weather", "sun progress", "empty"];
|
||||
|
||||
const weatherData = ["humidity", "wind", "empty"];
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'circlesclock' },
|
||||
'< Back': back,
|
||||
|
@ -76,21 +80,27 @@
|
|||
format: () => (settings.showWidgets ? 'Yes' : 'No'),
|
||||
onchange: x => save('showWidgets', x),
|
||||
},
|
||||
'weather circle': {
|
||||
value: settings.weatherCircleData ? weatherData.indexOf(settings.weatherCircleData) : 0,
|
||||
min: 0, max: 2,
|
||||
format: v => weatherData[v],
|
||||
onchange: x => save('weatherCircleData', weatherData[x]),
|
||||
},
|
||||
'left': {
|
||||
value: settings.circle1 ? valuesCircleTypes.indexOf(settings.circle1) : 0,
|
||||
min: 0, max: 4,
|
||||
min: 0, max: 6,
|
||||
format: v => namesCircleTypes[v],
|
||||
onchange: x => save('circle1', valuesCircleTypes[x]),
|
||||
},
|
||||
'middle': {
|
||||
value: settings.circle2 ? valuesCircleTypes.indexOf(settings.circle2) : 2,
|
||||
min: 0, max: 4,
|
||||
min: 0, max: 6,
|
||||
format: v => namesCircleTypes[v],
|
||||
onchange: x => save('circle2', valuesCircleTypes[x]),
|
||||
},
|
||||
'right': {
|
||||
value: settings.circle3 ? valuesCircleTypes.indexOf(settings.circle3) : 3,
|
||||
min: 0, max: 4,
|
||||
min: 0, max: 6,
|
||||
format: v => namesCircleTypes[v],
|
||||
onchange: x => save('circle3', valuesCircleTypes[x]),
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"id": "clickms",
|
||||
"name": "Click Master",
|
||||
"version": "0.01",
|
||||
"description": "Get several friends to start the game, then compete to see who can press BTN1 the most!",
|
||||
"icon": "click-master.png",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"clickms.app.js","url":"click-master.js"},
|
||||
{"name":"clickms.img","url":"click-master-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "cliclockJS2Enhanced",
|
||||
"name": "Commandline-Clock JS2 Enhanced",
|
||||
"shortName": "CLI-Clock JS2",
|
||||
"version": "0.03",
|
||||
"description": "Simple CLI-Styled Clock with enhancements. Modes that are hard to use and unneded are removed (BPM, battery info, memory ect) credit to hughbarney for the original code and design. Also added HID media controlls, just swipe on the clock face to controll the media! Gadgetbride support coming soon(hopefully) Thanks to t0m1o1 for media controls!",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screengrab.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock,cli,command,bash,shell",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"cliclockJS2Enhanced.app.js","url":"app.js"},
|
||||
{"name":"cliclockJS2Enhanced.img","url":"app.icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": "clicompleteclk",
|
||||
"name": "CLI complete clock",
|
||||
"shortName":"CLI cmplt clock",
|
||||
"version":"0.03",
|
||||
"description": "Command line styled clock with lots of information",
|
||||
"icon": "app.png",
|
||||
"allow_emulator": true,
|
||||
"type": "clock",
|
||||
"tags": "clock,cli,command,bash,shell,weather,hrt",
|
||||
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"clicompleteclk.app.js","url":"app.js"},
|
||||
{"name":"clicompleteclk.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"clicompleteclk.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [{"name":"clicompleteclk.json"}]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "cliock",
|
||||
"name": "Commandline-Clock",
|
||||
"shortName": "CLI-Clock",
|
||||
"version": "0.15",
|
||||
"description": "Simple CLI-Styled Clock",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot_cli.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock,cli,command,bash,shell",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"cliock.app.js","url":"app.js"},
|
||||
{"name":"cliock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "clock2x3",
|
||||
"name": "2x3 Pixel Clock",
|
||||
"version": "0.05",
|
||||
"description": "This is a simple clock using minimalist 2x3 pixel numerical digits",
|
||||
"icon": "clock2x3.png",
|
||||
"screenshots": [{"url":"screenshot_pixel.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"clock2x3.app.js","url":"clock2x3-app.js"},
|
||||
{"name":"clock2x3.img","url":"clock2x3-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "clotris",
|
||||
"name": "Clock-Tris",
|
||||
"version": "0.01",
|
||||
"description": "A fully functional clone of a classic game of falling blocks",
|
||||
"icon": "clock-tris.png",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS"],
|
||||
"screenshots": [{"url":"bangle1-clock-tris-screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"clotris.app.js","url":"clock-tris.js"},
|
||||
{"name":"clotris.img","url":"clock-tris-icon.js","evaluate":true},
|
||||
{"name":".trishig","url":"clock-tris-high"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "color_catalog",
|
||||
"name": "Colors Catalog",
|
||||
"shortName": "Colors Catalog",
|
||||
"version": "0.01",
|
||||
"description": "Displays RGB565 and RGB888 colors, its name and code in screen.",
|
||||
"icon": "app.png",
|
||||
"tags": "Color,input,buttons,touch,UI",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"color_catalog.app.js","url":"app.js"},
|
||||
{"name":"color_catalog.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{ "id": "colorful_clock",
|
||||
"name": "Colorful Analog Clock",
|
||||
"shortName":"Colorful Clock",
|
||||
"version":"0.03",
|
||||
"description": "a colorful analog clock",
|
||||
"icon": "app-icon.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"app-screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"colorful_clock.app.js","url":"app.js"},
|
||||
{"name":"colorful_clock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id":"colorwheel",
|
||||
"name":"Color Wheel",
|
||||
"tags":"app,tool",
|
||||
"version":"0.01",
|
||||
"description":"a tappable wheel of good-looking colors",
|
||||
"readme":"README.md",
|
||||
"supports":["BANGLEJS2"],
|
||||
"allow_emulator":true,
|
||||
"icon":"colorwheel.png",
|
||||
"storage": [
|
||||
{"name":"colorwheel.app.js","url":"app.js"},
|
||||
{"name":"colorwheel.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"id": "compass",
|
||||
"name": "Compass",
|
||||
"version": "0.05",
|
||||
"description": "Simple compass that points North",
|
||||
"icon": "compass.png",
|
||||
"screenshots": [{"url":"screenshot_compass.png"}],
|
||||
"tags": "tool,outdoors",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"compass.app.js","url":"compass.js"},
|
||||
{"name":"compass.img","url":"compass-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{ "id": "configurable_clock",
|
||||
"name": "Configurable Analog Clock",
|
||||
"shortName":"Configurable Clock",
|
||||
"version":"0.02",
|
||||
"description": "an analog clock with several kinds of faces, hands and colors to choose from",
|
||||
"icon": "app-icon.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"app-screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"configurable_clock.app.js","url":"app.js"},
|
||||
{"name":"configurable_clock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{ "id": "contourclock",
|
||||
"name": "Contour Clock",
|
||||
"shortName" : "Contour Clock",
|
||||
"version":"0.01",
|
||||
"icon": "app.png",
|
||||
"description": "A Minimalist clockface with large Digits. Looks best with the dark theme",
|
||||
"screenshots" : [{"url":"screenshot.png"}],
|
||||
"tags": "clock",
|
||||
"allow_emulator":true,
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"type": "clock",
|
||||
"storage": [
|
||||
{"name":"contourclock.app.js","url":"app.js"},
|
||||
{"name":"contourclock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"id": "coretemp",
|
||||
"name": "CoreTemp",
|
||||
"version": "0.03",
|
||||
"description": "Display CoreTemp device sensor data",
|
||||
"icon": "coretemp.png",
|
||||
"type": "app",
|
||||
"tags": "health",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"coretemp.wid.js","url":"widget.js"},
|
||||
{"name":"coretemp.app.js","url":"coretemp.js"},
|
||||
{"name":"coretemp.recorder.js","url":"recorder.js"},
|
||||
{"name":"coretemp.settings.js","url":"settings.js"},
|
||||
{"name":"coretemp.img","url":"coretemp-icon.js","evaluate":true},
|
||||
{"name":"coretemp.boot.js","url":"boot.js"}
|
||||
],
|
||||
"data": [{"name":"coretemp.json","url":"app-settings.json"}],
|
||||
"screenshots": [{"url":"screenshot.png"}]
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"id": "countdowntimer",
|
||||
"name": "Countdown Timer",
|
||||
"version": "0.01",
|
||||
"description": "A simple countdown timer with a focus on usability",
|
||||
"icon": "countdowntimer.png",
|
||||
"tags": "timer,tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"countdowntimer.app.js","url":"countdowntimer.js"},
|
||||
{"name":"countdowntimer.img","url":"countdowntimer-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "counter",
|
||||
"name": "Counter",
|
||||
"version": "0.03",
|
||||
"description": "Simple counter",
|
||||
"icon": "counter_icon.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
"screenshots": [{"url":"bangle1-counter-screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"counter.app.js","url":"counter.js"},
|
||||
{"name":"counter.img","url":"counter-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "cprassist",
|
||||
"name": "CPR Assist",
|
||||
"version": "0.02",
|
||||
"description": "Provides assistance while performing a CPR",
|
||||
"icon": "cprassist-icon.png",
|
||||
"tags": "tool,firstaid",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"bangle1-CPR-assist-screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"cprassist.app.js","url":"cprassist.js"},
|
||||
{"name":"cprassist.img","url":"cprassist-icon.js","evaluate":true},
|
||||
{"name":"cprassist.settings.js","url":"settings.js"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,11 @@
|
|||
# Crow Clock
|
||||
|
||||
Crow Clock features the face of Mystery Science Theater's Crow T. Robot.
|
||||
The code is based almost entirely on Bold Clock.
|
||||
|
||||
## Features
|
||||
|
||||
Its got Crow right there on the face! What more do you need?
|
||||
|
||||

|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkG/4AImcikUzBpIAHmURiIXBAYMjCx0hiU/AwfzA4wWIiYJHmMSCxUxgYLKERH/+UU8kvBY/zp1FBZEhp3uDA/yBQURFw8fl3tBoKJEmQWB9vk+MfFw3/n3SEwVCn/zkgGCkni/4wFFwP/mnjkQRB93UpoDBDoM96f/+JUEmKxB+nhl8ypvtolE73kofyj1PPYKSEWAXy8UimckFoXUiUzkUuC4IqBIwogB8gWBJAPd7pGCSAJECJAfxfAXzokSp3t7wvBAYPkkNNB4ZICRoIACokkFYIAE7viogPDkQCBMYkkonuolFqtVqgGCC4hgB+bEEmlNqEFolAAQfUoQPDLgPyVYku8sArx0BAQQGB6TTFOwYABnvVgFEqnkp1FokAhoXEIoPxeYn0CANEggXBoAGCoYQEibCDVAdFqgtBGIPlPII/EC46oBpqIB73tAYPURwioCVIQwFUQIACAoIuFC5AYBpwXD8idEC5fyd4tPC58093tCoIzBRooXCO43/n3u7wXBDQPiC58uI4vkBw0Ta4oABmq5BAAVFqQXITA0wgAAEgSdGj/yTI0lC4tUC44BBL41ACwcEO43xFoMTU47YC8ne8YNFRoTAG+jXMCgUxPAvzFwS/B9qFGIgXyMA00d4Xe6QLFRghgGGAfkFwxDEJAwkBqtVWY3/iQPEJA3zUwIhGUoQADiIXPkIeGGAkzmYXBkczDIZAHGAUyolE7vuqEA73tA4MjH44gCineaYYXBbQlBPo6SCrwQDFYIFD8qMEAA0hroqEAAXkqR8GRYshqotBAAdViQWLJQcVDIVVqJELGQ0ykUikYsJ"))
|
|
@ -0,0 +1,153 @@
|
|||
var img = require("heatshrink").decompress(atob("y2WwkG/4A/AFUzAA5H/mUiiIACiAEDkUjJvUzI4UAABMBJoJM2+cyI4qRDTIYLEiU/JORIFawczHwPzAgKiCCAkjSWExaIcSaBvzkKaDTFw0BJAaARmZMDMBxJhZIJ9T+b3DclRJDSSSYHJdRJESSayKDzIAMJLpLCmIgeABAoDET0yiBLkmRyjN0RwEgEjEsHyEoMBEr/zkMAgtFSsAlBqlVJYIleXIMFp3uJb5JC73kqDieXAVU9wABJb0hineEYPkgEAcTfzkCUBJIRLdSYNVJIQjCcTjeBgFdJQhLCmZJeSwcTN7LeBiUUJQvtJa5JBqlOEQvkiUQgMvSjMCmZKGJYcjJLnu6sjkJ5BSjMj+ZKHcYURJaJJCbooACoM/GAKWXSgQEBkq5CO41FqJLPmURqjcGEQRKBGIqUUMYchOoPkgpLG6hLBFwJJT91ArwDBoQyHSib5DmJGB8lRijFG8lViUzD5EzkMVoj7IJQVSGZCURgBhDJQXtiUhPo/kGgMjJgvzSQVNCo3UEAJwCibJISiL3E+RvD+cVGo/tolBiUimYABmSSCVQy1Cka7DJQY1CAwgANkCUEM4MdXQcxiorBHA9EqMRAAMVqgQJqLUBJQXlFwqWBn5JP+SUBCYnzihKEkRLJTIQABSI5JEJQQHBJQv/kMAj7fRVIxKCoM/mIBBJZQAL8lBifzFIMlEgaCHJJyoJEoVAicxOQMyitNJKXUoMSn5KBDYJKCHA0ggKeFABEySg6eBZYNAiMhDwQvBqjWG7oABJI1EqMiOIPziUhgoKBqTOPAA7yBLY8xbAPRkURBoZLCcgXUolVAAdEUYXkotRia7C+cRiUUJRLPBgTPGb5//+Ne93lkchNIZ9CJgNFIoQADJoRIBCAJiEgMzJQPkRZDhO+SlJ+RKCl/ygMjJQfzVgNURoQAE6lFqB+BmI1C+UQifzJRfxcJswb5BABjpKBj/yiURJYJKBcQRIGAAdESoS7BLwMRifyrpuCaJMCJRbfJDIJxCJQMTmRLB+cRitOJJQAB8lFiMvmUhkfyWgK5B6rVJkEAHhDfMAAK8D+YPBJYMiJJxLCqqtCn5KEoI+JmI9LBgJjJ/8lJQoUBitNH4nUogAEJYrjBIQPxl8xMYJKKcJkgb5IABmonBqMzJQLeBJIhICA4fdJgxLBJQUjkIiBqQwJHxZWMRoInBqERJQMxqg5DIAPebo4KBd4fUSwMxiMQp3tJRbUK+TsLBoKVCkMBkUVIYSSBcYoAF9qYEoMSkIBB73kGJg/JKpQYDrouBmcVqqCCSRSYHCAPkqtQkclAoJKLapTrBC5QYBihKBn4DBQgwANUwJcCosvJQR8LIAU/KiBKHmZKCbhhLJAYPlkckA4JKMaxDqKJQgoB6MhqhJVAAPkJYPliS3DGRZBIKZAAGXwPVgtOGQQAH7oADbASXGMQNFghKOa4MDBAswdI6uHp1FrvtJIjOBqoALogUEohmBqvuoQxMJQMBBAsQgQXMJQNEbwVO9xNBqtQgAANgpNBToRQBotUiQyNkMARonzgAXO+VVO4LFB8lFJAdURINVIokVBAIQEDYIZBSwUTPp0Al45EgAXO+UF7x8B9tFIAdUHAI5BIIcFbYYSEJYQLBoAyPgEfAwfxKIoXKb4Q2FgpJCJYoSBAAdALwiWC8pKO+cQCAkxgLnEJRVeFYPtRQZAGIIRTF93ldYgcBcIJKQgQGDkBKPmIpB73kGgpKFUIMEBArrEgFe9xMBJRxECAohQEJRx/EgpAFIII9CT44ACK4NN9xKPbQibBgZKXRYXkoiMETwYKDoJKGSqBKGCx/yr3kpxKHoEFIodVAgYKDdQIWENYIzQgEvAgcfCx8UJQLTBAAVUbQQECJQoKEJQldNIQ4CJRxcCJ4gAM+cU91EaYJ+EoCaEJQgKCgpKFM4NOoLOCGZpKDmMACx//ktOonuJRZXBJQoKBJQcF7wdBqIyP/5KWmNeYQQ0DaobbEAgZcBrxPDcwPtpvkG4QAOiECG4UBCyDhD9qWCgpBBorfDKwNUAoQKBBwIUE6lOosvGaEgJQUgJSLhCFwKFCQwSeBIgblDBQLXBUgSUCMwNRZCH/mBGCJwYAPmQvC93UJYJBDIQRUCqhSDKYSfCMoTfRJQReBJSbhCGAJLDbAVEqoAFLYJUBqEFCAKcCoLfR/8xJQPziBKS/8kIQXeGoJACotVqlNR4SlBA4JUCJ4QXCoLfRJQsDJSUlolNPoSSEJAbnEBQNUIoIRBbwNEqJKS+MAJQT4S/8hiMVGQQ/Dc4IAHBYNdJIVE9tBiMSJSkvJSvzmcxineGwVFTQZLJpwSB91FiUzmYxS+RKXJgUhZwPUaQJJKAANFqpJDSSRKdJYdEbxQAD9tVptFiJJVJThLCShziDSaxKGj4cWJYMVShoACqMjFi5KDgBKY+UUJJ/u6rCXJT0xSiDhBqRK1kveJSHuoM/JTUQWa/zb4/UogABprhHl5Kz+VeJI7oCJgLhGFrBKbmJBCHgZEF8gNF9tSJWcl7w8ERwwHBJYtBn5KyiqNLBAVNJTnxJTXziiUMBI/ll5KXn5KBgZKWjo5D9qUHT45KXmMBVwMggT8WrzfMAAThE8jEWJUPUJJLhFJS8wJQcBJSyPEHwjhHJT6ZBJS1U7xKPCAhKWbgcxgBKWitVqlN9qaEJQ9O6lFqtQJS0QJQiZBACfziEAAAJNBogAJI4QRBY4RKVMQUygEvfqxKCACJ8CPCkAJQXygEffqxKUgJKaZAL9VcAgASYaqQBC4QyBgYcWACp4VmJiEiD+VJV0Bn4FCkAFEJSNVJKlVJSpEFKApKRqlFJKUFohKU+baFcwpKRr3tS6MFp3kJS0DGYj+VJQPu8lQJKHu8sfFikACwnzJSvzinuJZ8FpoSB8rCUbI7nFJSfu6lUogAJotUCIVFJS0/A4kggIHFAB0lHAXuogEDAA3kbwIABoIrUIQKdNNJ45DJRneAgVSYKjYH+UAiZKUrxKPAYYqUIJBTBD6sUHQXUJRRWD8oqU+LXHTxC1OJQfkcoZKKoIzGABswgM/BI0hgAJHABklIwRKKBQftqIpURYIWHmKfHABsxrqKGJQ3eAYTfU+cACxHyiAhVcJrfZRQMjGZCgJABkhaQaWHKYfkqQnUkEBCxMxgJtU+SWK8hSDop9IXpiJBGZTsJSxtNbAZEDJIPeSjA9MK5gkLiqSDJYIACJIXuoKUUaYMAaZbtLEplUbgft7pIDbwMSEiiHBHhhYBcKvzkJLEAAlFiJuVb4LSM+ThWJYbjDTIRJBYxbfYcIUAOSpLCitUogACqsSkYgWb5rhZFQcAgtVqEAgKTWb4USGB8CSyywCAAZ6OABMhb5wwDOy5Kdb6CnSJU0xgETLsRKjGwMADCJeSJUUyZiaWYJTfzDgMjCyUhSyxKb+UAgQXT+SWWfIIADgSUqPwaWTmZKGmZnSmSsWSx3zmczkUiiIACiDgFBQYQBCgIiLPioADkMAiSKHmUikMRitVqtEAAVFJQkFBQYQBqMRikikZOHV4MCSiqWEfQaPBRoJGBHANN73uAAfVJQkEBYnt6hPCJwJNBIQfzV4IuDSzAjBmaPBIxBKPAAhOCiMSIgQtEAC5nCicyiNUog2JJSZNDopMBmSUbfocAgtNGhhKVAAPkqATBiZJaWgcFpxKk91AgEBbzIAD+S1BqneGZvlJQlUJJ1FJILebAAcyJYQ0N9tEqoABqirO6jfBiRJe//zcQVNZh4AQ8hJBgTedJYkgJYKCOJKbegAAfycQKXeJM5LFohJa6hJoAAMyJYVU7xJXohJCiZJmJYkAqtEACtFJIc/JVBLEJoQARI4IABboJJqJYzlBbZ9VJIhIrAAXzkJMEolNI5HUJAkAiSSsTA0Rco1UogACbYsAiLctTBBMGABEBiMimZIzAAczmUhJpBHBiUjJHCZEmczkQAFI4La0AGoA=="));
|
||||
|
||||
var hour_hand = {
|
||||
width : 61, height : 8, bpp : 1,
|
||||
transparent : 0,
|
||||
buffer : E.toArrayBuffer(atob("/////////////////////////////////////////////////////////////////////////////////w=="))
|
||||
};
|
||||
var minute_hand = {
|
||||
width : 110, height : 4, bpp : 1,
|
||||
transparent : 0,
|
||||
buffer : E.toArrayBuffer(atob("/////////////////////////////////////////////////////////////////////////w=="))
|
||||
};
|
||||
|
||||
//g.fillRect(0,24,239,239); // Apps area
|
||||
let intervalRef = null;
|
||||
const p180 = Math.PI/180;
|
||||
const clock_center = {x:Math.floor((g.getWidth()-1)/2), y:24+Math.floor((g.getHeight()-25)/2)};
|
||||
// ={ x: 119, y: 131 }
|
||||
const radius = Math.floor((g.getWidth()-24+1)/2); // =108
|
||||
|
||||
let tick0 = Graphics.createArrayBuffer(30,8,1,{msb:true});
|
||||
tick0.fillRect(0,0,tick0.getWidth()-1, tick0.getHeight()-1);
|
||||
let tick5 = Graphics.createArrayBuffer(20,6,1,{msb:true});
|
||||
tick5.fillRect(0,0,tick5.getWidth()-1, tick5.getHeight()-1);
|
||||
let tick1 = Graphics.createArrayBuffer(8,4,1,{msb:true});
|
||||
tick1.fillRect(0,0,tick1.getWidth()-1, tick1.getHeight()-1);
|
||||
|
||||
// Adjust hand lengths to be within 'tick' points
|
||||
minute_hand.width=radius-tick1.getWidth()-6;
|
||||
hour_hand.width=radius-tick5.getWidth()-6;
|
||||
|
||||
function big_wheel_x(angle){
|
||||
return clock_center.x + radius * Math.cos(angle*p180);
|
||||
}
|
||||
function big_wheel_y(angle){
|
||||
return clock_center.y + radius * Math.sin(angle*p180);
|
||||
}
|
||||
function rotate_around_x(center_x, angle, tick){
|
||||
return center_x + Math.cos(angle*p180) * tick.getWidth()/2;
|
||||
}
|
||||
function rotate_around_y(center_y, angle, tick){
|
||||
return center_y + Math.sin(angle*p180) * tick.getWidth()/2;
|
||||
}
|
||||
function hour_pos_x(angle){
|
||||
return clock_center.x + Math.cos(angle*p180) * hour_hand.width/2;
|
||||
}
|
||||
function hour_pos_y(angle){
|
||||
return clock_center.y + Math.sin(angle*p180) * hour_hand.width/2;
|
||||
}
|
||||
function minute_pos_x(angle){
|
||||
return clock_center.x + Math.cos(angle*p180) * minute_hand.width/2;
|
||||
}
|
||||
function minute_pos_y(angle){
|
||||
return clock_center.y + Math.sin(angle*p180) * minute_hand.width/2;
|
||||
}
|
||||
function minute_angle(date){
|
||||
//let minutes = date.getMinutes() + date.getSeconds()/60;
|
||||
let minutes = date.getMinutes();
|
||||
return 6*minutes - 90;
|
||||
}
|
||||
function hour_angle(date){
|
||||
let hours= date.getHours() + date.getMinutes()/60;
|
||||
return 30*hours - 90;
|
||||
}
|
||||
|
||||
function draw_clock(){
|
||||
//console.log("draw_clock");
|
||||
let date = new Date();
|
||||
g.reset();
|
||||
g.clearRect(0,24,239,239); // clear app area
|
||||
|
||||
g.drawImage(img, 12, 24);
|
||||
|
||||
// draw cross lines for testing
|
||||
// g.setColor(1,0,0);
|
||||
// g.drawLine(clock_center.x - radius, clock_center.y, clock_center.x + radius, clock_center.y);
|
||||
// g.drawLine(clock_center.x, clock_center.y - radius, clock_center.x, clock_center.y + radius);
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
let ticks = [0, 90, 180, 270];
|
||||
ticks.forEach((item)=>{
|
||||
let agl = item+180;
|
||||
g.drawImage(tick0.asImage(), rotate_around_x(big_wheel_x(item), agl, tick0), rotate_around_y(big_wheel_y(item), agl, tick0), {rotate:agl*p180});
|
||||
});
|
||||
ticks = [30, 60, 120, 150, 210, 240, 300, 330];
|
||||
ticks.forEach((item)=>{
|
||||
let agl = item+180;
|
||||
g.drawImage(tick5.asImage(), rotate_around_x(big_wheel_x(item), agl, tick5), rotate_around_y(big_wheel_y(item), agl, tick5), {rotate:agl*p180});
|
||||
});
|
||||
|
||||
let hour_agl = hour_angle(date);
|
||||
let minute_agl = minute_angle(date);
|
||||
g.drawImage(hour_hand, hour_pos_x(hour_agl), hour_pos_y(hour_agl), {rotate:hour_agl*p180}); //
|
||||
g.drawImage(minute_hand, minute_pos_x(minute_agl), minute_pos_y(minute_agl), {rotate:minute_agl*p180}); //
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillCircle(clock_center.x, clock_center.y, 6);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillCircle(clock_center.x, clock_center.y, 3);
|
||||
|
||||
// draw minute ticks. Takes long time to draw!
|
||||
g.setColor(g.theme.fg);
|
||||
for (var i=0; i<60; i++){
|
||||
let agl = i*6+180;
|
||||
g.drawImage(tick1.asImage(), rotate_around_x(big_wheel_x(i*6), agl, tick1), rotate_around_y(big_wheel_y(i*6), agl, tick1), {rotate:agl*p180});
|
||||
}
|
||||
|
||||
g.flip();
|
||||
//console.log(date);
|
||||
}
|
||||
function clearTimers(){
|
||||
//console.log("clearTimers");
|
||||
if(intervalRef) {
|
||||
clearInterval(intervalRef);
|
||||
intervalRef = null;
|
||||
//console.log("interval is cleared");
|
||||
}
|
||||
}
|
||||
function startTimers(){
|
||||
//console.log("startTimers");
|
||||
if(intervalRef) clearTimers();
|
||||
intervalRef = setInterval(draw_clock, 60*1000);
|
||||
//console.log("interval is set");
|
||||
draw_clock();
|
||||
}
|
||||
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
if (on) {
|
||||
//console.log("lcdPower: on");
|
||||
Bangle.drawWidgets();
|
||||
startTimers();
|
||||
} else {
|
||||
//console.log("lcdPower: off");
|
||||
clearTimers();
|
||||
}
|
||||
});
|
||||
Bangle.on('faceUp',function(up){
|
||||
//console.log("faceUp: " + up + " LCD: " + Bangle.isLCDOn());
|
||||
if (up && !Bangle.isLCDOn()) {
|
||||
//console.log("faceUp and LCD off");
|
||||
clearTimers();
|
||||
Bangle.setLCDPower(true);
|
||||
}
|
||||
});
|
||||
|
||||
g.clear();
|
||||
|
||||
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
startTimers();
|
||||
// Show launcher when button pressed
|
||||
Bangle.setUI("clock");
|
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "crowclk",
|
||||
"name": "Crow Clock",
|
||||
"version": "0.01",
|
||||
"description": "A simple clock based on Bold Clock that has MST3K's Crow T. Robot for a face",
|
||||
"icon": "crow_clock.png",
|
||||
"screenshots": [{"url":"screenshot_crow.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"crowclk.app.js","url":"crow_clock.js"},
|
||||
{"name":"crowclk.img","url":"crow_clock-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue