Merge branch 'espruino:master' into master
17
README.md
|
@ -134,6 +134,7 @@ Apps are listed in the Bangle.js menu, accessible from a clock app via the middl
|
|||
* `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
|
||||
* `ChangeLog` - A file containing a list of changes to your app so users can see what's changed
|
||||
|
||||
#### `app-icon.js`
|
||||
|
||||
|
@ -189,9 +190,23 @@ When the widget is to be drawn, `x` and `y` values are set up in `WIDGETS["mywid
|
|||
and `draw` can then use `this.x` and `this.y` to figure out where it needs to draw to.
|
||||
|
||||
|
||||
### ChangeLog
|
||||
|
||||
This is a file containing a list of changes to your app so users can see what's changed, for example:
|
||||
|
||||
```
|
||||
0.01: New App!
|
||||
0.02: Changed the colors
|
||||
0.03: Made the app run quicker
|
||||
```
|
||||
|
||||
Entries should be newest last, with the version number of the last entry matching the version in `metadata.json`
|
||||
|
||||
Please keep the same format at the example as the file needs to be parsed by the BangleApps tools.
|
||||
|
||||
### `app.info` format
|
||||
|
||||
This is the file that's **auto-generated** and loaded onto Bangle.js by the App Loader,
|
||||
This is the file that's **auto-generated** from `metadata.json` and loaded onto Bangle.js by the App Loader,
|
||||
and which gives information about the app for the Launcher.
|
||||
|
||||
```
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: initial release
|
||||
1.00: first official release (also fixes bug in Customizer)
|
|
@ -321,7 +321,7 @@
|
|||
|
||||
function chosenClockHands () {
|
||||
switch (ClockHands) {
|
||||
case 'simple': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-simpled-clock-hands/main/ClockHands.js')"
|
||||
case 'simple': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-simple-clock-hands/main/ClockHands.js')"
|
||||
case 'rounded': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-rounded-clock-hands/main/ClockHands.js')"
|
||||
case 'hollow': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-hollow-clock-hands/main/ClockHands.js')"
|
||||
case 'custom': return "require('" + ClockHandsURL + "')"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "ac_ac",
|
||||
"name": "A Configurable Analog Clock",
|
||||
"shortName":"Configurable Clock",
|
||||
"version":"0.03",
|
||||
"version":"1.00",
|
||||
"description": "AC-AC, a highly customizable analog clock with several clock faces, hands and complications to choose from",
|
||||
"icon": "app-icon.png",
|
||||
"type": "clock",
|
||||
|
|
|
@ -43,3 +43,7 @@ charge.
|
|||
|
||||
This app is based in the work done by [jeffmer](https://github.com/jeffmer/JeffsBangleAppsDev)
|
||||
|
||||
|
||||
Written by: [Hugh Barney](https://github.com/hughbarney) For support
|
||||
and discussion please post in the [Bangle JS
|
||||
Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
0.01: Display pressure as number and hand
|
||||
0.02: Use theme color
|
||||
0.03: workaround for some firmwares that return 'undefined' for first call to barometer
|
||||
|
|
|
@ -1,77 +1,77 @@
|
|||
const center = {
|
||||
var center = {
|
||||
x: g.getWidth()/2,
|
||||
y: g.getHeight()/2,
|
||||
};
|
||||
|
||||
const MIN = 940;
|
||||
const MAX = 1090;
|
||||
const NUMBER_OF_VALUES = MAX - MIN;
|
||||
const SCALE_TICK_STEP = 5;
|
||||
const SCALE_VALUES_STEP = 25;
|
||||
const NUMBER_OF_LABELS = NUMBER_OF_VALUES / SCALE_VALUES_STEP;
|
||||
const NUMBER_OF_TICKS = NUMBER_OF_VALUES / SCALE_TICK_STEP;
|
||||
const ZERO_OFFSET = (Math.PI / 4) * 3;
|
||||
const SCALE_SPAN = (Math.PI / 2) * 3;
|
||||
const TICK_LENGTH = 10;
|
||||
const HAND_LENGTH = 45;
|
||||
const HAND_WIDTH = 5;
|
||||
var MIN = 940;
|
||||
var MAX = 1090;
|
||||
var NUMBER_OF_VALUES = MAX - MIN;
|
||||
var SCALE_TICK_STEP = 5;
|
||||
var SCALE_VALUES_STEP = 25;
|
||||
var NUMBER_OF_LABELS = NUMBER_OF_VALUES / SCALE_VALUES_STEP;
|
||||
var NUMBER_OF_TICKS = NUMBER_OF_VALUES / SCALE_TICK_STEP;
|
||||
var ZERO_OFFSET = (Math.PI / 4) * 3;
|
||||
var SCALE_SPAN = (Math.PI / 2) * 3;
|
||||
var TICK_LENGTH = 10;
|
||||
var HAND_LENGTH = 45;
|
||||
var HAND_WIDTH = 5;
|
||||
|
||||
function generatePoly(radius, width, angle){
|
||||
const x = center.x + Math.cos(angle) * radius;
|
||||
const y = center.y + Math.sin(angle) * radius;
|
||||
const d = {
|
||||
var x = center.x + Math.cos(angle) * radius;
|
||||
var y = center.y + Math.sin(angle) * radius;
|
||||
var d = {
|
||||
x: width/2 * Math.cos(angle + Math.PI/2),
|
||||
y: width/2 * Math.sin(angle + Math.PI/2),
|
||||
};
|
||||
|
||||
const poly = [center.x - d.x, center.y - d.y, center.x + d.x, center.y + d.y, x + d.x, y + d.y, x - d.x, y - d.y];
|
||||
var poly = [center.x - d.x, center.y - d.y, center.x + d.x, center.y + d.y, x + d.x, y + d.y, x - d.x, y - d.y];
|
||||
|
||||
return poly;
|
||||
}
|
||||
|
||||
function drawHand(value){
|
||||
g.setColor(256, 0, 0);
|
||||
g.setColor(g.theme.fg2);
|
||||
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("Vector",15);
|
||||
g.drawString(value, center.x, center.y * 2 - 15, true);
|
||||
|
||||
const angle = SCALE_SPAN / NUMBER_OF_VALUES * (value - MIN) + ZERO_OFFSET;
|
||||
var angle = SCALE_SPAN / NUMBER_OF_VALUES * (value - MIN) + ZERO_OFFSET;
|
||||
g.fillPoly(generatePoly(HAND_LENGTH, HAND_WIDTH, angle), true);
|
||||
g.fillCircle(center.x ,center.y, 4);
|
||||
}
|
||||
|
||||
|
||||
function drawTicks(){
|
||||
g.setColor(1,1,1);
|
||||
g.setColor(g.theme.fg);
|
||||
for(let i= 0; i <= NUMBER_OF_TICKS; i++){
|
||||
const angle = (i * (SCALE_SPAN/NUMBER_OF_TICKS)) + ZERO_OFFSET;
|
||||
var angle = (i * (SCALE_SPAN/NUMBER_OF_TICKS)) + ZERO_OFFSET;
|
||||
|
||||
const tickWidth = i%5==0 ? 5 : 2;
|
||||
var tickWidth = i%5==0 ? 5 : 2;
|
||||
g.fillPoly(generatePoly(center.x, tickWidth, angle), true);
|
||||
}
|
||||
|
||||
g.setColor(0,0,0);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillCircle(center.x,center.y,center.x - TICK_LENGTH);
|
||||
}
|
||||
|
||||
|
||||
function drawScaleLabels(){
|
||||
g.setColor(1,1,1);
|
||||
g.setColor(g.theme.fg);
|
||||
g.setFont("Vector",12);
|
||||
|
||||
let label = MIN;
|
||||
for (let i=0;i <= NUMBER_OF_LABELS; i++){
|
||||
const angle = (i * (SCALE_SPAN/NUMBER_OF_LABELS)) + ZERO_OFFSET;
|
||||
const labelDimensions = g.stringMetrics(label);
|
||||
var angle = (i * (SCALE_SPAN/NUMBER_OF_LABELS)) + ZERO_OFFSET;
|
||||
var labelDimensions = g.stringMetrics(label);
|
||||
|
||||
const LABEL_PADDING = 5;
|
||||
const radius = center.x - TICK_LENGTH - LABEL_PADDING;
|
||||
const x = center.x + Math.cos(angle) * radius;
|
||||
const y = center.y + Math.sin(angle) * radius;
|
||||
var LABEL_PADDING = 5;
|
||||
var radius = center.x - TICK_LENGTH - LABEL_PADDING;
|
||||
var x = center.x + Math.cos(angle) * radius;
|
||||
var y = center.y + Math.sin(angle) * radius;
|
||||
|
||||
const visualX = x > center.x ? x - labelDimensions.width : x + labelDimensions.width > center.x ? x - (labelDimensions.width / 2) : x;
|
||||
const visualY = y >= center.y - labelDimensions.height / 2 ? y - labelDimensions.height / 2 : y;
|
||||
var visualX = x > center.x ? x - labelDimensions.width : x + labelDimensions.width > center.x ? x - (labelDimensions.width / 2) : x;
|
||||
var visualY = y >= center.y - labelDimensions.height / 2 ? y - labelDimensions.height / 2 : y;
|
||||
|
||||
g.drawString(label, visualX, visualY);
|
||||
|
||||
|
@ -80,29 +80,29 @@ function drawScaleLabels(){
|
|||
}
|
||||
|
||||
function drawIcons() {
|
||||
const sunIcon = {
|
||||
width : 24, height : 24, bpp : 4,
|
||||
var sunIcon = {
|
||||
width : 24, height : 24, bpp : 1,
|
||||
transparent : 0,
|
||||
buffer : require("heatshrink").decompress(atob("AAkP+ALeA40PAYf/BYv/CYYLBBwIICCQ4ACHI4ICEIgkEAg48GDApcFAoYPBBY5NDBZIjLHZpTLNZiDKTZSzMZZT7iA="))
|
||||
buffer : require("heatshrink").decompress(atob("gEYAQ0IgEQjkAnEMv0wgH/gEB4eAgcA4EHgHgg0AsHmgFnAQQICBwQUCDQQgCEwQsCGQQ+IA"))
|
||||
};
|
||||
g.drawImage(sunIcon, center.x + 15, center.y - 12);
|
||||
|
||||
const sunRainIcon = {
|
||||
width : 24, height : 24, bpp : 4,
|
||||
var sunRainIcon = {
|
||||
width : 24, height : 24, bpp : 1,
|
||||
transparent : 0,
|
||||
buffer : require("heatshrink").decompress(atob("AB/wBbEPBAoGEDI/wh4jJBQIMJEgUP///IpAJCBgf/+ALCAQRJFAoIHECgI7FIYwSEHAoGBEQwsEDIJdHCYYLLFwwTEQQwGFQQQACYpYpLf0AAEA"))
|
||||
buffer : require("heatshrink").decompress(atob("AAeAAQmEgEQhEAhIIBg1ggEEj8AhEw4HokFAglAnEGoEGgHAgcRgEBkQCBgQCBgcAgUBwARBv/4HAcgiAFDCoIAC"))
|
||||
};
|
||||
g.drawImage(sunRainIcon, center.x - 12, 30);
|
||||
|
||||
const rainIcon = {
|
||||
width : 24, height : 24, bpp : 4,
|
||||
var rainIcon = {
|
||||
width : 24, height : 24, bpp : 1,
|
||||
transparent : 0,
|
||||
buffer : require("heatshrink").decompress(atob("ADnwBRP/AIQAGh4ZKA4YLLh//EwoTFh4GCCIIfGDAQ5DIQ5bIBbQvII4gAGWLwzBOoarLCw4RKLBAAgA"))
|
||||
buffer : require("heatshrink").decompress(atob("AA0gAQPMgEBgUAgQCCgPwAgMYj0AhkQgEECwICGBYMIj//+ArFgURwAqBB4NEgEQghAJ"))
|
||||
};
|
||||
g.drawImage(rainIcon, center.x - 44, center.y - 12);
|
||||
}
|
||||
|
||||
g.setBgColor(0,0,0);
|
||||
g.setBgColor(g.theme.bg);
|
||||
g.clear();
|
||||
|
||||
drawTicks();
|
||||
|
@ -110,9 +110,13 @@ drawScaleLabels();
|
|||
drawIcons();
|
||||
|
||||
try {
|
||||
Bangle.getPressure().then(data => {
|
||||
drawHand(Math.round(data.pressure));
|
||||
});
|
||||
function baroHandler(data) {
|
||||
if (data===undefined) // workaround for https://github.com/espruino/BangleApps/issues/1429
|
||||
setTimeout(() => Bangle.getPressure().then(baroHandler), 500);
|
||||
else
|
||||
drawHand(Math.round(data.pressure));
|
||||
}
|
||||
Bangle.getPressure().then(baroHandler);
|
||||
} catch(e) {
|
||||
print(e.message);
|
||||
print("barometer not supporter, show a demo value");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "barometer",
|
||||
"name": "Barometer",
|
||||
"shortName":"Barometer",
|
||||
"version":"0.01",
|
||||
"version":"0.03",
|
||||
"description": "A simple barometer that displays the current air pressure",
|
||||
"icon": "barometer.png",
|
||||
"tags": "tool,outdoors",
|
||||
|
|
|
@ -45,3 +45,4 @@
|
|||
0.39: Fix passkey support (fix https://github.com/espruino/Espruino/issues/2035)
|
||||
0.40: Bootloader now rebuilds for new firmware versions
|
||||
0.41: Add Keyboard and Mouse Bluetooth HID option
|
||||
0.42: Sort *.boot.js files lexically and by optional numeric priority, e.g. appname.<priority>.boot.js
|
||||
|
|
|
@ -195,7 +195,19 @@ if (!Bangle.appRect) { // added in 2v11 - polyfill for older firmwares
|
|||
|
||||
// Append *.boot.js files
|
||||
// These could change bleServices/bleServiceOptions if needed
|
||||
require('Storage').list(/\.boot\.js/).forEach(bootFile=>{
|
||||
var getPriority = /.*\.(\d+)\.boot\.js$/;
|
||||
require('Storage').list(/\.boot\.js/).sort((a,b)=>{
|
||||
var aPriority = a.match(getPriority);
|
||||
var bPriority = b.match(getPriority);
|
||||
if (aPriority && bPriority){
|
||||
return parseInt(aPriority[1]) - parseInt(bPriority[1]);
|
||||
} else if (aPriority && !bPriority){
|
||||
return -1;
|
||||
} else if (!aPriority && bPriority){
|
||||
return 1;
|
||||
}
|
||||
return a > b;
|
||||
}).forEach(bootFile=>{
|
||||
// we add a semicolon so if the file is wrapped in (function(){ ... }()
|
||||
// with no semicolon we don't end up with (function(){ ... }()(function(){ ... }()
|
||||
// which would cause an error!
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "boot",
|
||||
"name": "Bootloader",
|
||||
"version": "0.41",
|
||||
"version": "0.42",
|
||||
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
||||
"icon": "bootloader.png",
|
||||
"type": "bootloader",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# Bowser Watchface
|
||||
|
||||
Show your evil character.
|
||||
With style!
|
||||
|
||||
Bowser jumps once every minute to advance the clock. hehe
|
|
@ -0,0 +1 @@
|
|||
E.toArrayBuffer(atob("Ly+EARERERERERERERERER//8RERERERERERERERERERERERERERERzP//d3EREREREREREREREREREREREREcz//3dxERERERERERERERERERERERERHMz/93dxERERERERERERERERERERERERd8zHd3f/8REREREREREREREREREREREXfMx3d3//ERERERERERERERERERERERF3d3d3d//3cRHBEREREREREREREREREXd3d3d3f/93fMDMERERERERERERERERF3d3d3d3//d3zAzBERERERERERERERERd3d3fMd3f//8zMwREREREREREREREREXd3d8zMd3f/zMzMERERERERERERERERF3d3fMzHd3/8zMzBERERERERER93d3//d3d3x3/Mx3fMzP8REREREREXzP/3d//3d3d8d3fMzMz/8RERERERERF8z/93f/93d3fHd3zMzM//ERERER//93zM//93//d3d3z/d/9////xEREREcz/d3zM/3d///d3d8zHERH/ERERERERHM/3d8zP93f//3d3fMxxER/xERERER/3zMd3d3d3d3f///d3zM8REREREREREf93d3d3d3d3d3d///d3zMERERERERERH/d3d3d3d3d3d3f//3d8zBERERERERERd3d3f///d3d3d3//93fMzP8RERERERH//3d3zP/3d//////3d3d8wRERERERER//93d8z/93f/////93d3fMEREREREREc//d3fMx3f//MzMd3d3d3dxERERERERHMx3d3d3d//8zMAPd3zMzHERERERERH/d3d3d3d3f3fMzwDMzAD8zBERERERER/3d3d3d3d393zM8AzMwA/MwRERERERH913d///d3d/d3AAzMzMAAzMERERERERREd3fM/3d//3d//MzMzMwP/BEREREREURHd3zP93f/93f/zMzMzMD/wRERERERFP/3d8zHd3//d3d3zMzMzAAMERERERER//93d3d3d//3d3d8zMzMwP8BEREREREf//d3d3d3f/93d3fMzMzMD/ARERERERFMzHf/93d3/3d3d3EczM/wABERERERERR3x3fMd3f/93d3dxEREREREREREREREUd8d3zHd3//d3d3cRERERERERERERERFHd3d3d///d3d3dxERERERERERERERERR3d3f///93d3dxEREREREREREREREREUd3d3////d3d3cRERERERERERERERERH//////3d3d3d8ERERERERERERERERER/////MzMx3fMz/8REREREREREREREREf////zMzMd3zM//EREREREREREREREREf/8zMzMzMzMzP//ERERERERERERERERERzMzM/8zP/xEREREREREREREREREREREczMzP/Mz/8REREREREREREREREREREczMzMz//8///xERERERERERERERERERA="))
|
|
@ -0,0 +1,102 @@
|
|||
var sprite = {
|
||||
width : 47, height : 47, bpp : 3,
|
||||
transparent : 1,
|
||||
buffer : require("heatshrink").decompress(atob("kmSpICFn/+BAwCImV//VICJuT//SogRMpmT/2SCJtSyQDB/4RMymRkmX/gRLygDC3/piVhCJElAYf/pNIkgRIlIDCl/6pVBkIRIGwWJEYPypMJCI9KGwQRBLANIPRI2CGoPkyVCBwmeyVLTYNJom8yImBz4gEqV/6Vf+g2BPwf/IIq8C/+kyVRkgDBp/5CIX/+mkz/+y/9BIOf0v6///5LdCz+kCIOk34RBYQMSp5XBGQVk/pNBAQP/9IyBxGSv4yCk/1OIK8EC4QgEpM/JgJ+EGoIRBTApQCEYvplLOFXIIdBO4SqBeQJABGoeTDQMlk5WCAAPSYQLgEz4aBlM/9IgB/7CCcAvP/QsBiVfUwOJBgUiCIcmpAVCy/+pMAKwMkRgIRCp6VBAwW6qVOgmSgPkwgRDv53E6WSuEkyEPRgmf2VJv5HBl2SgAKBwEJRgnJiVKp/Sr/0y/yBQOQv56DKwVSv2STwO/DgWD/BADmaDByRoBYoQRCgFCCIf/+jgDNwOUAwMg/kSPQbODX4IJBAwUH8B6DsmRl5oBl7OBklMyV+gBoDycSxMpiVLZwS8EAQeYyjaByR6BBIJBDAQnEIgbFCogOFRgQDBr//I4L0EAQsxAYP//5WCGQ6MCAAKbCpKYEAQiMB//kIQOUyf+CJF/CIIEBTYOfcgQRHBQv/CJKnBpP8GRTCDJIPkGRQCB5I3C/n/EZUgA"))
|
||||
};
|
||||
|
||||
const boxes = {
|
||||
width : 122, height : 56, bpp : 3,
|
||||
transparent : 1,
|
||||
buffer : require("heatshrink").decompress(atob("kmZkmSpICPwgDBmQUQAQMJAYNkFiOSiQDB5JESAYQsSpADByYsSyBZBydt23bAR+wgFJkwUQAQNggGSposR23AgMkzZESwECpM2IiUAgmSFiW2gDlBFiVsgDlBFiXYgDNBL4MDWZy2FgEGWZy2FgENWZy2EL4MbWZpTBWwZfBXJpTCWwZiCWZpTBWwZiCWZsbWwhiCWZpWCWwTORWwgXRWwgXRWwZESWwZESWwZESWwYXRWwgXRW362/W362/W362/W362/W362/W362/W362/W362/W362/W362/WwuAgazOWwsAgyzOWwsAhqzOWwhfBjazNKYK2DL4K5NKYS2DMQSzNKYK2DMQSzNja2EMQSzNKwS2CZyK2EC6K2EC6K2DIiS2DIiS2DIiUAFoMAAFTkBFtckyAtrLgWSpICnLIIsqyVAgAsqpIA="))
|
||||
};
|
||||
|
||||
const background = {
|
||||
width : 176, height : 176, bpp : 3,
|
||||
transparent : 5,
|
||||
buffer : require("heatshrink").decompress(atob("kmSpIC/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/ATWAgEAIP1///8iRB8gf/AAOCIPdIIARBBoJB/+E4IP4ABghB9v4CB8BB5g/92//9pB7wP/97FEIO9IgDACAAn8iVBIOlHH4xBDnA+wyY9IAAmB/BB//5B/IOQ/OAARBup5B/yV/IP5B/IP5BRt5B7/wDC7aD8/w+B+3bBgP7IP5B7HYNt23/AQPfIPX/9oCC24IDINwCBIRAAHIOACBHI3+g4EC/l/4BByAQkA//wpED//4gGAhJB3pMAgQFBgEBH3AC/AX4C/AX4C/AX4C/AX4C/AUOAgBB/v//ghB9gf///gH3UgiVIIAJBBwRB5j+CIIf8uBB5//wIIXb//+hJB6o/92/7v5B7/0/97GCIPYAG4MgIP/BjkSIP34/hB//5B/AAQ+0IP5B/IP5BN7ZB97///wCBIPX93yAB2wCB+5B5tv//dt24CB35B5v/+n/t+P/I4PH8ESIO38gFA/+CgH/+EIgiD3gACCPoMAgQ+2AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/ASVIgAACgRB/IPY8GkAHBiRB/IPBLKgJB/IP5B/AQUAkmQghB/IP2AgEAyVAiRB/IP5BBpMAIP5B/IIUkgBB/IP5BpoAsBgJBOgEEIIoIBIP5BlyE27dt2EEIJ4CBBAlIgRBgpEAhu2IIO24ESQwxB/IJQhGkEJIL8GHwQCDgOweQpB/IKMkwAKJILVgAofYeQhBzsEAIKICLoESILmBQARBBtuwgZB3kA4B4ENIgJBcpMAIMYCDIOcAgEbHYgCGsEJkhEBE6cBIP5BZfYQ+JIIkDsEBIP5BVyEAIKtAHxgCDwBEBINk2IKCGCIKmSpECIP5BUkEBHyACD2BBUFoMJIP5BSpEbHyQCDIP5BXkmAIP5B/AQcAbKJB/ILH/AAP8hM/AgWSv4KCAAP+gmfAoXJk4ME//gpIEC8mTBgvwkgEC+QRDAAX4gVPAgP5kgsCLwWQh/kMIUf5LuFg4jBAoMBKAJ5EwF/AoUA/yFFoE/CI6RDgY+BCIQsDIP5B/IP5B/IP5B/IJ/AIJfghJBKv0EIJcAIJfwIP5BMhMAAAMEz5BGgmABoVJII9IBgUkII8kBgUSII8CoAMBhJB/IIsQoMAYoP/AAP4YpAMC/+BII9/BgXAYpAMC8DFIBgXwIIcCIP6DCgkQh/kCIRBIbQcBIJAFCgBBICI5BE/IRDFgQA="))
|
||||
};
|
||||
|
||||
numbersDims = {
|
||||
width: 20,
|
||||
height: 44
|
||||
};
|
||||
const numbers = [
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARGQKYQIDAwUEBxMAAQNAgECpMgAQMkB4IOIAQQLCgEQBwQaBgEBB1oCBBwYCCiRWDCIRWEO5wOHAX4CnA=")),
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARNIKYIIEwEAggOKNIQODyAHCBxQsWB3TUFgMgA4sSBwzU/AVA=")),
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKggIBAwkCBw+QCIQLCgIRCDQcQBwwyDDwUSCgVAAwIOBEwI7EpI7FBw4FDghZGHwgOEF4Y+CEYQ+DBxQADNAIAFNAIOFa/4CoA=")),
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKosSAwsBBw4aCoEAgQjEBoIpEBwtIBoIUEwEAggUDBwwyDDoWQA4ZWHhIIEJQoOCgI+EBwMQEAYOJO4oLBO4oRDJQrX/AU4")),
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARNIKgQIDwAGBgQOJNQYOCyAHDBxEggB6BBwYDBiVABxIjBCIIODF4YOEAAkBV40QBwxiDNAosEB0IC/AUg")),
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ5UFkmQAwkCBxIdGCIIIDBxAsTgAaEkEASooOBiQOVJQgOBiBKDBxMSJQwRBLIgRCBwjX/AVA=")),
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARGQKgYICAwcCBxADBiQdDkEANYoOGEAYyEHYoOIHYqfFBxIdDBAMQFgZHCBysSFgwRBO46GFa/4CnA")),
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ5VGiAGFgIOIDQUgBwUCEYQOJGQYNBHAlADQgOHwEAggUDpANBCgYpBBwmQAwJiGhIjDB1gC/AU4A=")),
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKYYICAwcEBxGQgAaDgVJgACBDQQOJgB6CBwcAiQODHa4AEhIRBpAHDiARBwAGCgIgCFIYOCFIYOHiQrEJQxlCBwzX/AVAA=")),
|
||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKggIBAwkCBw+QCIQLCgIRCDQcQBzkSTAsBHYoOIL4gOCMooOENAYOCoA4EBwoqDgiGGF4gOEa/4CoA=")),
|
||||
];
|
||||
digitPositions = [ // relative to the box
|
||||
{x:13, y:6}, {x:32, y:6},
|
||||
{x:74, y:6}, {x:93, y:6},
|
||||
];
|
||||
|
||||
var drawTimeout;
|
||||
const animation_duration = 1; // seconds
|
||||
const animation_steps = 20;
|
||||
const jump_height = 45; // top coordinate of the jump
|
||||
const seconds_per_minute = 60;
|
||||
|
||||
function draw() {
|
||||
const now = new Date();
|
||||
g.drawImage(background, 0, 0);
|
||||
var boxTL_x = 27; var boxTL_y = 29;
|
||||
var sprite_TL_x = 72; var sprite_TL_y = 161 - sprite.height;
|
||||
const seconds = now.getSeconds()%seconds_per_minute + now.getMilliseconds()/1000;
|
||||
const hours = now.getHours();
|
||||
const minutes = now.getMinutes();
|
||||
|
||||
var time_advance = seconds / animation_duration;
|
||||
|
||||
if (time_advance < 0.5) {
|
||||
sprite_TL_y += (jump_height - sprite_TL_y) * time_advance * 2;
|
||||
} else if (time_advance < 1) {
|
||||
sprite_TL_y = jump_height + (sprite_TL_y-jump_height) * (time_advance-0.5) * 2;
|
||||
}
|
||||
const box_penetration = boxTL_y + boxes.height - sprite_TL_y;
|
||||
if (box_penetration > 0) {
|
||||
boxTL_y -= box_penetration;
|
||||
}
|
||||
g.drawImage(boxes, boxTL_x, boxTL_y);
|
||||
g.drawImage(numbers[(hours / 10) >> 0], boxTL_x+digitPositions[0].x, boxTL_y+digitPositions[0].y);
|
||||
g.drawImage(numbers[(hours % 10) >> 0], boxTL_x+digitPositions[1].x, boxTL_y+digitPositions[1].y);
|
||||
g.drawImage(numbers[(minutes / 10) >> 0], boxTL_x+digitPositions[2].x, boxTL_y+digitPositions[2].y);
|
||||
g.drawImage(numbers[(minutes % 10) >> 0], boxTL_x+digitPositions[3].x, boxTL_y+digitPositions[3].y);
|
||||
g.drawImage(sprite, sprite_TL_x, sprite_TL_y);
|
||||
Bangle.drawWidgets();
|
||||
|
||||
const timeout = time_advance <= 1?
|
||||
animation_duration / animation_steps
|
||||
: (seconds_per_minute - seconds);
|
||||
setTimeout( _=>{
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, timeout * 1000);
|
||||
}
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.setTheme({bg:"#00f",fg:"#fff",dark:true}).clear();
|
||||
|
||||
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();
|
||||
|
||||
draw();
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,14 @@
|
|||
{ "id": "bowserWF",
|
||||
"name": "Bowser Watchface",
|
||||
"shortName":"Bowser Watchface",
|
||||
"version":"0.01",
|
||||
"description": "Let bowser show you the time",
|
||||
"icon": "app.png",
|
||||
"tags": "",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"bowserWF.app.js","url":"app.js"},
|
||||
{"name":"bowserWF.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -17,3 +17,6 @@
|
|||
0.06: Fix bug if no request waiting time is set
|
||||
Fix bug if no connection data was cached
|
||||
Fix error during disconnect
|
||||
0.07: Recorder icon only blue if values actually arive
|
||||
Adds some preset modes and a custom one
|
||||
Restructure the settings menu
|
||||
|
|
|
@ -29,6 +29,7 @@ Heart Rate Service (`180D`) and characteristic (`2A37`).
|
|||
So far it has been tested on:
|
||||
|
||||
* CooSpo Bluetooth Heart Rate Monitor
|
||||
* Wahoo TICKR X 2
|
||||
|
||||
## Internals
|
||||
|
||||
|
@ -36,7 +37,6 @@ This replaces `Bangle.setHRMPower` with its own implementation.
|
|||
|
||||
## TODO
|
||||
|
||||
* Maybe a `bthrm.settings.js` and app (that calls it) to enable it to be turned on and off
|
||||
* A widget to show connection state?
|
||||
* Specify a specific device by address?
|
||||
|
||||
|
|
|
@ -548,9 +548,7 @@
|
|||
E.on("kill", ()=>{
|
||||
if (gatt && gatt.connected){
|
||||
log("Got killed, trying to disconnect");
|
||||
var promise = gatt.disconnect();
|
||||
promise.then(()=>log("Disconnected on kill"));
|
||||
promise.catch((e)=>log("Error during disconnnect on kill", e));
|
||||
var promise = gatt.disconnect().then(()=>log("Disconnected on kill")).catch((e)=>log("Error during disconnnect on kill", e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"mode": 1,
|
||||
"enabled": true,
|
||||
"replace": true,
|
||||
"debuglog": false,
|
||||
|
@ -6,6 +7,12 @@
|
|||
"allowFallback": true,
|
||||
"warnDisconnect": false,
|
||||
"fallbackTimeout": 10,
|
||||
"custom_replace": false,
|
||||
"custom_debuglog": false,
|
||||
"custom_startWithHrm": false,
|
||||
"custom_allowFallback": false,
|
||||
"custom_warnDisconnect": false,
|
||||
"custom_fallbackTimeout": 10,
|
||||
"gracePeriodNotification": 0,
|
||||
"gracePeriodConnect": 0,
|
||||
"gracePeriodService": 0,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "bthrm",
|
||||
"name": "Bluetooth Heart Rate Monitor",
|
||||
"shortName": "BT HRM",
|
||||
"version": "0.06",
|
||||
"version": "0.07",
|
||||
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
@ -12,7 +12,7 @@
|
|||
"storage": [
|
||||
{"name":"bthrm.app.js","url":"bthrm.js"},
|
||||
{"name":"bthrm.recorder.js","url":"recorder.js"},
|
||||
{"name":"bthrm.boot.js","url":"boot.js"},
|
||||
{"name":"bthrm.0.boot.js","url":"boot.js"},
|
||||
{"name":"bthrm.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"bthrm.settings.js","url":"settings.js"},
|
||||
{"name":"bthrm.default.json","url":"default.json"}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
Bangle.removeListener('BTHRM', onHRM);
|
||||
if (Bangle.setBTRHMPower) Bangle.setBTHRMPower(0,"recorder");
|
||||
},
|
||||
draw : (x,y) => g.setColor((Bangle.isBTHRMConnected && Bangle.isBTHRMConnected())?"#00f":"#88f").drawImage(atob("DAwBAAAAMMeef+f+f+P8H4DwBgAA"),x,y)
|
||||
draw : (x,y) => g.setColor((bpm != "")?"#00f":"#88f").drawImage(atob("DAwBAAAAMMeef+f+f+P8H4DwBgAA"),x,y)
|
||||
};
|
||||
}
|
||||
})
|
||||
|
|
|
@ -20,182 +20,147 @@
|
|||
var mainmenu = {
|
||||
'': { 'title': 'Bluetooth HRM' },
|
||||
'< Back': back,
|
||||
'Use BT HRM': {
|
||||
value: !!settings.enabled,
|
||||
format: v => settings.enabled ? "On" : "Off",
|
||||
'Mode': {
|
||||
value: 0 | settings.mode,
|
||||
min: 0,
|
||||
max: 3,
|
||||
format: v => ["Off", "Default", "Both", "Custom"][v],
|
||||
onchange: v => {
|
||||
writeSettings("enabled",v);
|
||||
settings.mode = v;
|
||||
switch (v){
|
||||
case 0:
|
||||
writeSettings("enabled",false);
|
||||
break;
|
||||
case 1:
|
||||
writeSettings("enabled",true);
|
||||
writeSettings("replace",true);
|
||||
writeSettings("debuglog",false);
|
||||
writeSettings("startWithHrm",true);
|
||||
writeSettings("allowFallback",true);
|
||||
writeSettings("fallbackTimeout",10);
|
||||
break;
|
||||
case 2:
|
||||
writeSettings("enabled",true);
|
||||
writeSettings("replace",false);
|
||||
writeSettings("debuglog",false);
|
||||
writeSettings("startWithHrm",false);
|
||||
writeSettings("allowFallback",false);
|
||||
break;
|
||||
case 3:
|
||||
writeSettings("enabled",true);
|
||||
writeSettings("replace",settings.custom_replace);
|
||||
writeSettings("debuglog",settings.custom_debuglog);
|
||||
writeSettings("startWithHrm",settings.custom_startWithHrm);
|
||||
writeSettings("allowFallback",settings.custom_allowFallback);
|
||||
writeSettings("fallbackTimeout",settings.custom_fallbackTimeout);
|
||||
break;
|
||||
}
|
||||
writeSettings("mode",v);
|
||||
}
|
||||
},
|
||||
'Replace HRM': {
|
||||
value: !!settings.replace,
|
||||
format: v => settings.replace ? "On" : "Off",
|
||||
onchange: v => {
|
||||
writeSettings("replace",v);
|
||||
}
|
||||
},
|
||||
'Start with HRM': {
|
||||
value: !!settings.startWithHrm,
|
||||
format: v => settings.startWithHrm ? "On" : "Off",
|
||||
onchange: v => {(function(back) {
|
||||
function writeSettings(key, value) {
|
||||
var s = require('Storage').readJSON(FILE, true) || {};
|
||||
s[key] = value;
|
||||
require('Storage').writeJSON(FILE, s);
|
||||
readSettings();
|
||||
}
|
||||
'Custom Mode': function() { E.showMenu(submenu_custom); },
|
||||
'Debug': function() { E.showMenu(submenu_debug); }
|
||||
};
|
||||
|
||||
function readSettings(){
|
||||
settings = Object.assign(
|
||||
require('Storage').readJSON("bthrm.default.json", true) || {},
|
||||
require('Storage').readJSON(FILE, true) || {}
|
||||
);
|
||||
}
|
||||
|
||||
var FILE="bthrm.json";
|
||||
var settings;
|
||||
readSettings();
|
||||
|
||||
var mainmenu = {
|
||||
'': { 'title': 'Bluetooth HRM' },
|
||||
'< Back': back,
|
||||
'Use BT HRM': {
|
||||
value: !!settings.enabled,
|
||||
format: v => settings.enabled ? "On" : "Off",
|
||||
var submenu_debug = {
|
||||
'' : { title: "Debug"},
|
||||
'< Back': function() { E.showMenu(mainmenu); },
|
||||
'Alert on disconnect': {
|
||||
value: !!settings.warnDisconnect,
|
||||
format: v => settings.warnDisconnect ? "On" : "Off",
|
||||
onchange: v => {
|
||||
writeSettings("enabled",v);
|
||||
writeSettings("warnDisconnect",v);
|
||||
}
|
||||
},
|
||||
'Replace HRM': {
|
||||
value: !!settings.replace,
|
||||
format: v => settings.replace ? "On" : "Off",
|
||||
'Debug log': {
|
||||
value: !!settings.debuglog,
|
||||
format: v => settings.debuglog ? "On" : "Off",
|
||||
onchange: v => {
|
||||
writeSettings("replace",v);
|
||||
writeSettings("debuglog",v);
|
||||
}
|
||||
},
|
||||
'Grace periods': function() { E.showMenu(submenu_grace); }
|
||||
};
|
||||
|
||||
var submenu_custom = {
|
||||
'' : { title: "Custom mode"},
|
||||
'< Back': function() { E.showMenu(mainmenu); },
|
||||
'Replace HRM': {
|
||||
value: !!settings.custom_replace,
|
||||
format: v => settings.custom_replace ? "On" : "Off",
|
||||
onchange: v => {
|
||||
writeSettings("custom_replace",v);
|
||||
}
|
||||
},
|
||||
'Start w. HRM': {
|
||||
value: !!settings.startWithHrm,
|
||||
format: v => settings.startWithHrm ? "On" : "Off",
|
||||
value: !!settings.custom_startWithHrm,
|
||||
format: v => settings.custom_startWithHrm ? "On" : "Off",
|
||||
onchange: v => {
|
||||
writeSettings("startWithHrm",v);
|
||||
writeSettings("custom_startWithHrm",v);
|
||||
}
|
||||
},
|
||||
'HRM Fallback': {
|
||||
value: !!settings.allowFallback,
|
||||
format: v => settings.allowFallback ? "On" : "Off",
|
||||
value: !!settings.custom_allowFallback,
|
||||
format: v => settings.custom_allowFallback ? "On" : "Off",
|
||||
onchange: v => {
|
||||
writeSettings("allowFallback",v);
|
||||
writeSettings("custom_allowFallback",v);
|
||||
}
|
||||
},
|
||||
'Fallback Timeout': {
|
||||
value: settings.fallbackTimeout,
|
||||
value: settings.custom_fallbackTimeout,
|
||||
min: 5,
|
||||
max: 60,
|
||||
step: 5,
|
||||
format: v=>v+"s",
|
||||
onchange: v => {
|
||||
writeSettings("fallbackTimout",v*1000);
|
||||
writeSettings("custom_fallbackTimout",v*1000);
|
||||
}
|
||||
},
|
||||
'Conn. Alert': {
|
||||
value: !!settings.warnDisconnect,
|
||||
format: v => settings.warnDisconnect ? "On" : "Off",
|
||||
};
|
||||
|
||||
var submenu_grace = {
|
||||
'' : { title: "Grace periods"},
|
||||
'< Back': function() { E.showMenu(submenu_debug); },
|
||||
'Request': {
|
||||
value: settings.gracePeriodRequest,
|
||||
min: 0,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
format: v=>v+"ms",
|
||||
onchange: v => {
|
||||
writeSettings("warnDisconnect",v);
|
||||
writeSettings("gracePeriodRequest",v);
|
||||
}
|
||||
},
|
||||
'Debug log': {
|
||||
value: !!settings.debuglog,
|
||||
format: v => settings.debuglog ? "On" : "Off",
|
||||
'Connect': {
|
||||
value: settings.gracePeriodConnect,
|
||||
min: 0,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
format: v=>v+"ms",
|
||||
onchange: v => {
|
||||
writeSettings("debuglog",v);
|
||||
writeSettings("gracePeriodConnect",v);
|
||||
}
|
||||
},
|
||||
'Grace periods >': function() { E.showMenu(submenu); }
|
||||
};
|
||||
|
||||
var submenu = {
|
||||
'' : { title: "Grace periods"},
|
||||
'< Back': function() { E.showMenu(mainmenu); },
|
||||
'Request': {
|
||||
value: settings.gracePeriodRequest,
|
||||
min: 0,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
format: v=>v+"ms",
|
||||
onchange: v => {
|
||||
writeSettings("gracePeriodRequest",v);
|
||||
}
|
||||
},
|
||||
'Connect': {
|
||||
value: settings.gracePeriodConnect,
|
||||
min: 0,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
format: v=>v+"ms",
|
||||
onchange: v => {
|
||||
writeSettings("gracePeriodConnect",v);
|
||||
}
|
||||
},
|
||||
'Notification': {
|
||||
value: settings.gracePeriodNotification,
|
||||
min: 0,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
format: v=>v+"ms",
|
||||
onchange: v => {
|
||||
writeSettings("gracePeriodNotification",v);
|
||||
}
|
||||
},
|
||||
'Service': {
|
||||
value: settings.gracePeriodService,
|
||||
min: 0,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
format: v=>v+"ms",
|
||||
onchange: v => {
|
||||
writeSettings("gracePeriodService",v);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
E.showMenu(mainmenu);
|
||||
})
|
||||
writeSettings("startWithHrm",v);
|
||||
}
|
||||
},
|
||||
'Fallback to HRM': {
|
||||
value: !!settings.allowFallback,
|
||||
format: v => settings.allowFallback ? "On" : "Off",
|
||||
onchange: v => {
|
||||
writeSettings("allowFallback",v);
|
||||
}
|
||||
},
|
||||
'Fallback Timeout': {
|
||||
value: settings.fallbackTimeout,
|
||||
min: 5,
|
||||
max: 60,
|
||||
step: 5,
|
||||
format: v=>v+"s",
|
||||
onchange: v => {
|
||||
writeSettings("fallbackTimout",v*1000);
|
||||
}
|
||||
},
|
||||
'Conn. Alert': {
|
||||
value: !!settings.warnDisconnect,
|
||||
format: v => settings.warnDisconnect ? "On" : "Off",
|
||||
onchange: v => {
|
||||
writeSettings("warnDisconnect",v);
|
||||
}
|
||||
},
|
||||
'Debug log': {
|
||||
value: !!settings.debuglog,
|
||||
format: v => settings.debuglog ? "On" : "Off",
|
||||
onchange: v => {
|
||||
writeSettings("debuglog",v);
|
||||
}
|
||||
},
|
||||
'Grace periods': function() { E.showMenu(submenu); }
|
||||
'Notification': {
|
||||
value: settings.gracePeriodNotification,
|
||||
min: 0,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
format: v=>v+"ms",
|
||||
onchange: v => {
|
||||
writeSettings("gracePeriodNotification",v);
|
||||
}
|
||||
},
|
||||
'Service': {
|
||||
value: settings.gracePeriodService,
|
||||
min: 0,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
format: v=>v+"ms",
|
||||
onchange: v => {
|
||||
writeSettings("gracePeriodService",v);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var submenu = {
|
||||
|
|
|
@ -1,11 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Make overriding the HRM event optional
|
||||
Emit BTHRM event for external sensor
|
||||
Add recorder app plugin
|
||||
0.03: Prevent readings from internal sensor mixing into BT values
|
||||
Mark events with src property
|
||||
Show actual source of event in app
|
||||
0.04: Allow reading additional data if available: HRM battery and position
|
||||
Better caching of scanned BT device properties
|
||||
New setting for not starting the BTHRM together with HRM
|
||||
Save some RAM by not definining functions if disabled in settings
|
||||
0.02: Write available data on reset or kill
|
||||
|
|
|
@ -11,34 +11,30 @@ var currentSlot = 0;
|
|||
var hrvSlots = [10,20,30,60,120,300];
|
||||
var hrvValues = {};
|
||||
var rrRmsProgress;
|
||||
var saved = false;
|
||||
|
||||
var rrNumberOfValues = 0;
|
||||
var rrSquared = 0;
|
||||
var rrLastValue
|
||||
var rrLastValue;
|
||||
var rrMax;
|
||||
var rrMin;
|
||||
|
||||
function calcHrv(rr){
|
||||
//Calculate HRV with RMSSD method: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5624990/
|
||||
for (currentRr of rr){
|
||||
for (var currentRr of rr){
|
||||
if (!rrMax) rrMax = currentRr;
|
||||
if (!rrMin) rrMin = currentRr;
|
||||
rrMax = Math.max(rrMax, currentRr);
|
||||
rrMin = Math.min(rrMin, currentRr);
|
||||
//print("Calc for: " + currentRr);
|
||||
rrNumberOfValues++;
|
||||
if (!rrLastValue){
|
||||
rrLastValue = currentRr;
|
||||
continue;
|
||||
}
|
||||
rrSquared += (rrLastValue - currentRr)*(rrLastValue - currentRr);
|
||||
|
||||
//print("rr²: " + rrSquared);
|
||||
|
||||
rrLastValue = currentRr;
|
||||
}
|
||||
var rms = Math.sqrt(rrSquared / rrNumberOfValues);
|
||||
//print("rms: " + rms);
|
||||
return rms;
|
||||
}
|
||||
|
||||
|
@ -56,17 +52,36 @@ function draw(y, hrv) {
|
|||
if (hrvValues[hrvSlots[i]]) str += hrvValues[hrvSlots[i]].toFixed(1) + "ms";
|
||||
g.setFontVector(16).drawString(str,px,y+44+(i*17));
|
||||
}
|
||||
|
||||
|
||||
g.setRotation(3);
|
||||
g.setFontVector(12).drawString("Reset",g.getHeight()/2, g.getWidth()-10);
|
||||
g.setRotation(0);
|
||||
}
|
||||
|
||||
function write(){
|
||||
if (!hrvValues[hrvSlots[0]]){
|
||||
return;
|
||||
}
|
||||
|
||||
var file = require('Storage').open("bthrv.csv", "a");
|
||||
var data = new Date(startingTime).toISOString();
|
||||
for (var i = 0; i < hrvSlots.length; i++ ){
|
||||
data += ",";
|
||||
if (hrvValues[hrvSlots[i]]){
|
||||
data += hrvValues[hrvSlots[i]];
|
||||
}
|
||||
}
|
||||
|
||||
data += "," + rrMax + "," + rrMin + ","+rrNumberOfValues;
|
||||
data += "\n";
|
||||
file.write(data);
|
||||
Bangle.buzz(500);
|
||||
}
|
||||
|
||||
function onBtHrm(e) {
|
||||
if (e.rr && !startingTime) Bangle.buzz(500);
|
||||
if (e.rr && !startingTime) startingTime=Date.now();
|
||||
//print("Event:" + e.rr);
|
||||
|
||||
|
||||
var hrv = calcHrv(e.rr);
|
||||
if (hrv){
|
||||
if (currentSlot <= hrvSlots.length && (Date.now() - startingTime) > (hrvSlots[currentSlot] * 1000) && !hrvValues[hrvSlots[currentSlot]]){
|
||||
|
@ -74,35 +89,25 @@ function onBtHrm(e) {
|
|||
currentSlot++;
|
||||
}
|
||||
}
|
||||
if (!saved && currentSlot == hrvSlots.length){
|
||||
var file = require('Storage').open("bthrv.csv", "a");
|
||||
var data = new Date(startingTime).toISOString();
|
||||
for (var c of hrvSlots){
|
||||
data+=","+hrvValues[c];
|
||||
}
|
||||
data+="," + rrMax + "," + rrMin + ","+rrNumberOfValues;
|
||||
data+="\n";
|
||||
file.write(data);
|
||||
saved = true;
|
||||
Bangle.buzz(500);
|
||||
}
|
||||
|
||||
if (hrv){
|
||||
if (!ui){
|
||||
if (!ui){
|
||||
Bangle.setUI("leftright", ()=>{
|
||||
resetHrv();
|
||||
clear(30);
|
||||
});
|
||||
ui = true;
|
||||
}
|
||||
|
||||
draw(30, hrv);
|
||||
}
|
||||
}
|
||||
|
||||
function resetHrv(){
|
||||
write();
|
||||
hrvValues={};
|
||||
startingTime=undefined;
|
||||
currentSlot=0;
|
||||
saved=false;
|
||||
rrNumberOfValues = 0;
|
||||
rrSquared = 0;
|
||||
rrLastValue = undefined;
|
||||
|
@ -117,7 +122,6 @@ g.clear();
|
|||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
|
||||
if (Bangle.setBTHRMPower){
|
||||
Bangle.on('BTHRM', onBtHrm);
|
||||
Bangle.setBTHRMPower(1,'bthrv');
|
||||
|
@ -133,6 +137,11 @@ if (Bangle.setBTHRMPower){
|
|||
file.write(data);
|
||||
}
|
||||
|
||||
E.on('kill', ()=>{
|
||||
write();
|
||||
Bangle.setBTHRMPower(0,'bthrv');
|
||||
});
|
||||
|
||||
g.reset().setFont("6x8",2).setFontAlign(0,0);
|
||||
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
|
||||
} else {
|
||||
|
@ -140,4 +149,3 @@ if (Bangle.setBTHRMPower){
|
|||
g.drawString("Missing BT HRM",g.getWidth()/2,g.getHeight()/2 - 16);
|
||||
}
|
||||
|
||||
E.on('kill', ()=>Bangle.setBTHRMPower(0,'bthrv'));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "bthrv",
|
||||
"name": "Bluetooth Heart Rate variance calculator",
|
||||
"shortName": "BT HRV",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "Calculates HRV from a a BT HRM with interval data",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
|
@ -19,3 +19,4 @@
|
|||
Colors of circles can be configured
|
||||
Color depending on value (green -> red, red -> green) option
|
||||
Good HRM value will not be overwritten so fast anymore
|
||||
0.10: Use roboto font for time, date and day of week and center align them
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"minHR": 40,
|
||||
"maxHR": 200,
|
||||
"confidence": 0,
|
||||
"stepGoal": 10000,
|
||||
"stepDistanceGoal": 8000,
|
||||
"stepLength": 0.8,
|
||||
"batteryWarn": 30,
|
||||
"showWidgets": false,
|
||||
"weatherCircleData": "humidity",
|
||||
"circleCount": 3,
|
||||
"circle1": "hr",
|
||||
"circle2": "steps",
|
||||
"circle3": "battery",
|
||||
"circle4": "weather",
|
||||
"circle1color": "green-red",
|
||||
"circle2color": "#0000ff",
|
||||
"circle3color": "red-green",
|
||||
"circle4color": "#ffff00",
|
||||
"circle1colorizeIcon": true,
|
||||
"circle2colorizeIcon": true,
|
||||
"circle3colorizeIcon": true,
|
||||
"circle4colorizeIcon": false,
|
||||
"hrmValidity": 60
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "circlesclock",
|
||||
"name": "Circles clock",
|
||||
"shortName":"Circles clock",
|
||||
"version":"0.09",
|
||||
"version":"0.10",
|
||||
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],
|
||||
|
@ -13,7 +13,8 @@
|
|||
"storage": [
|
||||
{"name":"circlesclock.app.js","url":"app.js"},
|
||||
{"name":"circlesclock.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"circlesclock.settings.js","url":"settings.js"}
|
||||
{"name":"circlesclock.settings.js","url":"settings.js"},
|
||||
{"name":"circlesclock.default.json","url":"default.json"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"circlesclock.json"}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "circlesclock.json";
|
||||
const storage = require('Storage');
|
||||
let settings = storage.readJSON(SETTINGS_FILE, 1) || {};
|
||||
let settings = Object.assign(
|
||||
storage.readJSON("circlesclock.default.json", true) || {},
|
||||
storage.readJSON(SETTINGS_FILE, true) || {}
|
||||
);
|
||||
|
||||
function save(key, value) {
|
||||
settings[key] = value;
|
||||
storage.write(SETTINGS_FILE, settings);
|
||||
|
@ -10,8 +14,8 @@
|
|||
const valuesCircleTypes = ["empty", "steps", "stepsDist", "hr", "battery", "weather", "sunprogress", "temperature", "pressure", "altitude"];
|
||||
const namesCircleTypes = ["empty", "steps", "distance", "heart", "battery", "weather", "sun", "temperature", "pressure", "altitude"];
|
||||
|
||||
const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff", "#fff", "#000", "green-red", "red-green"];
|
||||
const namesColors = ["default", "red", "green", "blue", "yellow", "magenta", "cyan", "white", "black", "green->red", "red->green"];
|
||||
const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff", "#fff", "#000", "green-red", "red-green"];
|
||||
const namesColors = ["default", "red", "green", "blue", "yellow", "magenta", "cyan", "white", "black", "green->red", "red->green"];
|
||||
|
||||
const weatherData = ["empty", "humidity", "wind"];
|
||||
|
||||
|
@ -20,7 +24,7 @@
|
|||
'': { 'title': 'Circles clock' },
|
||||
/*LANG*/'< Back': back,
|
||||
/*LANG*/'circle count': {
|
||||
value: "circleCount" in settings ? settings.circleCount : 3,
|
||||
value: settings.circleCount,
|
||||
min: 3,
|
||||
max : 4,
|
||||
step: 1,
|
||||
|
@ -33,7 +37,7 @@
|
|||
/*LANG*/'heartrate': ()=>showHRMenu(),
|
||||
/*LANG*/'steps': ()=>showStepMenu(),
|
||||
/*LANG*/'battery warn': {
|
||||
value: "batteryWarn" in settings ? settings.batteryWarn : 30,
|
||||
value: settings.batteryWarn,
|
||||
min: 10,
|
||||
max : 100,
|
||||
step: 10,
|
||||
|
@ -43,12 +47,12 @@
|
|||
onchange: x => save('batteryWarn', x),
|
||||
},
|
||||
/*LANG*/'show widgets': {
|
||||
value: "showWidgets" in settings ? settings.showWidgets : false,
|
||||
value: !!settings.showWidgets,
|
||||
format: () => (settings.showWidgets ? 'Yes' : 'No'),
|
||||
onchange: x => save('showWidgets', x),
|
||||
},
|
||||
/*LANG*/'weather circle': {
|
||||
value: settings.weatherCircleData ? weatherData.indexOf(settings.weatherCircleData) : 1,
|
||||
/*LANG*/'weather data': {
|
||||
value: weatherData.indexOf(settings.weatherCircleData),
|
||||
min: 0, max: 2,
|
||||
format: v => weatherData[v],
|
||||
onchange: x => save('weatherCircleData', weatherData[x]),
|
||||
|
@ -62,7 +66,7 @@
|
|||
'': { 'title': /*LANG*/'Heartrate' },
|
||||
/*LANG*/'< Back': ()=>showMainMenu(),
|
||||
/*LANG*/'minimum': {
|
||||
value: "minHR" in settings ? settings.minHR : 40,
|
||||
value: settings.minHR,
|
||||
min: 0,
|
||||
max : 250,
|
||||
step: 5,
|
||||
|
@ -72,7 +76,7 @@
|
|||
onchange: x => save('minHR', x),
|
||||
},
|
||||
/*LANG*/'maximum': {
|
||||
value: "maxHR" in settings ? settings.maxHR : 200,
|
||||
value: settings.maxHR,
|
||||
min: 20,
|
||||
max : 250,
|
||||
step: 5,
|
||||
|
@ -82,7 +86,7 @@
|
|||
onchange: x => save('maxHR', x),
|
||||
},
|
||||
/*LANG*/'min. confidence': {
|
||||
value: "confidence" in settings ? settings.confidence : 0,
|
||||
value: settings.confidence,
|
||||
min: 0,
|
||||
max : 100,
|
||||
step: 10,
|
||||
|
@ -92,7 +96,7 @@
|
|||
onchange: x => save('confidence', x),
|
||||
},
|
||||
/*LANG*/'valid period': {
|
||||
value: "hrmValidity" in settings ? settings.hrmValidity : 30,
|
||||
value: settings.hrmValidity,
|
||||
min: 10,
|
||||
max : 600,
|
||||
step: 10,
|
||||
|
@ -110,7 +114,7 @@
|
|||
'': { 'title': /*LANG*/'Steps' },
|
||||
/*LANG*/'< Back': ()=>showMainMenu(),
|
||||
/*LANG*/'goal': {
|
||||
value: "stepGoal" in settings ? settings.stepGoal : 10000,
|
||||
value: settings.stepGoal,
|
||||
min: 2000,
|
||||
max : 50000,
|
||||
step: 2000,
|
||||
|
@ -120,7 +124,7 @@
|
|||
onchange: x => save('stepGoal', x),
|
||||
},
|
||||
/*LANG*/'distance goal': {
|
||||
value: "stepDistanceGoal" in settings ? settings.stepDistanceGoal : 8000,
|
||||
value: settings.stepDistanceGoal,
|
||||
min: 2000,
|
||||
max : 30000,
|
||||
step: 1000,
|
||||
|
@ -130,7 +134,7 @@
|
|||
onchange: x => save('stepDistanceGoal', x),
|
||||
},
|
||||
/*LANG*/'step length': {
|
||||
value: "stepLength" in settings ? settings.stepLength : 0.8,
|
||||
value: settings.stepLength,
|
||||
min: 0.1,
|
||||
max : 1.5,
|
||||
step: 0.01,
|
||||
|
@ -142,9 +146,6 @@
|
|||
};
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
const defaultCircleTypes = ["steps", "hr", "battery", "weather"];
|
||||
|
||||
function showCircleMenu(circleId) {
|
||||
const circleName = "circle" + circleId;
|
||||
const colorKey = circleName + "color";
|
||||
|
@ -154,19 +155,19 @@
|
|||
'': { 'title': /*LANG*/'Circle ' + circleId },
|
||||
/*LANG*/'< Back': ()=>showMainMenu(),
|
||||
/*LANG*/'data': {
|
||||
value: settings[circleName]!=undefined ? valuesCircleTypes.indexOf(settings[circleName]) : valuesCircleTypes.indexOf(defaultCircleTypes[circleId -1]),
|
||||
value: valuesCircleTypes.indexOf(settings[circleName]),
|
||||
min: 0, max: valuesCircleTypes.length - 1,
|
||||
format: v => namesCircleTypes[v],
|
||||
onchange: x => save(circleName, valuesCircleTypes[x]),
|
||||
},
|
||||
/*LANG*/'color': {
|
||||
value: settings[colorKey] ? valuesColors.indexOf(settings[colorKey]) : 0,
|
||||
value: valuesColors.indexOf(settings[colorKey]) || 0,
|
||||
min: 0, max: valuesColors.length - 1,
|
||||
format: v => namesColors[v],
|
||||
onchange: x => save(colorKey, valuesColors[x]),
|
||||
},
|
||||
/*LANG*/'colorize icon': {
|
||||
value: colorizeIconKey in settings ? settings[colorizeIconKey] : false,
|
||||
value: settings[colorizeIconKey] || false,
|
||||
format: () => (settings[colorizeIconKey] ? 'Yes' : 'No'),
|
||||
onchange: x => save(colorizeIconKey, x),
|
||||
},
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
0.01: New app
|
||||
0.20: Settings menu, different fonts, inverse fonts
|
||||
0.21: Fixed settings menu, four more fonts
|
||||
0.22: Changed timing code, original "Nunito" Font is back!
|
||||
0.23: Customizer! Unused fonts no longer take up precious memory.
|
||||
0.24: Added previews to the customizer.
|
||||
|
|
|
@ -6,16 +6,6 @@ if (settings.fontIndex==undefined) {
|
|||
settings.fontIndex=0;
|
||||
require('Storage').writeJSON("myapp.json", settings);
|
||||
}
|
||||
require("FontTeletext10x18Ascii").add(Graphics);
|
||||
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
queueDraw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
function draw() {
|
||||
var date = new Date();
|
||||
|
@ -28,9 +18,12 @@ function draw() {
|
|||
require('contourclock').drawClock(settings.fontIndex);
|
||||
}
|
||||
|
||||
require("FontTeletext10x18Ascii").add(Graphics);
|
||||
Bangle.setUI("clock");
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
draw();
|
||||
queueDraw();
|
||||
setTimeout(function() {
|
||||
setInterval(draw,60000);
|
||||
}, 60000 - Date.now() % 60000);
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
<style>
|
||||
input[type=checkbox] {
|
||||
opacity:0;
|
||||
}
|
||||
input[type=checkbox] + label {
|
||||
opacity:0.2;
|
||||
}
|
||||
input[type=checkbox]:checked + label {
|
||||
opacity:1;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h3> Select Fonts to upload:</h3>
|
||||
<form>
|
||||
<input type="checkbox" id="BarlowCond">
|
||||
<label for="BarlowCond"><img src="fonts/BarlowCond-p1.png"> <img src="fonts/BarlowCond-p2.png"></label><br>
|
||||
<input type="checkbox" id="BebasNeue">
|
||||
<label for="BebasNeue"><img src="fonts/BebasNeue-p1.png"> <img src="fonts/BebasNeue-p2.png"></label><br>
|
||||
<input type="checkbox" id="Dekko">
|
||||
<label for="Dekko"><img src="fonts/Dekko-p1.png"> <img src="fonts/Dekko-p2.png"></label><br>
|
||||
<input type="checkbox" id="DinAlternate">
|
||||
<label for="DinAlternate"><img src="fonts/DinAlternate-p1.png"> <img src="fonts/DinAlternate-p2.png"></label><br>
|
||||
<input type="checkbox" id="Impact">
|
||||
<label for="Impact"><img src="fonts/Impact-p1.png"> <img src="fonts/Impact-p2.png"></label><br>
|
||||
<input type="checkbox" id="Nunito">
|
||||
<label for="Nunito"><img src="fonts/Nunito-p1.png"> <img src="fonts/Nunito-p2.png"></label><br>
|
||||
<input type="checkbox" id="OpenSansEC">
|
||||
<label for="OpenSansEC"><img src="fonts/OpenSansEC-p1.png"> <img src="fonts/OpenSansEC-p2.png"></label><br>
|
||||
<input type="checkbox" id="Phosphate">
|
||||
<label for="Phosphate"><img src="fonts/Phosphate-p1.png"> <img src="fonts/Phosphate-p2.png"></label><br>
|
||||
<input type="checkbox" id="Quicksand">
|
||||
<label for="Quicksand"><img src="fonts/Quicksand-p1.png"> <img src="fonts/Quicksand-p2.png"></label><br>
|
||||
<input type="checkbox" id="SairaEC">
|
||||
<label for="SairaEC"><img src="fonts/SairaEC-p1.png"> <img src="fonts/SairaEC-p2.png"></label><br>
|
||||
<input type="checkbox" id="Teko">
|
||||
<label for="Teko"><img src="fonts/Teko-p1.png"> <img src="fonts/Teko-p2.png"></label><br>
|
||||
<input type="checkbox" id="Yumaro">
|
||||
<label for="Yumaro"><img src="fonts/Yumaro-p1.png"> <img src="fonts/Yumaro-p2.png"></label><br>
|
||||
<input type="checkbox" id="YuseiMagic">
|
||||
<label for="YuseiMagic"><img src="fonts/YuseiMagic-p1.png"> <img src="fonts/YuseiMagic-p2.png"></label><br>
|
||||
<p><button id="upload" class="btn btn-primary">Upload selected Fonts</button></p>
|
||||
<script src="../../core/lib/customize.js"></script>
|
||||
<script>
|
||||
FontList = ["BarlowCond", "BebasNeue", "Dekko", "DinAlternate",
|
||||
"Impact", "Nunito", "OpenSansEC", "Phosphate", "Quicksand", "SairaEC",
|
||||
"Yumaro", "YuseiMagic"]
|
||||
// When the 'upload' button is clicked...
|
||||
document.getElementById("upload").addEventListener("click", function() {
|
||||
var n=0;
|
||||
var fonts = [];
|
||||
for (fontName of FontList) {
|
||||
if (document.getElementById(fontName).checked==true) {
|
||||
var f = new Object();
|
||||
fonts.push({
|
||||
name:"contourclock-"+n+".json",
|
||||
url:"font-"+fontName+".json"
|
||||
});
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (n>0)
|
||||
sendCustomizedApp({storage:fonts});
|
||||
else
|
||||
alert("Please select at least one Font!");
|
||||
|
||||
});
|
||||
</script>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name":"Nunito",
|
||||
"size":"80",
|
||||
"characters":[
|
||||
{"width" : "62", "buffer":"VVVVVVV//////9VVVVVVVVVVVVX////////1VVVVVVVVVVX/wAAAAAA/9VVVVVVVVVV/AAAAAAAAD/VVVVVVVVV/AAAAAAAAAA/VVVVVVVVfwAAAAAAAAAAP1VVVVVVXwAAAAAAAAAAAP1VVVVVV8AAAAAAAAAAAAD1VVVVVfAAAAAAAAAAAAAPVVVVVXwAAAAAAAAAAAAAPVVVVV8AAAAAAAAAAAAAAPVVVVXwAAAAAAAAAAAAAAPVVVV8AAAAAAAAAAAAAAA9VVVXwAAAAAAAAAAAAAAA9VVV8AAAAAAAAAAAAAAAD1VVXwAAAAAAAAAAAAAAAD1VV8AAAAAAAAAAAAAAAAPVVXwAAAAAAA/8AAAAAAAPVV8AAAAAAA//8AAAAAAA9VXwAAAAAAP1V8AAAAAAD1VfAAAAAAD1VV8AAAAAAD1XwAAAAAA9VVV8AAAAAAPVfAAAAAAD1VVV8AAAAAA9V8AAAAAA9VVVXwAAAAAD1XwAAAAAD1VVVfAAAAAAD18AAAAAAPVVVVfAAAAAAPXwAAAAAD1VVVV8AAAAAA9fAAAAAAPVVVVXwAAAAAD18AAAAAA9VVVVfAAAAAAPXwAAAAAD1VVVV8AAAAAAPfAAAAAAPVVVVXwAAAAAA98AAAAAA9VVVVXwAAAAAD/AAAAAAD1VVVVfAAAAAAP8AAAAAAPVVVVV8AAAAAA/wAAAAAA9VVVVXwAAAAAD/AAAAAAD1VVVVfAAAAAAP8AAAAAA9VVVVV8AAAAAA/wAAAAAD1VVVVXwAAAAAD/AAAAAAPVVVVVfAAAAAAP8AAAAAA9VVVVV8AAAAAA/wAAAAAD1VVVVXwAAAAAD/AAAAAAPVVVVVfAAAAAAP8AAAAAA9VVVVV8AAAAAA/wAAAAAA9VVVVXwAAAAAD/AAAAAAD1VVVVfAAAAAAP8AAAAAAPVVVVV8AAAAAA/wAAAAAA9VVVVXwAAAAAD3wAAAAAD1VVVVfAAAAAAPfAAAAAAPVVVVXwAAAAAA98AAAAAA9VVVVfAAAAAAD3wAAAAAD1VVVV8AAAAAA9fAAAAAAPVVVVXwAAAAAD18AAAAAA9VVVVfAAAAAAPXwAAAAAA9VVVV8AAAAAA9XwAAAAAD1VVVfAAAAAAD1fAAAAAAPVVVV8AAAAAA9V8AAAAAAPVVVXwAAAAAD1XwAAAAAA9VVV8AAAAAAPVXwAAAAAA9VVXwAAAAAA9VfAAAAAAA9VV8AAAAAAPVV8AAAAAAA9V/AAAAAAA9VV8AAAAAAA//wAAAAAAD1VXwAAAAAAA/wAAAAAAA9VVXwAAAAAAAAAAAAAAAD1VVfAAAAAAAAAAAAAAAA9VVV8AAAAAAAAAAAAAAAD1VVV8AAAAAAAAAAAAAAA9VVVV8AAAAAAAAAAAAAAD1VVVXwAAAAAAAAAAAAAA9VVVVXwAAAAAAAAAAAAAPVVVVVXwAAAAAAAAAAAAA9VVVVVXwAAAAAAAAAAAAPVVVVVVfwAAAAAAAAAAAD1VVVVVVfwAAAAAAAAAAD9VVVVVVVXwAAAAAAAAAA/VVVVVVVVX8AAAAAAAAA/VVVVVVVVVX/AAAAAAAA/1VVVVVVVVVV/8AAAAAD/1VVVVVVVVVVVf///////1VVVVVVVVVVVVV//////VVVVVVVQ=="},
|
||||
{"width" : "61", "buffer":"VVVVVVVVf////VVVVVVVVVVVVVVV/////9VVVVVVVVVVVVVX/AAAAD1VVVVVVVVVVVVf8AAAAAPVVVVVVVVVVVVfwAAAAAA9VVVVVVVVVVV/AAAAAAAPVVVVVVVVVVX/AAAAAAAA9VVVVVVVVVX8AAAAAAAAPVVVVVVVVVfwAAAAAAAAD1VVVVVVVVfwAAAAAAAAA9VVVVVVVV/AAAAAAAAAAPVVVVVVVV/AAAAAAAAAAD1VVVVVVX8AAAAAAAAAAA9VVVVVVX8AAAAAAAAAAAPVVVVVVXwAAAAAAAAAAAD1VVVVVXwAAAAAAAAAAAA9VVVVVXwAAAAAAAAAAAAPVVVVVV8AAAAAAAAAAAAD1VVVVVfAAAAAAAAAAAAA9VVVVVfAAAAAAAAAAAAAPVVVVVXwAAAAAAAAAAAAD1VVVVV8AAAAAAAAAAAAA9VVVVVfAAAAAAAAAAAAAPVVVVVXwAAAAAAAAAAAAD1VVVVVfAAAAAAAAAAAAA9VVVVVXwAAAAAAAAAAAAPVVVVVVfAAAAPAAAAAAAD1VVVVVXwAAA/8AAAAAAA9VVVVVVfAAD/XwAAAAAAPVVVVVVV///9V8AAAAAAD1VVVVVVX//1VfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVX///wAAAAAAA///9VVVV////wAAAAAAAD////VVX/wAAAAAAAAAAAAAAP9VX8AAAAAAAAAAAAAAAAD1V8AAAAAAAAAAAAAAAAAPV8AAAAAAAAAAAAAAAAAD1fAAAAAAAAAAAAAAAAAAPfAAAAAAAAAAAAAAAAAAD3wAAAAAAAAAAAAAAAAAA98AAAAAAAAAAAAAAAAAAPfAAAAAAAAAAAAAAAAAAD3wAAAAAAAAAAAAAAAAAA98AAAAAAAAAAAAAAAAAAPXwAAAAAAAAAAAAAAAAAD18AAAAAAAAAAAAAAAAAD1fAAAAAAAAAAAAAAAAAA9V8AAAAAAAAAAAAAAAAAPVXwAAAAAAAAAAAAAAAA/VVf/////////////////VVV////////////////9VVVVVVVVVVVVVVVVVVVVVU="},
|
||||
{"width" : "61", "buffer":"VVVVV////////1VVVVVVVVVVf/////////9VVVVVVVVX/8AAAAAAAA/9VVVVVVVf/AAAAAAAAAAP9VVVVVV/wAAAAAAAAAAAP1VVVVV/AAAAAAAAAAAAAP1VVVV8AAAAAAAAAAAAAA/VVVV8AAAAAAAAAAAAAAA9VVV8AAAAAAAAAAAAAAAD1VVfAAAAAAAAAAAAAAAA9VVXwAAAAAAAAAAAAAAAD1VXwAAAAAAAAAAAAAAAA9VV8AAAAAAAAAAAAAAAAD1VfAAAAAAAAAAAAAAAAA9VXwAAAAAAAAAAAAAAAAD1VfAAAAAAAAAAAAAAAAA9VXwAAAAAAAAAAAAAAAAPVV8AAAAAD/8AAAAAAAAD1VXwAAAD////AAAAAAAAPVVfAAA//1Vf8AAAAAAAD1VV/wP/1VVVXwAAAAAAA9VVX//9VVVVVfAAAAAAAPVVVV/VVVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVVfAAAAAAD1VVVVVVVVVVVXwAAAAAA9VVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVVfAAAAAAD1VVVVVVVVVVVXwAAAAAA9VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVV8AAAAAAA9VVVVVVVVVVV8AAAAAAA9VVVVVVVVVVV8AAAAAAAPVVVVVVVVVVV8AAAAAAAPVVVVVVVVVVV8AAAAAAAD1VVVVVVVVVV8AAAAAAAD1VVVVVVVVVV8AAAAAAAA9VVVVVVVVVV8AAAAAAAA9VVVVVVVVVV8AAAAAAAA9VVVVVVVVVV8AAAAAAAA9VVVVVVVVVV8AAAAAAAAPVVVVVVVVVV8AAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAPVVVVVVVVVVfAAAAAAAAA//////1VVVXwAAAAAAAAD//////9VVXwAAAAAAAAAAAAAAA/9VXwAAAAAAAAAAAAAAAAP1V8AAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAAD1XwAAAAAAAAAAAAAAAAA9XwAAAAAAAAAAAAAAAAAD18AAAAAAAAAAAAAAAAAA9fAAAAAAAAAAAAAAAAAAPXwAAAAAAAAAAAAAAAAAD18AAAAAAAAAAAAAAAAAA9XwAAAAAAAAAAAAAAAAAPV8AAAAAAAAAAAAAAAAAPVfAAAAAAAAAAAAAAAAAD1XwAAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAA9VV/AAAAAAAAAAAAAAAD9VVX////////////////9VVVX///////////////1VVVVVVVVVVVVVVVVVVVVVU="},
|
||||
{"width" : "60", "buffer":"VVVVVf///////9VVVVVVVVVV//////////9VVVVVVVVf/wAAAAAAAD/1VVVVVVf/AAAAAAAAAAD/VVVVVX/wAAAAAAAAAAAP9VVVVfwAAAAAAAAAAAAA/VVVV8AAAAAAAAAAAAAAD1VVV8AAAAAAAAAAAAAAA9VVXwAAAAAAAAAAAAAAAPVVXwAAAAAAAAAAAAAAAD1VXwAAAAAAAAAAAAAAAD1VXwAAAAAAAAAAAAAAAA9VXwAAAAAAAAAAAAAAAA9VXwAAAAAAAAAAAAAAAAPVXwAAAAAAAAAAAAAAAAPVXwAAAAAAAAAAAAAAAAPVXwAAAAAAAAAAAAAAAAPVV8AAAAAD/8AAAAAAAAD1VfAAAA////8AAAAAAAD1VXwAA//9VX/AAAAAAAD1VV////VVVVXwAAAAAAD1VVf//VVVVVV8AAAAAAD1VVVVVVVVVVVfAAAAAAD1VVVVVVVVVVVfAAAAAAD1VVVVVVVVVVVfAAAAAAD1VVVVVVVVVVVfAAAAAAD1VVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVX8AAAAAAPVVVVVVVVVVV/wAAAAAA9VVVVVVVX///8AAAAAAA9VVVVVVX////AAAAAAAD1VVVVVVf8AAAAAAAAAAD1VVVVVV8AAAAAAAAAAAPVVVVVVXwAAAAAAAAAAD9VVVVVVXwAAAAAAAAAAP1VVVVVVfAAAAAAAAAAA9VVVVVVVfAAAAAAAAAAD1VVVVVVVfAAAAAAAAAAD1VVVVVVVfAAAAAAAAAAA/VVVVVVVfAAAAAAAAAAAP1VVVVVVfAAAAAAAAAAAA/VVVVVVfAAAAAAAAAAAAP1VVVVVXwAAAAAAAAAAAA9VVVVVXwAAAAAAAAAAAAPVVVVVV8AAAAAAAAAAAAPVVVVVVfwAAAAAAAAAAAD1VVVVVX////8AAAAAAAD1VVVVVVf////8AAAAAAA9VVVVVVVVVVX/AAAAAAA9VVVVVVVVVVVXwAAAAAA9VVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVX/1VVVVVVV8AAAAAAPVX///1VVVVVXwAAAAAAPVf8AP/1VVVV/AAAAAAAPV8AAAP//9f/8AAAAAAAPXwAAAAP////AAAAAAAAPXwAAAAAADwAAAAAAAAAPfAAAAAAAAAAAAAAAAAA9fAAAAAAAAAAAAAAAAAA9fAAAAAAAAAAAAAAAAAA98AAAAAAAAAAAAAAAAAD18AAAAAAAAAAAAAAAAAD18AAAAAAAAAAAAAAAAAPVfAAAAAAAAAAAAAAAAAPVfAAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAD1VXwAAAAAAAAAAAAAAAPVVV8AAAAAAAAAAAAAAD9VVVfAAAAAAAAAAAAAA/1VVVX/AAAAAAAAAAAAP9VVVVV//AAAAAAAAAAD/VVVVVVV//wAAAAAAAP/1VVVVVVVV//////////9VVVVVVVVVVf///////1VVVVVV"},
|
||||
{"width" : "65", "buffer":"VVVVVVVVVVVVf///1VVVVVVVVVVVVVVVVf////9VVVVVVVVVVVVVVVX8AAAD9VVVVVVVVVVVVVVV8AAAAA9VVVVVVVVVVVVVVfAAAAAD1VVVVVVVVVVVVVXwAAAAAD1VVVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVVfAAAAAAAD1VVVVVVVVVVVXwAAAAAAAD1VVVVVVVVVVV8AAAAAAAAPVVVVVVVVVVVXwAAAAAAAA9VVVVVVVVVVV8AAAAAAAAD1VVVVVVVVVVfAAAAAAAAAPVVVVVVVVVVV8AAAAAAAAA9VVVVVVVVVVfAAAAAAAAAD1VVVVVVVVVXwAAAAAAAAAPVVVVVVVVVV8AAAAAAAAAA9VVVVVVVVVXwAAAAAAAAAD1VVVVVVVVV8AAAAAAAAAAPVVVVVVVVVfAAAAAAAAAAA9VVVVVVVVV8AAAAAAAAAAD1VVVVVVVVfAAAAAAAAAAAPVVVVVVVVXwAAAAAAAAAAA9VVVVVVVVfAAAAAAAAAAAD1VVVVVVVXwAAAAAAAAAAAPVVVVVVVV8AAAAAAAAAAAA9VVVVVVVXwAAAAAAAAAAAD1VVVVVVV8AAAAAAAAAAAAPVVVVVVVfAAAAAAAAAAAAA9VVVVVVV8AAAAAwAAAAAAD1VVVVVVfAAAAAPwAAAAAAPVVVVVVXwAAAAD3wAAAAAA9VVVVVVfAAAAA9fAAAAAAD1VVVVVXwAAAAD18AAAAAAPVVVVVV8AAAAA9XwAAAAAA9VVVVVfAAAAAPVfAAAAAAD1VVVVV8AAAAA9V8AAAAAAPVVVVVfAAAAAPVXwAAAAAA9VVVVXwAAAAD1VfAAAAAAD1VVVVfAAAAAPVV8AAAAAAPVVVVXwAAAAD1VXwAAAAAA9VVVV8AAAAAPVVfAAAAAAD1VVVXwAAAAD1VV8AAAAAAPVVVV8AAAAA9VVXwAAAAAA9VVVXwAAAAD1VVfAAAAAAD1VVV8AAAAAD///wAAAAAAD/1VXwAAAAAD//8AAAAAAAD//VfAAAAAAAAAAAAAAAAAAD/XwAAAAAAAAAAAAAAAAAAAPfAAAAAAAAAAAAAAAAAAAA98AAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAA98AAAAAAAAAAAAAAAAAAAD3wAAAAAAAAAAAAAAAAAAAPX8AAAAAAAAAAAAAAAAAAD1X/AAAAAAAAAAAAAAAAAD9VV////////8AAAAAAAD//VVVf///////8AAAAAAA//VVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVVVXwAAAAAD1VVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVVV8AAAAAA9VVVVVVVVVVVVVV8AAAAAD1VVVVVVVVVVVVVXwAAAAAPVVVVVVVVVVVVVVfAAAAAA9VVVVVVVVVVVVVV8AAAAAPVVVVVVVVVVVVVVV8AAAAA9VVVVVVVVVVVVVVV8AAAAPVVVVVVVVVVVVVVVV/AAAP1VVVVVVVVVVVVVVVV////9VVVVVVVVVVVVVVVVVf//9VVVVVQ=="},
|
||||
{"width" : "60", "buffer":"VVVX///////////1VVVVVV///////////////VVVVX/8AAAAAAAAAAAP/9VVVfAAAAAAAAAAAAAAA/VVV8AAAAAAAAAAAAAAAPVVXwAAAAAAAAAAAAAAAD1VXwAAAAAAAAAAAAAAAD1VXwAAAAAAAAAAAAAAAA9VXwAAAAAAAAAAAAAAAA9VXwAAAAAAAAAAAAAAAA9VXwAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAD1VfAAAAAAAAAAAAAAAAD1VfAAAAAAAAAAAAAAAAPVVfAAAAAAAAAAAAAAAA/VVfAAAAAAAAAAAAAAP/1VVfAAAAAAA/////////VVVfAAAAAAD///////1VVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVfAAAAAAPV/////VVVVVVfAAAAAAD///////VVVVVfAAAAAAA/AAAAA/9VVVVfAAAAAAAAAAAAAA/1VVVfAAAAAAAAAAAAAAD9VVVfAAAAAAAAAAAAAAAP1VVfAAAAAAAAAAAAAAAD9VVfAAAAAAAAAAAAAAAAPVVfAAAAAAAAAAAAAAAAPVVfAAAAAAAAAAAAAAAAD1VfAAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAAPVfAAAAAAAAAAAAAAAAAD1XwAAAAAAAAAAAAAAAAD1XwAAAAAAAAAAAAAAAAD1XwAAAAAAAAAAAAAAAAA9XwAAAAAA//wAAAAAAAA9XwAAAAA////wAAAAAAA9XwAAAAP/VVf8AAAAAAA9V/AAAP/VVVVfAAAAAAAPVf////1VVVVXwAAAAAAPVV///1VVVVVV8AAAAAAPVVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVV8AAAAAAPVVX1VVVVVVVV8AAAAAAPVX//1VVVVVVXwAAAAAAPV/8P/1VVVVVfAAAAAAA9X8AAP/9VVVX8AAAAAAA9fAAAAP//9//wAAAAAAA9fAAAAAD///8AAAAAAAA98AAAAAAADAAAAAAAAAD18AAAAAAAAAAAAAAAAAD18AAAAAAAAAAAAAAAAAD18AAAAAAAAAAAAAAAAAPV8AAAAAAAAAAAAAAAAAPV8AAAAAAAAAAAAAAAAA9V8AAAAAAAAAAAAAAAAD1V8AAAAAAAAAAAAAAAAD1V8AAAAAAAAAAAAAAAAPVVfAAAAAAAAAAAAAAAA9VVfAAAAAAAAAAAAAAAP1VVXwAAAAAAAAAAAAAA/VVVV/AAAAAAAAAAAAAP1VVVVf/AAAAAAAAAAAD/VVVVVV//AAAAAAAAAA/1VVVVVVV//wAAAAAAD/9VVVVVVVVV//////////VVVVVVVVVVVf//////9VVVVVVV"},
|
||||
{"width" : "62", "buffer":"VVVVVVVVX///////1VVVVVVVVVVVf/////////VVVVVVVVVV//AAAAAAAD/9VVVVVVVV/8AAAAAAAAAP/VVVVVVVfwAAAAAAAAAAA/VVVVVVfwAAAAAAAAAAAAPVVVVVX8AAAAAAAAAAAAAPVVVVV8AAAAAAAAAAAAAA9VVVVfAAAAAAAAAAAAAAD1VVVXwAAAAAAAAAAAAAAD1VVV8AAAAAAAAAAAAAAAPVVVfAAAAAAAAAAAAAAAA9VVXwAAAAAAAAAAAAAAAD1VV8AAAAAAAAAAAAAAAAPVVXwAAAAAAAAAAAAAAAA9VV8AAAAAAAAAAAAAAAAPVVfAAAAAAAAAAAAAAAAA9VV8AAAAAAAAD//AAAAAPVVfAAAAAAAAP////AAAA9VV8AAAAAAAP/VVf//AA/VVXwAAAAAAD9VVVVf///1VV8AAAAAAA9VVVVVVf/1VVXwAAAAAAPVVVVVVVVVVVV8AAAAAAD1VVVVVVVVVVVXwAAAAAA9VVVVVVVVVVVVfAAAAAAPVVVVVVVVVVVVV8AAAAAA9VVVX/VVVVVVVfAAAAAAPVVX////1VVVVV8AAAAAA9VX//AP/9VVVVXwAAAAAD1X/AAAAD/VVVVfAAAAAAPX/AAAAAA/VVVV8AAAAAD1/AAAAAAAP1VVXwAAAAAPfAAAAAAAAP1VV8AAAAAAPwAAAAAAAAD1VXwAAAAAAMAAAAAAAAAD1VfAAAAAAAAAAAAAAAAAPVV8AAAAAAAAAAAAAAAAAPVXwAAAAAAAAAAAAAAAAAPVfAAAAAAAAAAAAAAAAAA9V8AAAAAAAAAAAAAAAAAA9XwAAAAAAAAAAAAAAAAAD1fAAAAAAAAAAAAAAAAAAD18AAAAAAAAAAAAAAAAAAPXwAAAAAAAAA/AAAAAAAA9fAAAAAAAAA//wAAAAAAA98AAAAAAAA/1fwAAAAAAD3wAAAAAAAP1VXwAAAAAAPfAAAAAAAD1VVXwAAAAAA9fAAAAAAAPVVVXwAAAAAD18AAAAAAD1VVVXwAAAAAD3wAAAAAAPVVVVfAAAAAAPfAAAAAAA9VVVV8AAAAAA98AAAAAAD1VVVXwAAAAAD3wAAAAAAPVVVVfAAAAAAPXwAAAAAA9VVVV8AAAAAA9fAAAAAAD1VVVXwAAAAAPV8AAAAAAPVVVVfAAAAAA9XwAAAAAA9VVVV8AAAAAD1XwAAAAAA9VVVfAAAAAAPVfAAAAAAD1VVXwAAAAAA9V8AAAAAAD1VV8AAAAAAD1V8AAAAAAD9V/AAAAAAA9VXwAAAAAAD//wAAAAAAD1VXwAAAAAAA/wAAAAAAAPVVfAAAAAAAAAAAAAAAAD1VVfAAAAAAAAAAAAAAAAPVVV8AAAAAAAAAAAAAAAD1VVV8AAAAAAAAAAAAAAAPVVVV8AAAAAAAAAAAAAAD1VVVXwAAAAAAAAAAAAAAPVVVVXwAAAAAAAAAAAAAD1VVVVXwAAAAAAAAAAAAA9VVVVVXwAAAAAAAAAAAAPVVVVVVX8AAAAAAAAAAAD1VVVVVVX8AAAAAAAAAAD9VVVVVVVV/AAAAAAAAAD/VVVVVVVVV/8AAAAAAAD/VVVVVVVVVVf/wAAAAAD/VVVVVVVVVVVV////////VVVVVVVVVVVVVX//////VVVVVVQ=="},
|
||||
{"width" : "63", "buffer":"VVV//////////////1VVVVX/////////////////VVV//AAAAAAAAAAAAAAP/9VX8AAAAAAAAAAAAAAAAA/VXwAAAAAAAAAAAAAAAAAD1fAAAAAAAAAAAAAAAAAAD1fAAAAAAAAAAAAAAAAAAA98AAAAAAAAAAAAAAAAAAA98AAAAAAAAAAAAAAAAAAA98AAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAA98AAAAAAAAAAAAAAAAAAA9fAAAAAAAAAAAAAAAAAAA9fAAAAAAAAAAAAAAAAAAA9XwAAAAAAAAAAAAAAAAAA9X8AAAAAAAAAAAAAAAAAA9Vf/wAAAAAAAAAAAAAAAD1VX/////////wAAAAAAAD1VVVf///////8AAAAAAAPVVVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVV8AAAAAAA9VVVVVVVVVVVV8AAAAAAA9VVVVVVVVVVVXwAAAAAAD1VVVVVVVVVVVXwAAAAAAD1VVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVV8AAAAAAAPVVVVVVVVVVVV8AAAAAAA9VVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVXwAAAAAAD1VVVVVVVVVVVfAAAAAAAD1VVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVV8AAAAAAA9VVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVXwAAAAAAD1VVVVVVVVVVVfAAAAAAAD1VVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVV8AAAAAAAPVVVVVVVVVVVV8AAAAAAA9VVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVXwAAAAAAD1VVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVV8AAAAAAAPVVVVVVVVVVVV8AAAAAAA9VVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVXwAAAAAAD1VVVVVVVVVVVfAAAAAAAD1VVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVV8AAAAAAAPVVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVfAAAAAAAD1VVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVV8AAAAAAAPVVVVVVVVVVVV8AAAAAAA9VVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVXwAAAAAAD1VVVVVVVVVVVfAAAAAAAD1VVVVVVVVVVV8AAAAAAAPVVVVVVVVVVVV8AAAAAAAPVVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVfAAAAAAAD1VVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVV8AAAAAAAPVVVVVVVVVVVV8AAAAAAA9VVVVVVVVVVVXwAAAAAAA9VVVVVVVVVVVXwAAAAAAD1VVVVVVVVVVVXwAAAAAAD1VVVVVVVVVVVXwAAAAAAPVVVVVVVVVVVVfAAAAAAAPVVVVVVVVVVVVfAAAAAAA9VVVVVVVVVVVVXwAAAAAD1VVVVVVVVVVVVXwAAAAAD1VVVVVVVVVVVVXwAAAAAPVVVVVVVVVVVVVV8AAAAA9VVVVVVVVVVVVVVfwAAA/1VVVVVVVVVVVVVVX/////VVVVVVVVVVVVVVVVf///VVVVVVVVVVVVV"},
|
||||
{"width" : "64", "buffer":"VVVVVVX///////9VVVVVVVVVVVV//////////VVVVVVVVVVX/wAAAAAAAP/1VVVVVVVVf8AAAAAAAAAD/1VVVVVVV/wAAAAAAAAAAA/VVVVVVV/AAAAAAAAAAAAA/VVVVVV8AAAAAAAAAAAAAD9VVVVV8AAAAAAAAAAAAAAD1VVVV8AAAAAAAAAAAAAAAPVVVV8AAAAAAAAAAAAAAAD1VVV8AAAAAAAAAAAAAAAAPVVVfAAAAAAAAAAAAAAAAA9VVfAAAAAAAAAAAAAAAAAPVVXwAAAAAAAAAAAAAAAAD1VV8AAAAAAAAAAAAAAAAAPVV8AAAAAAAAAAAAAAAAAD1VfAAAAAAAD//wAAAAAAA9VXwAAAAAAD///wAAAAAAPVV8AAAAAAD1VV/AAAAAAA9VfAAAAAAD1VVV8AAAAAAPVXwAAAAAD1VVVfAAAAAAD1V8AAAAAD1VVVV8AAAAAA9VfAAAAAA9VVVVfAAAAAAPVXwAAAAAPVVVVXwAAAAAD1V8AAAAAD1VVVV8AAAAAD1VfAAAAAA9VVVVfAAAAAA9VXwAAAAAPVVVVXwAAAAAPVVfAAAAAA9VVVXwAAAAAD1VXwAAAAAD1VVXwAAAAAA9VV8AAAAAAPVVXwAAAAAA9VVXwAAAAAA///wAAAAAAPVVV8AAAAAAD//wAAAAAAPVVVXwAAAAAAAAAAAAAAAD1VVV8AAAAAAAAAAAAAAAD1VVVXwAAAAAAAAAAAAAAD1VVVVfAAAAAAAAAAAAAAD1VVVVV8AAAAAAAAAAAAAD1VVVVVXwAAAAAAAAAAAAD1VVVVVVfAAAAAAAAAAAAA9VVVVVVfAAAAAAAAAAAAAD1VVVVV/AAAAAAAAAAAAAAPVVVVV/AAAAAAAAAAAAAAA9VVVV8AAAAAAAAAAAAAAAD1VVV8AAAAAAAAAAAAAAAAPVVV8AAAAAAAAAAAAAAAAA9VVfAAAAAAAAAAAAAAAAAD1VfAAAAAAAAAAAAAAAAAA9VXwAAAAAAD///AAAAAAAD1XwAAAAAAP////AAAAAAA9V8AAAAAAP1VVX8AAAAAAD18AAAAAAPVVVVXwAAAAAA9fAAAAAAPVVVVVfAAAAAAPXwAAAAAPVVVVVV8AAAAAD18AAAAAD1VVVVVfAAAAAAPfAAAAAA9VVVVVV8AAAAAD3wAAAAAPVVVVVVfAAAAAA/wAAAAAD1VVVVVfAAAAAAP8AAAAAA9VVVVVXwAAAAAD/AAAAAAPVVVVVV8AAAAAA98AAAAAA9VVVVV8AAAAAAPfAAAAAAD1VVVV8AAAAAAD3wAAAAAAP9VVX8AAAAAAD18AAAAAAA////8AAAAAAA9fAAAAAAAAP//wAAAAAAAPV8AAAAAAAAAAAAAAAAAAD1fAAAAAAAAAAAAAAAAAAD1XwAAAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAAA9VV8AAAAAAAAAAAAAAAAAPVVfAAAAAAAAAAAAAAAAAPVVV8AAAAAAAAAAAAAAAAPVVVXwAAAAAAAAAAAAAAAD1VVVfAAAAAAAAAAAAAAAP1VVVV/AAAAAAAAAAAAAAP1VVVVX8AAAAAAAAAAAAA/VVVVVVX8AAAAAAAAAAAD/VVVVVVVf/AAAAAAAAAA/9VVVVVVVVf/8AAAAAAAP/1VVVVVVVVVX/////////9VVVVVVVVVVVVf///////VVVVVVU="},
|
||||
{"width" : "61", "buffer":"VVVVVV///////VVVVVVVVVVVVX////////1VVVVVVVVVVf8AAAAAAD/1VVVVVVVVV/wAAAAAAAA/1VVVVVVVX/AAAAAAAAAA/VVVVVVVX8AAAAAAAAAAA9VVVVVVXwAAAAAAAAAAAD9VVVVVXwAAAAAAAAAAAAP1VVVVXwAAAAAAAAAAAAAPVVVVXwAAAAAAAAAAAAAA9VVVV8AAAAAAAAAAAAAAPVVVV8AAAAAAAAAAAAAAA9VVV8AAAAAAAAAAAAAAAD1VVfAAAAAAAAAAAAAAAA9VVfAAAAAAAAAAAAAAAAD1VXwAAAAAAAAAAAAAAAA9VV8AAAAAAA//AAAAAAAD1V8AAAAAAA//8AAAAAAA9VfAAAAAAA9VXwAAAAAAD1XwAAAAAA9VVfAAAAAAA9XwAAAAAA9VVV8AAAAAAD18AAAAAA9VVVXwAAAAAA9fAAAAAAPVVVV8AAAAAAPXwAAAAAPVVVVXwAAAAAD18AAAAAD1VVVV8AAAAAAPfAAAAAA9VVVVfAAAAAAD3wAAAAAPVVVVXwAAAAAA98AAAAAD1VVVV8AAAAAAPfAAAAAA9VVVVfAAAAAAA/wAAAAAD1VVVfAAAAAAAP8AAAAAA9VVVXwAAAAAAD/AAAAAAD1VVV8AAAAAAA/wAAAAAA9VVV8AAAAAAAP8AAAAAAD9VV8AAAAAAAD/AAAAAAAP/f8AAAAAAAA98AAAAAAAP/8AAAAAAAAPfAAAAAAAADAAAAAAAAAD3wAAAAAAAAAAAAAAAAAA9fAAAAAAAAAAAAAAAAAAPXwAAAAAAAAAAAAAAAAAD1fAAAAAAAAAAAAAAAAAA9XwAAAAAAAAAAAAAAAAAPVfAAAAAAAAAAAAAAAAAD1XwAAAAAAAAAAAAAAAAA9VfAAAAAAAAAAAAAAAAAPVV8AAAAAAAAAMAAAAAAD1VXwAAAAAAAAPwAAAAAA9VVfAAAAAAAAPfAAAAAAPVVV/AAAAAAA/XwAAAAAD1VVX/AAAAAD/V8AAAAAA9VVVX/wAAAP9VfAAAAAA9VVVVX/////1VfAAAAAAPVVVVVV////VVXwAAAAAD1VVVVVVVVVVVV8AAAAAA9VVVVVVVVVVVV8AAAAAA9VVVVVVVVVVVV8AAAAAAPVVVVVVVVVVVVfAAAAAAD1VVVVVVVVVVVfAAAAAAA9VVf//VVVVVV/AAAAAAA9VV////1VVVX/AAAAAAAPVV/AAD//9f/8AAAAAAAPVV8AAAA////wAAAAAAAD1VfAAAAAAPAAAAAAAAAD1VfAAAAAAAAAAAAAAAAA9VXwAAAAAAAAAAAAAAAA9VV8AAAAAAAAAAAAAAAAPVVfAAAAAAAAAAAAAAAAPVVXwAAAAAAAAAAAAAAAPVVV8AAAAAAAAAAAAAAAPVVVfAAAAAAAAAAAAAAAD1VVXwAAAAAAAAAAAAAAP1VVV8AAAAAAAAAAAAAAP1VVVfAAAAAAAAAAAAAAPVVVVV8AAAAAAAAAAAAA/VVVVVXwAAAAAAAAAAAA/VVVVVVf8AAAAAAAAAAP9VVVVVVV/8AAAAAAAAA/9VVVVVVVVf/wAAAAAA//VVVVVVVVVVf////////9VVVVVVVVVVVV//////9VVVVVVVVU="},
|
||||
{"width" : "25", "buffer":"VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX//1VVVVf///9VVV/wAA/1VV/AAAAPVV8AAAAA9V8AAAAAD1fAAAAAA9fAAAAAAD3wAAAAAA/wAAAAAAP8AAAAAAD/AAAAAAA/wAAAAAAP8AAAAAAD/AAAAAAA/wAAAAAAPfAAAAAAD3wAAAAAA98AAAAAA9XwAAAAAPVfAAAAAPVV8AAAAPVVX/AAA/VVVf////VVVVX//9VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf//VVVVX////1VVX/AAD/VVXwAAAA9VXwAAAAD1XwAAAAAPXwAAAAAD18AAAAAAPfAAAAAAD/AAAAAAA/wAAAAAAP8AAAAAAD/AAAAAAA/wAAAAAAP8AAAAAAD/AAAAAAA98AAAAAAPfAAAAAAD18AAAAAD1fAAAAAA9V8AAAAA9VXwAAAA9VVf8AAP9VVV////9VVVVf//VVU="}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 726 B |
After Width: | Height: | Size: 662 B |
After Width: | Height: | Size: 695 B |
After Width: | Height: | Size: 585 B |
After Width: | Height: | Size: 763 B |
After Width: | Height: | Size: 660 B |
After Width: | Height: | Size: 633 B |
After Width: | Height: | Size: 551 B |
After Width: | Height: | Size: 670 B |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 753 B |
After Width: | Height: | Size: 690 B |
After Width: | Height: | Size: 845 B |
After Width: | Height: | Size: 759 B |
After Width: | Height: | Size: 659 B |
After Width: | Height: | Size: 572 B |
After Width: | Height: | Size: 843 B |
After Width: | Height: | Size: 749 B |
After Width: | Height: | Size: 609 B |
After Width: | Height: | Size: 519 B |
After Width: | Height: | Size: 611 B |
After Width: | Height: | Size: 521 B |
After Width: | Height: | Size: 774 B |
After Width: | Height: | Size: 670 B |
After Width: | Height: | Size: 838 B |
After Width: | Height: | Size: 717 B |
|
@ -0,0 +1 @@
|
|||
|
|
@ -1,49 +1,49 @@
|
|||
exports.drawClock = function(fontIndex) {
|
||||
var digits = [];
|
||||
if (!require("Storage").read("contourclock-"+Math.abs(parseInt(fontIndex+0.5))+".json")) return (false);
|
||||
var font = JSON.parse(require("Storage").read("contourclock-"+Math.abs(parseInt(fontIndex+0.5))+".json"));
|
||||
var digits = [];
|
||||
fontFile=require("Storage").read("contourclock-"+Math.abs(parseInt(fontIndex+0.5))+".json");
|
||||
if (fontFile==undefined) return(false); //exit if font file not found
|
||||
var font = JSON.parse(fontFile);
|
||||
for (var n in font.characters) {
|
||||
digits.push({width: parseInt(font.characters[n].width),
|
||||
height: font.size,
|
||||
bpp: 2,
|
||||
transparent: 1,
|
||||
buffer:E.toArrayBuffer(atob(font.characters[n].buffer))});
|
||||
height: font.size,
|
||||
bpp: 2,
|
||||
transparent: 1,
|
||||
buffer:E.toArrayBuffer(atob(font.characters[n].buffer))});
|
||||
}
|
||||
if (n!=10) return (false); //font file seems to be invalid
|
||||
var x=0;
|
||||
var y = g.getHeight()/2-digits[0].height/2;
|
||||
var date = new Date();
|
||||
g.clearRect(0,38,g.getWidth()-1,138);
|
||||
d1=parseInt(date.getHours()/10);
|
||||
d2=parseInt(date.getHours()%10);
|
||||
d3=10;
|
||||
d4=parseInt(date.getMinutes()/10);
|
||||
d5=parseInt(date.getMinutes()%10);
|
||||
w1=digits[d1].width;
|
||||
w2=digits[d2].width;
|
||||
w3=digits[d3].width;
|
||||
w4=digits[d4].width;
|
||||
w5=digits[d5].width;
|
||||
squeeze=(g.getWidth()-w5)/(w1+w2+w3+w4);
|
||||
if (fontIndex<0) {
|
||||
fg=g.theme.fg;
|
||||
bg=g.theme.bg;
|
||||
g.setColor(bg);
|
||||
g.setBgColor(fg);
|
||||
}
|
||||
g.drawImage(digits[d1],x,y);
|
||||
x+=parseInt(w1*squeeze);
|
||||
g.drawImage(digits[d2],x,y);
|
||||
x+=parseInt(w2*squeeze);
|
||||
g.drawImage(digits[d3],x,y);
|
||||
x+=parseInt(w3*squeeze);
|
||||
g.drawImage(digits[d4],x,y);
|
||||
x+=parseInt(w4*squeeze);
|
||||
g.drawImage(digits[d5],x,y);
|
||||
if (fontIndex<0) {
|
||||
g.setColor(fg);
|
||||
g.setBgColor(bg);
|
||||
}
|
||||
return font.name;
|
||||
}
|
||||
var x=0;
|
||||
var y = g.getHeight()/2-digits[0].height/2;
|
||||
var date = new Date();
|
||||
//g.clearRect(0,24,g.getWidth()-1,137);
|
||||
|
||||
g.clearRect(0,38,g.getWidth()-1,138);
|
||||
d1=parseInt(date.getHours()/10);
|
||||
d2=parseInt(date.getHours()%10);
|
||||
d3=10;
|
||||
d4=parseInt(date.getMinutes()/10);
|
||||
d5=parseInt(date.getMinutes()%10);
|
||||
w1=digits[d1].width;
|
||||
w2=digits[d2].width;
|
||||
w3=digits[d3].width;
|
||||
w4=digits[d4].width;
|
||||
w5=digits[d5].width;
|
||||
squeeze=(g.getWidth()-w5)/(w1+w2+w3+w4);
|
||||
if (fontIndex<0) {
|
||||
fg=g.theme.fg;
|
||||
bg=g.theme.bg;
|
||||
g.setColor(bg);
|
||||
g.setBgColor(fg);
|
||||
}
|
||||
g.drawImage(digits[d1],x,y);
|
||||
x+=parseInt(w1*squeeze);
|
||||
g.drawImage(digits[d2],x,y);
|
||||
x+=parseInt(w2*squeeze);
|
||||
g.drawImage(digits[d3],x,y);
|
||||
x+=parseInt(w3*squeeze);
|
||||
g.drawImage(digits[d4],x,y);
|
||||
x+=parseInt(w4*squeeze);
|
||||
g.drawImage(digits[d5],x,y);
|
||||
if (fontIndex<0) {
|
||||
g.setColor(fg);
|
||||
g.setBgColor(bg);
|
||||
}
|
||||
return font.name;
|
||||
}
|
||||
|
|
|
@ -1,32 +1,18 @@
|
|||
{ "id": "contourclock",
|
||||
"name": "Contour Clock",
|
||||
"shortName" : "Contour Clock",
|
||||
"version":"0.21",
|
||||
"version":"0.24",
|
||||
"icon": "app.png",
|
||||
"description": "A Minimalist clockface with large Digits. Now with more fonts!",
|
||||
"screenshots" : [{"url":"cc-screenshot-1.png"},{"url":"cc-screenshot-2.png"}],
|
||||
"tags": "clock",
|
||||
"allow_emulator":true,
|
||||
"custom": "custom.html",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"type": "clock",
|
||||
"storage": [
|
||||
{"name":"contourclock.app.js","url":"app.js"},
|
||||
{"name":"contourclock.settings.js","url":"contourclock.settings.js"},
|
||||
{"name":"contourclock","url":"lib.js"},
|
||||
{"name":"contourclock-0.json","url":"font-Impact.json"},
|
||||
{"name":"contourclock-1.json","url":"font-Yumaro.json"},
|
||||
{"name":"contourclock-2.json","url":"font-DinAlternate.json"},
|
||||
{"name":"contourclock-3.json","url":"font-BebasNeue.json"},
|
||||
{"name":"contourclock-4.json","url":"font-Dekko.json"},
|
||||
{"name":"contourclock-5.json","url":"font-Dosis.json"},
|
||||
{"name":"contourclock-6.json","url":"font-Phosphate.json"},
|
||||
{"name":"contourclock-7.json","url":"font-Quicksand.json"},
|
||||
{"name":"contourclock-8.json","url":"font-SairaCond.json"},
|
||||
{"name":"contourclock-9.json","url":"font-YuseiMagic.json"},
|
||||
{"name":"contourclock-10.json","url":"font-BarlowCond.json"},
|
||||
{"name":"contourclock-11.json","url":"font-OpenSansEC.json"},
|
||||
{"name":"contourclock-12.json","url":"font-SairaEC.json"},
|
||||
{"name":"contourclock-13.json","url":"font-Teko.json"},
|
||||
{"name":"contourclock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -5,3 +5,4 @@
|
|||
0.05: add Bangle 2 version
|
||||
0.06: Adds settings page (hide clocks or launchers)
|
||||
0.07: Adds setting for directly launching app on touch for Bangle 2
|
||||
0.08: Optimize line wrapping for Bangle 2
|
||||
|
|
|
@ -45,11 +45,23 @@ function draw_icon(p,n,selected) {
|
|||
g.setColor(g.theme.fg);
|
||||
try{g.drawImage(apps[p*4+n].icon,x+12,y+4);} catch(e){}
|
||||
g.setFontAlign(0,-1,0).setFont("6x8",1);
|
||||
var txt = apps[p*4+n].name.split(" ");
|
||||
for (var i = 0; i < txt.length; i++) {
|
||||
txt[i] = txt[i].trim();
|
||||
g.drawString(txt[i],x+36,y+54+i*8);
|
||||
var txt = apps[p*4+n].name.replace(/([a-z])([A-Z])/g, "$1 $2").split(" ");
|
||||
var lineY = 0;
|
||||
var line = "";
|
||||
while (txt.length > 0){
|
||||
var c = txt.shift();
|
||||
|
||||
if (c.length + 1 + line.length > 13){
|
||||
if (line.length > 0){
|
||||
g.drawString(line.trim(),x+36,y+54+lineY*8);
|
||||
lineY++;
|
||||
}
|
||||
line = c;
|
||||
} else {
|
||||
line += " " + c;
|
||||
}
|
||||
}
|
||||
g.drawString(line.trim(),x+36,y+54+lineY*8);
|
||||
}
|
||||
|
||||
function drawPage(p){
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "dtlaunch",
|
||||
"name": "Desktop Launcher",
|
||||
"version": "0.07",
|
||||
"version": "0.08",
|
||||
"description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.",
|
||||
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
|
||||
"icon": "icon.png",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "floralclk",
|
||||
"name": "Floral Clock",
|
||||
"version": "0.01",
|
||||
"description": "A clock with a flower background by [Lillith May](https://www.instagram.com/_lilustrations_/). **Note: Works on any Bangle.js 2 but requires firmware 2v11 or later on Bangle.js 1**",
|
||||
"description": "A clock with a flower background by [Lillith May](https://www.instagram.com/_lilustrations_/)",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot_floral.png"}],
|
||||
"type": "clock",
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
0.06: Bangle.js 2 support
|
||||
0.07: Fix "previous" button image
|
||||
0.08: Fix scrolling title background color
|
||||
0.09: Move event listener from widget to boot code, stops music from showing up in messages
|
||||
|
|
|
@ -175,10 +175,8 @@ function rIcon(l) {
|
|||
}
|
||||
let layout;
|
||||
function makeUI() {
|
||||
global.gbmusic_active = true; // we don't need our widget (needed for <2.09 devices)
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
delete (global.gbmusic_active);
|
||||
const Layout = require("Layout");
|
||||
layout = new Layout({
|
||||
type: "v", c: [
|
||||
|
@ -331,7 +329,7 @@ function formatNum(info) {
|
|||
* Update music info
|
||||
* @param {Object} info - Gadgetbridge musicinfo event
|
||||
*/
|
||||
function musicInfo(info) {
|
||||
function info(info) {
|
||||
scrollStop();
|
||||
layout.title.label = info.track || "";
|
||||
layout.album.label = info.album || "";
|
||||
|
@ -360,7 +358,7 @@ let tPxt, tIxt; // Timeouts to eXiT when Paused/Inactive for too long
|
|||
* Update music state
|
||||
* @param {Object} e - Gadgetbridge musicstate event
|
||||
*/
|
||||
function musicState(e) {
|
||||
function state(e) {
|
||||
stat = e.state;
|
||||
// if paused for five minutes, load the clock
|
||||
// (but timeout resets if we get new info, even while paused)
|
||||
|
@ -584,8 +582,8 @@ function startEmulator() {
|
|||
println: (line) => {console.log("Bluetooth:", line);},
|
||||
};
|
||||
// some example info
|
||||
GB({"t": "musicinfo", "artist": "Some Artist Name", "album": "The Album Name", "track": "The Track Title Goes Here", "dur": 241, "c": 2, "n": 2});
|
||||
GB({"t": "musicstate", "state": "play", "position": 0, "shuffle": 1, "repeat": 1});
|
||||
info({"t": "musicinfo", "artist": "Some Artist Name", "album": "The Album Name", "track": "The Track Title Goes Here", "dur": 241, "c": 2, "n": 2});
|
||||
state({"t": "musicstate", "state": "play", "position": 0, "shuffle": 1, "repeat": 1});
|
||||
}
|
||||
}
|
||||
function startWatches() {
|
||||
|
@ -596,25 +594,6 @@ function startWatches() {
|
|||
|
||||
function start() {
|
||||
makeUI();
|
||||
// start listening for music updates
|
||||
const _GB = global.GB;
|
||||
global.GB = (event) => {
|
||||
// we eat music events!
|
||||
switch(event.t) {
|
||||
case "musicinfo":
|
||||
musicInfo(event);
|
||||
break;
|
||||
case "musicstate":
|
||||
musicState(event);
|
||||
break;
|
||||
default:
|
||||
// pass on other events
|
||||
if (_GB) {
|
||||
setTimeout(_GB, 0, event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
startWatches();
|
||||
tick();
|
||||
startEmulator();
|
||||
|
@ -625,11 +604,11 @@ function init() {
|
|||
let saved = require("Storage").readJSON("gbmusic.load.json", true);
|
||||
require("Storage").erase("gbmusic.load.json");
|
||||
if (saved) {
|
||||
// autoloaded: load state was saved by widget
|
||||
// autoloaded: load state as saved by widget
|
||||
auto = true;
|
||||
start();
|
||||
musicInfo(saved.info);
|
||||
musicState(saved.state);
|
||||
info(saved.info);
|
||||
state(saved.state);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
setTimeout( // make other boot code run first, so we override e.g. android.boot.js GB
|
||||
() => {
|
||||
const APP = global.__FILE__==="gbmusic.app.js",
|
||||
a = !!(require("Storage").readJSON("gbmusic.json", 1) || {}).autoStart;
|
||||
|
||||
let s, i; // state, info
|
||||
/**
|
||||
* Save current song and check if we want to load the gbmusic app
|
||||
*
|
||||
* Only runs while other apps are loaded
|
||||
*/
|
||||
function check() {
|
||||
if (s!=="play" || !i || !a || !Bangle.CLOCK) return; // only launch app if we know which song we are playing, and autoLoad is enabled
|
||||
delete (i.t);
|
||||
// store info and launch music app
|
||||
require("Storage").writeJSON("gbmusic.load.json", {
|
||||
state: s,
|
||||
info: i,
|
||||
});
|
||||
load("gbmusic.app.js");
|
||||
}
|
||||
|
||||
global.GB = (_GB => e => {
|
||||
// we eat music events!
|
||||
switch(e.t) {
|
||||
case "musicinfo":
|
||||
i = e;
|
||||
return APP ? info(e) : check();
|
||||
case "musicstate":
|
||||
s = e.state;
|
||||
return APP ? state(e) : check();
|
||||
default:
|
||||
// pass on other events
|
||||
if (_GB) setTimeout(_GB, 0, e);
|
||||
}
|
||||
})(global.GB);
|
||||
}, 1);
|
|
@ -2,7 +2,7 @@
|
|||
"id": "gbmusic",
|
||||
"name": "Gadgetbridge Music Controls",
|
||||
"shortName": "Music Controls",
|
||||
"version": "0.08",
|
||||
"version": "0.09",
|
||||
"description": "Control the music on your Gadgetbridge-connected phone",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screenshot_v1.png"},{"url":"screenshot_v2.png"}],
|
||||
|
@ -14,7 +14,7 @@
|
|||
"storage": [
|
||||
{"name":"gbmusic.app.js","url":"app.js"},
|
||||
{"name":"gbmusic.settings.js","url":"settings.js"},
|
||||
{"name":"gbmusic.wid.js","url":"widget.js"},
|
||||
{"name":"gbmusic.boot.js","url":"boot.js"},
|
||||
{"name":"gbmusic.img","url":"icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"gbmusic.json"},{"name":"gbmusic.load.json"}]
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
(() => {
|
||||
if (global.gbmusic_active || !(require("Storage").readJSON("gbmusic.json", 1) || {}).autoStart) {
|
||||
return;
|
||||
}
|
||||
if (typeof __FILE__ === 'string') { // only exists since 2v09
|
||||
const info = require("Storage").readJSON(__FILE__.split(".")[0]+".info", 1) || false;
|
||||
if (info && info.type!=="clock") { // info can have no type (but then it isn't a clock)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let state, info;
|
||||
function checkMusic() {
|
||||
if (state!=="play" || !info) {
|
||||
return;
|
||||
}
|
||||
// playing music: launch music app
|
||||
require("Storage").writeJSON("gbmusic.load.json", {
|
||||
state: state,
|
||||
info: info,
|
||||
});
|
||||
load("gbmusic.app.js");
|
||||
}
|
||||
|
||||
const _GB = global.GB;
|
||||
global.GB = (event) => {
|
||||
// we eat music events!
|
||||
switch(event.t) {
|
||||
case "musicinfo":
|
||||
info = event;
|
||||
delete (info.t);
|
||||
checkMusic();
|
||||
break;
|
||||
case "musicstate":
|
||||
state = event.state;
|
||||
checkMusic();
|
||||
break;
|
||||
default:
|
||||
if (_GB) {
|
||||
setTimeout(_GB, 0, event);
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
|
@ -107,3 +107,8 @@ try {
|
|||
|
||||
* Some useful code on Github can be found [here](https://portal.u-blox.com/s/question/0D52p0000925T00CAE/ublox-max-m8q-getting-stuck-when-sleeping-with-extint-pin-control)
|
||||
and [here](https://github.com/thasti/utrak/blob/master/gps.c)
|
||||
|
||||
|
||||
Written by: [Hugh Barney, with support from Gordon Williams](https://github.com/hughbarney) For support
|
||||
and discussion please post in the [Bangle JS
|
||||
Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "health",
|
||||
"name": "Health Tracking",
|
||||
"version": "0.11",
|
||||
"description": "Logs health data and provides an app to view it (requires firmware 2v10.100 or later)",
|
||||
"description": "Logs health data and provides an app to view it",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,health",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
|
|
|
@ -280,3 +280,8 @@ The following error codes will be displayed if one of the dependancies is not me
|
|||
* Add a small graph to the heart rate monitor app
|
||||
* Add a facility to call the Arrow calibration process
|
||||
* Maybe create waypoints.json file if missing
|
||||
|
||||
|
||||
Written by: [Hugh Barney](https://github.com/hughbarney) For support
|
||||
and discussion please post in the [Bangle JS
|
||||
Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
|
|
|
@ -12,4 +12,7 @@
|
|||
0.12: Added humidity as an option to display.
|
||||
0.13: Improved battery visualization.
|
||||
0.14: Added altitude as an option to display.
|
||||
0.15: Using wpedom to count steps.
|
||||
0.15: Using wpedom to count steps.
|
||||
0.16: Improved stability. Wind can now be shown.
|
||||
0.17: Settings for mph/kph and other minor improvements.
|
||||
0.18: Fullscreen mode can now be enabled or disabled in the settings.
|
|
@ -1,8 +1,9 @@
|
|||
# LCARS clock
|
||||
|
||||
A simple LCARS inspired clock.
|
||||
Note: To display the steps, the health app is required. If this app is not installed, the data will not be shown.
|
||||
To contribute you can open a PR at this [GitHub Repo]( https://github.com/peerdavid/BangleApps)
|
||||
Note: To display the steps, the wpedom app is required. To show weather data
|
||||
such as temperature, humidity or window you BangleJS must be connected
|
||||
with Gadgetbride and the weather app must be installed.
|
||||
|
||||
## Control
|
||||
* Tap left / right to change between screens.
|
||||
|
@ -10,7 +11,7 @@ To contribute you can open a PR at this [GitHub Repo]( https://github.com/peerda
|
|||
|
||||
## Features
|
||||
* LCARS Style watch face.
|
||||
* Full screen mode - widgets are still loaded but not shown.
|
||||
* Enable or disable fullscreen mode (widgets are always loaded, but hidden if fullscreen).
|
||||
* Tab on left/right to switch between different screens.
|
||||
* Cusomizable data that is shown on screen 1 (steps, weather etc.)
|
||||
* Shows random and real images of planets.
|
||||
|
@ -19,25 +20,23 @@ To contribute you can open a PR at this [GitHub Repo]( https://github.com/peerda
|
|||
* Display graphs (day or month) for steps + hrm on the second screen.
|
||||
|
||||
## Data that can be configured
|
||||
* Steps - Steps loaded via the health module
|
||||
* Steps - Steps loaded via the wpedom app.
|
||||
* Battery - Current battery level in %
|
||||
* VREF - Voltage of battery
|
||||
* HRM - Last measured HRM
|
||||
* Temp - Weather temperature loaded via the weather module + gadgetbridge
|
||||
* Humidity - Humidity loaded via the weather module + gadgetbridge
|
||||
* Wind - Wind loaded via the weather module + gadgetbridge. Set kph / mph in the settings.
|
||||
* Altitude - Shows the altitude in m.
|
||||
* CoreT - Temperature of device
|
||||
|
||||
## Multiple screens support
|
||||
Access different screens via tap on the left/ right side of the screen
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
# Ideas
|
||||
- Tap top / bottom to disable steps (also icon) and start a timer
|
||||
|
||||
## Contributors
|
||||
- [David Peer](https://github.com/peerdavid).
|
||||
- [Adam Schmalhofer](https://github.com/adamschmalhofer).
|
||||
|
|
After Width: | Height: | Size: 772 B |
After Width: | Height: | Size: 769 B |
|
@ -5,7 +5,9 @@ let settings = {
|
|||
alarm: -1,
|
||||
dataRow1: "Steps",
|
||||
dataRow2: "Temp",
|
||||
dataRow3: "Battery"
|
||||
dataRow3: "Battery",
|
||||
speed: "kph",
|
||||
fullscreen: false,
|
||||
};
|
||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||
for (const key in saved_settings) {
|
||||
|
@ -29,23 +31,39 @@ let lcarsViewPos = 0;
|
|||
// let hrmValue = 0;
|
||||
var plotMonth = false;
|
||||
|
||||
|
||||
/*
|
||||
* Requirements and globals
|
||||
*/
|
||||
|
||||
|
||||
var bgLeft = {
|
||||
var bgLeftFullscreen = {
|
||||
width : 27, height : 176, bpp : 3,
|
||||
transparent : 0,
|
||||
buffer : require("heatshrink").decompress(atob("AAUM2XLlgCCwAJBBAuy4EAmQIF5cggAIGlmwgYIG2XIF42wF4ImGF4ImHJoQmGJoQdJhZNHNY47CgRNGBIJZHHgRiGBIRQ/KH5QCAFCh/eX5Q/KAwdCAGVbtu27YCCoAJBkuWrNlAQRGCiwRDAQPQBIMJCIYCBsAJBgomEtu0WoQmEy1YBIMBHYttIwQ7FyxQ/KHFlFAQ7F2weCHYplKChRTCCg5TCHw5TMAD0GzVp0wCCBBGaBIMaBAtpwECBA2mwEJBAugDgMmCIwJBF5EABAtoeQQvGCYQdPJoI7LMQzTCLJKAGzAJBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A=="))
|
||||
};
|
||||
|
||||
var bgRight = {
|
||||
var bgLeftNotFullscreen = {
|
||||
width : 27, height : 152, bpp : 3,
|
||||
transparent : 0,
|
||||
buffer : require("heatshrink").decompress(atob("AAUM2XLlgCCwAJBBAuy4EAmQIF5cggAIGlmwgYIG2XIF42wF4ImGF4ImHJoQmGJoQdJhZNHNY47CgRNGBIJZHHgRiGBIRQ/KH5QCAGVbtu27YCCoAJBkuWrNlAQRkCiwRDAQPQBIMJCIYCBsAJBgomEtu0WoQmEy1YBIMBHYttIwQ7FyxQ/KHFlFAQ7F2weCHYplKChRTCCg5TCHw5TMAD0GzVp0wCCBBGaBIMaBAtpwECBA2mwEJBAugDgMmCIwJBF5EABAtoeQQvGCYQdPJoI7LMQzTCLJKAGzAJBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A="))
|
||||
};
|
||||
|
||||
var bgRightFullscreen = {
|
||||
width : 27, height : 176, bpp : 3,
|
||||
transparent : 0,
|
||||
buffer : require("heatshrink").decompress(atob("lmy5YCDBIUyBAmy5AJBhYUG2EAhgIFAQMAgQIGCgQABCg4ABEAwUNFI2AKZHAKZEgGRZTGOIUDQxJxGKH5Q/agwAnUP7y/KH4yGeVYAJrdt23bAQVABIMly1ZsoCCMgUWCIYCB6AJBhIRDAQNgBIMFEwlt2i1CEwmWrAJBgI7FtpGCHYuWKH5QxEwpQDlo7F0A7IqBZBEwo7BCIwCBJo53CJoxiCJpIAdgOmzVpAQR/CgAIEAQJ2CBAoCBBIMmCg1oD4QLGFQUCCjQ+CKYw+CKY4JCKYwoCGRMaGREJDoroCgwdFzBlLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJox/CgAA="))
|
||||
};
|
||||
|
||||
var bgRightNotFullscreen = {
|
||||
width : 27, height : 152, bpp : 3,
|
||||
transparent : 0,
|
||||
buffer : require("heatshrink").decompress(atob("lmy5YCDBIUyBAmy5AJBhYUG2EAhgIFAQMAgQIGCgQABCg4ABEAwUNFI2AKZHAKZEgGRZTGOIUDQxJxGKH5Q/agwAxrdt23bAQVABIMly1ZsoCCMgUWCIYCB6AJBhIRDAQNgBIMFEwlt2i1CEwmWrAJBgI7FtpGCHYuWKH5QxEwpQDlo7F0A7IqBZBEwo7BCIwCBJo53CJoxiCJpIAdgOmzVpAQR/CgAIEAQJ2CBAoCBBIMmCg1oD4QLGFQUCCjQ+CKYw+CKY4JCKYwoCGRMaGREJDoroCgwdFzBlLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJox/CgA="))
|
||||
};
|
||||
|
||||
var bgLeft = settings.fullscreen ? bgLeftFullscreen : bgLeftNotFullscreen;
|
||||
var bgRight= settings.fullscreen ? bgRightFullscreen : bgRightNotFullscreen;
|
||||
|
||||
var iconEarth = {
|
||||
width : 50, height : 50, bpp : 3,
|
||||
buffer : require("heatshrink").decompress(atob("AFtx48ECBsDwU5k/yhARLjgjBjlzAQMQEZcIkOP/fn31IEZgCBnlz58cEpM4geugEgwU/8+WNZJHDuHHvgmBCQ8goEOnVgJoMnyV58mACItHI4X8uAFBuVHnnz4BuGxk4////Egz3IkmWvPgNw8f/prB//BghTC+AjE7848eMjNnzySBwUJkmf/BuGuPDAQIjBiPHhhTCSQnjMo0ITANJn44Dg8MuFBggCCiFBcAJ0Bv5xEh+ITo2OhHkyf/OIQdBWwVHhgjBNwUE+fP/5EEgePMoYLBhMgyVJk/+BQQdC688I4XxOIc8v//NAvr+QEBj/5NwKVBy1/QYUciPBhk1EAJrC+KeC489QYaMBgU/8BNB9+ChEjz1Jkn/QYMBDQIgCcYTCCiP/nlzJQmenMAgV4//uy/9wRaB/1J8iVCcAfHjt9TYYICnhKCgRKBw159/v//r927OIeeoASBDQccvv3791KYVDBYPLJQeCnPnz//AAP6ocEjEkXgMgJQtz79fLAP8KYkccAcJ8Gf/f/xu/cAMQ4eP5MlyQRCMolx40YsOGBAPfnnzU4KVDpKMBvz8Dh0/8me7IICgkxJQXPIgZTD58sEgcJk+eNoONnFBhk4/5uB/pcDg5KD+4mEv4CBXISVDhEn31/8/+mH7x//JQK5CAAMB4JBCnnxJQf/+fJEgkAa4L+CAQOOjMn/1bXIRxDJQXx58f//Hhlz/88EgsChMgz/Zs/+nfkyV/8huDOI6SD498NwoACi1Z8+S/Plz17/+QCI7jC+ZxBmfPnojIAAMDcYWSp//2wRJEwq2GABECjMgNYwAmA="))
|
||||
|
@ -121,18 +139,22 @@ function queueDraw() {
|
|||
function printRow(text, value, y, c){
|
||||
g.setFontAntonioMedium();
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.setColor(c);
|
||||
g.fillRect(79, y-2, 85 ,y+18);
|
||||
|
||||
g.setFontAlign(0,-1,0);
|
||||
g.drawString(value, 110, y);
|
||||
|
||||
// Print background
|
||||
g.setColor(c);
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.fillRect(133, y-2, 165 ,y+18);
|
||||
g.fillCircle(161, y+8, 10);
|
||||
g.fillRect(80, y-2, 165 ,y+18);
|
||||
g.fillCircle(163, y+8, 10);
|
||||
g.setColor(cBlack);
|
||||
g.drawString(text, 135, y);
|
||||
|
||||
// Plot text
|
||||
width = g.stringWidth(value);
|
||||
g.setColor(cBlack);
|
||||
g.fillRect(130-width-8, y-2, 130, y+18);
|
||||
g.setColor(c);
|
||||
g.setFontAlign(1,-1,0);
|
||||
g.drawString(value, 126, y);
|
||||
}
|
||||
|
||||
|
||||
|
@ -174,6 +196,11 @@ function _drawData(key, y, c){
|
|||
var weather = getWeather();
|
||||
value = weather.hum;
|
||||
|
||||
} else if (key == "WIND"){
|
||||
text = "WND";
|
||||
var weather = getWeather();
|
||||
value = weather.wind;
|
||||
|
||||
} else if (key == "ALTITUDE"){
|
||||
should_print= false;
|
||||
text = "ALT";
|
||||
|
@ -207,7 +234,7 @@ function drawHorizontalBgLine(color, x1, x2, y, h){
|
|||
|
||||
|
||||
function drawInfo(){
|
||||
if(lcarsViewPos != 0){
|
||||
if(lcarsViewPos != 0 || !settings.fullscreen){
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -248,16 +275,16 @@ function drawState(){
|
|||
hours % 4 == 1 ? iconMars :
|
||||
hours % 4 == 2 ? iconMoon :
|
||||
iconEarth;
|
||||
g.drawImage(iconImg, 24, 118);
|
||||
g.drawImage(iconImg, 23, 118);
|
||||
g.setColor(cWhite);
|
||||
g.drawString("STATUS", 24+25, 108);
|
||||
g.drawString("STATUS", 23+26, 108);
|
||||
} else {
|
||||
// Alarm within symbol
|
||||
g.setColor(cOrange);
|
||||
g.drawString("ALARM", 24+25, 108);
|
||||
g.drawString("ALARM", 23+26, 108);
|
||||
g.setColor(cWhite);
|
||||
g.setFontAntonioLarge();
|
||||
g.drawString(getAlarmMinutes(), 24+25, 108+35);
|
||||
g.drawString(getAlarmMinutes(), 23+26, 108+35);
|
||||
}
|
||||
|
||||
g.setFontAlign(-1, -1, 0);
|
||||
|
@ -266,9 +293,10 @@ function drawState(){
|
|||
|
||||
function drawPosition0(){
|
||||
// Draw background image
|
||||
g.drawImage(bgLeft, 0, 0);
|
||||
drawHorizontalBgLine(cBlue, 25, 120, 0, 4);
|
||||
drawHorizontalBgLine(cBlue, 130, 176, 0, 4);
|
||||
var offset = settings.fullscreen ? 0 : 24;
|
||||
g.drawImage(bgLeft, 0, offset);
|
||||
drawHorizontalBgLine(cBlue, 25, 120, offset, 4);
|
||||
drawHorizontalBgLine(cBlue, 130, 176, offset, 4);
|
||||
drawHorizontalBgLine(cPurple, 20, 70, 80, 4);
|
||||
drawHorizontalBgLine(cPurple, 80, 176, 80, 4);
|
||||
drawHorizontalBgLine(cOrange, 35, 110, 87, 4);
|
||||
|
@ -294,15 +322,26 @@ function drawPosition0(){
|
|||
var currentDate = new Date();
|
||||
var timeStr = locale.time(currentDate,1);
|
||||
g.setFontAntonioLarge();
|
||||
g.drawString(timeStr, 27, 10);
|
||||
if(settings.fullscreen){
|
||||
g.drawString(timeStr, 27, 10);
|
||||
} else {
|
||||
g.drawString(timeStr, 27, 33);
|
||||
}
|
||||
|
||||
// Write date
|
||||
g.setColor(cWhite);
|
||||
g.setFontAntonioMedium();
|
||||
var dayStr = locale.dow(currentDate, true).toUpperCase();
|
||||
dayStr += " " + currentDate.getDate();
|
||||
dayStr += " " + locale.month(currentDate, 1).toUpperCase();
|
||||
g.drawString(dayStr, 30, 56);
|
||||
if(settings.fullscreen){
|
||||
var dayStr = locale.dow(currentDate, true).toUpperCase();
|
||||
dayStr += " " + currentDate.getDate();
|
||||
dayStr += " " + locale.month(currentDate, 1).toUpperCase();
|
||||
g.drawString(dayStr, 30, 56);
|
||||
} else {
|
||||
var dayStr = locale.dow(currentDate, true).toUpperCase();
|
||||
var date = currentDate.getDate();
|
||||
g.drawString(dayStr, 128, 35);
|
||||
g.drawString(date, 128, 55);
|
||||
}
|
||||
|
||||
// Draw data
|
||||
g.setFontAlign(-1, -1, 0);
|
||||
|
@ -317,8 +356,11 @@ function drawPosition0(){
|
|||
|
||||
function drawPosition1(){
|
||||
// Draw background image
|
||||
g.drawImage(bgRight, 149, 0);
|
||||
drawHorizontalBgLine(cBlue, 0, 140, 0, 4);
|
||||
var offset = settings.fullscreen ? 0 : 24;
|
||||
g.drawImage(bgRight, 149, offset);
|
||||
if(settings.fullscreen){
|
||||
drawHorizontalBgLine(cBlue, 0, 140, offset, 4);
|
||||
}
|
||||
drawHorizontalBgLine(cPurple, 0, 80, 80, 4);
|
||||
drawHorizontalBgLine(cPurple, 90, 150, 80, 4);
|
||||
drawHorizontalBgLine(cOrange, 0, 50, 87, 4);
|
||||
|
@ -378,8 +420,13 @@ function drawPosition1(){
|
|||
g.setFontAlign(1, 1, 0);
|
||||
g.setFontAntonioMedium();
|
||||
g.setColor(cWhite);
|
||||
g.drawString("M-HRM", 154, 27);
|
||||
g.drawString("M-STEPS [K]", 154, 115);
|
||||
|
||||
if(settings.fullscreen){
|
||||
g.drawString("M-HRM", 154, 27);
|
||||
g.drawString("M-STEPS [K]", 154, 115);
|
||||
} else {
|
||||
g.drawString("MONTH", 154, 115);
|
||||
}
|
||||
|
||||
// Plot day
|
||||
} else {
|
||||
|
@ -419,28 +466,40 @@ function drawPosition1(){
|
|||
g.setFontAlign(1, 1, 0);
|
||||
g.setFontAntonioMedium();
|
||||
g.setColor(cWhite);
|
||||
g.drawString("D-HRM", 154, 27);
|
||||
g.drawString("D-STEPS", 154, 115);
|
||||
|
||||
if(settings.fullscreen){
|
||||
g.drawString("D-HRM", 154, 27);
|
||||
g.drawString("D-STEPS", 154, 115);
|
||||
} else {
|
||||
g.drawString("DAY", 154, 115);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function draw(){
|
||||
// First handle alarm to show this correctly afterwards
|
||||
handleAlarm();
|
||||
// Queue draw first to ensure that its called in one minute again.
|
||||
queueDraw();
|
||||
|
||||
// Next draw the watch face
|
||||
g.reset();
|
||||
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
||||
// First handle alarm to show this correctly afterwards
|
||||
handleAlarm();
|
||||
|
||||
// Draw current lcars position
|
||||
if(lcarsViewPos == 0){
|
||||
drawPosition0();
|
||||
} else if (lcarsViewPos == 1) {
|
||||
drawPosition1();
|
||||
}
|
||||
// Next draw the watch face
|
||||
g.reset();
|
||||
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
||||
|
||||
// Queue draw in one minute
|
||||
queueDraw();
|
||||
// Draw current lcars position
|
||||
if(lcarsViewPos == 0){
|
||||
drawPosition0();
|
||||
} else if (lcarsViewPos == 1) {
|
||||
drawPosition1();
|
||||
}
|
||||
|
||||
// After drawing the watch face, we can draw the widgets
|
||||
if(settings.fullscreen){
|
||||
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||
} else {
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -463,16 +522,16 @@ function getSteps() {
|
|||
|
||||
|
||||
function getWeather(){
|
||||
var weather;
|
||||
var weatherJson;
|
||||
|
||||
try {
|
||||
weather = require('weather').get();
|
||||
weatherJson = storage.readJSON('weather.json');
|
||||
} catch(ex) {
|
||||
// Return default
|
||||
}
|
||||
|
||||
if (weather === undefined){
|
||||
weather = {
|
||||
if(weatherJson === undefined){
|
||||
return {
|
||||
temp: "-",
|
||||
hum: "-",
|
||||
txt: "-",
|
||||
|
@ -480,12 +539,22 @@ function getWeather(){
|
|||
wdir: "-",
|
||||
wrose: "-"
|
||||
};
|
||||
} else {
|
||||
weather.temp = locale.temp(Math.round(weather.temp-273.15))
|
||||
weather.hum = weather.hum + "%";
|
||||
}
|
||||
|
||||
return weather;
|
||||
var weather = weatherJson.weather;
|
||||
|
||||
// Temperature
|
||||
weather.temp = locale.temp(weather.temp-273.15);
|
||||
|
||||
// Humidity
|
||||
weather.hum = weather.hum + "%";
|
||||
|
||||
// Wind
|
||||
const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
|
||||
var speedFactor = settings.speed == "kph" ? 1.0 : 1.0 / 1.60934;
|
||||
weather.wind = Math.round(wind[1] * speedFactor);
|
||||
|
||||
return weather
|
||||
}
|
||||
|
||||
|
||||
|
@ -633,16 +702,7 @@ Bangle.on('touch', function(btn, e){
|
|||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
/*
|
||||
* we are not drawing the widgets as we are taking over the whole screen
|
||||
* so we will blank out the draw() functions of each widget and change the
|
||||
* area to the top bar doesn't get cleared.
|
||||
*/
|
||||
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||
|
||||
// Clear the screen once, at startup and draw clock
|
||||
g.setTheme({bg:"#000",fg:"#fff",dark:true}).clear();
|
||||
draw();
|
||||
|
||||
// After drawing the watch face, we can draw the widgets
|
||||
// Bangle.drawWidgets();
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
alarm: -1,
|
||||
dataRow1: "Battery",
|
||||
dataRow2: "Steps",
|
||||
dataRow3: "Temp"
|
||||
dataRow3: "Temp",
|
||||
speed: "kph",
|
||||
fullscreen: false,
|
||||
};
|
||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||
for (const key in saved_settings) {
|
||||
|
@ -18,35 +20,53 @@
|
|||
storage.write(SETTINGS_FILE, settings)
|
||||
}
|
||||
|
||||
var data_options = ["Steps", "Battery", "VREF", "HRM", "Temp", "Humidity", "Altitude", "CoreT"];
|
||||
var dataOptions = ["Steps", "Battery", "VREF", "HRM", "Temp", "Humidity", "Wind", "Altitude", "CoreT"];
|
||||
var speedOptions = ["kph", "mph"];
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'LCARS Clock' },
|
||||
'< Back': back,
|
||||
'Row 1': {
|
||||
value: 0 | data_options.indexOf(settings.dataRow1),
|
||||
min: 0, max: 7,
|
||||
format: v => data_options[v],
|
||||
value: 0 | dataOptions.indexOf(settings.dataRow1),
|
||||
min: 0, max: 8,
|
||||
format: v => dataOptions[v],
|
||||
onchange: v => {
|
||||
settings.dataRow1 = data_options[v];
|
||||
settings.dataRow1 = dataOptions[v];
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Row 2': {
|
||||
value: 0 | data_options.indexOf(settings.dataRow2),
|
||||
min: 0, max: 7,
|
||||
format: v => data_options[v],
|
||||
value: 0 | dataOptions.indexOf(settings.dataRow2),
|
||||
min: 0, max: 8,
|
||||
format: v => dataOptions[v],
|
||||
onchange: v => {
|
||||
settings.dataRow2 = data_options[v];
|
||||
settings.dataRow2 = dataOptions[v];
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Row 3': {
|
||||
value: 0 | data_options.indexOf(settings.dataRow3),
|
||||
min: 0, max: 7,
|
||||
format: v => data_options[v],
|
||||
value: 0 | dataOptions.indexOf(settings.dataRow3),
|
||||
min: 0, max: 8,
|
||||
format: v => dataOptions[v],
|
||||
onchange: v => {
|
||||
settings.dataRow3 = data_options[v];
|
||||
settings.dataRow3 = dataOptions[v];
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Full Screen': {
|
||||
value: settings.fullscreen,
|
||||
format: () => (settings.fullscreen ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.fullscreen = !settings.fullscreen;
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Speed': {
|
||||
value: 0 | speedOptions.indexOf(settings.speed),
|
||||
min: 0, max: 1,
|
||||
format: v => speedOptions[v],
|
||||
onchange: v => {
|
||||
settings.speed = speedOptions[v];
|
||||
save();
|
||||
},
|
||||
}
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
"name": "LCARS Clock",
|
||||
"shortName":"LCARS",
|
||||
"icon": "lcars.png",
|
||||
"version":"0.15",
|
||||
"version":"0.18",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"description": "Library Computer Access Retrieval System (LCARS) clock.",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"screenshots": [
|
||||
{"url":"screenshot_1.png"},
|
||||
{"url":"screenshot_3.png"}],
|
||||
"storage": [
|
||||
{"name":"lcars.app.js","url":"lcars.app.js"},
|
||||
{"name":"lcars.img","url":"lcars.icon.js","evaluate":true},
|
||||
|
|
Before Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 4.6 KiB |
|
@ -10,6 +10,9 @@
|
|||
<select id="languages" class="form-select">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input id="translations" type="checkbox" /> <label for="translations">Add common language translations like "Yes", "No", "On", "Off"<br/><i>(Not recommended. For translations use the option under <code>More...</code> in the app loader.</i></label>
|
||||
</div>
|
||||
<p>Then click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||
|
||||
<script src="../../core/lib/customize.js"></script>
|
||||
|
@ -106,12 +109,18 @@ exports = { name : "en_GB", currencySym:"£",
|
|||
const lang = languageSelector.options[languageSelector.selectedIndex].value;
|
||||
console.log(`Language ${lang}`);
|
||||
|
||||
const translations = document.getElementById('translations').checked;
|
||||
console.log(`Translations: ${translations}`);
|
||||
|
||||
const locale = locales[lang];
|
||||
if (!locale) {
|
||||
alert(`Language ${lang} not found!`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!translations)
|
||||
locale.trans = null;
|
||||
|
||||
const codePageName = "ISO8859-1";
|
||||
if (locale.codePage)
|
||||
codePageName = locale.codePage;
|
||||
|
|
|
@ -30,3 +30,4 @@
|
|||
If showMessage called with no message (eg all messages deleted) now return to the clock (fix #1267)
|
||||
0.19: Use a larger font for message text if it'll fit
|
||||
0.20: Allow tapping on the body to show a scrollable view of the message and title in a bigger font (fix #1405, #1031)
|
||||
0.21: Improve list readability on dark theme
|
||||
|
|
|
@ -27,7 +27,7 @@ var fontBig = g.getFonts().includes("12x20")?"12x20":"6x8:2";
|
|||
var fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4";
|
||||
var colBg = g.theme.dark ? "#141":"#4f4";
|
||||
var colSBg1 = g.theme.dark ? "#121":"#cFc";
|
||||
var colSBg2 = g.theme.dark ? "#242":"#9F9";
|
||||
var colSBg2 = g.theme.dark ? "#000":"#9F9";
|
||||
// hack for 2v10 firmware's lack of ':size' font handling
|
||||
try {
|
||||
g.setFont("6x8:2");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "messages",
|
||||
"name": "Messages",
|
||||
"version": "0.20",
|
||||
"version": "0.21",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.01: First release
|
||||
0.02: Enhanced icon, make it bolder
|
||||
0.03: Fixed issue with defaulting back to London
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"icon": "mylocation.png",
|
||||
"type": "app",
|
||||
"screenshots": [{"url":"screenshot_1.png"}],
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "Sets and stores the lat and long of your preferred City or it can be set from the GPS. mylocation.json can be used by other apps that need your main location lat and lon. See README",
|
||||
"readme": "README.md",
|
||||
"tags": "tool,utility",
|
||||
|
|
|
@ -9,32 +9,35 @@ let s = {
|
|||
'lat': 51.5072,
|
||||
'lon': 0.1276,
|
||||
'location': "London"
|
||||
}
|
||||
};
|
||||
|
||||
function loadSettings() {
|
||||
settings = require('Storage').readJSON(SETTINGS_FILE, 1) || s;
|
||||
settings = require('Storage').readJSON(SETTINGS_FILE, 1) || {};
|
||||
for (const key in settings) {
|
||||
s[key] = settings[key]
|
||||
}
|
||||
}
|
||||
|
||||
function save() {
|
||||
settings = s
|
||||
require('Storage').write(SETTINGS_FILE, settings)
|
||||
settings = s;
|
||||
require('Storage').write(SETTINGS_FILE, settings);
|
||||
}
|
||||
|
||||
const locations = ["London", "Newcastle", "Edinburgh", "Paris", "New York", "Tokyo","???"];
|
||||
const lats = [51.5072 ,54.9783 ,55.9533 ,48.8566 ,40.7128 ,35.6762, 0.0];
|
||||
const lons = [-0.1276 ,-1.6178 ,-3.1883 ,2.3522 , -74.0060 ,139.6503, 0.0];
|
||||
const locations = ["London" ,"Newcastle","Edinburgh", "Paris" , "New York" , "Tokyo" , "Frankfurt", "Auckland", "???"];
|
||||
const lats = [ 51.5072 , 54.9783 , 55.9533 , 48.8566 , 40.7128 , 35.6762 , 50.1236 , -36.9 , 0.0 ];
|
||||
const lons = [ -0.1276 , -1.6178 , -3.1883 , 2.3522 , -74.0060 , 139.6503 , 8.6553 , 174.7832 , 0.0 ];
|
||||
|
||||
function setFromGPS() {
|
||||
Bangle.on('GPS', (gps) => {
|
||||
//console.log(".");
|
||||
if (gps.fix === 0) return;
|
||||
//console.log("fix from GPS");
|
||||
s = {'lat': gps.lat, 'lon': gps.lon, 'location': '???' }
|
||||
s = {'lat': gps.lat, 'lon': gps.lon, 'location': '???' };
|
||||
Bangle.buzz(1500); // buzz on first position
|
||||
Bangle.setGPSPower(0);
|
||||
save();
|
||||
|
||||
Bangle.setUI("updown", ()=>{ load() });
|
||||
Bangle.setUI("updown", ()=>{ load(); });
|
||||
E.showPrompt("Location has been saved from the GPS fix",{
|
||||
title:"Location Saved",
|
||||
buttons : {"OK":1}
|
||||
|
@ -49,13 +52,13 @@ function setFromGPS() {
|
|||
}
|
||||
|
||||
function showMainMenu() {
|
||||
console.log("showMainMenu");
|
||||
//console.log("showMainMenu");
|
||||
const mainmenu = {
|
||||
'': { 'title': 'My Location' },
|
||||
'<Back': ()=>{ load(); },
|
||||
'City': {
|
||||
value: 0 | locations.indexOf(s.location),
|
||||
min: 0, max: 6,
|
||||
min: 0, max: locations.length - 1,
|
||||
format: v => locations[v],
|
||||
onchange: v => {
|
||||
if (v != 6) {
|
||||
|
@ -67,7 +70,7 @@ function showMainMenu() {
|
|||
}
|
||||
},
|
||||
'Set From GPS': ()=>{ setFromGPS(); }
|
||||
}
|
||||
};
|
||||
return E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial release
|
|
@ -0,0 +1,20 @@
|
|||
# Neon X and IO X Clock
|
||||
|
||||
|  |  |
|
||||
|---------------------------------|--------------------------------------|
|
||||
| <center>Neon X</center> | <center>Neon IO X</center> |
|
||||
|
||||
This is a clock based on Pebble's Neon X and Neon IO X watchfaces by Sam Jerichow.
|
||||
Can be switched between in the Settings menu, which can be accessed through
|
||||
the app/widget settings menu of the Bangle.js
|
||||
|
||||
## Settings
|
||||
|
||||
### Neon IO X:
|
||||
Activate the Neon IO X clock look, a bit hard to read until one gets used to it.
|
||||
|
||||
### Thickness
|
||||
The thickness of watch lines, from 1 to 5.
|
||||
|
||||
### Date on touch
|
||||
Shows the current date as DD MM on touch and reverts back to time after 5 seconds or with another touch.
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"id": "neonx",
|
||||
"name": "Neon X & IO X Clock",
|
||||
"shortName": "Neon X Clock",
|
||||
"version": "0.01",
|
||||
"description": "Pebble Neon X & Neon IO X for Bangle.js",
|
||||
"icon": "neonx.png",
|
||||
"type": "clock",
|
||||
"readme": "README.md",
|
||||
"tags": "neonx,neonio,neoniox,clock",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url": "neonx-screenshot.png"}, {"url": "neoniox-screenshot.png"}],
|
||||
"storage": [
|
||||
{"name": "neonx.app.js", "url": "neonx.app.js"},
|
||||
{"name": "neonx.img", "url": "neonx-icon.js", "evaluate": true},
|
||||
{"name": "neonx.settings.js", "url": "neonx.settings.js"}
|
||||
],
|
||||
"data": [{"name": "neonx.json"}]
|
||||
}
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1 @@
|
|||
E.toArrayBuffer(atob("MDCDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAwAAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAALaAAAAAAAAAAG2AAAAAAAAAABQAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAgABttttttttsAAEkAAAAAAAEkANtttttttttgAEkAAAAAAAEkABttttttttsAAEkAAAAAAAEkAAAAAAAAAAAAAEkAAAAAAAEkAAAAAAAAAAAAAEkAAAAAAAEkAAAAAAAAAAAAAEkAAAAAAAEkAAAAAAAAAAAAAEkAAAAAAAEkAAAAAAAAAAAAAEkAAAAAAAEkAAAAAAAAAAAAAEkAAAAAAAEkAAAAAAAAAAAAAAgAAAAAAAEkAAAAAAAAAAAAAAAAAAAAAAEkAAAAAAAAAAAAAAAAAAAAAAEkAAAAAAAAAAAAAAAAAAAAAAEkAAAAAAAAAAAAAAAAAAAAAAEkAAAAAAAAAAAAAAAAAAAAAAEkAAAAAAAAAAAAAAAAAAAAAAEkAAAAAAAAAAAAAAAAAAAAAAEkABttttttttsAAAAAAAAAAAEkANtttttttttgAAAAAAAAAAAgABttttttttsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="))
|
After Width: | Height: | Size: 1.5 KiB |