Merge branch 'espruino:master' into master
39
README.md
|
@ -49,25 +49,25 @@ easily distinguish between file types, we use the following:
|
|||
|
||||
## Adding your app to the menu
|
||||
|
||||
* Come up with a unique (all lowercase, no spaces) name, we'll assume `7chname`. Bangle.js
|
||||
* Come up with a unique (all lowercase, no spaces) name, we'll assume `myappid`. Bangle.js
|
||||
is limited to 28 char filenames and appends a file extension (eg `.js`) so please
|
||||
try and keep filenames short to avoid overflowing the buffer.
|
||||
* Create a folder called `apps/<id>`, lets assume `apps/7chname`
|
||||
* 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...
|
||||
* `apps/7chname/app.png` should be a 48px icon
|
||||
* Use http://www.espruino.com/Image+Converter to create `apps/7chname/app-icon.js`, using a 1 bit, 4 bit or 8 bit Web Palette "Image String"
|
||||
* `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:
|
||||
|
||||
```
|
||||
{ "id": "7chname",
|
||||
{ "id": "myappid",
|
||||
"name": "My app's human readable name",
|
||||
"shortName" : "Short Name",
|
||||
"icon": "app.png",
|
||||
"description": "A detailed description of my great app",
|
||||
"tags": "",
|
||||
"storage": [
|
||||
{"name":"7chname.app.js","url":"app.js"},
|
||||
{"name":"7chname.img","url":"app-icon.js","evaluate":true}
|
||||
{"name":"myappid.app.js","url":"app.js"},
|
||||
{"name":"myappid.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
},
|
||||
```
|
||||
|
@ -95,12 +95,12 @@ Be aware of the delay between commits and updates on github.io - it can take a f
|
|||
Using the 'Storage' icon in [the Web IDE](https://www.espruino.com/ide/)
|
||||
(4 discs), upload your files into the places described in your JSON:
|
||||
|
||||
* `app-icon.js` -> `7chname.img`
|
||||
* `app-icon.js` -> `myappid.img`
|
||||
|
||||
Now load `app.js` up in the editor, and click the down-arrow to the bottom
|
||||
right of the `Send to Espruino` icon. Click `Storage` and then either choose
|
||||
`7chname.app.js` (if you'd uploaded your app previously), or `New File`
|
||||
and then enter `7chname.app.js` as the name.
|
||||
`myappid.app.js` (if you'd uploaded your app previously), or `New File`
|
||||
and then enter `myappid.app.js` as the name.
|
||||
|
||||
Now, clicking the `Send to Espruino` icon will load the app directly into
|
||||
Espruino **and** will automatically run it.
|
||||
|
@ -115,10 +115,13 @@ and set it to `Load default application`.
|
|||
## Example Applications
|
||||
|
||||
To make the process easier we've come up with some example applications that you can use as a base
|
||||
when creating your own. Just come up with a unique 7 character name, copy `apps/_example_app`
|
||||
or `apps/_example_widget` to `apps/7chname`, and add `apps/_example_X/add_to_apps.json` to
|
||||
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`.
|
||||
|
||||
**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.
|
||||
|
||||
**If you're making a widget** please start the name with `wid` to make
|
||||
it easy to find!
|
||||
|
||||
|
@ -192,8 +195,8 @@ and which gives information about the app for the Launcher.
|
|||
```
|
||||
{
|
||||
"name":"Short Name", // for Bangle.js menu
|
||||
"icon":"*7chname", // for Bangle.js menu
|
||||
"src":"-7chname", // source file
|
||||
"icon":"*myappid", // for Bangle.js menu
|
||||
"src":"-myappid", // source file
|
||||
"type":"widget/clock/app/bootloader", // optional, default "app"
|
||||
// if this is 'widget' then it's not displayed in the menu
|
||||
// if it's 'clock' then it'll be loaded by default at boot time
|
||||
|
@ -313,10 +316,10 @@ version of what's in `apps.json`:
|
|||
<script>
|
||||
document.getElementById("upload").addEventListener("click", function() {
|
||||
sendCustomizedApp({
|
||||
id : "7chname",
|
||||
id : "myappid",
|
||||
storage:[
|
||||
{name:"7chname.app.js", url:"app.js", content:app_source_code},
|
||||
{name:"7chname.img", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true},
|
||||
{name:"myappid.app.js", url:"app.js", content:app_source_code},
|
||||
{name:"myappid.img", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true},
|
||||
]
|
||||
});
|
||||
});
|
||||
|
@ -442,7 +445,7 @@ from the IDE.
|
|||
|
||||
### Misc Notes
|
||||
|
||||
- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `7chname.json`, then load it at startup.
|
||||
- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `myappid.json`, then load it at startup.
|
||||
|
||||
- 'Alarm' apps define a file called `alarm.js` which handles the actual alarm window.
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
theme: jekyll-theme-minimal
|
|
@ -0,0 +1 @@
|
|||
2021/11/18 | 1.0: Release for Bangle 2
|
|
@ -0,0 +1,14 @@
|
|||
# A Battery Widget (with percentage)
|
||||
|
||||
Show the current battery level and charging status in the top right of the clock, with charge percentage
|
||||
|
||||
* Works with Bangle 2
|
||||
* Simple design, no settings
|
||||
* Red when the batterly level is below 30%
|
||||
* Blue when charging
|
||||
* 40 pixels wide
|
||||
|
||||

|
||||
|
||||
## Creator
|
||||
[@alainsaas](https://github.com/alainsaas)
|
After Width: | Height: | Size: 65 KiB |
|
@ -0,0 +1,45 @@
|
|||
(function(){
|
||||
let COLORS = {
|
||||
'white': "#fff",
|
||||
'black': "#000",
|
||||
'charging': "#08f",
|
||||
'high': "#000",
|
||||
'low': "#f00",
|
||||
};
|
||||
|
||||
const levelColor = (l) => {
|
||||
if (Bangle.isCharging()) return COLORS.charging;
|
||||
if (l >= 30) return COLORS.high;
|
||||
return COLORS.low;
|
||||
};
|
||||
|
||||
function draw() {
|
||||
var s = 29;
|
||||
var x = this.x, y = this.y;
|
||||
|
||||
const l = E.getBattery();
|
||||
|
||||
let xl = x+4+l*(s-12)/100;
|
||||
|
||||
g.setColor(COLORS.white);
|
||||
g.fillRect(x+2,y+5,x+s-6,y+18);
|
||||
|
||||
g.setColor(levelColor(l));
|
||||
g.fillRect(x+1,y+3,x+s-5,y+4);
|
||||
g.fillRect(x+1,y+19,x+s-5,y+20);
|
||||
g.fillRect(x,y+4,x+1,y+19);
|
||||
g.fillRect(x+s-5,y+4,x+s-4,y+19);
|
||||
g.fillRect(x+s-3,y+8,x+s-2,y+16); // tip of the battery
|
||||
g.fillRect(x+4,y+15,xl,y+16); // charging bar
|
||||
|
||||
g.setColor(COLORS.black);
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont('6x8');
|
||||
g.drawString(l, x + 14, y + 10);
|
||||
}
|
||||
|
||||
Bangle.on('charging',function(charging) { draw(); });
|
||||
setInterval(()=>WIDGETS["a_battery_widget"].draw(), 60000);
|
||||
|
||||
WIDGETS["a_battery_widget"]={area:"tr",width:30,draw:draw};
|
||||
})();
|
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 6.3 KiB |
|
@ -0,0 +1,2 @@
|
|||
0.01: start of development
|
||||
0.02: first running version for BangleJs2
|
|
@ -0,0 +1,10 @@
|
|||
# TheBinWatch
|
||||
|
||||
Binary watch to train Your brain
|
||||
Inspired by the 80's LCD wrist watch from RALtec
|
||||
## Usage
|
||||
|
||||
- swipe to left or right to change displayed text (date, time, ...)
|
||||
- currently only available for BangeJs2
|
||||
- Widgets will not be shown
|
||||
- If bluetooth connection is not established an icon will show up
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("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"))
|
|
@ -0,0 +1,381 @@
|
|||
/***************************************************
|
||||
* BINARY WATCH
|
||||
* for Bangle 1 / 2
|
||||
* inspired by RAL tec binary wrist watch
|
||||
*
|
||||
* TODO:
|
||||
* - vibrate on full hour
|
||||
* -
|
||||
****************************************************/
|
||||
|
||||
/* reuqirements */
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
require("Font5x7Numeric7Seg").add(Graphics);
|
||||
|
||||
|
||||
/* constants and definitions */
|
||||
|
||||
/* Bangle 2: 176 x 176 */
|
||||
|
||||
/* month images */
|
||||
|
||||
var month = [
|
||||
/* JAN */ {width : 53, height : 24, bpp : 1, buffer : require("heatshrink").decompress(atob("AAMf/0D8AFBkM/9EvwMAgcM/3B30YgE4uEOh354EB4eAuFz90Ah0cgeDx9wgFw8Ecjk7wEDw8A8AIBgEcnEHg4IBgFh4EYnEDHYMF/8AwBID/BODgN/4EgAoI0BgODwExGgkDzg0FAII0D88A8PAnAIBAIMOgPBBAPAiBpCgPAQIOAmFwg0P/B5BwcAiE/JYYAHA"))},
|
||||
/* FEB */ {width : 51, height : 24, bpp : 1, buffer : require("heatshrink").decompress(atob("v/8n/+g/+if/hP/wM/8c/4Of8Ez/FwgE4gEHgFOAYUA8Ed4ADBgEcge4AYMAgeAu4DCgFwhwQBEIMOgPcAYMAgPAjN/4G/8EX/kf/EP/kB/+F/8C/+Ar/xGQkBGTE7wADBMIMHMotMgEGv+A7/hEYOf/EH/hvBh6FBIIKYFA"))},
|
||||
/* MAR */ {width : 52, height : 23, bpp : 1, buffer : require("heatshrink").decompress(atob("v/4j/+gf/hP/gV/6FP/HP8kz/cGv8OgHDwFwuEE8McnEHg8A905BgcO8ecBiM4BgMwuEGoeEi/8gX/wE4gH/4Ef/AMFx0QDIcA8BADnEOgIzCufABgk+Bglx+AMEh+OBgdwvnghk4gcGgfsgFDgEQoEeSgvg"))},
|
||||
/* APR */ {width : 52, height : 23, bpp : 1, buffer : require("heatshrink").decompress(atob("v/4j/4gf/hP/oV/4FP/HP9kz/EGv8OgPDwEguEE8EcnEHg8A9wMCuFwhwMTgAMBmFwg1f+EX/kC/+D/8A//AJIIMFxwZCgFwgAmCgEHnBNDgFz4AMEnwMEuPwBgkPxwMDuF88EMBgMGgfsgFDRgNAjyUF8A="))},
|
||||
/* MAY */ {width : 52, height : 24, bpp : 1, buffer : require("heatshrink").decompress(atob("v/4j/+AoMJ/8Cv/QAwPP8kz/cEgEugHDwFwsEG8McnEHg8A905BgVwh3jzgMRnAMBmAMBoeEi/8BgNgnEA//Ah/4BgcB/+OiAZCBgPgIARTB90BGYUAhwMahk4gYMBpkAocAiEP+CSDIAOAAwYMB"))},
|
||||
/* JUN */ {width : 53, height : 24, bpp : 1, buffer : require("heatshrink").decompress(atob("AAcD8ADBkMAhEvwIJBhkA4O+jEAnFwh0O/PAgPDwFwufugEOjkDwePuEAuHgjkcneAgeHgHgBAMAjk4g8HBAMAsPAjE4gY7BggCBwBPLkACBGgMBweAmI0EgecGgoBBGgfngHh4E4BAIBBh0B4IIB4EQmEEBAPA/0An5qBg0P/ED/xNBiAKBh6PCAAw="))},
|
||||
/* JUL */ {width : 53, height : 24, bpp : 1, buffer : require("heatshrink").decompress(atob("AA8hgEImAFBgcMgHB4AGBnFwh0OAoMB4eAuFwAwMOjkDweAAwNw8EcjggCw8A8HgAwMcnEHg40CsPAjE4AwUEAQIgCABMgGgcBGgMBGgo/BGggKBGgYBB8PAnA0BBQMOgJpC4EQmEENIX+gE/wFn/EP/ED/0Cv/gBQMP8EP/5QGA"))},
|
||||
/* AUG */ {width : 52, height : 24, bpp : 1, buffer : require("heatshrink").decompress(atob("n/8AocE/+gAgMP/1n+0QgGA//HgHhwEYl/wuEOjkDw8Ag4MB4E4uEABilhBgcv/EcgOCgEB/+AwBBB/AMBAgMCj/ngFgAwNw/wmCgImBBgIzDhwzFBikGhkBgUAs0AkEf4EH+A3Bgf+gBLBAwIMD"))},
|
||||
/* SEP */ {width : 51, height : 23, bpp : 1, buffer : require("heatshrink").decompress(atob("h/4j//g/+gf/wn/4M/8ABB5/wmf4mEAjkAg8Ap0AgeAgHgjvAgFwBwMD3EAhwOCu8AgIOCh3ggE4BwMB7gjCBwMYv/Ar/wi/8j/8IYMB/+BIYIODDwIyCLIMHGQYGB8JBDB4IyCAoMDw5BDB4JBDgEEMoZ6Cn/A8A6B8FP/kYgEf/EH/4eCA"))},
|
||||
/* OCT */ {width : 50, height : 23, bpp : 1, buffer : require("heatshrink").decompress(atob("h/gg/+j//w/8gf/h//+H+gF/wP//OAkHADAXgjlwAoU4g8cAgMYh0B44pCgeAuIYBgfADAnwnEDDAUcghCDgRMIsACBkAYFGKZKDngYFgJjBwAYCPgX4DAMHPgQYBgB8C8EGgAA="))},
|
||||
/* NOV */ {width : 51, height : 24, bpp : 1, buffer : require("heatshrink").decompress(atob("vkAgf4AoMX4GA/+ABIN8mEP8EggP350MgMGgF+vvDwFw8Ef4+4uEOjgiBu8OgIOBv8A8PAnFwEQMcnEHBwP8gOHgFh4EdHYNAgEQgJLFggFEhPAjFwg0cg4jDGQPnGQk8GQkPI4IyB8PDKwYOB+BWBMoMHnkOgHAn+A98BwEIh/4jnAHgX+gaGBAAcggAA=="))},
|
||||
/* DEC */ {width : 49, height : 23, bpp : 1, buffer : require("heatshrink").decompress(atob("v/gj//gP/5/4iYFC2f4hn/CAOcgMHgEBwEOgPDwEB4AJB8PAgHggeAuHggFwBoM4uEAnANBjgDBjgNBgwDBh0AiEAgowBAAQ6BwEAggFBv/BwAwBsIwWhwwDnEHAYIiBjhhDgEN/0Dn/Aj/hO4M/+Ef/JABv/8g/+A=="))},
|
||||
/* MAI */ {width : 44, height : 23, bpp : 1, buffer : require("heatshrink").decompress(atob("v/4j/+gEJ/8Cv/QgnP8kz/cA50A4eAuEc8McnEHgPOnIKD8ecBR04BQMwhlDwkX/kAoE4gH/4EABQlOiAVD8A2EgIrDBS0MnEDgHMGQMAiEEPwo="))},
|
||||
/* OKT */ {width : 51, height : 24, bpp : 1, buffer : require("heatshrink").decompress(atob("g/wAQMP//B/8DgPh//8j/AuF8n//jECh0fDAUA8PH4AGB8EcnIhBsEcgeHvkAj0DwFw98AgYjBh0dDAN4h0A4eAEgQDBl/4gFAE4MD/5OE3/ggIyBhk4gcAuAyCBIIyDIIIyDAgOAGQMBGQNwh8B4E4BwMB8BlCBIM8gF/AgMYg+Aj/wmA3B+EB/hBChiYGA"))},
|
||||
/* DEZ */ {width : 51, height : 23, bpp : 1, buffer : require("heatshrink").decompress(atob("n/wh//w//xP/gV/8F//Of4Fn/EH/04gUODAUHgHh4AFBnHgjk4BYUcgeHAoMB8eAuHgAwN4uEOjgFBh4jB4eAgED4ADBl/4gFwB4MD/4DBgQCB3/gC4PghgyBgPAGQl4gYyDjwgBGQQrBh0BGQVwDQM4F4MMLIJlEg3/gOfPAPgn/gk/+j/+h/8IoPh//gA="))}
|
||||
];
|
||||
|
||||
var imgSquid = {width : 88, height : 26, bpp : 1, buffer : require("heatshrink").decompress(atob("gE/AYUYgEH////0B//gBQM8BQgDB/AKHh/A/gKBvwKBAgMOj8AnwKHBAIMBgH/BQgmCAoPnBQl4AoOAgPnwAKDuEAgYKB4YKIgfD4AKDMAMB4EDwIKIg+B8AKIgAKIh8A+AKHh0AuAKHj0AvBMG4EcgE4K458Bnh4HnEAjiOHBwMeBQpKBEgMOXQ/wBwIKDaAZQBg4KDcwT0BAAOHfgoKHgE/wDaBAAL8DA="))};
|
||||
|
||||
var imgNoBT = {width : 20, height : 20, bpp : 3, transparent : 0, buffer : require("heatshrink").decompress(atob("///8mSpM/AoP/yUT/8yuYGB5AMB/1MyYUBkmT/P85MP+USBwOT8mQ/8JBwXyoVnyGSv8//Mhk14pMn//8BYNMwmSp/+pFJkgyBDoMkkgODpOSuQOE5M/KgIOCsmfz/JknPhMyof5n+Ss/wzMhn4OBk1+smQLoWTn/mHAM/+VJz4KBwhZBEYJ/CkM8yZVBAAQxBCgP/A="))};
|
||||
|
||||
const V2_X_STEP = 26;
|
||||
const V2_Y_STEP = 34;
|
||||
|
||||
const V2_TIME_Y_OFFSET = 8;
|
||||
const V2_HX = 36;
|
||||
const V2_HY = 0 + V2_TIME_Y_OFFSET;
|
||||
const V2_MX = 10;
|
||||
const V2_MY = 51 + V2_TIME_Y_OFFSET;
|
||||
const V2_SX = 10;
|
||||
const V2_SY = 95 + V2_TIME_Y_OFFSET;
|
||||
const V2_BT_X = 137; /* 145, 35 */
|
||||
const V2_BT_Y = 20;
|
||||
const V2_DX = 100;
|
||||
const V2_DY = 141;
|
||||
|
||||
const V2_BAT_POS_X = 21;
|
||||
const V2_BAT_POS_Y = 40;
|
||||
const V2_BAT_SIZE_X = 13;
|
||||
const V2_BAT_SIZE_Y = 2;
|
||||
|
||||
const V2_SCREEN_SIZE_X = 176;
|
||||
const V2_SCREEN_SIZE_Y = 176;
|
||||
const V2_BACKGROUND_IMAGE = "Background176_center.png";
|
||||
const V2_BG_COLOR = 0;
|
||||
const V2_FG_COLOR = 1;
|
||||
|
||||
/* Bangle 1: 240 x 240 */
|
||||
|
||||
const V1_X_STEP = 35;
|
||||
const V1_Y_STEP = 46;
|
||||
|
||||
const V1_TIME_Y_OFFSET = 41;
|
||||
const V1_HX = 48;
|
||||
const V1_HY = 0 + V1_TIME_Y_OFFSET;
|
||||
const V1_MX = 14;
|
||||
const V1_MY = 55 + V1_TIME_Y_OFFSET;
|
||||
const V1_SX = 14;
|
||||
const V1_SY = 110 + V1_TIME_Y_OFFSET;
|
||||
const V1_BT_X = 41;
|
||||
const V1_BT_Y = 14;
|
||||
//var BT_X = 20, BT_Y = 14;
|
||||
const V1_DX = 160;
|
||||
const V1_DY = 205;
|
||||
|
||||
const V1_BAT_POS_X = 175;
|
||||
const V1_BAT_POS_Y = 21;
|
||||
const V1_BAT_SIZE_X = 3;
|
||||
const V1_BAT_SIZE_Y = 5;
|
||||
const V1_SCREEN_SIZE_X = 240;
|
||||
const V1_SCREEN_SIZE_Y = 240;
|
||||
const V1_BACKGROUND_IMAGE = "Background240_center.png";
|
||||
const V1_BG_COLOR = 1;
|
||||
const V1_FG_COLOR = 0;
|
||||
|
||||
/* runtime settings */
|
||||
|
||||
var x_step = 0;
|
||||
var y_step = 0;
|
||||
|
||||
var time_y_offset = 0;
|
||||
var hx = 0, hy = 0;
|
||||
var mx = 0, my = 0;
|
||||
var sx = 0, sy = 0;
|
||||
var bt_x = 0, bt_y = 0;
|
||||
var dx = 0, dy = 0;
|
||||
|
||||
var bat_pos_x, bat_pos_y, bat_size_x, bat_size_y;
|
||||
var backgroundImage = "";
|
||||
var screen_size_x = 0;
|
||||
var screen_size_y = 0;
|
||||
var bg_color = 0;
|
||||
var fg_color = 1;
|
||||
|
||||
/* global variables */
|
||||
|
||||
var showDateTime = 2; /* show noting, time or date */
|
||||
var cg;
|
||||
var cgimg;
|
||||
|
||||
/* local functions */
|
||||
|
||||
/**
|
||||
* function drawSquare(...)
|
||||
*
|
||||
* go through all bits and draw a square if a bit
|
||||
* is set. So we get the binary representation
|
||||
* of the value
|
||||
* used to draw block for hours, mintutes, seconds, date
|
||||
*
|
||||
* @param gfx: graphic object to use
|
||||
* @param x: x-coordinate of 1st the square
|
||||
* @param y: y-coordinate of 1st the square
|
||||
* @param data: data conatining the bit information
|
||||
* @param numOfBits: number of bits to draw
|
||||
*/
|
||||
function drawSquare(gfx, x, y, data, numOfBits) {
|
||||
|
||||
for(i = numOfBits; i > 0 ; i--) {
|
||||
if( (data & 1) != 0) {
|
||||
gfx.fillRect(x + (i - 1) * x_step, y,
|
||||
x + i * x_step , y + y_step);
|
||||
}
|
||||
data >>= 1; /* shift one bit right */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function drawBinary(...)
|
||||
* draw the time in binary format
|
||||
* default display for geeks and real men
|
||||
|
||||
* @param h: hours
|
||||
* @param m: minutes
|
||||
* @param s: seconds
|
||||
*/
|
||||
function drawBinary(gfx, hour, minute, second) {
|
||||
gfx.clear(0);
|
||||
|
||||
if(hour > 12) {
|
||||
hour -= 12; /* we use for bit for hours so we only display 12 hours*/
|
||||
}
|
||||
drawSquare(gfx, hx, hy, hour, 4); /* set hour */
|
||||
drawSquare(gfx, mx, my, minute, 6); /* set minute */
|
||||
drawSquare(gfx, sx, sy, second, 6); /* set second */
|
||||
}
|
||||
|
||||
/**
|
||||
* function drawTime(...)
|
||||
* show time under the graphic
|
||||
* for wimps and commies
|
||||
*
|
||||
* @param h: hours
|
||||
* @param m: minutes
|
||||
* @param s: seconds
|
||||
*/
|
||||
|
||||
function drawTime(gfx, h, m, s) {
|
||||
var time = (" "+h).substr(-2) + ":" + ("0"+m).substr(-2)+ ":" + ("0"+s).substr(-2);
|
||||
|
||||
gfx.setFontAlign(0,-1); // align right bottom
|
||||
gfx.setFont("7x11Numeric7Seg", 2);
|
||||
gfx.drawString(time, gfx.getWidth() / 2, dy + 1, false /*clear background*/);
|
||||
}
|
||||
|
||||
/**
|
||||
* function drawDate(...)
|
||||
* show date under the graphic
|
||||
* (optionally)
|
||||
*
|
||||
* @param gfx: graphic object to use
|
||||
* @param d: date object
|
||||
*/
|
||||
var vMonth = 0;
|
||||
function drawDate(gfx, d) {
|
||||
var dateString = ""
|
||||
+ ("0" + d.getDate()).substr(-2)
|
||||
// + " "
|
||||
// + ("0" + d.getMonth()).substr(-2)
|
||||
// + " "
|
||||
// + ("0" + d.getFullYear()).substr(-2)
|
||||
;
|
||||
|
||||
gfx.setFontAlign(-1,-1); // align right bottom
|
||||
gfx.setFont("7x11Numeric7Seg",2); /* draw the current time font */
|
||||
gfx.drawString(dateString, dx, dy + 1, false /* don't clear background*/);
|
||||
gfx.drawImage(month[d.getMonth()], 40, dy);
|
||||
}
|
||||
|
||||
function toggleDateTime() {
|
||||
showDateTime++;
|
||||
if(showDateTime > 2){
|
||||
showDateTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function updateVTime() {
|
||||
vMonth++;
|
||||
if(vMonth >= 12 + 3) {
|
||||
vMonth = 0;
|
||||
}
|
||||
second++;
|
||||
if(second > 59) {
|
||||
second = 0;
|
||||
minute++;
|
||||
if(minute > 59) {
|
||||
minute = 0;
|
||||
hour++;
|
||||
if(hour > 12) {
|
||||
hour = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function drawBattery(...)
|
||||
* fill the battery symbol with blocks
|
||||
* according to the battery level
|
||||
*
|
||||
* @param gfx: graphic object
|
||||
* @param level: current battery level
|
||||
*/
|
||||
function drawBattery(gfx, level) {
|
||||
var pos_y = bat_pos_y - 1;
|
||||
var stepLevel = Math.round((level + 10) / 20);
|
||||
|
||||
for(i = 0; i < stepLevel; i++) {
|
||||
pos_y -= bat_size_y + 2;
|
||||
gfx.fillRect(bat_pos_x, pos_y,
|
||||
bat_pos_x + bat_size_x, pos_y + bat_size_y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function drawBattery(...)
|
||||
* fill the battery symbol with blocks
|
||||
* according to the battery level
|
||||
*
|
||||
* @param gfx: graphic object
|
||||
* @param level: current battery level
|
||||
*/
|
||||
function drawBT(gfx, status) {
|
||||
if(!status) {
|
||||
gfx.drawImage(imgNoBT, bt_x, bt_y);
|
||||
}
|
||||
}
|
||||
function setRuntimeValues(resolution) {
|
||||
if(240 == resolution) {
|
||||
x_step = V1_X_STEP;
|
||||
y_step = V1_Y_STEP;
|
||||
|
||||
time_y_offset = V1_TIME_Y_OFFSET;
|
||||
hx = V1_HX;
|
||||
hy = V1_HY;
|
||||
mx = V1_MX;
|
||||
my = V1_MY;
|
||||
sx = V1_SX;
|
||||
sy = V1_SY;
|
||||
bt_x = V1_BT_X;
|
||||
bt_y = V1_BT_Y;
|
||||
dx = V1_DX;
|
||||
dy = V1_DY;
|
||||
|
||||
screen_size_x = V1_SCREEN_SIZE_X;
|
||||
screen_size_y = V1_SCREEN_SIZE_Y;
|
||||
backgroundImage = V1_BACKGROUND_IMAGE;
|
||||
|
||||
bat_pos_x = V1_BAT_POS_X;
|
||||
bat_pos_y = V1_BAT_POS_Y;
|
||||
bat_size_x = V1_BAT_SIZE_X;
|
||||
bat_size_y = V1_BAT_SIZE_Y;
|
||||
|
||||
setWatch(toggleDateTime, BTN1, { repeat : true, edge: "falling"});
|
||||
|
||||
} else {
|
||||
x_step = V2_X_STEP;
|
||||
y_step = V2_Y_STEP;
|
||||
|
||||
time_y_offset = V2_TIME_Y_OFFSET;
|
||||
|
||||
hx = V2_HX;
|
||||
hy = V2_HY;
|
||||
mx = V2_MX;
|
||||
my = V2_MY;
|
||||
sx = V2_SX;
|
||||
sy = V2_SY;
|
||||
|
||||
bt_x = V2_BT_X;
|
||||
bt_y = V2_BT_Y;
|
||||
|
||||
dx = V2_DX;
|
||||
dy = V2_DY;
|
||||
|
||||
screen_size_x = V2_SCREEN_SIZE_X;
|
||||
screen_size_y = V2_SCREEN_SIZE_Y;
|
||||
backgroundImage = V2_BACKGROUND_IMAGE;
|
||||
|
||||
bat_pos_x = V2_BAT_POS_X;
|
||||
bat_pos_y = V2_BAT_POS_Y;
|
||||
bat_size_x = V2_BAT_SIZE_X;
|
||||
bat_size_y = V2_BAT_SIZE_Y;
|
||||
|
||||
Bangle.on('swipe', function(direction) { toggleDateTime(direction);});
|
||||
}
|
||||
cg = Graphics.createArrayBuffer(
|
||||
screen_size_x,screen_size_y, 1, {msb:true});
|
||||
|
||||
cgimg = {width:screen_size_x, height:screen_size_y, bpp:1,
|
||||
transparent:0, buffer:cg.buffer};
|
||||
|
||||
}
|
||||
var hour = 0, minute = 1, second = 50;
|
||||
var batVLevel = 20;
|
||||
|
||||
|
||||
function draw() {
|
||||
var d = new Date();
|
||||
var h = d.getHours(), m = d.getMinutes(), s = d.getSeconds();
|
||||
g.reset();
|
||||
|
||||
drawBinary(cg, h, m, s);
|
||||
|
||||
switch(showDateTime) {
|
||||
case 1:
|
||||
drawTime(cg, h, m, s);
|
||||
break;
|
||||
case 2:
|
||||
drawDate(cg, d);
|
||||
break;
|
||||
default:
|
||||
cg.drawImage(imgSquid, cg.getWidth() / 2 - 44, dy);
|
||||
}
|
||||
drawBattery(cg, /*batVLevel*/ E.getBattery());
|
||||
|
||||
batVLevel += 2;
|
||||
if(batVLevel > 100) {
|
||||
batVLevel = 0;
|
||||
}
|
||||
updateVTime();
|
||||
g.clear();
|
||||
g.drawImages([{image:cgimg},
|
||||
{image:require("Storage").read(backgroundImage)},
|
||||
// { x:bt_x, y:bt_y, rotate: 0, image:require("Storage").read("bt-icon.png")},
|
||||
]);
|
||||
drawBT(g, NRF.getSecurityStatus().connected);
|
||||
// Bangle.drawWidgets();
|
||||
const millis = d.getMilliseconds();
|
||||
setTimeout(draw, 1000-millis);
|
||||
// Bangle.loadWidgets();
|
||||
}
|
||||
|
||||
// Show launcher when button pressed
|
||||
Bangle.setUI("clock");
|
||||
setRuntimeValues(g.getWidth());
|
||||
g.reset().clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
draw();
|
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 708 B |
|
@ -103,7 +103,7 @@ if (Bangle.swipeHandler) {
|
|||
Bangle.removeListener("swipe", Bangle.swipeHandler);
|
||||
delete Bangle.swipeHandler;
|
||||
}
|
||||
if (Bangle.touchandler) {
|
||||
if (Bangle.touchHandler) {
|
||||
Bangle.removeListener("touch", Bangle.touchHandler);
|
||||
delete Bangle.touchHandler;
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AH4A8gAAKC8gKUC7Rf/C/PM5gDBjnBC6EcC4PBDIIbCC5/BAIIXVA4YXXAoRHUC6R3EC6KnEMAbv/C6oAKC8YA/AH4A/AH4Ax"))
|
|
@ -0,0 +1,216 @@
|
|||
var fontsize = g.getWidth()>200 ? 3 : 2;
|
||||
var fontsizeTime = g.getWidth()>200 ? 4 : 4;
|
||||
|
||||
var fontheight = 10*fontsize;
|
||||
var fontheightTime = 10*fontsizeTime;
|
||||
var locale = require("locale");
|
||||
var marginTop = 40;
|
||||
var flag = false;
|
||||
|
||||
var hrtOn = false;
|
||||
var hrtStr = "Hrt: ??? bpm";
|
||||
|
||||
const NONE_MODE = "none";
|
||||
const ID_MODE = "id";
|
||||
const VER_MODE = "ver";
|
||||
const BATT_MODE = "batt";
|
||||
const MEM_MODE = "mem";
|
||||
const STEPS_MODE = "step";
|
||||
const HRT_MODE = "hrt";
|
||||
const NONE_FN_MODE = "no_fn";
|
||||
const HRT_FN_MODE = "fn_hrt";
|
||||
|
||||
let infoMode = NONE_MODE;
|
||||
let functionMode = NONE_FN_MODE;
|
||||
|
||||
let textCol = g.theme.dark ? "#0f0" : "#080";
|
||||
|
||||
function drawAll(){
|
||||
updateTime();
|
||||
updateRest(new Date());
|
||||
}
|
||||
|
||||
function updateRest(now){
|
||||
writeLine(locale.dow(now),1);
|
||||
writeLine(locale.date(now,1),2);
|
||||
drawInfo(5);
|
||||
}
|
||||
function updateTime(){
|
||||
if (!Bangle.isLCDOn()) return;
|
||||
let now = new Date();
|
||||
writeLine(locale.time(now,1),0);
|
||||
writeLine(flag?" ":"_",3);
|
||||
flag = !flag;
|
||||
if(now.getMinutes() == 0)
|
||||
updateRest(now);
|
||||
}
|
||||
function writeLineStart(line){
|
||||
if (line==0){
|
||||
g.drawString(">",0,marginTop+(line)*fontheight);
|
||||
} else {
|
||||
g.drawString(">",4,marginTop+(line-1)*fontheight + fontheightTime);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function writeLine(str,line){
|
||||
if (line == 0){
|
||||
var y = marginTop+line*fontheightTime;
|
||||
g.setFont("6x8",fontsizeTime);
|
||||
g.setColor(textCol).setFontAlign(-1,-1);
|
||||
g.clearRect(0,y,((str.length+1)*40),y+fontheightTime-1);
|
||||
writeLineStart(line);
|
||||
g.drawString(str,25,y);
|
||||
} else {
|
||||
var y = marginTop+(line-1)*fontheight+fontheightTime;
|
||||
g.setFont("6x8",fontsize);
|
||||
g.setColor(textCol).setFontAlign(-1,-1);
|
||||
g.clearRect(0,y,((str.length+1)*20),y+fontheight-1);
|
||||
writeLineStart(line);
|
||||
g.drawString(str,25,y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function drawInfo(line) {
|
||||
let val;
|
||||
let str = "";
|
||||
let col = textCol; // green
|
||||
|
||||
//console.log("drawInfo(), infoMode=" + infoMode + " funcMode=" + functionMode);
|
||||
|
||||
switch(functionMode) {
|
||||
case NONE_FN_MODE:
|
||||
break;
|
||||
case HRT_FN_MODE:
|
||||
col = g.theme.dark ? "#0ff": "#088"; // cyan
|
||||
str = "HRM: " + (hrtOn ? "ON" : "OFF");
|
||||
drawModeLine(line,str,col);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(infoMode) {
|
||||
case NONE_MODE:
|
||||
col = g.theme.bg;
|
||||
str = "";
|
||||
break;
|
||||
case HRT_MODE:
|
||||
str = hrtStr;
|
||||
break;
|
||||
case STEPS_MODE:
|
||||
str = "Steps: " + stepsWidget().getSteps();
|
||||
break;
|
||||
case ID_MODE:
|
||||
val = NRF.getAddress().split(":");
|
||||
str = "Id: " + val[4] + val[5];
|
||||
break;
|
||||
case VER_MODE:
|
||||
str = "Fw: " + process.env.VERSION;
|
||||
break;
|
||||
case MEM_MODE:
|
||||
val = process.memory();
|
||||
str = "Memory: " + Math.round(val.usage*100/val.total) + "%";
|
||||
break;
|
||||
case BATT_MODE:
|
||||
default:
|
||||
str = "Battery: " + E.getBattery() + "%";
|
||||
}
|
||||
|
||||
drawModeLine(line,str,col);
|
||||
}
|
||||
|
||||
function drawModeLine(line, str, col) {
|
||||
g.setColor(col);
|
||||
var y = marginTop+line*fontheight;
|
||||
g.fillRect(0, y, 239, y+fontheight-1);
|
||||
g.setColor(g.theme.bg).setFontAlign(0, 0);
|
||||
g.drawString(str, g.getWidth()/2, y+fontheight/2);
|
||||
}
|
||||
|
||||
function changeInfoMode() {
|
||||
switch(functionMode) {
|
||||
case NONE_FN_MODE:
|
||||
break;
|
||||
case HRT_FN_MODE:
|
||||
hrtOn = !hrtOn;
|
||||
Bangle.buzz();
|
||||
Bangle.setHRMPower(hrtOn ? 1 : 0);
|
||||
if (hrtOn) infoMode = HRT_MODE;
|
||||
return;
|
||||
}
|
||||
|
||||
switch(infoMode) {
|
||||
case NONE_MODE:
|
||||
if (stepsWidget() !== undefined)
|
||||
infoMode = hrtOn ? HRT_MODE : STEPS_MODE;
|
||||
else
|
||||
infoMode = VER_MODE;
|
||||
break;
|
||||
case HRT_MODE:
|
||||
if (stepsWidget() !== undefined)
|
||||
infoMode = STEPS_MODE;
|
||||
else
|
||||
infoMode = VER_MODE;
|
||||
break;
|
||||
case STEPS_MODE:
|
||||
infoMode = ID_MODE;
|
||||
break;
|
||||
case ID_MODE:
|
||||
infoMode = VER_MODE;
|
||||
break;
|
||||
case VER_MODE:
|
||||
infoMode = BATT_MODE;
|
||||
break;
|
||||
case BATT_MODE:
|
||||
infoMode = MEM_MODE;
|
||||
break;
|
||||
case MEM_MODE:
|
||||
default:
|
||||
infoMode = NONE_MODE;
|
||||
}
|
||||
}
|
||||
|
||||
function changeFunctionMode() {
|
||||
//console.log("changeFunctionMode()");
|
||||
switch(functionMode) {
|
||||
case NONE_FN_MODE:
|
||||
functionMode = HRT_FN_MODE;
|
||||
break;
|
||||
case HRT_FN_MODE:
|
||||
default:
|
||||
functionMode = NONE_FN_MODE;
|
||||
}
|
||||
//console.log(functionMode);
|
||||
|
||||
}
|
||||
|
||||
function stepsWidget() {
|
||||
if (WIDGETS.activepedom !== undefined) {
|
||||
return WIDGETS.activepedom;
|
||||
} else if (WIDGETS.wpedom !== undefined) {
|
||||
return WIDGETS.wpedom;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Bangle.on('HRM', function(hrm) {
|
||||
if(hrm.confidence > 90){
|
||||
hrtStr = "Hrt: " + hrm.bpm + " bpm";
|
||||
} else {
|
||||
hrtStr = "Hrt: ??? bpm";
|
||||
}
|
||||
});
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
drawAll();
|
||||
Bangle.on('lcdPower',function(on) {
|
||||
if (on) drawAll();
|
||||
});
|
||||
var click = setInterval(updateTime, 1000);
|
||||
// Show launcher when button pressed
|
||||
Bangle.setUI("clockupdown", btn=>{
|
||||
if (btn<0) changeInfoMode();
|
||||
drawAll();
|
||||
});
|
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 421 B |
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,2 @@
|
|||
0.01: Initial Release
|
||||
0.02: Replace icon with one found on https://icons8.com
|
|
@ -0,0 +1,18 @@
|
|||
# Cube Scramble
|
||||
|
||||
A random scramble generator for the 3x3 Rubik's cube
|
||||
|
||||
## Future features
|
||||
|
||||
I'm keen to complete this project with
|
||||
|
||||
* Add a timer
|
||||
* Add the ability for times to be stored and exported
|
||||
|
||||
## Requests
|
||||
|
||||
Please reach out if you have feature requests or notice bugs.
|
||||
|
||||
## Creator
|
||||
|
||||
Made by [Nathan Lisgo](https://github.com/nlisgo)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("AH4A6iIAQCwkBC6MQC4kYxAACwMT/4ACmMe9wAC6IXLj4XD+IXE8IX/C9/zR4oXOmYABC6UTCwQXZjrXKf5IAHC713AAURindAAVBiatDmIXFi4XDuMdC4fRYooX/5nBC6Xc5gABC6UcCwQXF+aPMC471DC6MTCwQXHa4V2szXBC4bXBC5YAQC7se9wAC6MYxAACwJTCAAIXL8IXFQYoX/C/4XtjrXNu1ma4z/JAA4XEgAXRCwgA/AGo"))
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
// Scramble code from: https://raw.githubusercontent.com/bjcarlson42/blog-post-sample-code/master/Rubik's%20Cube%20JavaScript%20Scrambler/part_two.js
|
||||
const makeScramble = () => {
|
||||
const options = ["F", "F2", "F'", "R", "R2", "R'", "U", "U2", "U'", "B", "B2", "B'", "L", "L2", "L'", "D", "D2", "D'"];
|
||||
const numOptions = [0, 1, 2, 3, 4, 5]; // 0 = F, 1 = R, 2 = U, 3 = B, 4 = L, 5 = D
|
||||
const scrambleMoves = [];
|
||||
let bad = true;
|
||||
|
||||
while (bad) {
|
||||
let scramble = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
scramble.push(numOptions[getRandomInt(6)]);
|
||||
}
|
||||
// check if moves directly next to each other involve the same letter
|
||||
for (let i = 0; i < 20 - 1; i++) {
|
||||
if (scramble[i] == scramble[i + 1]) {
|
||||
bad = true;
|
||||
break;
|
||||
} else {
|
||||
bad = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// switch numbers to letters
|
||||
let move;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
switch (scramble[i]) {
|
||||
case 0:
|
||||
move = options[getRandomInt(3)]; // 0,1,2
|
||||
scrambleMoves.push(move);
|
||||
break;
|
||||
case 1:
|
||||
move = options[getRandomIntBetween(3, 6)]; // 3,4,5
|
||||
scrambleMoves.push(move);
|
||||
break;
|
||||
case 2:
|
||||
move = options[getRandomIntBetween(6, 9)]; // 6,7,8
|
||||
scrambleMoves.push(move);
|
||||
break;
|
||||
case 3:
|
||||
move = options[getRandomIntBetween(9, 12)]; // 9,10,11
|
||||
scrambleMoves.push(move);
|
||||
break;
|
||||
case 4:
|
||||
move = options[getRandomIntBetween(12, 15)]; // 12,13,14
|
||||
scrambleMoves.push(move);
|
||||
break;
|
||||
case 5:
|
||||
move = options[getRandomIntBetween(15, 18)]; // 15,16,17
|
||||
scrambleMoves.push(move);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return scrambleMoves;
|
||||
};
|
||||
|
||||
const getRandomInt = max => Math.floor(Math.random() * Math.floor(max)); // returns up to max - 1
|
||||
|
||||
const getRandomIntBetween = (min, max) => Math.floor(Math.random() * (max - min) + min);
|
||||
|
||||
const presentScramble = () => {
|
||||
g.clear();
|
||||
E.showMessage(makeScramble().join(" "));
|
||||
};
|
||||
|
||||
const init = () => {
|
||||
presentScramble();
|
||||
|
||||
setWatch(() => {
|
||||
presentScramble();
|
||||
}, BTN1, {repeat:true});
|
||||
};
|
||||
|
||||
init();
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,28 @@
|
|||
# Emojuino
|
||||
|
||||
Emojis & Espruino!
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Select an emoji and then tap to transmit! The emoji will be recognised by [Pareto Anywhere](https://www.reelyactive.com/pareto/anywhere/) open source middleware and any other program which observes the [InteroperaBLE Identifier](https://reelyactive.github.io/interoperable-identifier/) open standard.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
Currently implements a tiny subset of possible [Unicode emojis](https://unicode.org/emoji/charts/full-emoji-list.html) which are advertised as an [InteroperaBLE Identifier](https://reelyactive.github.io/interoperable-identifier/) encapsulated as Eddystone UID.
|
||||
|
||||
|
||||
## Controls
|
||||
|
||||
Swipe left/right to select the emoji to broadcast. Tap the screen to initiate the broadcast. Emoji will flash while broadcasting, which lasts for 5 seconds.
|
||||
|
||||
|
||||
## Requests
|
||||
|
||||
[Contact reelyActive](https://www.reelyactive.com/contact/) for support/updates.
|
||||
|
||||
|
||||
## Creator
|
||||
|
||||
Developed by [jeffyactive](https://github.com/jeffyactive) of [reelyActive](https://www.reelyactive.com)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkBiIAHkUoxGIwUiBxAAGiQVCAAeCkIWNCooADDBYWKDBYWEkc////+cyDBhxDCoQAD+YLDCw0YBQQVFAAYYCwIXFHQRDElGCJYgOCFw8vBwPyOgoJFGAg4BIoQWGDAhJCIwoLBHgYAGJQIjCIwguCnCRFRoeDGAZICAgOPFwaRGDAQfB/AwDBAYuCX44wDAgTrDBoIDBGYP/manBmYFBFYQPDwJeBD4iRGRoQ/FC4QqBEYIbERooTBCAeBNAIjBBQIDDAAggBG4IDDwQXBEQIDDUAgcCHASaBAYQTFMQpcFDYp+EEII9DAARRDFIIfDHIwXBVISlDC4YzD9wA0osFpwIF8lQqgWK8kAgEEBItABIIhGAAfgBoMABIoIChwX0jwED8oNBgoXFqAJBrwHD8IXEBwQNEEIYgFC4wAQ8MRC6sRC+BgULwIwHSINVpwuLC43kaAQABqgaHC4bZHAAkFqhGHGAovFAAYyDCwgwFL4IwGFxAwNFxIwG8lVCoSTEFw7bPCxAYNCxT0LIpIxMCpoyHFhI"))
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* Copyright reelyActive 2021
|
||||
* We believe in an open Internet of Things
|
||||
*/
|
||||
|
||||
|
||||
// Emojis are integer pairs with the form [ image, Unicode code point ]
|
||||
// For code points see https://unicode.org/emoji/charts/emoji-list.html
|
||||
const EMOJIS = [
|
||||
[ ':)', 0x1f642 ], // Slightly smiling
|
||||
[ ':|', 0x1f610 ], // Neutral
|
||||
[ ':(', 0x1f641 ], // Slightly frowning
|
||||
[ '+1', 0x1f44d ], // Thumbs up
|
||||
[ '-1', 0x1f44e ], // Thumbs down
|
||||
[ '<3', 0x02764 ], // Heart
|
||||
];
|
||||
const EMOJI_TRANSMISSION_MILLISECONDS = 5000;
|
||||
const BLINK_PERIOD_MILLISECONDS = 500;
|
||||
const TRANSMIT_BUZZ_MILLISECONDS = 200;
|
||||
const CYCLE_BUZZ_MILLISECONDS = 50;
|
||||
|
||||
// Non-user-configurable constants
|
||||
const IMAGE_INDEX = 0;
|
||||
const CODE_POINT_INDEX = 1;
|
||||
const BTN_WATCH_OPTIONS = { repeat: true, debounce: 20, edge: "falling" };
|
||||
const UNICODE_CODE_POINT_ELIDED_UUID = [ 0x49, 0x6f, 0x49, 0x44, 0x55,
|
||||
0x54, 0x46, 0x2d, 0x33, 0x32 ];
|
||||
|
||||
|
||||
// Global variables
|
||||
let emojiIndex = 0;
|
||||
let isToggleOn = false;
|
||||
let isTransmitting = false;
|
||||
let lastDragX = 0;
|
||||
let lastDragY = 0;
|
||||
|
||||
|
||||
// Cycle through emojis
|
||||
function cycleEmoji(isForward) {
|
||||
if(isTransmitting) { return; }
|
||||
|
||||
if(isForward) {
|
||||
emojiIndex = (emojiIndex + 1) % EMOJIS.length;
|
||||
}
|
||||
else if(--emojiIndex < 0) {
|
||||
emojiIndex = EMOJIS.length - 1;
|
||||
}
|
||||
|
||||
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
|
||||
Bangle.buzz(CYCLE_BUZZ_MILLISECONDS);
|
||||
}
|
||||
|
||||
|
||||
// Handle a touch: transmit displayed emoji
|
||||
function handleTouch(zone, event) {
|
||||
if(isTransmitting) { return; }
|
||||
|
||||
let emoji = EMOJIS[emojiIndex];
|
||||
transmitEmoji(emoji[IMAGE_INDEX], emoji[CODE_POINT_INDEX],
|
||||
EMOJI_TRANSMISSION_MILLISECONDS);
|
||||
Bangle.buzz(TRANSMIT_BUZZ_MILLISECONDS);
|
||||
}
|
||||
|
||||
|
||||
// Transmit the given code point for the given duration in milliseconds,
|
||||
// blinking the image once per second.
|
||||
function transmitEmoji(image, codePoint, duration) {
|
||||
let instance = [ 0x00, 0x00, (codePoint >> 24) & 0xff,
|
||||
(codePoint >> 16) & 0xff, (codePoint >> 8) & 0xff,
|
||||
codePoint & 0xff ];
|
||||
|
||||
require('ble_eddystone_uid').advertise(UNICODE_CODE_POINT_ELIDED_UUID,
|
||||
instance);
|
||||
isTransmitting = true;
|
||||
|
||||
let displayIntervalId = setInterval(toggleImage, BLINK_PERIOD_MILLISECONDS,
|
||||
image);
|
||||
|
||||
setTimeout(terminateEmoji, duration, displayIntervalId);
|
||||
}
|
||||
|
||||
|
||||
// Terminate the emoji transmission
|
||||
function terminateEmoji(displayIntervalId) {
|
||||
NRF.setAdvertising({ });
|
||||
isTransmitting = false;
|
||||
clearInterval(displayIntervalId);
|
||||
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
|
||||
}
|
||||
|
||||
|
||||
// Toggle the display between image/off
|
||||
function toggleImage(image) {
|
||||
if(isToggleOn) {
|
||||
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
|
||||
}
|
||||
else {
|
||||
g.clear();
|
||||
}
|
||||
isToggleOn = !isToggleOn;
|
||||
}
|
||||
|
||||
|
||||
// Draw the given emoji
|
||||
function drawImage(image) {
|
||||
g.clear();
|
||||
g.drawString(image, g.getWidth() / 2, g.getHeight() / 2);
|
||||
g.flip();
|
||||
}
|
||||
|
||||
|
||||
// Handle a drag event
|
||||
function handleDrag(event) {
|
||||
let isFingerReleased = (event.b === 0);
|
||||
|
||||
if(isFingerReleased) {
|
||||
let isHorizontalDrag = (Math.abs(lastDragX) >= Math.abs(lastDragY)) &&
|
||||
(lastDragX !== 0);
|
||||
|
||||
if(isHorizontalDrag) {
|
||||
cycleEmoji(lastDragX > 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lastDragX = event.dx;
|
||||
lastDragY = event.dy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Special function to handle display switch on
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
if(on) {
|
||||
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// On start: display the first emoji and handle drag and touch events
|
||||
g.clear();
|
||||
g.setFont('Vector', 80);
|
||||
g.setFontAlign(0, 0);
|
||||
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
|
||||
Bangle.on('touch', handleTouch);
|
||||
Bangle.on('drag', handleDrag);
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1 @@
|
|||
0.01: Launch app
|
|
@ -0,0 +1,8 @@
|
|||
# LCARS clock
|
||||
|
||||
A simple LCARS inspired clock that shows:
|
||||
* Current time
|
||||
* Current date
|
||||
* Battery level
|
||||
* Steps
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,99 @@
|
|||
const locale = require('locale');
|
||||
|
||||
|
||||
/*
|
||||
* Assets: Images, fonts etc.
|
||||
*/
|
||||
var img = {
|
||||
width : 176, height : 151, bpp : 3,
|
||||
transparent : 0,
|
||||
buffer : require("heatshrink").decompress(atob("gF58+eAR14IN1fvv374CN7yD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/AH4A/AH4A/AB1z588+YCN+RBuj158+eARyD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf4AUhyD/gEDQaHz4BCuQaNAIN0PQaHIIN0BQaF5IN0AQaHPkBBug6DQ8iEvQaE8yBBuhyDPAQNAINsBQaACBkhCuQaACpVo0cQaACo4CFGjyD/AAMPQf4ACQf4ADgiD+AH4A/AH8J02atICIwEAgPnz15AR3gEgM27dt2wCTF4IABgYROgN9+/fAR14ILsaQBKDakwjKF5oABKZ6DwgxTPQeEmQf5cPQeMBLhyDxgJTRQd0JKaKDuhKD/gENQf6D/F4VNQf8AKaKDvKBYnBAGZQKzBB1QZOwIGqDJsBA2QZJA3QZGYIPCDH4CD/0xA4QY+wIPKDGwCD/tpB6Qf6DHthA5QY1oIPSD/QY9gQf/bIPaD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/AF8JQYgCdsEHnnz54CJgIdLwEAhqDEATtggPnz15ARHkgIdLIIKAgQcCAgQcAA/gAA=="))
|
||||
}
|
||||
|
||||
Graphics.prototype.setFontMinaSmall = function(scale) {
|
||||
// Actual height 18 (17 - 0)
|
||||
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAA/8w/8wAAQAAAAAA4AA8AAAAA8AAwAAAAAAEABEABEQB/w/8AxEABEwB/w/8AxEABEABEAAAAH4MP8MMEM8GPcGOMGMMGMMH4ABwAAA/gAwgAggAggQwhw/nAAOAA4ADgAOfw8YwwQwAQQAYwAfwAPgAGAAfg85w/wwzgww4wwdgwHggHgAPwAIQAAA8AAwAAAAAAfwH/+fAP4ABgAAgAA4ABfAPH/+A/wAAAAAAEAAHgAfAAfAAHgAMAAAAAAAABgABgABgAf4AP4ABgABgABAAAAAADAADwADAAAAAgAAwAAwAAwAAwAAwAAAAADAADAADAAAAAAEAA8AH4A+AHwA+AAwAAAAAf/g//wwAwwAwwAwwAwwAwwAwf/gH+AAAAAAAYAAwAAwAA//wAAAAAAAAAwAwwBwwDwwDwwGwwcww4wfwwPAwAAAAAAwAwwQwwQwwQwwQwwYww4wf/gHHAAAAAEAAeAA+ADmAPGAcGAwGAh/wD/wAEAAEAAAAf4w/4wwwwwwwwwwwwwwwww/wgfgAAAAAAP/Af/gwwwwwwwwwwwwwwwwwww/gAAAgAAwAAwAAwAwwHww/Az4A/AA8AAAAAAAAfPg//wxwwwwwwwwwwwwww//wffgAAAAAAfwQ/wwwQwwYwwQwwQwwQw//wP/AAAAAAAMDAMDAMDAAAAAAAMDAMDwMDAAAAAAADgADgAHwAGwAMYAMYAIIAAAAEQAGYAGYAGYAGYAGYAGYAGYAAAAMYAMYAGwAGwAHgADgADAAAAAwAAwAAwAAwcwwcwwQAwQA/wAfgAAAAAAAB/8D/+TAGbHjbPzbMTbMzbMzb/zZ/zYAGf/+H/8AAAAAAABwAPwA+AH+A+GA8GAfmAD+AAfgADwAAQAAA//w//wwwwwwwwwwwwwxww//wffgAAAAAAP/Af/gwBwwAwwAwwAwwAwwAwwAwAAA//w//wwAwwAwwAwwAwwAw4Bwf/gH+AAAAAAAf/g//wwQwgQQgQQgQQgQQgQQgAQAAAf/w//wwQAgQAgQAgQAgQAgQAgAAAAAP/Af/gwAwwAwwAwwYwwYwwfwwfwAAAAAA//w//wAYAAYAAYAAYAAYAAYA//w//wAAA//w//wAAAAAAAAwAAwAAw//w//AAAA//w//wAYAA4AD8AHHAeDg4AwgAQAAAAAA//g//wAAwAAwAAwAAwAAwAAwAAQAAAP/w//w+AAPwAB+AAHwADwA/gH4A/AA/4A//wAAwAAA//w//wcAAPAADgAA4AAeAAHAADw//wAAAAAAH/Af/g4AwwAwwAwwAwwAwwAwcDwP/gB4AAAA//w//wwQAwQAwQAwYAwwA/wAPgAAAAH/Af/gwAwwAwwAwwA8wA8wA2cDkP/gB4AAAA//w//wwYAwYAwYAwcAwfA/zwPgwAAAAAAfgA/wwwQwwYwwYwwYwwYwwfwAPgAAAAAAwAAwAAwAA//w//wwAAwAAwAAwAAAAA/+A//gABwAAwAAwAAwAAwAAwAPg//AAAAAAA4AA/AAH4AA/AAHwADwAfgD8AfgA8AAgAAAAA4AA/AAH4AA/AAHwAHwA/AP4A/4Aw/AAHwAHwA/AP4A+AAwAAAAAwAw8DwOHAD8AB4AD8AOHA8DwwAwAAAAAAwAA8AAPAADwAA/wB/wHgAeAA4AAgAAgAQwBwwHwwOww8wxww3gw+Aw4AwwAQAAAH//f//YAAYAAQAAwAA+AAPwAB+AAPwAB8AAMQAAYAAYAAf//AAAAAA"), 32, atob("BgUHDAoRCwMGBggJBQYFBwwHCwsLCwsKCwsFBQkICQoPDAsKDAoKCwsEBgsKDgwMCgwLCwoMDBELCwoGBwY="), 18+(scale<<8)+(1<<16));
|
||||
}
|
||||
|
||||
Graphics.prototype.setFontMinaLarge = function(scale) {
|
||||
// Actual height 35 (34 - 0)
|
||||
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAB4AAAAAPgAAAAA+AAAAAD4AAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAH4AAAAD/gAAAB/8AAAA/+AAAAf/AAAAP/gAAAH/wAAAD/4AAAD/8AAAB/+AAAAP/AAAAA/AAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///4AA////wAH////gA+AAAfADwAAA8AOAAABwA4AAAHADgAAAcAOAAABwA4AAAHADgAAAcAOAAABwA4AAAHADgAAAcAOAAABwA8AAAPAD4AAB8AH////gAP///8AAf///gAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAcAAAAADgAAAAAOAAAAAB4AAAAAHAAAAAA8AAAAAD////8AP////wA/////AD////8AAAAAAAAAAAAAAAAAAAAAGAAAAAA4AAAHADgAAA8AOAAAHwA4AAA/ADgAAD8AOAAAfwA4AAD/ADgAAfcAOAAD5wA4AAfHADgAD4cAOAAfBwA4AD8HADwAfgcAPAD8BwAeA/AHAB+f4AcAD//ABwAH/wAHAAH8AAcAAAAAAAAAAAAAAAAAAAAAGAAABgAYAAAGADgAAAcAOAHABwA4AcAHADgBwAcAOAHABwA4AcAHADgBwAcAOAHABwA4AcAHADgBwAcAOAHABwA4AcAHADwB4A8APAPgDwAfD//+AB////4AD/8//AAD/B/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAA8AAAAAPwAAAAD/AAAAAf8AAAAH9wAAAB/HAAAAPwcAAAD+BwAAAfgHAAAH8AcAAB/ABwAAPwAHAAA+AAcAADgABwAAIAAHgAAAH///AAB///8AAH///wAAAAcAAAAABwAAAAAHAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAH/8AYAP//wBgA///AHAD//wAcAOAPABwA4A4AHADgDgAcAOAOABwA4A4AHADgDgAcAOAOABwA4A4AHADgDgA8AOAOADwA4A8APADgD8H4AOAH//gA4AP/8AAAAP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/gAAAD//+AAB///+AAP///8AB/BgH4APgOAHwA8A4APADgDgAcAOAOABwA4A4AHADgDgAcAOAOABwA4A4AHADgDgAcAOAOABwA4A4AHADgDwA8AOAP//wA4Af/+ABgA//wAAAA/8AAAAAAAAAAAAAAAAAAAAAA4AAAAADgAAAAAOAAAAAA4AAAAADgAAAAAOAAAAQA4AAAHADgAAD8AOAAA/wA4AAf+ADgAP/gAOAD/wAA4B/8AADg/+AAAOP/AAAA//wAAAD/4AAAAP8AAAAA/AAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/h/4AA//P/wAH////gA+B/AfADwD4A8AOAHABwA4AcAHADgBwAcAOAHABwA4AcAHADgBwAcAOAHABwA4AcAHADgBwAcAPAPgDwA8A+APAB////4AH////gAP/j/8AAD4B8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/wAAAA//gAYAH//ABwA//+AHADwB4AcAOADgBwA4AOAHADgA4AcAOADgBwA4AOAHADgA4AcAOADgBwA4AOAHADgA4AcAPADgDwA+AOAfAB+A4f4AD////AAH///4AAD//8AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAeAAB8AD4AAHwAPgAAfAA+AAB4AB4AAAAAAAAAAAAAAAAAAAAAA="), 46, atob("CxAaDhgYGBgZFhkZCw=="), 40+(scale<<8)+(1<<16));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Queue drawing every minute
|
||||
*/
|
||||
var drawTimeout;
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Draw watch face
|
||||
*/
|
||||
function draw(){
|
||||
g.reset();
|
||||
g.clearRect(0, 24, g.getWidth(), g.getHeight());
|
||||
|
||||
// Draw background image
|
||||
g.drawImage(img, 0, 24);
|
||||
|
||||
// Write time
|
||||
var currentDate = new Date();
|
||||
var timeStr = locale.time(currentDate,1);
|
||||
g.setFontAlign(0,0,0);
|
||||
g.setFontMinaLarge();
|
||||
g.drawString(timeStr, 115, 53);
|
||||
|
||||
// Write date
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setFontMinaSmall();
|
||||
|
||||
var dayName = locale.dow(currentDate, true).toUpperCase();
|
||||
var day = currentDate.getDate();
|
||||
g.drawString("DATE:", 40, 107);
|
||||
g.drawString(dayName + " " + day, 100, 105);
|
||||
|
||||
// Draw battery
|
||||
var bat = E.getBattery();
|
||||
g.drawString("BAT:", 40, 127);
|
||||
g.drawString(bat+"%", 100, 127);
|
||||
|
||||
// Draw steps
|
||||
var steps = Bangle.getStepCount();
|
||||
g.drawString("STEP:", 40, 147);
|
||||
g.drawString(steps, 100, 147);
|
||||
|
||||
// Queue draw in one minute
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.setTheme({bg:"#000",fg:"#fff",dark:true}).clear();
|
||||
|
||||
// draw immediately at first, queue update
|
||||
draw();
|
||||
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
||||
// Load widgets
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgeevPnAQsc+fPngCE+/fvoCEvAbIA4/AgFzEZwRBjwjNvBUBEZ3eCIMOEZtwCIMBEZuARYU5EZecTocHEZf0CIcBEbvgaggjKTwIAEbQpoHAAiSEeoYQHJQr1CCBJKEIgcBI4xKFaIdt3AOFgfuAYMeEYLRBj1pLQ4ICuYjBAgPbtoRHhu3AYN5VoMGzVpI49502AgPPVoM27dsK48N23cgE5CgOmzVoCI4LBzCSB8EP2wjJgILBAYMAhIjBsAjJzVwg47C7YRJEYhfBEZXmEZ53CI4q2BEAiVCkwjCNYaMGboQjDkBfDCAbdB04EBgyPDC4YAD/dt2wRCHIM5njXCCAcHboOmCIQ0B5/nfYT6DFIIjBeAcOvM8+EAjitFEYJEBAANzEYOeeowjCFgUDzwjB+YrDgAgBEYWcA4Mc+YjCvAQCgftEANuDIYOBEYXPNwIAIg4OCCgXkCBEOEZDvBEAhEB4AjF/inB8+OJQOOvILBoAjGU4IFDAQYjGbQIdCAQt4EY0DEZACDEYceEZACDC4bLBEZwCO"))
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -1,5 +1,9 @@
|
|||
# Changelog
|
||||
|
||||
## 2021-11-18
|
||||
|
||||
- [Feature] Ported to Banglejs2
|
||||
|
||||
## 2019-11-27
|
||||
|
||||
- [Feature] App now saves the last interval value
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.02: Ported to Banglejs2.
|
||||
0.01: New App!
|
||||
|
|
|
@ -11,8 +11,9 @@ const STATES = {
|
|||
var counterInterval;
|
||||
|
||||
class State {
|
||||
constructor (state) {
|
||||
constructor (state, device) {
|
||||
this.state = state;
|
||||
this.device = device;
|
||||
this.next = null;
|
||||
}
|
||||
|
||||
|
@ -47,8 +48,8 @@ class State {
|
|||
}
|
||||
|
||||
class InitState extends State {
|
||||
constructor (time) {
|
||||
super(STATES.INIT);
|
||||
constructor (device) {
|
||||
super(STATES.INIT, device);
|
||||
|
||||
this.timeCounter = parseInt(storage.read(".pomodo") || DEFAULT_TIME, 10);
|
||||
}
|
||||
|
@ -58,7 +59,7 @@ class InitState extends State {
|
|||
}
|
||||
|
||||
setButtons () {
|
||||
setWatch(() => {
|
||||
this.device.setBTN1(() => {
|
||||
if (this.timeCounter + 300 > 3599) {
|
||||
this.timeCounter = 3599;
|
||||
} else {
|
||||
|
@ -67,23 +68,23 @@ class InitState extends State {
|
|||
|
||||
this.draw();
|
||||
|
||||
}, BTN1, { repeat: true });
|
||||
});
|
||||
|
||||
setWatch(() => {
|
||||
this.device.setBTN3(() => {
|
||||
if (this.timeCounter - 300 > 0) {
|
||||
this.timeCounter -= 300;
|
||||
this.draw();
|
||||
}
|
||||
}, BTN3, { repeat: true });
|
||||
});
|
||||
|
||||
setWatch(() => {
|
||||
this.device.setBTN4(() => {
|
||||
if (this.timeCounter - 60 > 0) {
|
||||
this.timeCounter -= 60;
|
||||
this.draw();
|
||||
}
|
||||
}, BTN4, { repeat: true });
|
||||
});
|
||||
|
||||
setWatch(() => {
|
||||
this.device.setBTN5(() => {
|
||||
if (this.timeCounter + 60 > 3599) {
|
||||
this.timeCounter = 3599;
|
||||
} else {
|
||||
|
@ -92,15 +93,15 @@ class InitState extends State {
|
|||
|
||||
this.draw();
|
||||
|
||||
}, BTN5, { repeat: true });
|
||||
});
|
||||
|
||||
setWatch(() => {
|
||||
this.device.setBTN2(() => {
|
||||
this.saveTime();
|
||||
const startedState = new StartedState(this.timeCounter);
|
||||
const startedState = new StartedState(this.timeCounter, this.device);
|
||||
|
||||
this.setNext(startedState);
|
||||
this.next.go();
|
||||
}, BTN2, { repeat: true });
|
||||
});
|
||||
}
|
||||
|
||||
draw () {
|
||||
|
@ -112,14 +113,14 @@ class InitState extends State {
|
|||
}
|
||||
|
||||
class StartedState extends State {
|
||||
constructor (timeCounter) {
|
||||
super(STATES.STARTED);
|
||||
constructor (timeCounter, buttons) {
|
||||
super(STATES.STARTED, buttons);
|
||||
|
||||
this.timeCounter = timeCounter;
|
||||
}
|
||||
|
||||
draw () {
|
||||
drawCounter(this.timeCounter, 120, 120);
|
||||
drawCounter(this.timeCounter, g.getWidth() / 2, g.getHeight() / 2);
|
||||
}
|
||||
|
||||
init () {
|
||||
|
@ -137,15 +138,15 @@ class StartedState extends State {
|
|||
this.draw();
|
||||
}
|
||||
|
||||
const doneState = new DoneState();
|
||||
const doneState = new DoneState(this.device);
|
||||
this.setNext(doneState);
|
||||
counterInterval = setInterval(countDown.bind(this), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
class BreakState extends State {
|
||||
constructor () {
|
||||
super(STATES.BREAK);
|
||||
constructor (buttons) {
|
||||
super(STATES.BREAK, buttons);
|
||||
}
|
||||
|
||||
draw () {
|
||||
|
@ -153,44 +154,40 @@ class BreakState extends State {
|
|||
}
|
||||
|
||||
init () {
|
||||
const startedState = new StartedState(TIME_BREAK);
|
||||
const startedState = new StartedState(TIME_BREAK, this.device);
|
||||
|
||||
this.setNext(startedState);
|
||||
this.next.go();
|
||||
}
|
||||
}
|
||||
|
||||
class DoneState extends State {
|
||||
constructor () {
|
||||
super(STATES.DONE);
|
||||
constructor (device) {
|
||||
super(STATES.DONE, device);
|
||||
}
|
||||
|
||||
setButtons () {
|
||||
setWatch(() => {
|
||||
const initState = new InitState();
|
||||
clearTimeout(this.timeout);
|
||||
initState.go();
|
||||
}, BTN1, { repeat: true });
|
||||
this.device.setBTN1(() => {
|
||||
});
|
||||
|
||||
setWatch(() => {
|
||||
const breakState = new BreakState();
|
||||
clearTimeout(this.timeout);
|
||||
breakState.go();
|
||||
}, BTN3, { repeat: true });
|
||||
this.device.setBTN3(() => {
|
||||
});
|
||||
|
||||
setWatch(() => {
|
||||
}, BTN2, { repeat: true });
|
||||
this.device.setBTN2(() => {
|
||||
});
|
||||
}
|
||||
|
||||
draw () {
|
||||
g.clear();
|
||||
g.setFont("6x8", 2);
|
||||
g.setFontAlign(0, 0, 3);
|
||||
g.drawString("AGAIN", 230, 50);
|
||||
g.drawString("BREAK", 230, 190);
|
||||
g.setFont("Vector", 45);
|
||||
g.setFontAlign(-1, -1);
|
||||
|
||||
g.drawString('You\nare\na\nhero!', 50, 40);
|
||||
E.showPrompt("You are a hero!", {
|
||||
buttons : {"AGAIN":1,"BREAK":2}
|
||||
}).then((v) => {
|
||||
var nextSate = (v == 1
|
||||
? new InitState(this.device)
|
||||
: new BreakState(this.device));
|
||||
clearTimeout(this.timeout);
|
||||
nextSate.go();
|
||||
});
|
||||
}
|
||||
|
||||
init () {
|
||||
|
@ -215,13 +212,61 @@ class DoneState extends State {
|
|||
}
|
||||
}
|
||||
|
||||
class Bangle1 {
|
||||
setBTN1(callback) {
|
||||
setWatch(callback, BTN1, { repeat: true });
|
||||
}
|
||||
|
||||
setBTN2(callback) {
|
||||
setWatch(callback, BTN2, { repeat: true });
|
||||
}
|
||||
|
||||
setBTN3(callback) {
|
||||
setWatch(callback, BTN3, { repeat: true });
|
||||
}
|
||||
|
||||
setBTN4(callback) {
|
||||
setWatch(callback, BTN4, { repeat: true });
|
||||
}
|
||||
|
||||
setBTN5(callback) {
|
||||
setWatch(callback, BTN5, { repeat: true });
|
||||
}
|
||||
}
|
||||
|
||||
class Bangle2 {
|
||||
setBTN1(callback) {
|
||||
Bangle.on('touch', function(zone, e) {
|
||||
if (e.y < g.getHeight() / 2) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setBTN2(callback) {
|
||||
setWatch(callback, BTN1, { repeat: true });
|
||||
}
|
||||
|
||||
setBTN3(callback) {
|
||||
Bangle.on('touch', function(zone, e) {
|
||||
if (e.y > g.getHeight() / 2) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setBTN4(callback) { }
|
||||
|
||||
setBTN5(callback) { }
|
||||
}
|
||||
|
||||
function drawCounter (currentValue, x, y) {
|
||||
if (currentValue < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
x = x || 120;
|
||||
y = y || 120;
|
||||
x = x || g.getWidth() / 2;
|
||||
y = y || g.getHeight() / 2;
|
||||
|
||||
let minutes = 0;
|
||||
let seconds = 0;
|
||||
|
@ -249,7 +294,10 @@ function drawCounter (currentValue, x, y) {
|
|||
}
|
||||
|
||||
function init () {
|
||||
const initState = new InitState();
|
||||
device = (process.env.HWVERSION==1
|
||||
? new Bangle1()
|
||||
: new Bangle2());
|
||||
const initState = new InitState(device);
|
||||
initState.go();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,3 +34,5 @@
|
|||
0.29: Add Customize to Theme menu
|
||||
0.30: Move '< Back' to the top of menus
|
||||
0.31: Remove Bangle 1 settings when running on Bangle 2
|
||||
0.32: Fix 'beep' menu on Bangle.js 2
|
||||
0.33: Really fix 'beep' menu on Bangle.js 2 this time
|
||||
|
|
|
@ -38,7 +38,7 @@ function resetSettings() {
|
|||
quiet: 0, // quiet mode: 0: off, 1: priority only, 2: total silence
|
||||
timeout: 10, // Default LCD timeout in seconds
|
||||
vibrate: true, // Vibration enabled by default. App must support
|
||||
beep: "vib", // Beep enabled by default. App must support
|
||||
beep: BANGLEJS2?true:"vib", // Beep enabled by default. App must support
|
||||
timezone: 0, // Set the timezone for the device
|
||||
HID: false, // BLE HID mode, off by default
|
||||
clock: null, // a string for the default clock's name
|
||||
|
@ -72,8 +72,37 @@ if (!('qmOptions' in settings)) settings.qmOptions = {}; // easier if this alway
|
|||
const boolFormat = v => v ? "On" : "Off";
|
||||
|
||||
function showMainMenu() {
|
||||
var beepV = BANGLEJS2 ? [false,true] : [false, true, "vib"];
|
||||
var beepN = BANGLEJS2 ? ["Off","On"] : ["Off", "Piezo", "Vibrate"];
|
||||
var beepMenuItem;
|
||||
if (BANGLEJS2) {
|
||||
beepMenuItem = {
|
||||
value: settings.beep!=false,
|
||||
format: boolFormat,
|
||||
onchange: v => {
|
||||
settings.beep = v;
|
||||
updateSettings();
|
||||
if (settings.beep) {
|
||||
analogWrite(VIBRATE,0.1,{freq:2000});
|
||||
setTimeout(()=>VIBRATE.reset(),200);
|
||||
} // beep with vibration moter
|
||||
}
|
||||
};
|
||||
} else { // Bangle.js 1
|
||||
var beepV = [false, true, "vib"];
|
||||
var beepN = ["Off", "Piezo", "Vibrate"];
|
||||
beepMenuItem = {
|
||||
value: Math.max(0 | beepV.indexOf(settings.beep),0),
|
||||
min: 0, max: beepV.length-1,
|
||||
format: v => beepN[v],
|
||||
onchange: v => {
|
||||
settings.beep = beepV[v];
|
||||
if (v==1) { analogWrite(D18,0.5,{freq:2000});setTimeout(()=>D18.reset(),200); } // piezo on Bangle.js 1
|
||||
else if (v==2) { analogWrite(VIBRATE,0.1,{freq:2000});setTimeout(()=>VIBRATE.reset(),200); } // vibrate
|
||||
updateSettings();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const mainmenu = {
|
||||
'': { 'title': 'Settings' },
|
||||
'< Back': ()=>load(),
|
||||
|
@ -88,17 +117,7 @@ function showMainMenu() {
|
|||
updateSettings();
|
||||
}
|
||||
},
|
||||
'Beep': {
|
||||
value: 0 | beepV.indexOf(settings.beep),
|
||||
min: 0, max: 2,
|
||||
format: v => beepN[v],
|
||||
onchange: v => {
|
||||
settings.beep = beepV[v];
|
||||
if (v==1) { analogWrite(D18,0.5,{freq:2000});setTimeout(()=>D18.reset(),200); } // piezo
|
||||
else if (v==2) { analogWrite(D13,0.1,{freq:2000});setTimeout(()=>D13.reset(),200); } // vibrate
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'Beep': beepMenuItem,
|
||||
'Vibration': {
|
||||
value: settings.vibrate,
|
||||
format: boolFormat,
|
||||
|
@ -146,7 +165,7 @@ function showBLEMenu() {
|
|||
}
|
||||
},
|
||||
'HID': {
|
||||
value: 0 | hidV.indexOf(settings.HID),
|
||||
value: Math.max(0,0 | hidV.indexOf(settings.HID)),
|
||||
min: 0, max: 3,
|
||||
format: v => hidN[v],
|
||||
onchange: v => {
|
||||
|
|