Merge branch 'master' into new_branch
|
@ -0,0 +1,12 @@
|
||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: espruino
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: ['http://www.espruino.com/Donate']# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
@ -1,4 +1,4 @@
|
||||||
name: Node CI
|
name: build
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
@ -6,29 +6,22 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [16.x]
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository and submodules
|
- name: Checkout repository and submodules
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js 16.x
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: 16.x
|
||||||
- name: install testing dependencies
|
- name: Install testing dependencies
|
||||||
run: npm i
|
run: npm ci
|
||||||
- name: test all apps and widgets
|
- name: Test all apps and widgets
|
||||||
run: npm run test
|
run: npm test
|
||||||
- name: install typescript dependencies
|
- name: Install typescript dependencies
|
||||||
working-directory: ./typescript
|
working-directory: ./typescript
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: build types
|
- name: Build all TS apps and widgets
|
||||||
working-directory: ./typescript
|
working-directory: ./typescript
|
||||||
run: npm run build:types
|
run: npm run build
|
||||||
- name: build all TS apps and widgets
|
|
||||||
working-directory: ./typescript
|
|
||||||
run: npm run build
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
.htaccess
|
.htaccess
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.js.bak
|
*.js.bak
|
||||||
appdates.csv
|
appdates.csv
|
||||||
|
@ -12,3 +11,6 @@ tests/Layout/testresult.bmp
|
||||||
apps.local.json
|
apps.local.json
|
||||||
_site
|
_site
|
||||||
.jekyll-cache
|
.jekyll-cache
|
||||||
|
.owncloudsync.log
|
||||||
|
Desktop.ini
|
||||||
|
.sync_*.db*
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Bangle.js App Loader (and Apps)
|
Bangle.js App Loader (and Apps)
|
||||||
================================
|
================================
|
||||||
|
|
||||||
[](https://app.travis-ci.com/github/espruino/BangleApps)
|
[](https://github.com/espruino/BangleApps/actions/workflows/nodejs.yml)
|
||||||
|
|
||||||
* Try the **release version** at [banglejs.com/apps](https://banglejs.com/apps)
|
* Try the **release version** at [banglejs.com/apps](https://banglejs.com/apps)
|
||||||
* Try the **development version** at [espruino.github.io](https://espruino.github.io/BangleApps/)
|
* Try the **development version** at [espruino.github.io](https://espruino.github.io/BangleApps/)
|
||||||
|
@ -191,7 +191,7 @@ widget bar at the top of the screen they can add themselves to the global
|
||||||
|
|
||||||
```
|
```
|
||||||
WIDGETS["mywidget"]={
|
WIDGETS["mywidget"]={
|
||||||
area:"tl", // tl (top left), tr (top right)
|
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
|
||||||
sortorder:0, // (Optional) determines order of widgets in the same corner
|
sortorder:0, // (Optional) determines order of widgets in the same corner
|
||||||
width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
|
width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
|
||||||
draw:draw // called to draw the widget
|
draw:draw // called to draw the widget
|
||||||
|
@ -324,7 +324,7 @@ and which gives information about the app for the Launcher.
|
||||||
```
|
```
|
||||||
|
|
||||||
* name, icon and description present the app in the app loader.
|
* name, icon and description present the app in the app loader.
|
||||||
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher` or empty.
|
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher`, `bluetooth` or empty.
|
||||||
* storage is used to identify the app files and how to handle them
|
* storage is used to identify the app files and how to handle them
|
||||||
* data is used to clean up files when the app is uninstalled
|
* data is used to clean up files when the app is uninstalled
|
||||||
|
|
||||||
|
|
|
@ -149,11 +149,11 @@ function drawHSeg(x1,y1,x2,y2,Num,Color,Size) {
|
||||||
if (Color == "fg") {
|
if (Color == "fg") {
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
} else {
|
} else {
|
||||||
g.setColor(mColor[0],mColor[1],mColor[2]);
|
g.setColor(mColor[0],mColor[1],mColor[2]);
|
||||||
}
|
}
|
||||||
g.fillCircle(x1+Dx+(i-1)*(x2-x1)/7,y1+Dy+(j-1)*(y2-y1)/7,Size);
|
g.fillCircle(x1+Dx+(i-1)*(x2-x1)/7,y1+Dy+(j-1)*(y2-y1)/7,Size);
|
||||||
} else {
|
} else {
|
||||||
g.setColor(bColor[0],bColor[1],bColor[2]);
|
g.setColor(bColor[0],bColor[1],bColor[2]);
|
||||||
g.fillCircle(x1+Dx+(i-1)*(x2-x1)/7,y1+Dy+(j-1)*(y2-y1)/7,1);
|
g.fillCircle(x1+Dx+(i-1)*(x2-x1)/7,y1+Dy+(j-1)*(y2-y1)/7,1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ function drawSSeg(x1,y1,x2,y2,Num,Color,Size) {
|
||||||
for (let j = 1; j < 8; j++) {
|
for (let j = 1; j < 8; j++) {
|
||||||
if (Font[Num][j-1][i-1] == 1) {
|
if (Font[Num][j-1][i-1] == 1) {
|
||||||
if (Color == "fg") {
|
if (Color == "fg") {
|
||||||
g.setColor(sColor[0],sColor[1],sColor[2]);
|
g.setColor(sColor[0],sColor[1],sColor[2]);
|
||||||
} else {
|
} else {
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
//g.setColor(0.7,0.7,0.7);
|
//g.setColor(0.7,0.7,0.7);
|
||||||
|
@ -253,8 +253,8 @@ function actions(v){
|
||||||
if(BTN1.read() === true) {
|
if(BTN1.read() === true) {
|
||||||
print("BTN pressed");
|
print("BTN pressed");
|
||||||
Bangle.showLauncher();
|
Bangle.showLauncher();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(v==-1){
|
if(v==-1){
|
||||||
print("up swipe event");
|
print("up swipe event");
|
||||||
if(settings.swupApp != "") load(settings.swupApp);
|
if(settings.swupApp != "") load(settings.swupApp);
|
||||||
|
@ -269,7 +269,7 @@ function actions(v){
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Messages status
|
// Get Messages status
|
||||||
var messages = require("Storage").readJSON("messages.json",1)||[];
|
var messages_installed = require("Storage").read("messages") !== undefined;
|
||||||
|
|
||||||
//var BTconnected = NRF.getSecurityStatus().connected;
|
//var BTconnected = NRF.getSecurityStatus().connected;
|
||||||
//NRF.on('connect',BTconnected = NRF.getSecurityStatus().connected);
|
//NRF.on('connect',BTconnected = NRF.getSecurityStatus().connected);
|
||||||
|
@ -289,27 +289,27 @@ function drawWidgeds() {
|
||||||
g.setColor((g.getBPP()>8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
|
g.setColor((g.getBPP()>8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
|
||||||
else
|
else
|
||||||
g.setColor(g.theme.dark ? "#666" : "#999");
|
g.setColor(g.theme.dark ? "#666" : "#999");
|
||||||
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="),x1Bt,y1Bt);
|
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="),x1Bt,y1Bt);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Battery
|
//Battery
|
||||||
//print(E.getBattery());
|
//print(E.getBattery());
|
||||||
//print(Bangle.isCharging());
|
//print(Bangle.isCharging());
|
||||||
|
|
||||||
var x1B = 130;
|
var x1B = 130;
|
||||||
var y1B = 2;
|
var y1B = 2;
|
||||||
var x2B = x1B + 20;
|
var x2B = x1B + 20;
|
||||||
var y2B = y1B + 15;
|
var y2B = y1B + 15;
|
||||||
|
|
||||||
g.setColor(g.theme.bg);
|
g.setColor(g.theme.bg);
|
||||||
g.clearRect(x1B,y1B,x2B,y2B);
|
g.clearRect(x1B,y1B,x2B,y2B);
|
||||||
|
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.drawRect(x1B,y1B,x2B,y2B);
|
g.drawRect(x1B,y1B,x2B,y2B);
|
||||||
g.fillRect(x1B,y1B,x1B+(E.getBattery()*(x2B-x1B)/100),y2B);
|
g.fillRect(x1B,y1B,x1B+(E.getBattery()*(x2B-x1B)/100),y2B);
|
||||||
g.fillRect(x2B,y1B+(y2B-y1B)/2-3,x2B+4,y1B+(y2B-y1B)/2+3);
|
g.fillRect(x2B,y1B+(y2B-y1B)/2-3,x2B+4,y1B+(y2B-y1B)/2+3);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Messages
|
//Messages
|
||||||
|
|
||||||
|
@ -318,25 +318,25 @@ function drawWidgeds() {
|
||||||
var x2M = x1M + 25;
|
var x2M = x1M + 25;
|
||||||
var y2M = y2B;
|
var y2M = y2B;
|
||||||
|
|
||||||
if (messages.some(m=>m.new)) {
|
if (messages_installed && require("messages").status() == "new") {
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.fillRect(x1M,y1M,x2M,y2M);
|
g.fillRect(x1M,y1M,x2M,y2M);
|
||||||
g.setColor(g.theme.bg);
|
g.setColor(g.theme.bg);
|
||||||
g.drawLine(x1M,y1M,x1M+(x2M-x1M)/2,y1M+(y2M-y1M)/2);
|
g.drawLine(x1M,y1M,x1M+(x2M-x1M)/2,y1M+(y2M-y1M)/2);
|
||||||
g.drawLine(x1M+(x2M-x1M)/2,y1M+(y2M-y1M)/2,x2M,y1M);
|
g.drawLine(x1M+(x2M-x1M)/2,y1M+(y2M-y1M)/2,x2M,y1M);
|
||||||
}
|
}
|
||||||
|
|
||||||
var strDow = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
|
var strDow = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
var dow = d.getDay(),day = d.getDate(), month = d.getMonth() + 1, year = d.getFullYear();
|
var dow = d.getDay(),day = d.getDate(), month = d.getMonth() + 1, year = d.getFullYear();
|
||||||
|
|
||||||
print(strDow[dow] + ' ' + day + '.' + month + ' ' + year);
|
print(strDow[dow] + ' ' + day + '.' + month + ' ' + year);
|
||||||
|
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.setFontAlign(-1, -1,0);
|
g.setFontAlign(-1, -1,0);
|
||||||
g.setFont("Vector", 20);
|
g.setFont("Vector", 20);
|
||||||
g.drawString(strDow[dow] + ' ' + day, 0, 0, true);
|
g.drawString(strDow[dow] + ' ' + day, 0, 0, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -354,7 +354,7 @@ function SetFull(on) {
|
||||||
} else {
|
} else {
|
||||||
Ys = 30;
|
Ys = 30;
|
||||||
Bangle.setUI("updown",actions);
|
Bangle.setUI("updown",actions);
|
||||||
Bangle.on('swipe', function(direction) {
|
Bangle.on('swipe', function(direction) {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 1:
|
case 1:
|
||||||
print("swipe left event");
|
print("swipe left event");
|
||||||
|
@ -362,7 +362,7 @@ function SetFull(on) {
|
||||||
print(settings.swleftApp);
|
print(settings.swleftApp);
|
||||||
break;
|
break;
|
||||||
case -1:
|
case -1:
|
||||||
print("swipe right event");
|
print("swipe right event");
|
||||||
if(settings.swrightApp != "") load(settings.swrightApp);
|
if(settings.swrightApp != "") load(settings.swrightApp);
|
||||||
print(settings.swrightApp);
|
print(settings.swrightApp);
|
||||||
break;
|
break;
|
||||||
|
@ -374,7 +374,7 @@ function SetFull(on) {
|
||||||
|
|
||||||
SegH = (Ye-Ys)/2;
|
SegH = (Ye-Ys)/2;
|
||||||
Dy = SegH/16;
|
Dy = SegH/16;
|
||||||
|
|
||||||
draw();
|
draw();
|
||||||
|
|
||||||
if (on != true) {
|
if (on != true) {
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: Initial version for upload
|
0.01: Initial version for upload
|
||||||
0.02: better theme support, configurable colors, small improvements
|
0.02: Better theme support, configurable colors, small improvements
|
||||||
|
0.03: Use `messages` library to check for new messages
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "7x7dotsclock",
|
{ "id": "7x7dotsclock",
|
||||||
"name": "7x7 Dots Clock",
|
"name": "7x7 Dots Clock",
|
||||||
"shortName":"7x7 Dots Clock",
|
"shortName":"7x7 Dots Clock",
|
||||||
"version":"0.02",
|
"version":"0.03",
|
||||||
"description": "A clock with a big 7x7 dots Font",
|
"description": "A clock with a big 7x7 dots Font",
|
||||||
"icon": "dotsfontclock.png",
|
"icon": "dotsfontclock.png",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Fullscreen settings.
|
0.02: Fullscreen settings.
|
||||||
|
0.03: Tell clock widgets to hide.
|
||||||
|
|
|
@ -115,6 +115,9 @@ function draw() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show launcher when middle button pressed
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
|
|
||||||
// Clear the screen once, at startup
|
// Clear the screen once, at startup
|
||||||
|
@ -140,5 +143,3 @@ Bangle.on('lock', function(isLocked) {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Show launcher when middle button pressed
|
|
||||||
Bangle.setUI("clock");
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "90sclk",
|
"id": "90sclk",
|
||||||
"name": "90s Clock",
|
"name": "90s Clock",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "A 90s style watch-face",
|
"description": "A 90s style watch-face",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
@ -9,7 +9,7 @@ currently-running apps */
|
||||||
|
|
||||||
// add your widget
|
// add your widget
|
||||||
WIDGETS["mywidget"]={
|
WIDGETS["mywidget"]={
|
||||||
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
|
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right), be aware that not all apps support widgets at the bottom of the screen
|
||||||
width: 28, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
|
width: 28, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
|
||||||
draw:draw // called to draw the widget
|
draw:draw // called to draw the widget
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,4 +4,6 @@
|
||||||
0.04: Obey system quiet mode
|
0.04: Obey system quiet mode
|
||||||
0.05: Battery optimisation, add the pause option, bug fixes
|
0.05: Battery optimisation, add the pause option, bug fixes
|
||||||
0.06: Add a temperature threshold to detect (and not alert) if the BJS isn't worn. Better support for the peoples using the app at night
|
0.06: Add a temperature threshold to detect (and not alert) if the BJS isn't worn. Better support for the peoples using the app at night
|
||||||
0.07: Fix bug on the cutting edge firmware
|
0.07: Fix bug on the cutting edge firmware
|
||||||
|
0.08: Use default Bangle formatter for booleans
|
||||||
|
0.09: New app screen (instead of showing settings or the alert) and some optimisations
|
|
@ -0,0 +1,37 @@
|
||||||
|
(function () {
|
||||||
|
// load variable before defining functions cause it can trigger a ReferenceError
|
||||||
|
const activityreminder = require("activityreminder");
|
||||||
|
const storage = require("Storage");
|
||||||
|
let activityreminder_data = activityreminder.loadData();
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
E.showPrompt("Inactivity detected", {
|
||||||
|
title: "Activity reminder",
|
||||||
|
buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 }
|
||||||
|
}).then(function (v) {
|
||||||
|
if (v == 1) {
|
||||||
|
activityreminder_data.okDate = new Date();
|
||||||
|
}
|
||||||
|
if (v == 2) {
|
||||||
|
activityreminder_data.dismissDate = new Date();
|
||||||
|
}
|
||||||
|
if (v == 3) {
|
||||||
|
activityreminder_data.pauseDate = new Date();
|
||||||
|
}
|
||||||
|
activityreminder.saveData(activityreminder_data);
|
||||||
|
load();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Obey system quiet mode:
|
||||||
|
if (!(storage.readJSON('setting.json', 1) || {}).quiet) {
|
||||||
|
Bangle.buzz(400);
|
||||||
|
}
|
||||||
|
setTimeout(load, 20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
run();
|
||||||
|
|
||||||
|
})();
|
|
@ -1,46 +1,54 @@
|
||||||
(function () {
|
(function () {
|
||||||
// load variable before defining functions cause it can trigger a ReferenceError
|
// load variable before defining functions cause it can trigger a ReferenceError
|
||||||
const activityreminder = require("activityreminder");
|
const activityreminder = require("activityreminder");
|
||||||
const storage = require("Storage");
|
let activityreminder_data = activityreminder.loadData();
|
||||||
const activityreminder_settings = activityreminder.loadSettings();
|
let W = g.getWidth();
|
||||||
let activityreminder_data = activityreminder.loadData();
|
// let H = g.getHeight();
|
||||||
|
|
||||||
function drawAlert() {
|
function getHoursMins(date){
|
||||||
E.showPrompt("Inactivity detected", {
|
var h = date.getHours();
|
||||||
title: "Activity reminder",
|
var m = date.getMinutes();
|
||||||
buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 }
|
return (""+h).substr(-2) + ":" + ("0"+m).substr(-2);
|
||||||
}).then(function (v) {
|
}
|
||||||
if (v == 1) {
|
|
||||||
activityreminder_data.okDate = new Date();
|
|
||||||
}
|
|
||||||
if (v == 2) {
|
|
||||||
activityreminder_data.dismissDate = new Date();
|
|
||||||
}
|
|
||||||
if (v == 3) {
|
|
||||||
activityreminder_data.pauseDate = new Date();
|
|
||||||
}
|
|
||||||
activityreminder.saveData(activityreminder_data);
|
|
||||||
load();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Obey system quiet mode:
|
|
||||||
if (!(storage.readJSON('setting.json', 1) || {}).quiet) {
|
|
||||||
Bangle.buzz(400);
|
|
||||||
}
|
|
||||||
setTimeout(load, 20000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function run() {
|
|
||||||
if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) {
|
|
||||||
drawAlert();
|
|
||||||
} else {
|
|
||||||
eval(storage.read("activityreminder.settings.js"))(() => load());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function drawData(name, value, y){
|
||||||
|
g.drawString(name, 10, y);
|
||||||
|
g.drawString(value, 100, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInfo() {
|
||||||
|
var h=18, y = h;
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.setFont("Vector",h).setFontAlign(-1,-1);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
g.drawLine(0,25,W,25);
|
||||||
|
g.drawLine(0,26,W,26);
|
||||||
|
|
||||||
|
g.drawString("Current Cycle", 10, y+=h);
|
||||||
|
drawData("Start", getHoursMins(activityreminder_data.stepsDate), y+=h);
|
||||||
|
drawData("Steps", getCurrentSteps(), y+=h);
|
||||||
|
|
||||||
|
/*
|
||||||
|
g.drawString("Button Press", 10, y+=h*2);
|
||||||
|
drawData("Ok", getHoursMins(activityreminder_data.okDate), y+=h);
|
||||||
|
drawData("Dismiss", getHoursMins(activityreminder_data.dismissDate), y+=h);
|
||||||
|
drawData("Pause", getHoursMins(activityreminder_data.pauseDate), y+=h);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentSteps(){
|
||||||
|
let health = Bangle.getHealthStatus("day");
|
||||||
|
return health.steps - activityreminder_data.stepsOnDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function run() {
|
||||||
g.clear();
|
g.clear();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
run();
|
drawInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
})();
|
})();
|
|
@ -1,70 +1,81 @@
|
||||||
(function () {
|
(function () {
|
||||||
// load variable before defining functions cause it can trigger a ReferenceError
|
// load variable before defining functions cause it can trigger a ReferenceError
|
||||||
const activityreminder = require("activityreminder");
|
const activityreminder = require("activityreminder");
|
||||||
const activityreminder_settings = activityreminder.loadSettings();
|
const activityreminder_settings = activityreminder.loadSettings();
|
||||||
let activityreminder_data = activityreminder.loadData();
|
let activityreminder_data = activityreminder.loadData();
|
||||||
|
|
||||||
if (activityreminder_data.firstLoad) {
|
if (activityreminder_data.firstLoad) {
|
||||||
activityreminder_data.firstLoad = false;
|
activityreminder_data.firstLoad = false;
|
||||||
|
activityreminder.saveData(activityreminder_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
if (isNotWorn()) return;
|
||||||
|
let now = new Date();
|
||||||
|
let h = now.getHours();
|
||||||
|
|
||||||
|
if (isDuringAlertHours(h)) {
|
||||||
|
let health = Bangle.getHealthStatus("day");
|
||||||
|
if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed
|
||||||
|
|| health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch
|
||||||
|
activityreminder_data.stepsOnDate = health.steps;
|
||||||
|
activityreminder_data.stepsDate = now;
|
||||||
activityreminder.saveData(activityreminder_data);
|
activityreminder.saveData(activityreminder_data);
|
||||||
}
|
|
||||||
|
|
||||||
function run() {
|
|
||||||
if (isNotWorn()) return;
|
|
||||||
let now = new Date();
|
|
||||||
let h = now.getHours();
|
|
||||||
|
|
||||||
if (isDuringAlertHours(h)) {
|
|
||||||
let health = Bangle.getHealthStatus("day");
|
|
||||||
if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed
|
|
||||||
|| health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch
|
|
||||||
activityreminder_data.stepsOnDate = health.steps;
|
|
||||||
activityreminder_data.stepsDate = now;
|
|
||||||
activityreminder.saveData(activityreminder_data);
|
|
||||||
/* todo in a futur release
|
|
||||||
Add settimer to trigger like 30 secs after going in this part cause the person have been walking
|
|
||||||
(pass some argument to run() to handle long walks and not triggering so often)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) {
|
|
||||||
load('activityreminder.app.js');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNotWorn() {
|
|
||||||
return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature());
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDuringAlertHours(h) {
|
|
||||||
if (activityreminder_settings.startHour < activityreminder_settings.endHour) { // not passing through midnight
|
|
||||||
return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour);
|
|
||||||
} else { // passing through midnight
|
|
||||||
return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Bangle.on('midnight', function () {
|
|
||||||
/*
|
|
||||||
Usefull trick to have the app working smothly for people using it at night
|
|
||||||
*/
|
|
||||||
let now = new Date();
|
|
||||||
let h = now.getHours();
|
|
||||||
if (activityreminder_settings.enabled && isDuringAlertHours(h)) {
|
|
||||||
// updating only the steps and keeping the original stepsDate on purpose
|
|
||||||
activityreminder_data.stepsOnDate = 0;
|
|
||||||
activityreminder.saveData(activityreminder_data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
if (activityreminder_settings.enabled) {
|
|
||||||
setInterval(run, 60000);
|
|
||||||
/* todo in a futur release
|
/* todo in a futur release
|
||||||
increase setInterval time to something that is still sensible (5 mins ?)
|
Add settimer to trigger like 30 secs after going in this part cause the person have been walking
|
||||||
when we added a settimer
|
(pass some argument to run() to handle long walks and not triggering so often)
|
||||||
*/
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mustAlert(now)) {
|
||||||
|
load('activityreminder.alert.js');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNotWorn() {
|
||||||
|
return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature());
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDuringAlertHours(h) {
|
||||||
|
if (activityreminder_settings.startHour < activityreminder_settings.endHour) { // not passing through midnight
|
||||||
|
return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour);
|
||||||
|
} else { // passing through midnight
|
||||||
|
return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mustAlert(now) {
|
||||||
|
if ((now - activityreminder_data.stepsDate) / 60000 > activityreminder_settings.maxInnactivityMin) { // inactivity detected
|
||||||
|
if ((now - activityreminder_data.okDate) / 60000 > 3 && // last alert anwsered with ok was more than 3 min ago
|
||||||
|
(now - activityreminder_data.dismissDate) / 60000 > activityreminder_settings.dismissDelayMin && // last alert was more than dismissDelayMin ago
|
||||||
|
(now - activityreminder_data.pauseDate) / 60000 > activityreminder_settings.pauseDelayMin) { // last alert was more than pauseDelayMin ago
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bangle.on('midnight', function () {
|
||||||
|
/*
|
||||||
|
Usefull trick to have the app working smothly for people using it at night
|
||||||
|
*/
|
||||||
|
let now = new Date();
|
||||||
|
let h = now.getHours();
|
||||||
|
if (activityreminder_settings.enabled && isDuringAlertHours(h)) {
|
||||||
|
// updating only the steps and keeping the original stepsDate on purpose
|
||||||
|
activityreminder_data.stepsOnDate = 0;
|
||||||
|
activityreminder.saveData(activityreminder_data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (activityreminder_settings.enabled) {
|
||||||
|
setInterval(run, 60000);
|
||||||
|
/* todo in a futur release
|
||||||
|
increase setInterval time to something that is still sensible (5 mins ?)
|
||||||
|
when we added a settimer
|
||||||
|
*/
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,56 +1,44 @@
|
||||||
exports.loadSettings = function () {
|
exports.loadSettings = function () {
|
||||||
return Object.assign({
|
return Object.assign({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
startHour: 9,
|
startHour: 9,
|
||||||
endHour: 20,
|
endHour: 20,
|
||||||
maxInnactivityMin: 30,
|
maxInnactivityMin: 30,
|
||||||
dismissDelayMin: 15,
|
dismissDelayMin: 15,
|
||||||
pauseDelayMin: 120,
|
pauseDelayMin: 120,
|
||||||
minSteps: 50,
|
minSteps: 50,
|
||||||
tempThreshold: 27
|
tempThreshold: 27
|
||||||
}, require("Storage").readJSON("activityreminder.s.json", true) || {});
|
}, require("Storage").readJSON("activityreminder.s.json", true) || {});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.writeSettings = function (settings) {
|
exports.writeSettings = function (settings) {
|
||||||
require("Storage").writeJSON("activityreminder.s.json", settings);
|
require("Storage").writeJSON("activityreminder.s.json", settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.saveData = function (data) {
|
exports.saveData = function (data) {
|
||||||
require("Storage").writeJSON("activityreminder.data.json", data);
|
require("Storage").writeJSON("activityreminder.data.json", data);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.loadData = function () {
|
exports.loadData = function () {
|
||||||
let health = Bangle.getHealthStatus("day");
|
let health = Bangle.getHealthStatus("day");
|
||||||
let data = Object.assign({
|
let data = Object.assign({
|
||||||
firstLoad: true,
|
firstLoad: true,
|
||||||
stepsDate: new Date(),
|
stepsDate: new Date(),
|
||||||
stepsOnDate: health.steps,
|
stepsOnDate: health.steps,
|
||||||
okDate: new Date(1970),
|
okDate: new Date(1970),
|
||||||
dismissDate: new Date(1970),
|
dismissDate: new Date(1970),
|
||||||
pauseDate: new Date(1970),
|
pauseDate: new Date(1970),
|
||||||
},
|
},
|
||||||
require("Storage").readJSON("activityreminder.data.json") || {});
|
require("Storage").readJSON("activityreminder.data.json") || {});
|
||||||
|
|
||||||
if(typeof(data.stepsDate) == "string")
|
if (typeof (data.stepsDate) == "string")
|
||||||
data.stepsDate = new Date(data.stepsDate);
|
data.stepsDate = new Date(data.stepsDate);
|
||||||
if(typeof(data.okDate) == "string")
|
if (typeof (data.okDate) == "string")
|
||||||
data.okDate = new Date(data.okDate);
|
data.okDate = new Date(data.okDate);
|
||||||
if(typeof(data.dismissDate) == "string")
|
if (typeof (data.dismissDate) == "string")
|
||||||
data.dismissDate = new Date(data.dismissDate);
|
data.dismissDate = new Date(data.dismissDate);
|
||||||
if(typeof(data.pauseDate) == "string")
|
if (typeof (data.pauseDate) == "string")
|
||||||
data.pauseDate = new Date(data.pauseDate);
|
data.pauseDate = new Date(data.pauseDate);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.mustAlert = function(activityreminder_data, activityreminder_settings) {
|
|
||||||
let now = new Date();
|
|
||||||
if ((now - activityreminder_data.stepsDate) / 60000 > activityreminder_settings.maxInnactivityMin) { // inactivity detected
|
|
||||||
if ((now - activityreminder_data.okDate) / 60000 > 3 && // last alert anwsered with ok was more than 3 min ago
|
|
||||||
(now - activityreminder_data.dismissDate) / 60000 > activityreminder_settings.dismissDelayMin && // last alert was more than dismissDelayMin ago
|
|
||||||
(now - activityreminder_data.pauseDate) / 60000 > activityreminder_settings.pauseDelayMin) { // last alert was more than pauseDelayMin ago
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Activity Reminder",
|
"name": "Activity Reminder",
|
||||||
"shortName":"Activity Reminder",
|
"shortName":"Activity Reminder",
|
||||||
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
||||||
"version":"0.07",
|
"version":"0.09",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "tool,activity",
|
"tags": "tool,activity",
|
||||||
|
@ -13,11 +13,12 @@
|
||||||
{"name": "activityreminder.app.js", "url":"app.js"},
|
{"name": "activityreminder.app.js", "url":"app.js"},
|
||||||
{"name": "activityreminder.boot.js", "url": "boot.js"},
|
{"name": "activityreminder.boot.js", "url": "boot.js"},
|
||||||
{"name": "activityreminder.settings.js", "url": "settings.js"},
|
{"name": "activityreminder.settings.js", "url": "settings.js"},
|
||||||
|
{"name": "activityreminder.alert.js", "url": "alert.js"},
|
||||||
{"name": "activityreminder", "url": "lib.js"},
|
{"name": "activityreminder", "url": "lib.js"},
|
||||||
{"name": "activityreminder.img", "url": "app-icon.js", "evaluate": true}
|
{"name": "activityreminder.img", "url": "app-icon.js", "evaluate": true}
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
{"name": "activityreminder.s.json"},
|
{"name": "activityreminder.s.json"},
|
||||||
{"name": "activityreminder.data.json"}
|
{"name": "activityreminder.data.json", "storageFile": true}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,85 +1,86 @@
|
||||||
(function (back) {
|
(function (back) {
|
||||||
// Load settings
|
// Load settings
|
||||||
const activityreminder = require("activityreminder");
|
const activityreminder = require("activityreminder");
|
||||||
let settings = activityreminder.loadSettings();
|
let settings = activityreminder.loadSettings();
|
||||||
|
|
||||||
// Show the menu
|
function getMainMenu(){
|
||||||
E.showMenu({
|
var mainMenu = {
|
||||||
"": { "title": "Activity Reminder" },
|
"": { "title": "Activity Reminder" },
|
||||||
"< Back": () => back(),
|
"< Back": () => back(),
|
||||||
'Enable': {
|
'Enable': {
|
||||||
value: settings.enabled,
|
value: settings.enabled,
|
||||||
format: v => v ? "Yes" : "No",
|
onchange: v => {
|
||||||
onchange: v => {
|
settings.enabled = v;
|
||||||
settings.enabled = v;
|
activityreminder.writeSettings(settings);
|
||||||
activityreminder.writeSettings(settings);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Start hour': {
|
|
||||||
value: settings.startHour,
|
|
||||||
min: 0, max: 24,
|
|
||||||
onchange: v => {
|
|
||||||
settings.startHour = v;
|
|
||||||
activityreminder.writeSettings(settings);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'End hour': {
|
|
||||||
value: settings.endHour,
|
|
||||||
min: 0, max: 24,
|
|
||||||
onchange: v => {
|
|
||||||
settings.endHour = v;
|
|
||||||
activityreminder.writeSettings(settings);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Max inactivity': {
|
|
||||||
value: settings.maxInnactivityMin,
|
|
||||||
min: 15, max: 120,
|
|
||||||
onchange: v => {
|
|
||||||
settings.maxInnactivityMin = v;
|
|
||||||
activityreminder.writeSettings(settings);
|
|
||||||
},
|
|
||||||
format: x => {
|
|
||||||
return x + " min";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Dismiss delay': {
|
|
||||||
value: settings.dismissDelayMin,
|
|
||||||
min: 5, max: 60,
|
|
||||||
onchange: v => {
|
|
||||||
settings.dismissDelayMin = v;
|
|
||||||
activityreminder.writeSettings(settings);
|
|
||||||
},
|
|
||||||
format: x => {
|
|
||||||
return x + " min";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Pause delay': {
|
|
||||||
value: settings.pauseDelayMin,
|
|
||||||
min: 30, max: 240, step: 5,
|
|
||||||
onchange: v => {
|
|
||||||
settings.pauseDelayMin = v;
|
|
||||||
activityreminder.writeSettings(settings);
|
|
||||||
},
|
|
||||||
format: x => {
|
|
||||||
return x + " min";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Min steps': {
|
|
||||||
value: settings.minSteps,
|
|
||||||
min: 10, max: 500, step: 10,
|
|
||||||
onchange: v => {
|
|
||||||
settings.minSteps = v;
|
|
||||||
activityreminder.writeSettings(settings);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Temp Threshold': {
|
|
||||||
value: settings.tempThreshold,
|
|
||||||
min: 20, max: 40, step: 0.5,
|
|
||||||
format: v => v + "°C",
|
|
||||||
onchange: v => {
|
|
||||||
settings.tempThreshold = v;
|
|
||||||
activityreminder.writeSettings(settings);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
'Start hour': {
|
||||||
|
value: settings.startHour,
|
||||||
|
min: 0, max: 24,
|
||||||
|
onchange: v => {
|
||||||
|
settings.startHour = v;
|
||||||
|
activityreminder.writeSettings(settings);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'End hour': {
|
||||||
|
value: settings.endHour,
|
||||||
|
min: 0, max: 24,
|
||||||
|
onchange: v => {
|
||||||
|
settings.endHour = v;
|
||||||
|
activityreminder.writeSettings(settings);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Max inactivity': {
|
||||||
|
value: settings.maxInnactivityMin,
|
||||||
|
min: 15, max: 120,
|
||||||
|
onchange: v => {
|
||||||
|
settings.maxInnactivityMin = v;
|
||||||
|
activityreminder.writeSettings(settings);
|
||||||
|
},
|
||||||
|
format: x => x + "m"
|
||||||
|
},
|
||||||
|
'Dismiss delay': {
|
||||||
|
value: settings.dismissDelayMin,
|
||||||
|
min: 5, max: 60,
|
||||||
|
onchange: v => {
|
||||||
|
settings.dismissDelayMin = v;
|
||||||
|
activityreminder.writeSettings(settings);
|
||||||
|
},
|
||||||
|
format: x => x + "m"
|
||||||
|
},
|
||||||
|
'Pause delay': {
|
||||||
|
value: settings.pauseDelayMin,
|
||||||
|
min: 30, max: 240, step: 5,
|
||||||
|
onchange: v => {
|
||||||
|
settings.pauseDelayMin = v;
|
||||||
|
activityreminder.writeSettings(settings);
|
||||||
|
},
|
||||||
|
format: x => {
|
||||||
|
return x + "m";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Min steps': {
|
||||||
|
value: settings.minSteps,
|
||||||
|
min: 10, max: 500, step: 10,
|
||||||
|
onchange: v => {
|
||||||
|
settings.minSteps = v;
|
||||||
|
activityreminder.writeSettings(settings);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Temp Threshold': {
|
||||||
|
value: settings.tempThreshold,
|
||||||
|
min: 20, max: 40, step: 0.5,
|
||||||
|
format: v => v + "°C",
|
||||||
|
onchange: v => {
|
||||||
|
settings.tempThreshold = v;
|
||||||
|
activityreminder.writeSettings(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return mainMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the menu
|
||||||
|
E.showMenu(getMainMenu());
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
0.01: AdvCasio first version
|
||||||
|
0.02: Remove un-needed fonts to improve memory usage
|
||||||
|
0.03: Tell clock widgets to hide.
|
|
@ -0,0 +1,62 @@
|
||||||
|
# Adv Casio Clock
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175355586-1dfc0d66-6555-4385-b124-1605fdb71a11.jpg" width="250" />
|
||||||
|
|
||||||
|
An over-engineered clock inspired by Casio watches.<br/>
|
||||||
|
It has a dedicated timer, a scratchpad and can display the weather condition 4 days ahead.<br/>
|
||||||
|
It uses a <a target="_blank" href="https://dotgreg.github.io/advCasioBangleClock/">custom web app</a> to update its content.<br/>
|
||||||
|
Forked from the awesome Cassio Watch.<br/>
|
||||||
|
|
||||||
|
## Todo
|
||||||
|
|
||||||
|
- Improving quality of the background images, right now it is quite blurry.
|
||||||
|
- Improving screenshots quality.
|
||||||
|
- Improving web app look.
|
||||||
|
- Improving bangle app performances (using functions for images and specialized array).
|
||||||
|
|
||||||
|
## Functionalities
|
||||||
|
|
||||||
|
- Current time
|
||||||
|
- Current day and month
|
||||||
|
- Footsteps
|
||||||
|
- Battery
|
||||||
|
- Simple Timer embedded
|
||||||
|
- Weather forecast (7 days)
|
||||||
|
- Scratchpad
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
Clock:<br/>
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175519126-049caf93-73d0-4472-9650-33b28f80843c.jpg" width="250" />
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175519128-96926e32-2165-4c61-9364-843440304bb9.jpg" width="250" />
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175519130-4921073c-48fc-4c29-932d-d42acc3b395c.jpg" width="250" />
|
||||||
|
|
||||||
|
Web interface to update weather & scratchpad <br/>
|
||||||
|
<a target="_blank" ref="https://dotgreg.github.io/advCasioBangleClock/">https://dotgreg.github.io/advCasioBangleClock</a>
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175519121-851bb209-7192-40db-a014-490c344f7597.jpg" width="250" />
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
### How to update the tasks list / weather
|
||||||
|
- you will need a <a target="_blank" href="https://openweathermap.org/price#weather">free openweathermap.org api key</a>.
|
||||||
|
- go to https://dotgreg.github.io/advCasioBangleClock/
|
||||||
|
- Alternatively you can install it on your own server/heroku/service/github pages, the web-app code is <a target="_blank" href="https://github.com/dotgreg/advCasioBangleClock/tree/master/web-app">here</a>
|
||||||
|
- fill the location and the api key (it will be saved on your browser, no need to do it each time)
|
||||||
|
- edit the scratchpad with what you want
|
||||||
|
- click on sync
|
||||||
|
- reload your clock!
|
||||||
|
|
||||||
|
### How to start/stop the timer
|
||||||
|
- swipe up : add time (+5min)
|
||||||
|
- swipe down : remove time (-5min)
|
||||||
|
- swipe right : start timer
|
||||||
|
- swipe left : stop timer
|
||||||
|
|
||||||
|
## Links
|
||||||
|
### Issues, suggestions and bugtracker
|
||||||
|
<a target="_blank" href="https://github.com/dotgreg/advCasioBangleClock/issues">https://github.com/dotgreg/advCasioBangleClock/issues</a>
|
||||||
|
|
||||||
|
### Code repository (bangle app and web app)
|
||||||
|
<a target="_blank" href="https://github.com/dotgreg/advCasioBangleClock">https://github.com/dotgreg/advCasioBangleClock</a>
|
||||||
|
|
||||||
|
### Creator
|
||||||
|
<a target="_blank" href="https://github.com/dotgreg">https://github.com/dotgreg</a>
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwghC/AH4A/AGsCmUQC6kf/8wC6k///wgEv//zD4PxAQIJBABP//4QBC4IcBh/yEQIXKgP/l4rBl/yGAMP/4iBKJUC/5gBIAQVBBAMR/8gC5IQBAAMQC4IVBFoMjAYIXNmAXBgYXCPgQAJl/xHwPwj/yn5kC/55BUxSlC+JiBVgQ5BUxiDBUIIXBIQQXBcCoA/AH4ADXAUgUAUQBAkPeoTDFgIHBAALQEA4XwC4IOEAAQRBbAQBBCAIgBEYMQC4TnEC4XyeQgBDAAMwC4pIDC4kDAgJLD//xC5QIBNQISCFYIZCC4aEBAQRCDAAPyl4hBOIh3Cn53GNgMRiKxGBAR5BAoYA/AH4A/AH4A5A"))
|
|
@ -0,0 +1,304 @@
|
||||||
|
const storage = require('Storage');
|
||||||
|
|
||||||
|
require("Font8x12").add(Graphics);
|
||||||
|
require("Font7x11Numeric7Seg").add(Graphics);
|
||||||
|
|
||||||
|
function bigThenSmall(big, small, x, y) {
|
||||||
|
g.setFont("7x11Numeric7Seg", 2);
|
||||||
|
g.drawString(big, x, y);
|
||||||
|
x += g.stringWidth(big);
|
||||||
|
g.setFont("8x12");
|
||||||
|
g.drawString(small, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClockBg() {
|
||||||
|
return require("heatshrink").decompress(atob("icVgf/ABv8v4DBx4CB+PH8F+nAGB48fwEHBwXjxwqBuPH//+nAGBBwIjCAwI2D/wGBgIyDI4QGDwAGBHYX/4AGBn4UFEYQpCEYYpCAAMfMhP4FIgABwJ8OEBIA=="));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// sun, cloud, rain, thunder
|
||||||
|
var iconsWeather = [
|
||||||
|
require("heatshrink").decompress(atob("i8Ugf/ACcfA434BA/AAwsAv0/8F/BAcDwEHHIpECFI3wn4GC/gOC+PAGoXggEH/+ODQgXBGQv/wAbBBAnguEACIn4gfxI4JXFwJmG/kPBA3jSynw")), require("heatshrink").decompress(atob("i0Ugf/AEXggIGE/0A/kPBAmBCIN/A4Y8CgAICwEHBYoUE/ACCj4sDn4CBC4YyDwBrDCgYA3A")), require("heatshrink").decompress(atob("h8Rgf/AAuBAgf8h4FDCwM/AgPA/gFC/0HgEBBQPwnEfDoWAg4jC/gOCAoQmBAQXjFIV//8f//4IQP4j/+gAIB4EcHII4CAoI+DLQJXF/AA==")), require("heatshrink").decompress(atob("h0Pgf/AA8fAYX+g4EC8EBAgXADAeAgAECgAOC/wrCDQIOBBYfwgAaC/kAn4EB/EAv4aDHAeBIg38"))
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
function getBackgroundImage() {
|
||||||
|
return require("heatshrink").decompress(atob("2GwghC/AH4A/AH4AMl////wAwURiQECgUzmcxBQQCBiYUBBARW+LAcCAgcPBYgFBkAIFG7kQiAKIiIKBgISOAAJBD//zKQfxK4vyAoMQCgn/ERBhBBYR5BAwR1DB4Y2DgYPCGIQRCCQcP+EfGJI0FEgRSCGAQCCX4JXCkAhDn4lI+HyK4YWBFIPzJYJXHAIMSK4cwJ4I3CAYMzA4cfcRMBdwytBK4i6FK4IUCMgYAEGIITBK4cCaAPwgJXB+fzK4sAgYtCK5EfA4pXR+AmBaIZYCK6KcCAwSjDEYXx/8vK5QRCK4kPK6cDkJREBIMBfgIrDK5svUAIQBAwIaCK4w+DK4YGBK7IaBboIuCK4gFCJwYBBiBCCCgQhHHYgGDgArBK5IGDAYMgJ4Xwn53BGgLVDmBXKAAinDLpJXCAAYhHR4YODn/wJIPyTYZXDE4RXD+ECNILIDAIPwj4xIAAYNCR4fyVIYLFA4KEBBAglKAGUCmcykEAiMQBIURBYM/BgIUEgcz+bTKAH4A/AH4A/AHP/AGY1d+BWCh5X/LCpW1K74fgG/5X/AH5X/K9Bg/K63wK/5XWgBX/K6pWBK/5XU+BWBh5J/K6auCK/5XTVwRfFAH5XOKwRX/K6auDh5I/K6SuDWP5XSVwYADWX6vXK/5XQWQpW/K6auDJP5XWV35XT+Cu/K7Ku/K65H/K6hW/K7EPI35XWIv5XWAH5X/K/4A/K/5X/K/4A/K9cAAH4A/AFzz/AHRX/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/40VAH4A/AFzLb+EPDm4AdK/5X/K+PwgEAHy5X9HgMAK/5XXH6xX/H65X/K/5X/K98AK7sAgBX3DjBWFO644DSTHwGzJXED4RXaDoLqcK7weWDIQcXK8I6YK77KXK4o8DPbY6ZK7qvDDy6vdR7JXDh60EDyw5BAIRXYSwjMbAgIhUDwJZCHwJX0GwjRWNwIAEHSwBCDSpXFH4pXzDS5XIEARXVSYbQEDaYzCK+6vcKaxXNDypX9HwQkbHS40COSpXKK2A6CHgRXcPIhX0SwpXYVuQ6EgBX/K644YODBXkSDJX/K/5X/DtRX6gA3YOkRWbLDZX4KwYA/AG8F5vdABncKH4AGhpRJAYXNAgPAKP4AF5vMJwoDBAQIKE6BR/AAvc5vO9wAB7oCB9veAoPcAoPcK+kwh8AgcA98An//gH/+sD//wCISgBJ4IABAYpaC9vdK4UP/9AAQNQr/zgHwEYNQFYQAh+EP+FegH+A4QBCMQIKBAAPNK4yxBA4RXCV4YZBE4IjChwCDmApCK8VdmHggHgFYf0SQJXE5nMK4anCAoYHC5pXCaQJXBop+BqAGEK7f/AAQeEKwQrBqCtDAILjBCQfNK4JTCAYZXF7qvD//gV4S2DgEFFIYAECgIACMC8PKoIBB8n1K4ivF5vc5xOCWYZbBAYavHU4RXCr4pEAEMDfoNQGoMEgEwYQPwAoIBBAAPM5ipC7oDCVIIAE7hXCD4SdBiEP+gGBgihCFYIAz5pXBAAnN7oIB7nc5gOBK4QA/K4pNCWgSpCBInNK/4AGhncKIStC7gCBA4QAC4BR/AAysCABZW/AHwA="));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// schedule a draw for the next minute
|
||||||
|
let rocketInterval;
|
||||||
|
var drawTimeout;
|
||||||
|
function queueDraw() {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function() {
|
||||||
|
drawTimeout = undefined;
|
||||||
|
draw();
|
||||||
|
}, 60000 - (Date.now() % 60000));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clearIntervals() {
|
||||||
|
if (rocketInterval) clearInterval(rocketInterval);
|
||||||
|
rocketInterval = undefined;
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// TIMER FUNC
|
||||||
|
//
|
||||||
|
var timer_time = 0;
|
||||||
|
var alreadyListenTouch = false;
|
||||||
|
function initTouchTimer () {
|
||||||
|
if (alreadyListenTouch) return;
|
||||||
|
alreadyListenTouch = true;
|
||||||
|
|
||||||
|
Bangle.on('swipe', function(dirX,dirY) {
|
||||||
|
if (canTouch === false) return;
|
||||||
|
var njson = getDataJson();
|
||||||
|
if (!njson) return;
|
||||||
|
|
||||||
|
if (dirX === -1) {
|
||||||
|
timer_time = 0;
|
||||||
|
delete njson.timer;
|
||||||
|
setDataJson(njson);
|
||||||
|
}
|
||||||
|
else if (dirX === 1) {
|
||||||
|
var now = new Date().getTime();
|
||||||
|
njson.timer = now + (timer_time * 1000 * 60);
|
||||||
|
Bangle.setLocked(true);
|
||||||
|
setDataJson(njson);
|
||||||
|
Bangle.buzz(200, 0);
|
||||||
|
timer_time = 0;
|
||||||
|
}
|
||||||
|
else if (dirY === -1) {
|
||||||
|
if (canTouch === false || njson.timer) return;
|
||||||
|
timer_time = timer_time + 5;
|
||||||
|
}
|
||||||
|
else if (dirY === 1) {
|
||||||
|
if (canTouch === false || njson.timer) return;
|
||||||
|
timer_time = timer_time - 5;
|
||||||
|
}
|
||||||
|
draw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
initTouchTimer ();
|
||||||
|
});
|
||||||
|
|
||||||
|
function getTimerTime() {
|
||||||
|
// if timer_time !== -1, take it
|
||||||
|
if (timer_time !== 0) {
|
||||||
|
return timer_time + "m";
|
||||||
|
} else {
|
||||||
|
// else, show diff between njsontime and now
|
||||||
|
var njson = getDataJson();
|
||||||
|
if (!njson) return false;
|
||||||
|
var now = new Date().getTime();
|
||||||
|
var diff = Math.round((njson.timer - now) / (1000 * 60));
|
||||||
|
//console.log(123, njson, diff, now, njson.timer - now);
|
||||||
|
if (diff > 0) return diff + "m";
|
||||||
|
else if (njson.timer) {
|
||||||
|
Bangle.buzz(1000, 1);
|
||||||
|
console.log("END OF TIMER");
|
||||||
|
delete njson.timer;
|
||||||
|
setDataJson(njson);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// if diff is <0, delete timer from json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function drawTimer() {
|
||||||
|
//g.drawString(getTimerTime(), 100, 100);
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
var t = 97;
|
||||||
|
var l = 105;
|
||||||
|
var time = getTimerTime();
|
||||||
|
if (time || timer_time !== 0) g.drawString(time, l+5, t+0);
|
||||||
|
if (time && timer_time === 0) g.drawImage(getClockBg(), l-20, t+2, { scale: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// DATA READING
|
||||||
|
//
|
||||||
|
function getDataJson(){
|
||||||
|
var res = {"tasks":"", "weather":[]};
|
||||||
|
try {
|
||||||
|
res = storage.readJSON('advcasio.data.json');
|
||||||
|
} catch(ex) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
function setDataJson(resJson){
|
||||||
|
try {
|
||||||
|
res = storage.writeJSON('advcasio.data.json', resJson);
|
||||||
|
} catch(ex) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
var dataJson = getDataJson();
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// WEATHER!
|
||||||
|
//
|
||||||
|
function drawWeather(arr) {
|
||||||
|
g.setFont("6x8", 1);
|
||||||
|
var p = {l: 8, tText: 40, tIcon:20, decal:25};
|
||||||
|
var today = new Date().getTime();
|
||||||
|
var yesterday = today - (1000 * 60 * 60 * 24);
|
||||||
|
var testday = today + (1000 * 60 * 60 * 24 * 2);
|
||||||
|
//12h auj > 12h hier qui est sup a 0h auj
|
||||||
|
//23h59 hier est sup a 0h auj
|
||||||
|
var j = 0;
|
||||||
|
for(var i = 0; i<arr.length;i++) {
|
||||||
|
if (arr[i][2] > yesterday && j < 4) {
|
||||||
|
g.drawString(arr[i][0], p.l + p.decal*j + 4, p.tText);
|
||||||
|
g.drawImage(iconsWeather[arr[i][1]], p.l + p.decal*j, p.tIcon, { scale: 1 });
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// DRAWING FUNCS
|
||||||
|
//
|
||||||
|
function drawTasks(str) {
|
||||||
|
g.setFont("6x8", 1);
|
||||||
|
var t = 57;
|
||||||
|
var l = 0;
|
||||||
|
g.drawString(str, l+5, t+0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSteps() {
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
var t = 132;
|
||||||
|
var l = 150;
|
||||||
|
g.drawString(getSteps(), l+5, t+0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawClock() {
|
||||||
|
g.setFont("7x11Numeric7Seg", 3);
|
||||||
|
g.clearRect(80, 57, 170, 96);
|
||||||
|
g.setColor(255, 255, 255);
|
||||||
|
var l = 77;
|
||||||
|
var t = 57;
|
||||||
|
var w = 170;
|
||||||
|
var h = 116;
|
||||||
|
g.drawRect(l, t, w, h);
|
||||||
|
g.fillRect(l, t, w, h);
|
||||||
|
g.setColor(0, 0, 0);
|
||||||
|
g.drawString(require("locale").time(new Date(), 1), 76, 60);
|
||||||
|
|
||||||
|
// day
|
||||||
|
//g.setFont("8x12", 1);
|
||||||
|
//g.setFont("9x18", 1);
|
||||||
|
//g.drawString(require("locale").dow(new Date(), 2).toUpperCase(), 25, 136);
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
g.drawString(require("locale").dow(new Date(), 2), 18, 130);
|
||||||
|
|
||||||
|
// month
|
||||||
|
g.setFont("8x12");
|
||||||
|
g.drawString(require("locale").month(new Date(), 2).toUpperCase(), 80, 127);
|
||||||
|
|
||||||
|
// day nb
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
const time = new Date().getDate();
|
||||||
|
g.drawString(time < 10 ? "0" + time : time, 78, 137);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBattery() {
|
||||||
|
bigThenSmall(E.getBattery(), "%", 140, 23);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getSteps() {
|
||||||
|
var steps = 0;
|
||||||
|
try{
|
||||||
|
if (WIDGETS.wpedom !== undefined) {
|
||||||
|
steps = WIDGETS.wpedom.getSteps();
|
||||||
|
} else if (WIDGETS.activepedom !== undefined) {
|
||||||
|
steps = WIDGETS.activepedom.getSteps();
|
||||||
|
} else {
|
||||||
|
steps = Bangle.getHealthStatus("day").steps;
|
||||||
|
}
|
||||||
|
} catch(ex) {
|
||||||
|
// In case we failed, we can only show 0 steps.
|
||||||
|
return "? k";
|
||||||
|
}
|
||||||
|
|
||||||
|
steps = Math.round(steps/1000);
|
||||||
|
return steps + "k";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
|
||||||
|
queueDraw();
|
||||||
|
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
g.setColor(255, 255, 255);
|
||||||
|
g.fillRect(0, 0, g.getWidth(), g.getHeight());
|
||||||
|
let background = getBackgroundImage();
|
||||||
|
g.drawImage(background, 0, 0, { scale: 1 });
|
||||||
|
|
||||||
|
|
||||||
|
g.setColor(0, 0, 0);
|
||||||
|
if(dataJson && dataJson.weather) drawWeather(dataJson.weather);
|
||||||
|
if(dataJson && dataJson.tasks) drawTasks(dataJson.tasks);
|
||||||
|
|
||||||
|
|
||||||
|
g.setFontAlign(0,-1);
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
|
||||||
|
drawSteps();
|
||||||
|
g.setFontAlign(-1,-1);
|
||||||
|
drawClock();
|
||||||
|
drawBattery();
|
||||||
|
drawTimer();
|
||||||
|
// Hide widgets
|
||||||
|
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save batt power, does not seem to work although...
|
||||||
|
var canTouch = true;
|
||||||
|
Bangle.on("lcdPower", (on) => {
|
||||||
|
if (on) {
|
||||||
|
draw();
|
||||||
|
} else {
|
||||||
|
canTouch = false;
|
||||||
|
clearIntervals();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Bangle.on("lock", (locked) => {
|
||||||
|
clearIntervals();
|
||||||
|
draw();
|
||||||
|
if (!locked) {
|
||||||
|
canTouch = true;
|
||||||
|
} else {
|
||||||
|
canTouch = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
|
// Load widgets, but don't show them
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
draw();
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1 @@
|
||||||
|
{"tasks":"", "weather":[]};
|
|
@ -0,0 +1,25 @@
|
||||||
|
{ "id": "advcasio",
|
||||||
|
"name": "Advanced Casio Clock",
|
||||||
|
"shortName":"advcasio",
|
||||||
|
"version":"0.03",
|
||||||
|
"description": "An over-engineered clock inspired by Casio watches. It has a 4 days weather, a timer using swipe and a scratchpad. Can be updated using a dedicated webapp.",
|
||||||
|
"icon": "app.png",
|
||||||
|
"tags": "clock",
|
||||||
|
"type": "clock",
|
||||||
|
"screenshots": [
|
||||||
|
{ "url": "screenshot-clock-1.jpg" },
|
||||||
|
{ "url": "screenshot-clock-2.jpg" },
|
||||||
|
{ "url": "screenshot-clock-3.jpg" },
|
||||||
|
{ "url": "screenshot-webapp.jpg" }
|
||||||
|
],
|
||||||
|
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"allow_emulator":true,
|
||||||
|
"storage": [
|
||||||
|
{"name":"advcasio.app.js","url":"app.js"},
|
||||||
|
{"name":"advcasio.img","url":"app-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{ "name": "advcasio.data.json", "url": "data.json", "storageFile": true }
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 230 KiB |
|
@ -1 +1,4 @@
|
||||||
0.01: Basic agenda with events from GB
|
0.01: Basic agenda with events from GB
|
||||||
|
0.02: Added settings page to force calendar sync
|
||||||
|
0.03: Disable past events display from settings
|
||||||
|
0.04: Added awareness of allDay field
|
||||||
|
|
|
@ -24,19 +24,23 @@ var fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4";
|
||||||
|
|
||||||
//FIXME maybe write the end from GB already? Not durationInSeconds here (or do while receiving?)
|
//FIXME maybe write the end from GB already? Not durationInSeconds here (or do while receiving?)
|
||||||
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||||
|
var settings = require("Storage").readJSON("agenda.settings.json",true)||{};
|
||||||
|
|
||||||
CALENDAR=CALENDAR.sort((a,b)=>a.timestamp - b.timestamp)
|
CALENDAR=CALENDAR.sort((a,b)=>a.timestamp - b.timestamp);
|
||||||
|
|
||||||
function getDate(timestamp) {
|
function getDate(timestamp) {
|
||||||
return new Date(timestamp*1000);
|
return new Date(timestamp*1000);
|
||||||
}
|
}
|
||||||
function formatDateLong(date, includeDay) {
|
function formatDateLong(date, includeDay, allDay) {
|
||||||
if(includeDay)
|
let shortTime = Locale.time(date,1)+Locale.meridian(date);
|
||||||
return Locale.date(date)+" "+Locale.time(date,1);
|
if(allDay) shortTime = "";
|
||||||
return Locale.time(date,1);
|
if(includeDay || allDay)
|
||||||
|
return Locale.date(date)+" "+shortTime;
|
||||||
|
return shortTime;
|
||||||
}
|
}
|
||||||
function formatDateShort(date) {
|
function formatDateShort(date, allDay) {
|
||||||
return Locale.date(date).replace(/\d\d\d\d/,"")+Locale.time(date,1);
|
return Locale.date(date).replace(/\d\d\d\d/,"")+(allDay?
|
||||||
|
"" : Locale.time(date,1)+Locale.meridian(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines = [];
|
var lines = [];
|
||||||
|
@ -45,7 +49,7 @@ function showEvent(ev) {
|
||||||
if(!ev) return;
|
if(!ev) return;
|
||||||
g.setFont(bodyFont);
|
g.setFont(bodyFont);
|
||||||
//var lines = [];
|
//var lines = [];
|
||||||
if (ev.title) lines = g.wrapString(ev.title, g.getWidth()-10)
|
if (ev.title) lines = g.wrapString(ev.title, g.getWidth()-10);
|
||||||
var titleCnt = lines.length;
|
var titleCnt = lines.length;
|
||||||
var start = getDate(ev.timestamp);
|
var start = getDate(ev.timestamp);
|
||||||
var end = getDate((+ev.timestamp) + (+ev.durationInSeconds));
|
var end = getDate((+ev.timestamp) + (+ev.durationInSeconds));
|
||||||
|
@ -53,17 +57,17 @@ function showEvent(ev) {
|
||||||
if (titleCnt) lines.push(""); // add blank line after title
|
if (titleCnt) lines.push(""); // add blank line after title
|
||||||
if(start.getDay() == end.getDay() && start.getMonth() == end.getMonth())
|
if(start.getDay() == end.getDay() && start.getMonth() == end.getMonth())
|
||||||
includeDay = false;
|
includeDay = false;
|
||||||
if(includeDay) {
|
if(includeDay || ev.allDay) {
|
||||||
lines = lines.concat(
|
lines = lines.concat(
|
||||||
/*LANG*/"Start:",
|
/*LANG*/"Start:",
|
||||||
g.wrapString(formatDateLong(start, includeDay), g.getWidth()-10),
|
g.wrapString(formatDateLong(start, includeDay, ev.allDay), g.getWidth()-10),
|
||||||
/*LANG*/"End:",
|
/*LANG*/"End:",
|
||||||
g.wrapString(formatDateLong(end, includeDay), g.getWidth()-10));
|
g.wrapString(formatDateLong(end, includeDay, ev.allDay), g.getWidth()-10));
|
||||||
} else {
|
} else {
|
||||||
lines = lines.concat(
|
lines = lines.concat(
|
||||||
g.wrapString(Locale.date(start), g.getWidth()-10),
|
g.wrapString(Locale.date(start), g.getWidth()-10),
|
||||||
g.wrapString(/*LANG*/"Start"+": "+formatDateLong(start, includeDay), g.getWidth()-10),
|
g.wrapString(/*LANG*/"Start"+": "+formatDateLong(start, includeDay, ev.allDay), g.getWidth()-10),
|
||||||
g.wrapString(/*LANG*/"End"+": "+formatDateLong(end, includeDay), g.getWidth()-10));
|
g.wrapString(/*LANG*/"End"+": "+formatDateLong(end, includeDay, ev.allDay), g.getWidth()-10));
|
||||||
}
|
}
|
||||||
if(ev.location)
|
if(ev.location)
|
||||||
lines = lines.concat(/*LANG*/"Location"+": ", g.wrapString(ev.location, g.getWidth()-10));
|
lines = lines.concat(/*LANG*/"Location"+": ", g.wrapString(ev.location, g.getWidth()-10));
|
||||||
|
@ -89,6 +93,12 @@ function showEvent(ev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showList() {
|
function showList() {
|
||||||
|
//it might take time for GB to delete old events, decide whether to show them grayed out or hide entirely
|
||||||
|
if(!settings.pastEvents) {
|
||||||
|
let now = new Date();
|
||||||
|
//TODO add threshold here?
|
||||||
|
CALENDAR = CALENDAR.filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000);
|
||||||
|
}
|
||||||
if(CALENDAR.length == 0) {
|
if(CALENDAR.length == 0) {
|
||||||
E.showMessage("No events");
|
E.showMessage("No events");
|
||||||
return;
|
return;
|
||||||
|
@ -101,10 +111,10 @@ function showList() {
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
|
g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
|
||||||
if (!ev) return;
|
if (!ev) return;
|
||||||
var isPast = ev.timestamp + ev.durationInSeconds < (new Date())/1000;
|
var isPast = false;
|
||||||
var x = r.x+2, title = ev.title;
|
var x = r.x+2, title = ev.title;
|
||||||
var body = formatDateShort(getDate(ev.timestamp))+"\n"+ev.location;
|
var body = formatDateShort(getDate(ev.timestamp),ev.allDay)+"\n"+(ev.location?ev.location:/*LANG*/"No location");
|
||||||
var m = ev.title+"\n"+ev.location, longBody=false;
|
if(settings.pastEvents) isPast = ev.timestamp + ev.durationInSeconds < (new Date())/1000;
|
||||||
if (title) g.setFontAlign(-1,-1).setFont(fontBig)
|
if (title) g.setFontAlign(-1,-1).setFont(fontBig)
|
||||||
.setColor(isPast ? "#888" : g.theme.fg).drawString(title, x,r.y+2);
|
.setColor(isPast ? "#888" : g.theme.fg).drawString(title, x,r.y+2);
|
||||||
if (body) {
|
if (body) {
|
||||||
|
@ -114,10 +124,8 @@ function showList() {
|
||||||
l = l.slice(0,3);
|
l = l.slice(0,3);
|
||||||
l[l.length-1]+="...";
|
l[l.length-1]+="...";
|
||||||
}
|
}
|
||||||
longBody = l.length>2;
|
|
||||||
g.drawString(l.join("\n"), x+10,r.y+20);
|
g.drawString(l.join("\n"), x+10,r.y+20);
|
||||||
}
|
}
|
||||||
//if (!longBody && msg.src) g.setFontAlign(1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2);
|
|
||||||
g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items
|
g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items
|
||||||
},
|
},
|
||||||
select : idx => showEvent(CALENDAR[idx]),
|
select : idx => showEvent(CALENDAR[idx]),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "agenda",
|
"id": "agenda",
|
||||||
"name": "Agenda",
|
"name": "Agenda",
|
||||||
"version": "0.02",
|
"version": "0.04",
|
||||||
"description": "Simple agenda",
|
"description": "Simple agenda",
|
||||||
"icon": "agenda.png",
|
"icon": "agenda.png",
|
||||||
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],
|
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],
|
||||||
|
@ -13,5 +13,6 @@
|
||||||
{"name":"agenda.app.js","url":"agenda.js"},
|
{"name":"agenda.app.js","url":"agenda.js"},
|
||||||
{"name":"agenda.settings.js","url":"settings.js"},
|
{"name":"agenda.settings.js","url":"settings.js"},
|
||||||
{"name":"agenda.img","url":"agenda-icon.js","evaluate":true}
|
{"name":"agenda.img","url":"agenda-icon.js","evaluate":true}
|
||||||
]
|
],
|
||||||
|
"data": [{"name":"agenda.settings.json"}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
Bluetooth.println("");
|
Bluetooth.println("");
|
||||||
Bluetooth.println(JSON.stringify(message));
|
Bluetooth.println(JSON.stringify(message));
|
||||||
}
|
}
|
||||||
|
var settings = require("Storage").readJSON("agenda.settings.json",1)||{};
|
||||||
|
function updateSettings() {
|
||||||
|
require("Storage").writeJSON("agenda.settings.json", settings);
|
||||||
|
}
|
||||||
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||||
var mainmenu = {
|
var mainmenu = {
|
||||||
"" : { "title" : "Agenda" },
|
"" : { "title" : "Agenda" },
|
||||||
|
@ -32,6 +36,13 @@
|
||||||
E.showAlert(/*LANG*/"You are not connected").then(()=>E.showMenu(mainmenu));
|
E.showAlert(/*LANG*/"You are not connected").then(()=>E.showMenu(mainmenu));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/*LANG*/"Show past events" : {
|
||||||
|
value : !!settings.pastEvents,
|
||||||
|
onchange: v => {
|
||||||
|
settings.pastEvents = v;
|
||||||
|
updateSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
E.showMenu(mainmenu);
|
E.showMenu(mainmenu);
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: First, proof of concept
|
||||||
|
0.02: Load AGPS data on app start and automatically in background
|
|
@ -0,0 +1,19 @@
|
||||||
|
# A-GPS Data
|
||||||
|
|
||||||
|
Load assisted GPS (A-GPS) data directly to your Bangle.js using the new http requests on Android GadgetBridge.
|
||||||
|
|
||||||
|
Will download A-GPS data in background (if enabled in settings).
|
||||||
|
|
||||||
|
The GNSS type can be configured in the settings.
|
||||||
|
|
||||||
|
Make sure:
|
||||||
|
* your GadgetBridge version supports http requests
|
||||||
|
* turn on internet access in GadgetBridge settings
|
||||||
|
|
||||||
|
Currently proof of concept on Bangle.js 2 only.
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
[@pidajo](https://github.com/pidajo)
|
||||||
|
|
||||||
|
## Contributor
|
||||||
|
[@myxor](https://github.com/myxor)
|
|
@ -0,0 +1 @@
|
||||||
|
atob("MDCEAAAAAAAAAAAAAAAAiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAIiIOIiIiAAAAAAAAAAAAAAAAAAAAAAAAIiDOIiIiAAAAAAAAAAAAAAAAAAAAAAAAIiDOIiIiIAAAAAAAAAAAAAAAAAAAAAACIiPOIiIiIAAAAAAAAAAAAAAAAAAAAAAiIj/OIiIiIgAAAAAAAAAAAAAAAAAAAAAiI//OIiIiIgAAAAAAAAAAAAAAAAAAAAAiI//OIiIiIiAAAAAAAAAAAAAAAAAAAAAiD//OIiIiIiAAAAAAAAAAAAAAAAAAAAIiP//OIiIiIiAAAAAAAAAAAAAAAAAAAAIg///OIiIiIiIAAAAAAAAAAAAAAAAAACIj///OIiIiIiIAAAAAAAAAAAAAAAAAACIP///OIiIiIiIgAAAAAAAAAAAAAAAAACI////OIiIiIiIgAAAAAAAAAAAAAAAAAiD////OIiIiIiIiAAAAAAAAAAAAAAAAAiP////OIiIiIiIiAAAAAAAAAAAAAAAAIiP////OIiIiIiIiIAAAAAAAAAAAAAAAIj/////OIiIiIiIiIAAAAAAAAAAAAAACIj/////OIiIiIiIiIgAAAAAAAAAAAAACI//////OIiIiIiIiIgAAAAAAAAAAAAAiI//////OIiIiIiIiIgAAAAAAAAAAAAAiIiIiIiIgzMzMzMziIiAAAAAAAAAAAAAiIiIiIiIj///////+IiAAAAAAAAAAAAIiIiIiIiIj////////4iIAAAAAAAAAAAIiIiIiIiIgzMzMzMzM4iIAAAAAAAAAACIP///////OIiIiIiIiIiIgAAAAAAAAACI////////OIiIiIiIiIiIgAAAAAAAAAiI////////OIiIiIiIiIiIiAAAAAAAAAiP////////OIiIiIiIiIiIiAAAAAAAAIiP////////OIiIiIiIiIiIiIAAAAAAAIj/////////OIiIiIiIiIiIiIAAAAAACIj////////ziIiIiIiIiIiIiIgAAAAACIP///////+IiIiIiIiIiIiIiIgAAAAACI//////84gYiIiIiIiIiIiIiIgAAAAAiD////84iIiAAAiIiIiIiIiIiIiAAAAAiP///ziIiIAAAAAIiIiIiIiIiIiAAAAIg/8ziIiIAAAAAAAAAIiIiIiIiIiIAAAIj/iIiIAAAAAAAAAAAAAIiIiIiIiIAACIiIiBgAAAAAAAAAAAAAAACIiIiIiIgACIiIgAAAAAAAAAAAAAAAAAAACIiIiIgACIgAAAAAAAAAAAAAAAAAAAAAAAiIiIgA==")
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,54 @@
|
||||||
|
function display(text1, text2) {
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
var img = require("Storage").read("agpsdata.img");
|
||||||
|
if (img) {
|
||||||
|
g.drawImage(img, g.getWidth() - 48, g.getHeight() - 48 - 24);
|
||||||
|
}
|
||||||
|
g.setFont("Vector", 18);
|
||||||
|
g.setFontAlign(0, 1);
|
||||||
|
g.drawString(text1, g.getWidth() / 2, g.getHeight() / 3 + 24);
|
||||||
|
if (text2 != undefined) {
|
||||||
|
g.setFont("Vector", 12);
|
||||||
|
g.setFontAlign(-1, -1);
|
||||||
|
g.drawString(text2, 5, g.getHeight() / 3 + 29);
|
||||||
|
}
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show launcher when middle button pressed
|
||||||
|
// Load widgets
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
let waiting = false;
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
waiting = false;
|
||||||
|
display("Retry?", "touch to retry");
|
||||||
|
Bangle.on("touch", () => { updateAgps(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAgps() {
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
if (!waiting) {
|
||||||
|
waiting = true;
|
||||||
|
display("Updating A-GPS...");
|
||||||
|
require("agpsdata").pull(function() {
|
||||||
|
waiting = false;
|
||||||
|
display("A-GPS updated.", "touch to close");
|
||||||
|
Bangle.on("touch", () => { load(); });
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
waiting = false;
|
||||||
|
E.showAlert(error, "Error")
|
||||||
|
.then(() => { start(); });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
display("Waiting...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateAgps();
|
|
@ -0,0 +1,33 @@
|
||||||
|
(function() {
|
||||||
|
let waiting = false;
|
||||||
|
let settings = require("Storage").readJSON("agpsdata.settings.json", 1) || {
|
||||||
|
enabled: true,
|
||||||
|
refresh: 1440
|
||||||
|
};
|
||||||
|
|
||||||
|
if (settings.refresh == undefined) settings.refresh = 1440;
|
||||||
|
|
||||||
|
function successCallback(){
|
||||||
|
waiting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorCallback(){
|
||||||
|
waiting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.enabled) {
|
||||||
|
let lastUpdate = settings.lastUpdate;
|
||||||
|
if (!lastUpdate || lastUpdate + settings.refresh * 1000 * 60 < Date.now()){
|
||||||
|
if (!waiting){
|
||||||
|
waiting = true;
|
||||||
|
require("agpsdata").pull(successCallback, errorCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInterval(() => {
|
||||||
|
if (!waiting && NRF.getSecurityStatus().connected){
|
||||||
|
waiting = true;
|
||||||
|
require("agpsdata").pull(successCallback, errorCallback);
|
||||||
|
}
|
||||||
|
}, settings.refresh * 1000 * 60);
|
||||||
|
}
|
||||||
|
})();
|
|
@ -0,0 +1 @@
|
||||||
|
{"enabled":true,"refresh":1440,"gnsstype":1}
|
|
@ -0,0 +1,75 @@
|
||||||
|
function readSettings() {
|
||||||
|
settings = Object.assign(
|
||||||
|
require('Storage').readJSON("agpsdata.default.json", true) || {},
|
||||||
|
require('Storage').readJSON(FILE, true) || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
var FILE = "agpsdata.settings.json";
|
||||||
|
var settings;
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
function setAGPS(data) {
|
||||||
|
var js = jsFromBase64(data);
|
||||||
|
try {
|
||||||
|
eval(js);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
console.log("error:", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function jsFromBase64(b64) {
|
||||||
|
var bin = atob(b64);
|
||||||
|
var chunkSize = 128;
|
||||||
|
var js = "Bangle.setGPSPower(1);\n"; // turn GPS on
|
||||||
|
var gnsstype = settings.gnsstype || 1; // default GPS
|
||||||
|
js += `Serial1.println("${CASIC_CHECKSUM("$PCAS04,"+gnsstype)}")\n`; // set GNSS mode
|
||||||
|
// What about:
|
||||||
|
// NAV-TIMEUTC (0x01 0x10)
|
||||||
|
// NAV-PV (0x01 0x03)
|
||||||
|
// or AGPS.zip uses AID-INI (0x0B 0x01)
|
||||||
|
|
||||||
|
for (var i=0;i<bin.length;i+=chunkSize) {
|
||||||
|
var chunk = bin.substr(i,chunkSize);
|
||||||
|
js += `Serial1.write(atob("${btoa(chunk)}"))\n`;
|
||||||
|
}
|
||||||
|
return js;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CASIC_CHECKSUM(cmd) {
|
||||||
|
var cs = 0;
|
||||||
|
for (var i=1;i<cmd.length;i++)
|
||||||
|
cs = cs ^ cmd.charCodeAt(i);
|
||||||
|
return cmd+"*"+cs.toString(16).toUpperCase().padStart(2, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLastUpdate() {
|
||||||
|
const file = "agpsdata.json";
|
||||||
|
let data = require("Storage").readJSON(file, 1) || {};
|
||||||
|
data.lastUpdate = Math.round(Date.now());
|
||||||
|
require("Storage").writeJSON(file, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.pull = function(successCallback, failureCallback) {
|
||||||
|
let uri = "https://www.espruino.com/agps/casic.base64";
|
||||||
|
if (Bangle.http){
|
||||||
|
Bangle.http(uri, {timeout:10000}).then(event => {
|
||||||
|
let result = setAGPS(event.resp);
|
||||||
|
if (result) {
|
||||||
|
updateLastUpdate();
|
||||||
|
if (successCallback) successCallback();
|
||||||
|
} else {
|
||||||
|
console.log("error applying AGPS data");
|
||||||
|
if (failureCallback) failureCallback("Error applying AGPS data");
|
||||||
|
}
|
||||||
|
}).catch((e)=>{
|
||||||
|
console.log("error", e);
|
||||||
|
if (failureCallback) failureCallback(e);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("error: No http method found");
|
||||||
|
if (failureCallback) failureCallback(/*LANG*/"No http method");
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
{ "id": "agpsdata",
|
||||||
|
"name": "A-GPS Data",
|
||||||
|
"shortName":"A-GPS Data",
|
||||||
|
"icon": "agpsdata.png",
|
||||||
|
"version":"0.02",
|
||||||
|
"description": "Download assisted GPS (A-GPS) data directly to your Bangle.js **using Gadgetbridge**",
|
||||||
|
"tags": "boot,tool,assisted,gps,agps,http",
|
||||||
|
"allow_emulator":true,
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"readme":"README.md",
|
||||||
|
"screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot2.png" } ],
|
||||||
|
"storage": [
|
||||||
|
{"name":"agpsdata.app.js","url":"app.js"},
|
||||||
|
{"name":"agpsdata.img","url":"agpsdata-icon.js","evaluate":true},
|
||||||
|
{"name":"agpsdata.default.json","url":"default.json"},
|
||||||
|
{"name":"agpsdata.boot.js","url":"boot.js"},
|
||||||
|
{"name":"agpsdata","url":"lib.js"},
|
||||||
|
{"name":"agpsdata.settings.js","url":"settings.js"}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name": "agpsdata.json"},
|
||||||
|
{"name": "agpsdata.settings.json"}
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,71 @@
|
||||||
|
(function(back) {
|
||||||
|
function writeSettings(key, value) {
|
||||||
|
var s = Object.assign(
|
||||||
|
require('Storage').readJSON(settingsDefaultFile, true) || {},
|
||||||
|
require('Storage').readJSON(settingsFile, true) || {});
|
||||||
|
s[key] = value;
|
||||||
|
require('Storage').writeJSON(settingsFile, s);
|
||||||
|
readSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function readSettings() {
|
||||||
|
settings = Object.assign(
|
||||||
|
require('Storage').readJSON(settingsDefaultFile, true) || {},
|
||||||
|
require('Storage').readJSON(settingsFile, true) || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
var settingsFile = "agpsdata.settings.json";
|
||||||
|
var settingsDefaultFile = "agpsdata.default.json";
|
||||||
|
|
||||||
|
var settings;
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
const gnsstypes = [
|
||||||
|
"", "GPS", "BDS", "GPS+BDS", "GLONASS", "GPS+GLONASS", "BDS+GLONASS",
|
||||||
|
"GPS+BDS+GLON."
|
||||||
|
];
|
||||||
|
|
||||||
|
function buildMainMenu() {
|
||||||
|
var mainmenu = {
|
||||||
|
'' : {'title' : 'AGPS download'},
|
||||||
|
'< Back' : back,
|
||||||
|
"Enabled" : {
|
||||||
|
value : !!settings.enabled,
|
||||||
|
onchange : v => { writeSettings("enabled", v); }
|
||||||
|
},
|
||||||
|
"Refresh every" : {
|
||||||
|
value : settings.refresh / 60,
|
||||||
|
min : 1,
|
||||||
|
max : 168,
|
||||||
|
step : 1,
|
||||||
|
format : v => v + "h",
|
||||||
|
onchange : v => { writeSettings("refresh", Math.round(v * 60)); }
|
||||||
|
},
|
||||||
|
"GNSS type" : {
|
||||||
|
value : settings.gnsstype,
|
||||||
|
min : 1,
|
||||||
|
max : 7,
|
||||||
|
step : 1,
|
||||||
|
format : v => gnsstypes[v],
|
||||||
|
onchange : x => writeSettings('gnsstype', x)
|
||||||
|
},
|
||||||
|
"Force refresh" : () => {
|
||||||
|
E.showMessage("Loading A-GPS data");
|
||||||
|
require("agpsdata")
|
||||||
|
.pull(
|
||||||
|
function() {
|
||||||
|
E.showAlert("Success").then(
|
||||||
|
() => { E.showMenu(buildMainMenu()); });
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
E.showAlert(error, "Error")
|
||||||
|
.then(() => { E.showMenu(buildMainMenu()); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return mainmenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu(buildMainMenu());
|
||||||
|
});
|
|
@ -31,3 +31,7 @@
|
||||||
0.29: Fix wrong 'dow' handling in new timer if first day of week is Monday
|
0.29: Fix wrong 'dow' handling in new timer if first day of week is Monday
|
||||||
0.30: Fix "Enable All"
|
0.30: Fix "Enable All"
|
||||||
0.31: Add seconds to timers
|
0.31: Add seconds to timers
|
||||||
|
0.32: Fix wrong hidden filter
|
||||||
|
Add option for auto-delete a timer after it expires
|
||||||
|
0.33: Allow hiding timers&alarms
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,10 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
value: alarm.as,
|
value: alarm.as,
|
||||||
onchange: v => alarm.as = v
|
onchange: v => alarm.as = v
|
||||||
},
|
},
|
||||||
|
/*LANG*/"Hidden": {
|
||||||
|
value: alarm.hidden || false,
|
||||||
|
onchange: v => alarm.hidden = v
|
||||||
|
},
|
||||||
/*LANG*/"Cancel": () => showMainMenu()
|
/*LANG*/"Cancel": () => showMainMenu()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -280,6 +284,14 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
||||||
value: timer.on,
|
value: timer.on,
|
||||||
onchange: v => timer.on = v
|
onchange: v => timer.on = v
|
||||||
},
|
},
|
||||||
|
/*LANG*/"Delete After Expiration": {
|
||||||
|
value: timer.del,
|
||||||
|
onchange: v => timer.del = v
|
||||||
|
},
|
||||||
|
/*LANG*/"Hidden": {
|
||||||
|
value: timer.hidden || false,
|
||||||
|
onchange: v => timer.hidden = v
|
||||||
|
},
|
||||||
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
|
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
|
||||||
/*LANG*/"Cancel": () => showMainMenu()
|
/*LANG*/"Cancel": () => showMainMenu()
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Alarms & Timers",
|
"name": "Alarms & Timers",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.31",
|
"version": "0.33",
|
||||||
"description": "Set alarms and timers on your Bangle",
|
"description": "Set alarms and timers on your Bangle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm,widget",
|
||||||
|
|
|
@ -2,7 +2,7 @@ WIDGETS["alarm"]={area:"tl",width:0,draw:function() {
|
||||||
if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
||||||
},reload:function() {
|
},reload:function() {
|
||||||
// don't include library here as we're trying to use as little RAM as possible
|
// don't include library here as we're trying to use as little RAM as possible
|
||||||
WIDGETS["alarm"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==false)) ? 24 : 0;
|
WIDGETS["alarm"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==true)) ? 24 : 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
WIDGETS["alarm"].reload();
|
WIDGETS["alarm"].reload();
|
||||||
|
|
|
@ -9,3 +9,7 @@
|
||||||
0.08: Handling of alarms
|
0.08: Handling of alarms
|
||||||
0.09: Alarm vibration, repeat, and auto-snooze now handled by sched
|
0.09: Alarm vibration, repeat, and auto-snooze now handled by sched
|
||||||
0.10: Fix SMS bug
|
0.10: Fix SMS bug
|
||||||
|
0.12: Use default Bangle formatter for booleans
|
||||||
|
0.13: Added Bangle.http function (see Readme file for more info)
|
||||||
|
0.14: Fix timeout of http function not being cleaned up
|
||||||
|
0.15: Allow method/body/headers to be specified for `http` (needs Gadgetbridge 0.68.0b or later)
|
||||||
|
|
|
@ -32,6 +32,25 @@ Responses are sent back to Gadgetbridge simply as one line of JSON.
|
||||||
|
|
||||||
More info on message formats on http://www.espruino.com/Gadgetbridge
|
More info on message formats on http://www.espruino.com/Gadgetbridge
|
||||||
|
|
||||||
|
## Functions provided
|
||||||
|
|
||||||
|
The boot code also provides some useful functions:
|
||||||
|
|
||||||
|
* `Bangle.messageResponse = function(msg,response)` - send a yes/no response to a message. `msg` is a message object, and `response` is a boolean.
|
||||||
|
* `Bangle.musicControl = function(cmd)` - control music, cmd = `play/pause/next/previous/volumeup/volumedown`
|
||||||
|
* `Bangle.http = function(url,options)` - make an HTTPS request to a URL and return a promise with the data. Requires the [internet enabled `Bangle.js Gadgetbridge` app](http://www.espruino.com/Gadgetbridge#http-requests). `options` can contain:
|
||||||
|
* `id` - a custom (string) ID
|
||||||
|
* `timeout` - a timeout for the request in milliseconds (default 30000ms)
|
||||||
|
* `xpath` an xPath query to run on the request (but right now the URL requested must be XML - HTML is rarely XML compliant)
|
||||||
|
|
||||||
|
eg:
|
||||||
|
|
||||||
|
```
|
||||||
|
Bangle.http("https://pur3.co.uk/hello.txt").then(data=>{
|
||||||
|
console.log("Got ",data);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Bangle.js can only hold one connection open at a time, so it's hard to see
|
Bangle.js can only hold one connection open at a time, so it's hard to see
|
||||||
|
|
|
@ -118,11 +118,53 @@
|
||||||
var cal = require("Storage").readJSON("android.calendar.json",true);
|
var cal = require("Storage").readJSON("android.calendar.json",true);
|
||||||
if (!cal || !Array.isArray(cal)) cal = [];
|
if (!cal || !Array.isArray(cal)) cal = [];
|
||||||
gbSend({t:"force_calendar_sync", ids: cal.map(e=>e.id)});
|
gbSend({t:"force_calendar_sync", ids: cal.map(e=>e.id)});
|
||||||
|
},
|
||||||
|
"http":function() {
|
||||||
|
//get the promise and call the promise resolve
|
||||||
|
if (Bangle.httpRequest === undefined) return;
|
||||||
|
var request=Bangle.httpRequest[event.id];
|
||||||
|
if (request === undefined) return; //already timedout or wrong id
|
||||||
|
delete Bangle.httpRequest[event.id];
|
||||||
|
clearTimeout(request.t); //t = timeout variable
|
||||||
|
if(event.err!==undefined) //if is error
|
||||||
|
request.j(event.err); //r = reJect function
|
||||||
|
else
|
||||||
|
request.r(event); //r = resolve function
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var h = HANDLERS[event.t];
|
var h = HANDLERS[event.t];
|
||||||
if (h) h(); else console.log("GB Unknown",event);
|
if (h) h(); else console.log("GB Unknown",event);
|
||||||
};
|
};
|
||||||
|
// HTTP request handling - see the readme
|
||||||
|
// options = {id,timeout,xpath}
|
||||||
|
Bangle.http = (url,options)=>{
|
||||||
|
options = options||{};
|
||||||
|
if (Bangle.httpRequest === undefined)
|
||||||
|
Bangle.httpRequest={};
|
||||||
|
if (options.id === undefined) {
|
||||||
|
// try and create a unique ID
|
||||||
|
do {
|
||||||
|
options.id = Math.random().toString().substr(2);
|
||||||
|
} while( Bangle.httpRequest[options.id]!==undefined);
|
||||||
|
}
|
||||||
|
//send the request
|
||||||
|
var req = {t: "http", url:url, id:options.id};
|
||||||
|
if (options.xpath) req.xpath = options.xpath;
|
||||||
|
if (options.method) req.method = options.method;
|
||||||
|
if (options.body) req.body = options.body;
|
||||||
|
if (options.headers) req.headers = options.headers;
|
||||||
|
gbSend(req);
|
||||||
|
//create the promise
|
||||||
|
var promise = new Promise(function(resolve,reject) {
|
||||||
|
//save the resolve function in the dictionary and create a timeout (30 seconds default)
|
||||||
|
Bangle.httpRequest[options.id]={r:resolve,j:reject,t:setTimeout(()=>{
|
||||||
|
//if after "timeoutMillisec" it still hasn't answered -> reject
|
||||||
|
delete Bangle.httpRequest[options.id];
|
||||||
|
reject("Timeout");
|
||||||
|
},options.timeout||30000)};
|
||||||
|
});
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
// Battery monitor
|
// Battery monitor
|
||||||
function sendBattery() { gbSend({ t: "status", bat: E.getBattery(), chg: Bangle.isCharging()?1:0 }); }
|
function sendBattery() { gbSend({ t: "status", bat: E.getBattery(), chg: Bangle.isCharging()?1:0 }); }
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "android",
|
"id": "android",
|
||||||
"name": "Android Integration",
|
"name": "Android Integration",
|
||||||
"shortName": "Android",
|
"shortName": "Android",
|
||||||
"version": "0.11",
|
"version": "0.15",
|
||||||
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,system,messages,notifications,gadgetbridge",
|
"tags": "tool,system,messages,notifications,gadgetbridge",
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
}),
|
}),
|
||||||
/*LANG*/"Keep Msgs" : {
|
/*LANG*/"Keep Msgs" : {
|
||||||
value : !!settings.keep,
|
value : !!settings.keep,
|
||||||
format : v=>v?/*LANG*/"Yes":/*LANG*/"No",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.keep = v;
|
settings.keep = v;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Fix bug if image clock wasn't installed
|
0.02: Fix bug if image clock wasn't installed
|
||||||
0.03: Update to use setUI
|
0.03: Update to use setUI
|
||||||
|
0.04: Tell clock widgets to hide. Move loadWidgets() so it only runs on
|
||||||
|
startup and not on every draw.
|
||||||
|
|
|
@ -87,7 +87,6 @@ if (g.drawImages) {
|
||||||
draw();
|
draw();
|
||||||
var secondInterval = setInterval(draw,100);
|
var secondInterval = setInterval(draw,100);
|
||||||
// load widgets
|
// load widgets
|
||||||
Bangle.loadWidgets();
|
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
// Stop when LCD goes off
|
// Stop when LCD goes off
|
||||||
Bangle.on('lcdPower',on=>{
|
Bangle.on('lcdPower',on=>{
|
||||||
|
@ -104,3 +103,5 @@ if (g.drawImages) {
|
||||||
}
|
}
|
||||||
// Show launcher when button pressed
|
// Show launcher when button pressed
|
||||||
Bangle.setUI("clock");
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "animclk",
|
"id": "animclk",
|
||||||
"name": "Animated Clock",
|
"name": "Animated Clock",
|
||||||
"shortName": "Anim Clock",
|
"shortName": "Anim Clock",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "An animated clock face using Mark Ferrari's amazing 8 bit game art and palette cycling: http://www.markferrari.com/art/8bit-game-art",
|
"description": "An animated clock face using Mark Ferrari's amazing 8 bit game art and palette cycling: http://www.markferrari.com/art/8bit-game-art",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -9,4 +9,5 @@
|
||||||
when weekday name and calendar weeknumber are on then display is <weekday short> #<calweek>
|
when weekday name and calendar weeknumber are on then display is <weekday short> #<calweek>
|
||||||
week is buffered until date or timezone changes
|
week is buffered until date or timezone changes
|
||||||
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
|
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
|
||||||
0.08: fixed calendar weeknumber not shortened to two digits
|
0.08: fixed calendar weeknumber not shortened to two digits
|
||||||
|
0.09: Use default Bangle formatter for booleans
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "antonclk",
|
"id": "antonclk",
|
||||||
"name": "Anton Clock",
|
"name": "Anton Clock",
|
||||||
"version": "0.08",
|
"version": "0.09",
|
||||||
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
||||||
"readme":"README.md",
|
"readme":"README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
(function(back) {
|
(function(back) {
|
||||||
var FILE = "antonclk.json";
|
var FILE = "antonclk.json";
|
||||||
// Load settings
|
|
||||||
var settings = Object.assign({
|
var settings = Object.assign({
|
||||||
secondsOnUnlock: false,
|
secondsOnUnlock: false,
|
||||||
}, require('Storage').readJSON(FILE, true) || {});
|
}, require('Storage').readJSON(FILE, true) || {});
|
||||||
|
@ -41,7 +40,6 @@
|
||||||
"Date": stringInSettings("dateOnMain", ["Long", "Short", "ISO8601"]),
|
"Date": stringInSettings("dateOnMain", ["Long", "Short", "ISO8601"]),
|
||||||
"Show Weekday": {
|
"Show Weekday": {
|
||||||
value: (settings.weekDay !== undefined ? settings.weekDay : true),
|
value: (settings.weekDay !== undefined ? settings.weekDay : true),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.weekDay = v;
|
settings.weekDay = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -49,7 +47,6 @@
|
||||||
},
|
},
|
||||||
"Show CalWeek": {
|
"Show CalWeek": {
|
||||||
value: (settings.calWeek !== undefined ? settings.calWeek : false),
|
value: (settings.calWeek !== undefined ? settings.calWeek : false),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.calWeek = v;
|
settings.calWeek = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -57,7 +54,6 @@
|
||||||
},
|
},
|
||||||
"Uppercase": {
|
"Uppercase": {
|
||||||
value: (settings.upperCase !== undefined ? settings.upperCase : true),
|
value: (settings.upperCase !== undefined ? settings.upperCase : true),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.upperCase = v;
|
settings.upperCase = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -65,7 +61,6 @@
|
||||||
},
|
},
|
||||||
"Vector font": {
|
"Vector font": {
|
||||||
value: (settings.vectorFont !== undefined ? settings.vectorFont : false),
|
value: (settings.vectorFont !== undefined ? settings.vectorFont : false),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.vectorFont = v;
|
settings.vectorFont = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -82,7 +77,6 @@
|
||||||
"Show": stringInSettings("secondsMode", ["Never", "Unlocked", "Always"]),
|
"Show": stringInSettings("secondsMode", ["Never", "Unlocked", "Always"]),
|
||||||
"With \":\"": {
|
"With \":\"": {
|
||||||
value: (settings.secondsWithColon !== undefined ? settings.secondsWithColon : true),
|
value: (settings.secondsWithColon !== undefined ? settings.secondsWithColon : true),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.secondsWithColon = v;
|
settings.secondsWithColon = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -90,7 +84,6 @@
|
||||||
},
|
},
|
||||||
"Color": {
|
"Color": {
|
||||||
value: (settings.secondsColoured !== undefined ? settings.secondsColoured : true),
|
value: (settings.secondsColoured !== undefined ? settings.secondsColoured : true),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.secondsColoured = v;
|
settings.secondsColoured = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -99,9 +92,6 @@
|
||||||
"Date": stringInSettings("dateOnSecs", ["Year", "Weekday", "No"])
|
"Date": stringInSettings("dateOnSecs", ["Year", "Weekday", "No"])
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actually display the menu
|
|
||||||
E.showMenu(mainmenu);
|
E.showMenu(mainmenu);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// end of file
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: Create astral clock app
|
0.01: Create astral clock app
|
||||||
0.02: Fixed Whirlpool galaxy RA/DA, larger compass display, fixed moonphase overlapping battery widget
|
0.02: Fixed Whirlpool galaxy RA/DA, larger compass display, fixed moonphase overlapping battery widget
|
||||||
0.03: Update to use Bangle.setUI instead of setWatch
|
0.03: Update to use Bangle.setUI instead of setWatch
|
||||||
|
0.04: Tell clock widgets to hide.
|
||||||
|
|
|
@ -767,6 +767,24 @@ function draw() {
|
||||||
g.clear();
|
g.clear();
|
||||||
current_moonphase = getMoonPhase();
|
current_moonphase = getMoonPhase();
|
||||||
|
|
||||||
|
Bangle.setUI("clockupdown", btn => {
|
||||||
|
if (btn==0) {
|
||||||
|
if (!processing) {
|
||||||
|
if (!modeswitch) {
|
||||||
|
modeswitch = true;
|
||||||
|
if (mode == "planetary") mode = "extras";
|
||||||
|
else mode = "planetary";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
modeswitch = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!processing)
|
||||||
|
ready_to_compute = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Load widgets
|
// Load widgets
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
@ -799,23 +817,6 @@ Bangle.setGPSPower(1);
|
||||||
// Show launcher when button pressed
|
// Show launcher when button pressed
|
||||||
Bangle.setClockMode();
|
Bangle.setClockMode();
|
||||||
|
|
||||||
Bangle.setUI("clockupdown", btn => {
|
|
||||||
if (btn==0) {
|
|
||||||
if (!processing) {
|
|
||||||
if (!modeswitch) {
|
|
||||||
modeswitch = true;
|
|
||||||
if (mode == "planetary") mode = "extras";
|
|
||||||
else mode = "planetary";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
modeswitch = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!processing)
|
|
||||||
ready_to_compute = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setWatch(function () {
|
setWatch(function () {
|
||||||
if (!astral_settings.astral_default) {
|
if (!astral_settings.astral_default) {
|
||||||
colours_switched = true;
|
colours_switched = true;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "astral",
|
"id": "astral",
|
||||||
"name": "Astral Clock",
|
"name": "Astral Clock",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "Clock that calculates and displays Alt Az positions of all planets, Sun as well as several other astronomy targets (customizable) and current Moon phase. Coordinates are calculated by GPS & time and onscreen compass assists orienting. See Readme before using.",
|
"description": "Clock that calculates and displays Alt Az positions of all planets, Sun as well as several other astronomy targets (customizable) and current Moon phase. Coordinates are calculated by GPS & time and onscreen compass assists orienting. See Readme before using.",
|
||||||
"icon": "app-icon.png",
|
"icon": "app-icon.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -12,3 +12,4 @@
|
||||||
0.12: Add settings to hide date,widgets
|
0.12: Add settings to hide date,widgets
|
||||||
0.13: Add font setting
|
0.13: Add font setting
|
||||||
0.14: Use ClockFace_menu.addItems
|
0.14: Use ClockFace_menu.addItems
|
||||||
|
0.15: Add Power saving option
|
|
@ -7,4 +7,5 @@ A simple digital clock showing seconds as a horizontal bar.
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
* `Show date`: display date at the bottom of screen
|
* `Show date`: display date at the bottom of screen
|
||||||
* `Font`: choose between bitmap or vector fonts
|
* `Font`: choose between bitmap or vector fonts
|
||||||
|
* `Power saving`: (Bangle.js 2 only) don't draw the seconds bar while the watch is locked
|
|
@ -13,16 +13,20 @@ let locale = require("locale");
|
||||||
locale.hasMeridian = (locale.meridian(date)!=="");
|
locale.hasMeridian = (locale.meridian(date)!=="");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let barW = 0, prevX = 0;
|
||||||
function renderBar(l) {
|
function renderBar(l) {
|
||||||
if (!this.fraction) {
|
"ram";
|
||||||
// zero-size fillRect stills draws one line of pixels, we don't want that
|
if (l) prevX = 0; // called from Layout: drawing area was cleared
|
||||||
return;
|
else l = clock.layout.bar;
|
||||||
}
|
let x2 = l.x+barW;
|
||||||
const width = this.fraction*l.w;
|
if (clock.powerSave && Bangle.isLocked()) x2 = 0; // hide bar
|
||||||
g.fillRect(l.x, l.y, l.x+width-1, l.y+l.height-1);
|
if (x2===prevX) return; // nothing to do
|
||||||
|
if (x2===0) x2--; // don't leave 1px line
|
||||||
|
if (x2<Math.max(0, prevX)) g.setBgColor(l.bgCol || g.theme.bg).clearRect(x2+1, l.y, prevX, l.y2);
|
||||||
|
else g.setColor(l.col || g.theme.fg).fillRect(prevX+1, l.y, x2, l.y2);
|
||||||
|
prevX = x2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function timeText(date) {
|
function timeText(date) {
|
||||||
if (!clock.is12Hour) {
|
if (!clock.is12Hour) {
|
||||||
return locale.time(date, true);
|
return locale.time(date, true);
|
||||||
|
@ -47,22 +51,21 @@ function dateText(date) {
|
||||||
return `${dayName} ${dayMonth}`;
|
return `${dayName} ${dayMonth}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const ClockFace = require("ClockFace"),
|
const ClockFace = require("ClockFace"),
|
||||||
clock = new ClockFace({
|
clock = new ClockFace({
|
||||||
precision:1,
|
precision: 1,
|
||||||
settingsFile:'barclock.settings.json',
|
settingsFile: "barclock.settings.json",
|
||||||
init: function() {
|
init: function() {
|
||||||
const Layout = require("Layout");
|
const Layout = require("Layout");
|
||||||
this.layout = new Layout({
|
this.layout = new Layout({
|
||||||
type: "v", c: [
|
type: "v", c: [
|
||||||
{
|
{
|
||||||
type: "h", c: [
|
type: "h", c: [
|
||||||
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col:g.theme.fg, bgCol: g.theme.bg}, // updated below
|
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col: g.theme.fg, bgCol: g.theme.bg}, // updated below
|
||||||
{id: "ampm", label: " ", type: "txt", font: "6x8:2", col:g.theme.fg, bgCol: g.theme.bg},
|
{id: "ampm", label: " ", type: "txt", font: "6x8:2", col: g.theme.fg, bgCol: g.theme.bg},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
{id: "bar", type: "custom", fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
||||||
this.showDate ? {height: 40} : {},
|
this.showDate ? {height: 40} : {},
|
||||||
this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {},
|
this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {},
|
||||||
],
|
],
|
||||||
|
@ -76,7 +79,8 @@ const ClockFace = require("ClockFace"),
|
||||||
this.layout.ampm.label = "";
|
this.layout.ampm.label = "";
|
||||||
thickness = Math.floor(Bangle.appRect.w/(5*6));
|
thickness = Math.floor(Bangle.appRect.w/(5*6));
|
||||||
}
|
}
|
||||||
this.layout.bar.height = thickness+1;
|
let bar = this.layout.bar;
|
||||||
|
bar.height = thickness+1;
|
||||||
if (this.font===1) { // vector
|
if (this.font===1) { // vector
|
||||||
const B2 = process.env.HWVERSION>1;
|
const B2 = process.env.HWVERSION>1;
|
||||||
if (this.is12Hour && locale.hasMeridian) {
|
if (this.is12Hour && locale.hasMeridian) {
|
||||||
|
@ -89,17 +93,32 @@ const ClockFace = require("ClockFace"),
|
||||||
this.layout.time.font = "6x8:"+thickness;
|
this.layout.time.font = "6x8:"+thickness;
|
||||||
}
|
}
|
||||||
this.layout.update();
|
this.layout.update();
|
||||||
|
bar.y2 = bar.y+bar.height-1;
|
||||||
},
|
},
|
||||||
update: function(date, c) {
|
update: function(date, c) {
|
||||||
|
"ram";
|
||||||
if (c.m) this.layout.time.label = timeText(date);
|
if (c.m) this.layout.time.label = timeText(date);
|
||||||
if (c.h) this.layout.ampm.label = ampmText(date);
|
if (c.h) this.layout.ampm.label = ampmText(date);
|
||||||
if (c.d && this.showDate) this.layout.date.label = dateText(date);
|
if (c.d && this.showDate) this.layout.date.label = dateText(date);
|
||||||
const SECONDS_PER_MINUTE = 60;
|
if (c.m) this.layout.render();
|
||||||
if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
|
if (c.s) {
|
||||||
this.layout.render();
|
barW = Math.round(date.getSeconds()/60*this.layout.bar.w);
|
||||||
|
renderBar();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
resume: function() {
|
resume: function() {
|
||||||
|
prevX = 0; // force redraw of bar
|
||||||
this.layout.forgetLazyState();
|
this.layout.forgetLazyState();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// power saving: only update once a minute while locked, hide bar
|
||||||
|
if (clock.powerSave) {
|
||||||
|
Bangle.on("lock", lock => {
|
||||||
|
clock.precision = lock ? 60 : 1;
|
||||||
|
clock.tick();
|
||||||
|
renderBar(); // hide/redraw bar right away
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
clock.start();
|
clock.start();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "barclock",
|
"id": "barclock",
|
||||||
"name": "Bar Clock",
|
"name": "Bar Clock",
|
||||||
"version": "0.14",
|
"version": "0.15",
|
||||||
"description": "A simple digital clock showing seconds as a bar",
|
"description": "A simple digital clock showing seconds as a bar",
|
||||||
"icon": "clock-bar.png",
|
"icon": "clock-bar.png",
|
||||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
||||||
|
|
|
@ -17,10 +17,14 @@
|
||||||
onchange: v => save("font", v),
|
onchange: v => save("font", v),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
require("ClockFace_menu").addItems(menu, save, {
|
let items = {
|
||||||
showDate: s.showDate,
|
showDate: s.showDate,
|
||||||
loadWidgets: s.loadWidgets,
|
loadWidgets: s.loadWidgets,
|
||||||
});
|
};
|
||||||
|
// Power saving for Bangle.js 1 doesn't make sense (no updates while screen is off anyway)
|
||||||
|
if (process.env.HWVERSION>1) {
|
||||||
|
items.powerSave = s.powerSave;
|
||||||
|
}
|
||||||
|
require("ClockFace_menu").addItems(menu, save, items);
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,3 +7,4 @@
|
||||||
0.07: Step count resets at midnight
|
0.07: Step count resets at midnight
|
||||||
0.08: Step count stored in memory to survive reloads. Now shows step count daily and since last reboot.
|
0.08: Step count stored in memory to survive reloads. Now shows step count daily and since last reboot.
|
||||||
0.09: NOW it really should reset daily (instead of every other day...)
|
0.09: NOW it really should reset daily (instead of every other day...)
|
||||||
|
0.10: Tell clock widgets to hide.
|
||||||
|
|
|
@ -416,13 +416,13 @@ var layout = new Layout( {
|
||||||
|
|
||||||
// Clear the screen once, at startup
|
// Clear the screen once, at startup
|
||||||
g.clear();
|
g.clear();
|
||||||
|
Bangle.setUI("clock");
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
Bangle.setUI("clock");
|
|
||||||
layout.render();
|
layout.render();
|
||||||
|
|
||||||
Bangle.on('lock', function(locked) {
|
Bangle.on('lock', function(locked) {
|
||||||
if(!locked) {
|
if(!locked) {
|
||||||
layout.render();
|
layout.render();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "Barcode clock",
|
"name": "Barcode clock",
|
||||||
"shortName":"Barcode clock",
|
"shortName":"Barcode clock",
|
||||||
"icon": "barcode.icon.png",
|
"icon": "barcode.icon.png",
|
||||||
"version":"0.09",
|
"version":"0.10",
|
||||||
"description": "EAN-8 compatible barcode clock.",
|
"description": "EAN-8 compatible barcode clock.",
|
||||||
"tags": "barcode,ean,ean-8,watchface,clock,clockface",
|
"tags": "barcode,ean,ean-8,watchface,clock,clockface",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: App Created!
|
0.01: App Created!
|
||||||
0.02: Update to use Bangle.setUI instead of setWatch
|
0.02: Update to use Bangle.setUI instead of setWatch
|
||||||
|
0.03: Tell clock widgets to hide.
|
||||||
|
|
|
@ -249,6 +249,9 @@ g.clear();
|
||||||
g.setColor(0, 0.5, 0).drawImage(bg_crack);
|
g.setColor(0, 0.5, 0).drawImage(bg_crack);
|
||||||
g.setColor(1, 1, 1).drawImage(batman);
|
g.setColor(1, 1, 1).drawImage(batman);
|
||||||
|
|
||||||
|
// Show launcher when button pressed
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
@ -256,5 +259,3 @@ Bangle.drawWidgets();
|
||||||
timeInterval = setInterval(showTime, 1000);
|
timeInterval = setInterval(showTime, 1000);
|
||||||
showTime();
|
showTime();
|
||||||
|
|
||||||
// Show launcher when button pressed
|
|
||||||
Bangle.setUI("clock");
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "batclock",
|
"id": "batclock",
|
||||||
"name": "Bat Clock",
|
"name": "Bat Clock",
|
||||||
"shortName": "Bat Clock",
|
"shortName": "Bat Clock",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Morphing Clock, with an awesome \"The Dark Knight\" themed logo.",
|
"description": "Morphing Clock, with an awesome \"The Dark Knight\" themed logo.",
|
||||||
"icon": "bat-clock.png",
|
"icon": "bat-clock.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.02: Modified for use with new bootloader and firmware
|
0.02: Modified for use with new bootloader and firmware
|
||||||
0.03: Update to use Bangle.setUI instead of setWatch
|
0.03: Update to use Bangle.setUI instead of setWatch
|
||||||
|
0.04: Tell clock widgets to hide.
|
||||||
|
|
|
@ -100,10 +100,12 @@ Bangle.on('lcdPower', on => {
|
||||||
if (on) drawClock();
|
if (on) drawClock();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show launcher when button pressed
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
g.clear();
|
g.clear();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
setInterval(() => { drawClock(); }, 1000);
|
setInterval(() => { drawClock(); }, 1000);
|
||||||
drawClock();
|
drawClock();
|
||||||
// Show launcher when button pressed
|
|
||||||
Bangle.setUI("clock");
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "bclock",
|
"id": "bclock",
|
||||||
"name": "Binary Clock",
|
"name": "Binary Clock",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "A simple binary clock watch face",
|
"description": "A simple binary clock watch face",
|
||||||
"icon": "clock-binary.png",
|
"icon": "clock-binary.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
0.03: Internationalisation; bug fix - battery icon responds promptly to charging state
|
0.03: Internationalisation; bug fix - battery icon responds promptly to charging state
|
||||||
0.04: bug fix
|
0.04: bug fix
|
||||||
0.05: proper fix for the race condition in queueDraw()
|
0.05: proper fix for the race condition in queueDraw()
|
||||||
|
0.06: Tell clock widgets to hide.
|
||||||
|
|
|
@ -85,7 +85,8 @@ Bangle.on('charging', (charging) => {
|
||||||
draw();
|
draw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
draw();
|
draw();
|
||||||
|
|
||||||
Bangle.setUI("clock");
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "bigdclock",
|
{ "id": "bigdclock",
|
||||||
"name": "Big digit clock containing just the essentials",
|
"name": "Big digit clock containing just the essentials",
|
||||||
"shortName":"Big digit clk",
|
"shortName":"Big digit clk",
|
||||||
"version":"0.05",
|
"version":"0.06",
|
||||||
"description": "A clock containing just the essentials, made as easy to read as possible for those of us that need glasses. It contains the time, the day-of-week, the day-of-month, and the current battery state-of-charge.",
|
"description": "A clock containing just the essentials, made as easy to read as possible for those of us that need glasses. It contains the time, the day-of-week, the day-of-month, and the current battery state-of-charge.",
|
||||||
"icon": "bigdclock.png",
|
"icon": "bigdclock.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Barometer altitude adjustment setting
|
0.02: Barometer altitude adjustment setting
|
||||||
|
0.03: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "bikespeedo",
|
"id": "bikespeedo",
|
||||||
"name": "Bike Speedometer (beta)",
|
"name": "Bike Speedometer (beta)",
|
||||||
"shortName": "Bike Speedometer",
|
"shortName": "Bike Speedometer",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Shows GPS speed, GPS heading, Compass heading, GPS altitude and Barometer altitude from internal sources",
|
"description": "Shows GPS speed, GPS heading, Compass heading, GPS altitude and Barometer altitude from internal sources",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"Screenshot.png"}],
|
"screenshots": [{"url":"Screenshot.png"}],
|
||||||
|
|
|
@ -33,12 +33,10 @@
|
||||||
'< Back': function() { E.showMenu(appMenu); },
|
'< Back': function() { E.showMenu(appMenu); },
|
||||||
'Speed' : {
|
'Speed' : {
|
||||||
value : settings.spdFilt,
|
value : settings.spdFilt,
|
||||||
format : v => v?"On":"Off",
|
|
||||||
onchange : () => { settings.spdFilt = !settings.spdFilt; writeSettings(); }
|
onchange : () => { settings.spdFilt = !settings.spdFilt; writeSettings(); }
|
||||||
},
|
},
|
||||||
'Altitude' : {
|
'Altitude' : {
|
||||||
value : settings.altFilt,
|
value : settings.altFilt,
|
||||||
format : v => v?"On":"Off",
|
|
||||||
onchange : () => { settings.altFilt = !settings.altFilt; writeSettings(); }
|
onchange : () => { settings.altFilt = !settings.altFilt; writeSettings(); }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Fixed bug where screen didn't clear so incorrect time displayed.
|
0.02: Fixed bug where screen didn't clear so incorrect time displayed.
|
||||||
0.03: Update to use Bangle.setUI instead of setWatch
|
0.03: Update to use Bangle.setUI instead of setWatch
|
||||||
|
0.04: Tell clock widgets to hide.
|
||||||
|
|
|
@ -164,9 +164,6 @@ Bangle.on('lcdPower',on=>{
|
||||||
draw(); // draw immediately
|
draw(); // draw immediately
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Load widgets
|
|
||||||
Bangle.loadWidgets();
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
// Show launcher when button pressed
|
// Show launcher when button pressed
|
||||||
Bangle.setUI("clockupdown", btn=>{
|
Bangle.setUI("clockupdown", btn=>{
|
||||||
if (btn!=1) return;
|
if (btn!=1) return;
|
||||||
|
@ -176,3 +173,6 @@ Bangle.setUI("clockupdown", btn=>{
|
||||||
displayTime = 0;
|
displayTime = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// Load widgets
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "binclock",
|
"id": "binclock",
|
||||||
"name": "Binary Clock",
|
"name": "Binary Clock",
|
||||||
"shortName": "Binary Clock",
|
"shortName": "Binary Clock",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "A binary clock with hours and minutes. BTN1 toggles a digital clock.",
|
"description": "A binary clock with hours and minutes. BTN1 toggles a digital clock.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -2,3 +2,5 @@
|
||||||
0.02: first running version for BangleJs2
|
0.02: first running version for BangleJs2
|
||||||
0.03: corrected icon, added screen shot, extended description
|
0.03: corrected icon, added screen shot, extended description
|
||||||
0.04: corrected format of background image (raw binary)
|
0.04: corrected format of background image (raw binary)
|
||||||
|
0.05: move setUI() up before draw() as to not have a false positive 'sanity
|
||||||
|
check' when building on github.
|
||||||
|
|
|
@ -334,6 +334,7 @@ function setRuntimeValues(resolution) {
|
||||||
var hour = 0, minute = 1, second = 50;
|
var hour = 0, minute = 1, second = 50;
|
||||||
var batVLevel = 20;
|
var batVLevel = 20;
|
||||||
|
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
|
@ -371,7 +372,6 @@ function draw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show launcher when button pressed
|
// Show launcher when button pressed
|
||||||
Bangle.setUI("clock");
|
|
||||||
setRuntimeValues(g.getWidth());
|
setRuntimeValues(g.getWidth());
|
||||||
g.reset().clear();
|
g.reset().clear();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"shortName":"BinWatch",
|
"shortName":"BinWatch",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
"version":"0.04",
|
"version":"0.05",
|
||||||
"supports": ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"allow_emulator":true,
|
"allow_emulator":true,
|
||||||
|
|
|
@ -5,3 +5,4 @@
|
||||||
0.04: Modified to account for changes in the behavior of Graphics.fillPoly
|
0.04: Modified to account for changes in the behavior of Graphics.fillPoly
|
||||||
0.05: Slight increase to draw speed after LCD on
|
0.05: Slight increase to draw speed after LCD on
|
||||||
0.06: Update to use Bangle.setUI instead of setWatch, allow themes and different size screens
|
0.06: Update to use Bangle.setUI instead of setWatch, allow themes and different size screens
|
||||||
|
0.07: Tell clock widgets to hide.
|
||||||
|
|
|
@ -99,6 +99,10 @@ function startTimers() {
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
intervalRef = setInterval(redraw,1000);
|
intervalRef = setInterval(redraw,1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show launcher when button pressed
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
startTimers();
|
startTimers();
|
||||||
Bangle.on('lcdPower',function(on) {
|
Bangle.on('lcdPower',function(on) {
|
||||||
|
@ -108,5 +112,3 @@ Bangle.on('lcdPower',function(on) {
|
||||||
clearTimers();
|
clearTimers();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Show launcher when button pressed
|
|
||||||
Bangle.setUI("clock");
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "blobclk",
|
"id": "blobclk",
|
||||||
"name": "Large Digit Blob Clock",
|
"name": "Large Digit Blob Clock",
|
||||||
"shortName": "Blob Clock",
|
"shortName": "Blob Clock",
|
||||||
"version": "0.06",
|
"version": "0.07",
|
||||||
"description": "A clock with big digits",
|
"description": "A clock with big digits",
|
||||||
"icon": "clock-blob.png",
|
"icon": "clock-blob.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
0.04: Work with themes, smaller screens
|
0.04: Work with themes, smaller screens
|
||||||
0.05: Adjust hand lengths to be within 'tick' points
|
0.05: Adjust hand lengths to be within 'tick' points
|
||||||
0.06: Removed "wake LCD on face-up"-feature: A watch-face should not set things like "wake LCD on face-up".
|
0.06: Removed "wake LCD on face-up"-feature: A watch-face should not set things like "wake LCD on face-up".
|
||||||
|
0.07: Tell clock widgets to hide.
|
||||||
|
|
|
@ -130,9 +130,10 @@ Bangle.on('lcdPower', (on) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show launcher when button pressed
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
g.clear();
|
g.clear();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
startTimers();
|
startTimers();
|
||||||
// Show launcher when button pressed
|
|
||||||
Bangle.setUI("clock");
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "boldclk",
|
"id": "boldclk",
|
||||||
"name": "Bold Clock",
|
"name": "Bold Clock",
|
||||||
"version": "0.06",
|
"version": "0.07",
|
||||||
"description": "Simple, readable and practical clock",
|
"description": "Simple, readable and practical clock",
|
||||||
"icon": "bold_clock.png",
|
"icon": "bold_clock.png",
|
||||||
"screenshots": [{"url":"screenshot_bold.png"}],
|
"screenshots": [{"url":"screenshot_bold.png"}],
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
...
|
||||||
|
0.02: First update with ChangeLog Added
|
||||||
|
0.03: updated watch face to use the ClockFace library
|
|
@ -1,102 +1,233 @@
|
||||||
var sprite = {
|
var sprite = {
|
||||||
width : 47, height : 47, bpp : 3,
|
width: 47,
|
||||||
transparent : 1,
|
height: 47,
|
||||||
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"))
|
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 = {
|
const boxes = {
|
||||||
width : 122, height : 56, bpp : 3,
|
width: 122,
|
||||||
transparent : 1,
|
height: 56,
|
||||||
buffer : require("heatshrink").decompress(atob("kmZkmSpICPwgDBmQUQAQMJAYNkFiOSiQDB5JESAYQsSpADByYsSyBZBydt23bAR+wgFJkwUQAQNggGSposR23AgMkzZESwECpM2IiUAgmSFiW2gDlBFiVsgDlBFiXYgDNBL4MDWZy2FgEGWZy2FgENWZy2EL4MbWZpTBWwZfBXJpTCWwZiCWZpTBWwZiCWZsbWwhiCWZpWCWwTORWwgXRWwgXRWwZESWwZESWwZESWwYXRWwgXRW362/W362/W362/W362/W362/W362/W362/W362/W362/W362/WwuAgazOWwsAgyzOWwsAhqzOWwhfBjazNKYK2DL4K5NKYS2DMQSzNKYK2DMQSzNja2EMQSzNKwS2CZyK2EC6K2EC6K2DIiS2DIiS2DIiUAFoMAAFTkBFtckyAtrLgWSpICnLIIsqyVAgAsqpIA="))
|
bpp: 3,
|
||||||
|
transparent: 1,
|
||||||
|
buffer: require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"kmZkmSpICPwgDBmQUQAQMJAYNkFiOSiQDB5JESAYQsSpADByYsSyBZBydt23bAR+wgFJkwUQAQNggGSposR23AgMkzZESwECpM2IiUAgmSFiW2gDlBFiVsgDlBFiXYgDNBL4MDWZy2FgEGWZy2FgENWZy2EL4MbWZpTBWwZfBXJpTCWwZiCWZpTBWwZiCWZsbWwhiCWZpWCWwTORWwgXRWwgXRWwZESWwZESWwZESWwYXRWwgXRW362/W362/W362/W362/W362/W362/W362/W362/W362/W362/WwuAgazOWwsAgyzOWwsAhqzOWwhfBjazNKYK2DL4K5NKYS2DMQSzNKYK2DMQSzNja2EMQSzNKwS2CZyK2EC6K2EC6K2DIiS2DIiS2DIiUAFoMAAFTkBFtckyAtrLgWSpICnLIIsqyVAgAsqpIA="
|
||||||
|
)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const background = {
|
const background = {
|
||||||
width : 176, height : 176, bpp : 3,
|
width: 176,
|
||||||
transparent : 5,
|
height: 176,
|
||||||
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="))
|
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 = {
|
numbersDims = {
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 44
|
height: 44,
|
||||||
};
|
};
|
||||||
const numbers = [
|
const numbers = [
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARGQKYQIDAwUEBxMAAQNAgECpMgAQMkB4IOIAQQLCgEQBwQaBgEBB1oCBBwYCCiRWDCIRWEO5wOHAX4CnA=")),
|
require("heatshrink").decompress(
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARNIKYIIEwEAggOKNIQODyAHCBxQsWB3TUFgMgA4sSBwzU/AVA=")),
|
atob(
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKggIBAwkCBw+QCIQLCgIRCDQcQBwwyDDwUSCgVAAwIOBEwI7EpI7FBw4FDghZGHwgOEF4Y+CEYQ+DBxQADNAIAFNAIOFa/4CoA=")),
|
"ikswcBkmSpIC/ARGQKYQIDAwUEBxMAAQNAgECpMgAQMkB4IOIAQQLCgEQBwQaBgEBB1oCBBwYCCiRWDCIRWEO5wOHAX4CnA="
|
||||||
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(
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARGQKgYICAwcCBxADBiQdDkEANYoOGEAYyEHYoOIHYqfFBxIdDBAMQFgZHCBysSFgwRBO46GFa/4CnA")),
|
atob("ikswcBkmSpIC/ARNIKYIIEwEAggOKNIQODyAHCBxQsWB3TUFgMgA4sSBwzU/AVA=")
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ5VGiAGFgIOIDQUgBwUCEYQOJGQYNBHAlADQgOHwEAggUDpANBCgYpBBwmQAwJiGhIjDB1gC/AU4A=")),
|
),
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKYYICAwcEBxGQgAaDgVJgACBDQQOJgB6CBwcAiQODHa4AEhIRBpAHDiARBwAGCgIgCFIYOCFIYOHiQrEJQxlCBwzX/AVAA=")),
|
require("heatshrink").decompress(
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKggIBAwkCBw+QCIQLCgIRCDQcQBzkSTAsBHYoOIL4gOCMooOENAYOCoA4EBwoqDgiGGF4gOEa/4CoA=")),
|
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
|
digitPositions = [
|
||||||
{x:13, y:6}, {x:32, y:6},
|
// relative to the box
|
||||||
{x:74, y:6}, {x:93, y:6},
|
{ 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_duration = 1; // seconds
|
||||||
const animation_steps = 20;
|
const animation_steps = 20;
|
||||||
const jump_height = 45; // top coordinate of the jump
|
const jump_height = 45; // top coordinate of the jump
|
||||||
const seconds_per_minute = 60;
|
const seconds_per_minute = 60;
|
||||||
|
|
||||||
function draw() {
|
const ClockFace = require("ClockFace");
|
||||||
const now = new Date();
|
const clock = new ClockFace({
|
||||||
g.drawImage(background, 0, 0);
|
precision: 60, // just once a minute
|
||||||
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
|
init: function() {
|
||||||
g.setTheme({bg:"#00f",fg:"#fff",dark:true}).clear();
|
// Clear the screen once, at startup
|
||||||
|
g.setTheme({ bg: "#00f", fg: "#fff", dark: true }).clear();
|
||||||
|
|
||||||
Bangle.on('lcdPower',on=>{
|
this.drawing = true;
|
||||||
if (on) {
|
|
||||||
draw(); // draw immediately, queue redraw
|
this.simpleDraw = function(now) {
|
||||||
} else { // stop draw timer
|
var boxTL_x = 27;
|
||||||
if (drawTimeout) {
|
var boxTL_y = 29;
|
||||||
clearTimeout(drawTimeout);
|
var sprite_TL_x = 72;
|
||||||
}
|
var sprite_TL_y = 161 - sprite.height;
|
||||||
drawTimeout = undefined;
|
const seconds =
|
||||||
}
|
(now.getSeconds() % seconds_per_minute) + now.getMilliseconds() / 1000;
|
||||||
|
const hours =
|
||||||
|
this.is12Hour && now.getHours() > 12
|
||||||
|
? now.getHours() - 12
|
||||||
|
: now.getHours();
|
||||||
|
|
||||||
|
const minutes = now.getMinutes();
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
pause: function() {
|
||||||
|
this.drawing = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
resume: function() {
|
||||||
|
this.drawing = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
draw: function(now) {
|
||||||
|
if (!this.drawing) {
|
||||||
|
this.simpleDraw(now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 =
|
||||||
|
this.is12Hour && now.getHours() > 12
|
||||||
|
? now.getHours() - 12
|
||||||
|
: 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();
|
||||||
|
|
||||||
|
if (this.drawing) {
|
||||||
|
const timeout =
|
||||||
|
time_advance <= 1 ? animation_duration / animation_steps : -999;
|
||||||
|
if (timeout > 0) {
|
||||||
|
setTimeout((_) => {
|
||||||
|
this.draw(new Date());
|
||||||
|
}, timeout * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function(date, changed) {
|
||||||
|
if (this.drawing && changed.m) {
|
||||||
|
this.draw(date);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show launcher when middle button pressed
|
clock.start();
|
||||||
Bangle.setUI("clock");
|
|
||||||
// Load widgets
|
|
||||||
Bangle.loadWidgets();
|
|
||||||
|
|
||||||
draw();
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
{
|
{
|
||||||
"id": "bowserWF",
|
"id": "bowserWF",
|
||||||
"name": "Bowser Watchface",
|
"name": "Bowser Watchface",
|
||||||
"shortName":"Bowser Watchface",
|
"shortName": "Bowser Watchface",
|
||||||
"version":"0.02",
|
"version": "0.03",
|
||||||
"description": "Let bowser show you the time",
|
"description": "Let bowser show you the time",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"supports" : ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"bowserWF.app.js","url":"app.js"},
|
{ "name": "bowserWF.app.js", "url": "app.js" },
|
||||||
{"name":"bowserWF.img","url":"app-icon.js","evaluate":true}
|
{ "name": "bowserWF.img", "url": "app-icon.js", "evaluate": true }
|
||||||
],
|
],
|
||||||
"data": [{"name":"bowserWF.json"}]
|
"data": [{ "name": "bowserWF.json" }]
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,3 +22,10 @@
|
||||||
Restructure the settings menu
|
Restructure the settings menu
|
||||||
0.08: Allow scanning for devices in settings
|
0.08: Allow scanning for devices in settings
|
||||||
0.09: Misc Fixes and improvements (https://github.com/espruino/BangleApps/pull/1655)
|
0.09: Misc Fixes and improvements (https://github.com/espruino/BangleApps/pull/1655)
|
||||||
|
0.10: Use default Bangle formatter for booleans
|
||||||
|
0.11: App now shows status info while connecting
|
||||||
|
Fixes to allow cached BluetoothRemoteGATTCharacteristic to work with 2v14.14 onwards (>1 central)
|
||||||
|
0.12: Fix HRM fallback handling
|
||||||
|
Use default boolean formatter in custom menu and directly apply config if useful
|
||||||
|
Allow recording unmodified internal HR
|
||||||
|
Better connection retry handling
|
||||||
|
|