Merge branch 'espruino:master' into master
|
@ -2,3 +2,4 @@
|
||||||
0.02: Fix the settings bug and some tweaking
|
0.02: Fix the settings bug and some tweaking
|
||||||
0.03: Do not alarm while charging
|
0.03: Do not alarm while charging
|
||||||
0.04: Obey system quiet mode
|
0.04: Obey system quiet mode
|
||||||
|
0.05: Battery optimisation, add the pause option, bug fixes
|
||||||
|
|
|
@ -7,8 +7,8 @@ Different settings can be personalized:
|
||||||
- Enable : Enable/Disable the app
|
- Enable : Enable/Disable the app
|
||||||
- Start hour: Hour to start the reminder
|
- Start hour: Hour to start the reminder
|
||||||
- End hour: Hour to end the reminder
|
- End hour: Hour to end the reminder
|
||||||
- Max inactivity: Maximum inactivity time to allow before the alert. From 15 to 60 min
|
- Max inactivity: Maximum inactivity time to allow before the alert. From 15 to 120 min
|
||||||
- Dismiss delay: Delay added before the next alert if the alert is dismissed. From 5 to 60 min
|
- Dismiss delay: Delay added before the next alert if the alert is dismissed. From 5 to 60 min
|
||||||
Notice: If Dissmiss delay > Max inactivity then it will be equal Max inactivity
|
- Pause delay: Same as Dismiss delay but longer (usefull for meetings and such). From 30 to 240 min
|
||||||
- Min steps: Minimal amount of steps to count as an activity
|
- Min steps: Minimal amount of steps to count as an activity
|
||||||
|
|
||||||
|
|
|
@ -1,40 +1,42 @@
|
||||||
function drawAlert(){
|
function drawAlert() {
|
||||||
E.showPrompt("Inactivity detected",{
|
E.showPrompt("Inactivity detected", {
|
||||||
title:"Activity reminder",
|
title: "Activity reminder",
|
||||||
buttons : {"Ok": true,"Dismiss": false}
|
buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 }
|
||||||
}).then(function(v) {
|
}).then(function (v) {
|
||||||
if(v == true){
|
if (v == 1) {
|
||||||
stepsArray = stepsArray.slice(0, activityreminder.maxInnactivityMin - 3);
|
activityreminder_data.okDate = new Date();
|
||||||
require("activityreminder").saveStepsArray(stepsArray);
|
}
|
||||||
}
|
if (v == 2) {
|
||||||
if(v == false){
|
activityreminder_data.dismissDate = new Date();
|
||||||
stepsArray = stepsArray.slice(0, activityreminder.maxInnactivityMin - activityreminder.dismissDelayMin);
|
}
|
||||||
require("activityreminder").saveStepsArray(stepsArray);
|
if (v == 3) {
|
||||||
}
|
activityreminder_data.pauseDate = new Date();
|
||||||
|
}
|
||||||
|
activityreminder.saveData(activityreminder_data);
|
||||||
load();
|
load();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Obey system quiet mode:
|
// Obey system quiet mode:
|
||||||
if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
if (!(storage.readJSON('setting.json', 1) || {}).quiet) {
|
||||||
Bangle.buzz(400);
|
Bangle.buzz(400);
|
||||||
}
|
}
|
||||||
setTimeout(load, 20000);
|
setTimeout(load, 20000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function run(){
|
function run() {
|
||||||
if(stepsArray.length == activityreminder.maxInnactivityMin){
|
if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) {
|
||||||
if (stepsArray[0] - stepsArray[stepsArray.length-1] < activityreminder.minSteps){
|
drawAlert();
|
||||||
drawAlert();
|
} else {
|
||||||
}
|
eval(storage.read("activityreminder.settings.js"))(() => load());
|
||||||
}else{
|
}
|
||||||
eval(require("Storage").read("activityreminder.settings.js"))(()=>load());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const activityreminder = require("activityreminder");
|
||||||
|
const storage = require("Storage");
|
||||||
g.clear();
|
g.clear();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
activityreminder = require("activityreminder").loadSettings();
|
const activityreminder_settings = activityreminder.loadSettings();
|
||||||
stepsArray = require("activityreminder").loadStepsArray();
|
const activityreminder_data = activityreminder.loadData();
|
||||||
run();
|
run();
|
||||||
|
|
|
@ -1,30 +1,45 @@
|
||||||
function run(){
|
function run() {
|
||||||
if (Bangle.isCharging()) return;
|
if (isNotWorn()) return;
|
||||||
var now = new Date();
|
let now = new Date();
|
||||||
var h = now.getHours();
|
let h = now.getHours();
|
||||||
if(h >= activityreminder.startHour && h < activityreminder.endHour){
|
let health = Bangle.getHealthStatus("day");
|
||||||
var health = Bangle.getHealthStatus("day");
|
|
||||||
stepsArray.unshift(health.steps);
|
if (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour) {
|
||||||
stepsArray = stepsArray.slice(0, activityreminder.maxInnactivityMin);
|
if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed
|
||||||
require("activityreminder").saveStepsArray(stepsArray);
|
|| health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch
|
||||||
}
|
activityreminder_data.stepsOnDate = health.steps;
|
||||||
else{
|
activityreminder_data.stepsDate = now;
|
||||||
if(stepsArray != []){
|
activityreminder.saveData(activityreminder_data);
|
||||||
stepsArray = [];
|
/* todo in a futur release
|
||||||
require("activityreminder").saveStepsArray(stepsArray);
|
add settimer to trigger like 10 secs after the stepsDate + minSteps
|
||||||
|
cancel all other timers of this app
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if(stepsArray.length >= activityreminder.maxInnactivityMin){
|
if(activityreminder.mustAlert(activityreminder_data, activityreminder_settings)){
|
||||||
if (stepsArray[0] - stepsArray[stepsArray.length-1] < activityreminder.minSteps){
|
|
||||||
load('activityreminder.app.js');
|
load('activityreminder.app.js');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isNotWorn() {
|
||||||
|
// todo in a futur release check temperature and mouvement in a futur release
|
||||||
|
return Bangle.isCharging();
|
||||||
|
}
|
||||||
|
|
||||||
activityreminder = require("activityreminder").loadSettings();
|
const activityreminder = require("activityreminder");
|
||||||
if(activityreminder.enabled) {
|
const activityreminder_settings = activityreminder.loadSettings();
|
||||||
stepsArray = require("activityreminder").loadStepsArray();
|
if (activityreminder_settings.enabled) {
|
||||||
|
const activityreminder_data = activityreminder.loadData();
|
||||||
|
if(activityreminder_data.firstLoad){
|
||||||
|
activityreminder_data.firstLoad =false;
|
||||||
|
activityreminder.saveData(activityreminder_data);
|
||||||
|
}
|
||||||
setInterval(run, 60000);
|
setInterval(run, 60000);
|
||||||
|
/* todo in a futur release
|
||||||
|
increase setInterval time to something that is still sensible (5 mins ?)
|
||||||
|
add settimer to trigger like 10 secs after the stepsDate + minSteps
|
||||||
|
cancel all other timers of this app
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,57 @@
|
||||||
exports.loadSettings = function() {
|
const storage = require("Storage");
|
||||||
|
|
||||||
|
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,
|
||||||
minSteps: 50
|
minSteps: 50
|
||||||
}, require("Storage").readJSON("activityreminder.s.json", true) || {});
|
}, storage.readJSON("activityreminder.s.json", true) || {});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.writeSettings = function(settings){
|
exports.writeSettings = function (settings) {
|
||||||
require("Storage").writeJSON("activityreminder.s.json", settings);
|
storage.writeJSON("activityreminder.s.json", settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.saveStepsArray = function(stepsArray) {
|
exports.saveData = function (data) {
|
||||||
require("Storage").writeJSON("activityreminder.sa.json", stepsArray);
|
storage.writeJSON("activityreminder.data.json", data);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.loadStepsArray = function(){
|
exports.loadData = function () {
|
||||||
return require("Storage").readJSON("activityreminder.sa.json") || [];
|
let health = Bangle.getHealthStatus("day");
|
||||||
};
|
const data = Object.assign({
|
||||||
|
firstLoad: true,
|
||||||
|
stepsDate: new Date(),
|
||||||
|
stepsOnDate: health.steps,
|
||||||
|
okDate: new Date(1970),
|
||||||
|
dismissDate: new Date(1970),
|
||||||
|
pauseDate: new Date(1970),
|
||||||
|
},
|
||||||
|
storage.readJSON("activityreminder.data.json") || {});
|
||||||
|
|
||||||
|
if(typeof(data.stepsDate) == "string")
|
||||||
|
data.stepsDate = new Date(data.stepsDate);
|
||||||
|
if(typeof(data.okDate) == "string")
|
||||||
|
data.okDate = new Date(data.okDate);
|
||||||
|
if(typeof(data.dismissDate) == "string")
|
||||||
|
data.dismissDate = new Date(data.dismissDate);
|
||||||
|
if(typeof(data.pauseDate) == "string")
|
||||||
|
data.pauseDate = new Date(data.pauseDate);
|
||||||
|
|
||||||
|
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.04",
|
"version":"0.05",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "tool,activity",
|
"tags": "tool,activity",
|
||||||
|
@ -18,6 +18,6 @@
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
{"name": "activityreminder.s.json"},
|
{"name": "activityreminder.s.json"},
|
||||||
{"name": "activityreminder.sa.json"}
|
{"name": "activityreminder.data.json"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,76 @@
|
||||||
(function(back) {
|
(function (back) {
|
||||||
// Load settings
|
// Load settings
|
||||||
var settings = require("activityreminder").loadSettings();
|
const activityreminder = require("activityreminder");
|
||||||
|
const settings = activityreminder.loadSettings();
|
||||||
|
|
||||||
// Show the menu
|
// Show the menu
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"" : { "title" : "Activity Reminder" },
|
"": { "title": "Activity Reminder" },
|
||||||
"< Back" : () => back(),
|
"< Back": () => back(),
|
||||||
'Enable': {
|
'Enable': {
|
||||||
value: settings.enabled,
|
value: settings.enabled,
|
||||||
format: v => v?"Yes":"No",
|
format: v => v ? "Yes" : "No",
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.enabled = v;
|
settings.enabled = v;
|
||||||
require("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);
|
||||||
},
|
},
|
||||||
'Start hour': {
|
format: x => {
|
||||||
value: settings.startHour,
|
return x + " min";
|
||||||
min: 0, max: 24,
|
}
|
||||||
onchange: v => {
|
},
|
||||||
settings.startHour = v;
|
'Dismiss delay': {
|
||||||
require("activityreminder").writeSettings(settings);
|
value: settings.dismissDelayMin,
|
||||||
}
|
min: 5, max: 60,
|
||||||
},
|
onchange: v => {
|
||||||
'End hour': {
|
settings.dismissDelayMin = v;
|
||||||
value: settings.endHour,
|
activityreminder.writeSettings(settings);
|
||||||
min: 0, max: 24,
|
},
|
||||||
onchange: v => {
|
format: x => {
|
||||||
settings.endHour = v;
|
return x + " min";
|
||||||
require("activityreminder").writeSettings(settings);
|
}
|
||||||
}
|
},
|
||||||
},
|
'Pause delay': {
|
||||||
'Max inactivity': {
|
value: settings.pauseDelayMin,
|
||||||
value: settings.maxInnactivityMin,
|
min: 30, max: 240,
|
||||||
min: 15, max: 120,
|
onchange: v => {
|
||||||
onchange: v => {
|
settings.pauseDelayMin = v;
|
||||||
settings.maxInnactivityMin = v;
|
activityreminder.writeSettings(settings);
|
||||||
require("activityreminder").writeSettings(settings);
|
},
|
||||||
},
|
format: x => {
|
||||||
format: x => {
|
return x + " min";
|
||||||
return x + " min";
|
}
|
||||||
}
|
},
|
||||||
},
|
'Min steps': {
|
||||||
'Dismiss delay': {
|
value: settings.minSteps,
|
||||||
value: settings.dismissDelayMin,
|
min: 10, max: 500,
|
||||||
min: 5, max: 60,
|
onchange: v => {
|
||||||
onchange: v => {
|
settings.minSteps = v;
|
||||||
settings.dismissDelayMin = v;
|
activityreminder.writeSettings(settings);
|
||||||
require("activityreminder").writeSettings(settings);
|
}
|
||||||
},
|
}
|
||||||
format: x => {
|
|
||||||
return x + " min";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Min steps': {
|
|
||||||
value: settings.minSteps,
|
|
||||||
min: 10, max: 500,
|
|
||||||
onchange: v => {
|
|
||||||
settings.minSteps = v;
|
|
||||||
require("activityreminder").writeSettings(settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,3 +26,5 @@
|
||||||
Add "Enable All", "Disable All" and "Remove All" actions
|
Add "Enable All", "Disable All" and "Remove All" actions
|
||||||
0.25: Fix redrawing selected Alarm/Timer entry inside edit submenu
|
0.25: Fix redrawing selected Alarm/Timer entry inside edit submenu
|
||||||
0.26: Add support for Monday as first day of the week (#1780)
|
0.26: Add support for Monday as first day of the week (#1780)
|
||||||
|
0.27: New UI!
|
||||||
|
0.28: Fix bug with alarms not firing when configured to fire only once
|
||||||
|
|
|
@ -1,7 +1,31 @@
|
||||||
Alarms & Timers
|
# Alarms & Timers
|
||||||
===============
|
|
||||||
|
|
||||||
This app allows you to add/modify any alarms and timers.
|
This app allows you to add/modify any alarms and timers.
|
||||||
|
|
||||||
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched)
|
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
||||||
to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
|
||||||
|
## Menu overview
|
||||||
|
|
||||||
|
- `New...`
|
||||||
|
- `New Alarm` → Configure a new alarm
|
||||||
|
- `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely)
|
||||||
|
- `New Timer` → Configure a new timer
|
||||||
|
- `Advanced`
|
||||||
|
- `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details
|
||||||
|
- `Enable All` → Enable _all_ disabled alarms & timers
|
||||||
|
- `Disable All` → Disable _all_ enabled alarms & timers
|
||||||
|
- `Delete All` → Delete _all_ alarms & timers
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
|
||||||
|
- [Gordon Williams](https://github.com/gfwilliams)
|
||||||
|
|
||||||
|
## Main Contributors
|
||||||
|
|
||||||
|
- [Alessandro Cocco](https://github.com/alessandrococco) - New UI, full rewrite, new features
|
||||||
|
- [Sabin Iacob](https://github.com/m0n5t3r) - Auto snooze support
|
||||||
|
- [storm64](https://github.com/storm64) - Fix redrawing in submenus
|
||||||
|
|
||||||
|
## Attributions
|
||||||
|
|
||||||
|
All icons used in this app are from [icons8](https://icons8.com).
|
||||||
|
|
|
@ -1,20 +1,160 @@
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
// 0 = Sunday (default), 1 = Monday
|
||||||
|
const firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0;
|
||||||
|
const WORKDAYS = 62
|
||||||
|
const WEEKEND = firstDayOfWeek ? 192 : 65;
|
||||||
|
const EVERY_DAY = firstDayOfWeek ? 254 : 127;
|
||||||
|
|
||||||
|
const iconAlarmOn = "\0" + atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/n+B/n+B/h+B/4+A/+8A//8Af/4AP/wAH/gAB+AAAAAAAAAA==");
|
||||||
|
const iconAlarmOff = "\0" + (g.theme.dark
|
||||||
|
? atob("GBjBAP////8AAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg=")
|
||||||
|
: atob("GBjBAP//AAAAAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg="));
|
||||||
|
|
||||||
|
const iconTimerOn = "\0" + (g.theme.dark
|
||||||
|
? atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA=")
|
||||||
|
: atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA="));
|
||||||
|
const iconTimerOff = "\0" + (g.theme.dark
|
||||||
|
? atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg=")
|
||||||
|
: atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg="));
|
||||||
|
|
||||||
// An array of alarm objects (see sched/README.md)
|
// An array of alarm objects (see sched/README.md)
|
||||||
var alarms = require("sched").getAlarms();
|
var alarms = require("sched").getAlarms();
|
||||||
|
|
||||||
// 0 = Sunday
|
function handleFirstDayOfWeek(dow) {
|
||||||
// 1 = Monday
|
if (firstDayOfWeek == 1) {
|
||||||
var firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0;
|
if ((dow & 1) == 1) {
|
||||||
|
// In the scheduler API Sunday is 1.
|
||||||
|
// Here the week starts on Monday and Sunday is ON so
|
||||||
|
// when I read the dow I need to move Sunday to 128...
|
||||||
|
dow += 127;
|
||||||
|
} else if ((dow & 128) == 128) {
|
||||||
|
// ... and then when I write the dow I need to move Sunday back to 1.
|
||||||
|
dow -= 127;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dow;
|
||||||
|
}
|
||||||
|
|
||||||
function getCurrentTime() {
|
// Check the first day of week and update the dow field accordingly.
|
||||||
var time = new Date();
|
alarms.forEach(alarm => alarm.dow = handleFirstDayOfWeek(alarm.dow));
|
||||||
return (
|
|
||||||
time.getHours() * 3600000 +
|
function showMainMenu() {
|
||||||
time.getMinutes() * 60000 +
|
const menu = {
|
||||||
time.getSeconds() * 1000
|
"": { "title": /*LANG*/"Alarms & Timers" },
|
||||||
);
|
"< Back": () => load(),
|
||||||
|
/*LANG*/"New...": () => showNewMenu()
|
||||||
|
};
|
||||||
|
|
||||||
|
alarms.forEach((e, index) => {
|
||||||
|
var label = e.timer
|
||||||
|
? require("time_utils").formatDuration(e.timer)
|
||||||
|
: require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeDOW(e)}` : "");
|
||||||
|
menu[label] = {
|
||||||
|
value: e.on ? (e.timer ? iconTimerOn : iconAlarmOn) : (e.timer ? iconTimerOff : iconAlarmOff),
|
||||||
|
onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
menu[/*LANG*/"Advanced"] = () => showAdvancedMenu();
|
||||||
|
|
||||||
|
E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNewMenu() {
|
||||||
|
E.showMenu({
|
||||||
|
"": { "title": /*LANG*/"New..." },
|
||||||
|
"< Back": () => showMainMenu(),
|
||||||
|
/*LANG*/"Alarm": () => showEditAlarmMenu(undefined, undefined),
|
||||||
|
/*LANG*/"Timer": () => showEditTimerMenu(undefined, undefined)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
|
var isNew = alarmIndex === undefined;
|
||||||
|
|
||||||
|
var alarm = require("sched").newDefaultAlarm();
|
||||||
|
alarm.dow = handleFirstDayOfWeek(alarm.dow);
|
||||||
|
|
||||||
|
if (selectedAlarm) {
|
||||||
|
Object.assign(alarm, selectedAlarm);
|
||||||
|
}
|
||||||
|
|
||||||
|
var time = require("time_utils").decodeTime(alarm.t);
|
||||||
|
|
||||||
|
const menu = {
|
||||||
|
"": { "title": isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm" },
|
||||||
|
"< Back": () => {
|
||||||
|
saveAlarm(alarm, alarmIndex, time);
|
||||||
|
showMainMenu();
|
||||||
|
},
|
||||||
|
/*LANG*/"Hour": {
|
||||||
|
value: time.h,
|
||||||
|
format: v => ("0" + v).substr(-2),
|
||||||
|
min: 0,
|
||||||
|
max: 23,
|
||||||
|
wrap: true,
|
||||||
|
onchange: v => time.h = v
|
||||||
|
},
|
||||||
|
/*LANG*/"Minute": {
|
||||||
|
value: time.m,
|
||||||
|
format: v => ("0" + v).substr(-2),
|
||||||
|
min: 0,
|
||||||
|
max: 59,
|
||||||
|
wrap: true,
|
||||||
|
onchange: v => time.m = v
|
||||||
|
},
|
||||||
|
/*LANG*/"Enabled": {
|
||||||
|
value: alarm.on,
|
||||||
|
onchange: v => alarm.on = v
|
||||||
|
},
|
||||||
|
/*LANG*/"Repeat": {
|
||||||
|
value: decodeDOW(alarm),
|
||||||
|
onchange: () => setTimeout(showEditRepeatMenu, 100, alarm.rp, alarm.dow, (repeat, dow) => {
|
||||||
|
alarm.rp = repeat;
|
||||||
|
alarm.dow = dow;
|
||||||
|
alarm.t = require("time_utils").encodeTime(time);
|
||||||
|
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/*LANG*/"Vibrate": require("buzz_menu").pattern(alarm.vibrate, v => alarm.vibrate = v),
|
||||||
|
/*LANG*/"Auto Snooze": {
|
||||||
|
value: alarm.as,
|
||||||
|
onchange: v => alarm.as = v
|
||||||
|
},
|
||||||
|
/*LANG*/"Cancel": () => showMainMenu()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isNew) {
|
||||||
|
menu[/*LANG*/"Delete"] = () => {
|
||||||
|
E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Alarm" }).then((confirm) => {
|
||||||
|
if (confirm) {
|
||||||
|
alarms.splice(alarmIndex, 1);
|
||||||
|
saveAndReload();
|
||||||
|
showMainMenu();
|
||||||
|
} else {
|
||||||
|
alarm.t = require("time_utils").encodeTime(time);
|
||||||
|
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAlarm(alarm, alarmIndex, time) {
|
||||||
|
alarm.t = require("time_utils").encodeTime(time);
|
||||||
|
alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0;
|
||||||
|
|
||||||
|
if (alarmIndex === undefined) {
|
||||||
|
alarms.push(alarm);
|
||||||
|
} else {
|
||||||
|
alarms[alarmIndex] = alarm;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAndReload();
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveAndReload() {
|
function saveAndReload() {
|
||||||
|
@ -23,249 +163,196 @@ function saveAndReload() {
|
||||||
|
|
||||||
require("sched").setAlarms(alarms);
|
require("sched").setAlarms(alarms);
|
||||||
require("sched").reload();
|
require("sched").reload();
|
||||||
|
|
||||||
|
// Fix after save
|
||||||
|
alarms.forEach(a => a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek));
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMainMenu() {
|
function decodeDOW(alarm) {
|
||||||
// Timer img "\0"+atob("DhKBAP////MDDAwwMGGBzgPwB4AeAPwHOBhgwMMzDez////w")
|
return alarm.rp
|
||||||
// Alarm img "\0"+atob("FBSBAABgA4YcMPDGP8Zn/mx/48//PP/zD/8A//AP/wD/8A//AP/wH/+D//w//8AAAADwAAYA")
|
? require("date_utils")
|
||||||
const menu = {
|
.dows(firstDayOfWeek, 2)
|
||||||
'': { 'title': /*LANG*/'Alarms&Timers' },
|
.map((day, index) => alarm.dow & (1 << (index + firstDayOfWeek)) ? day : "_")
|
||||||
/*LANG*/'< Back': () => { load(); },
|
.join("")
|
||||||
/*LANG*/'New Alarm': () => editAlarm(-1),
|
.toLowerCase()
|
||||||
/*LANG*/'New Timer': () => editTimer(-1)
|
: "Once"
|
||||||
};
|
}
|
||||||
alarms.forEach((alarm, idx) => {
|
|
||||||
alarm.dow = handleFirstDayOfWeek(alarm.dow, firstDayOfWeek);
|
|
||||||
|
|
||||||
var type, txt; // a leading space is currently required (JS error in Espruino 2v12)
|
function showEditRepeatMenu(repeat, dow, dowChangeCallback) {
|
||||||
if (alarm.timer) {
|
var originalRepeat = repeat;
|
||||||
type = /*LANG*/"Timer";
|
var originalDow = dow;
|
||||||
txt = " " + require("sched").formatTime(alarm.timer);
|
var isCustom = repeat && dow != WORKDAYS && dow != WEEKEND && dow != EVERY_DAY;
|
||||||
} else {
|
|
||||||
type = /*LANG*/"Alarm";
|
const menu = {
|
||||||
txt = " " + require("sched").formatTime(alarm.t);
|
"": { "title": /*LANG*/"Repeat Alarm" },
|
||||||
|
"< Back": () => dowChangeCallback(repeat, dow),
|
||||||
|
/*LANG*/"Once": {
|
||||||
|
// The alarm will fire once. Internally it will be saved
|
||||||
|
// as "fire every days" BUT the repeat flag is false so
|
||||||
|
// we avoid messing up with the scheduler.
|
||||||
|
value: !repeat,
|
||||||
|
onchange: () => dowChangeCallback(false, EVERY_DAY)
|
||||||
|
},
|
||||||
|
/*LANG*/"Workdays": {
|
||||||
|
value: repeat && dow == WORKDAYS,
|
||||||
|
onchange: () => dowChangeCallback(true, WORKDAYS)
|
||||||
|
},
|
||||||
|
/*LANG*/"Weekends": {
|
||||||
|
value: repeat && dow == WEEKEND,
|
||||||
|
onchange: () => dowChangeCallback(true, WEEKEND)
|
||||||
|
},
|
||||||
|
/*LANG*/"Every Day": {
|
||||||
|
value: repeat && dow == EVERY_DAY,
|
||||||
|
onchange: () => dowChangeCallback(true, EVERY_DAY)
|
||||||
|
},
|
||||||
|
/*LANG*/"Custom": {
|
||||||
|
value: isCustom ? decodeDOW({ rp: true, dow: dow }) : false,
|
||||||
|
onchange: () => setTimeout(showCustomDaysMenu, 10, isCustom ? dow : EVERY_DAY, dowChangeCallback, originalRepeat, originalDow)
|
||||||
}
|
}
|
||||||
if (alarm.rp) txt += "\0" + atob("FBaBAAABgAAcAAHn//////wAHsABzAAYwAAMAADAAAAAAwAAMAADGAAzgAN4AD//////54AAOAABgAA=");
|
};
|
||||||
// rename duplicate alarms
|
|
||||||
if (menu[type + txt]) {
|
|
||||||
var n = 2;
|
|
||||||
while (menu[type + " " + n + txt]) n++;
|
|
||||||
txt = type + " " + n + txt;
|
|
||||||
} else txt = type + txt;
|
|
||||||
// add to menu
|
|
||||||
menu[txt] = {
|
|
||||||
value: "\0" + atob(alarm.on ? "EhKBAH//v/////////////5//x//j//H+eP+Mf/A//h//z//////////3//g" : "EhKBAH//v//8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA///3//g"),
|
|
||||||
onchange: function () {
|
|
||||||
setTimeout(alarm.timer ? editTimer : editAlarm, 10, idx, alarm);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (alarms.some(e => !e.on)) {
|
E.showMenu(menu);
|
||||||
menu[/*LANG*/"Enable All"] = () => enableAll(true);
|
|
||||||
}
|
|
||||||
if (alarms.some(e => e.on)) {
|
|
||||||
menu[/*LANG*/"Disable All"] = () => enableAll(false);
|
|
||||||
}
|
|
||||||
if (alarms.length > 0) {
|
|
||||||
menu[/*LANG*/"Delete All"] = () => deleteAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WIDGETS["alarm"]) WIDGETS["alarm"].reload();
|
|
||||||
return E.showMenu(menu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function editDOW(dow, onchange) {
|
function showCustomDaysMenu(dow, dowChangeCallback, originalRepeat, originalDow) {
|
||||||
const menu = {
|
const menu = {
|
||||||
'': { 'title': /*LANG*/'Days of Week' },
|
"": { "title": /*LANG*/"Custom Days" },
|
||||||
/*LANG*/'< Back': () => onchange(dow)
|
"< Back": () => {
|
||||||
|
// If the user unchecks all the days then we assume repeat = once
|
||||||
|
// and we force the dow to every day.
|
||||||
|
var repeat = dow > 0;
|
||||||
|
dowChangeCallback(repeat, repeat ? dow : EVERY_DAY)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
require("date_utils").dows(firstDayOfWeek).forEach((day, i) => {
|
require("date_utils").dows(firstDayOfWeek).forEach((day, i) => {
|
||||||
menu[day] = {
|
menu[day] = {
|
||||||
value: !!(dow & (1 << (i + firstDayOfWeek))),
|
value: !!(dow & (1 << (i + firstDayOfWeek))),
|
||||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
|
||||||
onchange: v => v ? (dow |= 1 << (i + firstDayOfWeek)) : (dow &= ~(1 << (i + firstDayOfWeek)))
|
onchange: v => v ? (dow |= 1 << (i + firstDayOfWeek)) : (dow &= ~(1 << (i + firstDayOfWeek)))
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
menu[/*LANG*/"Cancel"] = () => setTimeout(showEditRepeatMenu, 10, originalRepeat, originalDow, dowChangeCallback)
|
||||||
|
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function editAlarm(alarmIndex, alarm) {
|
function showEditTimerMenu(selectedTimer, timerIndex) {
|
||||||
var newAlarm = alarmIndex < 0;
|
var isNew = timerIndex === undefined;
|
||||||
var a = require("sched").newDefaultAlarm();
|
|
||||||
a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek);
|
|
||||||
|
|
||||||
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
var timer = require("sched").newDefaultTimer();
|
||||||
if (alarm) Object.assign(a, alarm);
|
|
||||||
var t = require("sched").decodeTime(a.t);
|
if (selectedTimer) {
|
||||||
|
Object.assign(timer, selectedTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
var time = require("time_utils").decodeTime(timer.timer);
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
'': { 'title': /*LANG*/'Alarm' },
|
"": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" },
|
||||||
/*LANG*/'< Back': () => {
|
"< Back": () => {
|
||||||
saveAlarm(newAlarm, alarmIndex, a, t);
|
saveTimer(timer, timerIndex, time);
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
},
|
},
|
||||||
/*LANG*/'Hours': {
|
/*LANG*/"Hours": {
|
||||||
value: t.hrs, min: 0, max: 23, wrap: true,
|
value: time.h,
|
||||||
onchange: v => t.hrs = v
|
min: 0,
|
||||||
|
max: 23,
|
||||||
|
wrap: true,
|
||||||
|
onchange: v => time.h = v
|
||||||
},
|
},
|
||||||
/*LANG*/'Minutes': {
|
/*LANG*/"Minutes": {
|
||||||
value: t.mins, min: 0, max: 59, wrap: true,
|
value: time.m,
|
||||||
onchange: v => t.mins = v
|
min: 0,
|
||||||
|
max: 59,
|
||||||
|
wrap: true,
|
||||||
|
onchange: v => time.m = v
|
||||||
},
|
},
|
||||||
/*LANG*/'Enabled': {
|
/*LANG*/"Enabled": {
|
||||||
value: a.on,
|
value: timer.on,
|
||||||
format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
|
onchange: v => timer.on = v
|
||||||
onchange: v => a.on = v
|
|
||||||
},
|
},
|
||||||
/*LANG*/'Repeat': {
|
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
|
||||||
value: a.rp,
|
|
||||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
|
||||||
onchange: v => a.rp = v
|
|
||||||
},
|
|
||||||
/*LANG*/'Days': {
|
|
||||||
value: decodeDOW(a.dow),
|
|
||||||
onchange: () => setTimeout(editDOW, 100, a.dow, d => {
|
|
||||||
a.dow = d;
|
|
||||||
a.t = require("sched").encodeTime(t);
|
|
||||||
editAlarm(alarmIndex, a);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate = v),
|
|
||||||
/*LANG*/'Auto Snooze': {
|
|
||||||
value: a.as,
|
|
||||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
|
||||||
onchange: v => a.as = v
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
menu[/*LANG*/"Cancel"] = () => showMainMenu();
|
if (!isNew) {
|
||||||
|
menu[/*LANG*/"Delete"] = () => {
|
||||||
if (!newAlarm) {
|
E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Timer" }).then((confirm) => {
|
||||||
menu[/*LANG*/"Delete"] = function () {
|
if (confirm) {
|
||||||
alarms.splice(alarmIndex, 1);
|
alarms.splice(timerIndex, 1);
|
||||||
saveAndReload();
|
saveAndReload();
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
|
} else {
|
||||||
|
timer.timer = require("time_utils").encodeTime(time);
|
||||||
|
setTimeout(showEditTimerMenu, 10, timer, timerIndex)
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveAlarm(newAlarm, alarmIndex, a, t) {
|
function saveTimer(timer, timerIndex, time) {
|
||||||
a.t = require("sched").encodeTime(t);
|
timer.timer = require("time_utils").encodeTime(time);
|
||||||
a.last = (a.t < getCurrentTime()) ? (new Date()).getDate() : 0;
|
timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer;
|
||||||
|
timer.last = 0;
|
||||||
|
|
||||||
if (newAlarm) {
|
if (timerIndex === undefined) {
|
||||||
alarms.push(a);
|
alarms.push(timer);
|
||||||
} else {
|
} else {
|
||||||
alarms[alarmIndex] = a;
|
alarms[timerIndex] = timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAndReload();
|
saveAndReload();
|
||||||
}
|
}
|
||||||
|
|
||||||
function editTimer(alarmIndex, alarm) {
|
function showAdvancedMenu() {
|
||||||
var newAlarm = alarmIndex < 0;
|
E.showMenu({
|
||||||
var a = require("sched").newDefaultTimer();
|
"": { "title": /*LANG*/"Advanced" },
|
||||||
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
"< Back": () => showMainMenu(),
|
||||||
if (alarm) Object.assign(a, alarm);
|
/*LANG*/"Scheduler Settings": () => eval(require("Storage").read("sched.settings.js"))(() => showAdvancedMenu()),
|
||||||
var t = require("sched").decodeTime(a.timer);
|
/*LANG*/"Enable All": () => enableAll(true),
|
||||||
|
/*LANG*/"Disable All": () => enableAll(false),
|
||||||
const menu = {
|
/*LANG*/"Delete All": () => deleteAll()
|
||||||
'': { 'title': /*LANG*/'Timer' },
|
});
|
||||||
/*LANG*/'< Back': () => {
|
|
||||||
saveTimer(newAlarm, alarmIndex, a, t);
|
|
||||||
showMainMenu();
|
|
||||||
},
|
|
||||||
/*LANG*/'Hours': {
|
|
||||||
value: t.hrs, min: 0, max: 23, wrap: true,
|
|
||||||
onchange: v => t.hrs = v
|
|
||||||
},
|
|
||||||
/*LANG*/'Minutes': {
|
|
||||||
value: t.mins, min: 0, max: 59, wrap: true,
|
|
||||||
onchange: v => t.mins = v
|
|
||||||
},
|
|
||||||
/*LANG*/'Enabled': {
|
|
||||||
value: a.on,
|
|
||||||
format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
|
|
||||||
onchange: v => a.on = v
|
|
||||||
},
|
|
||||||
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate = v),
|
|
||||||
};
|
|
||||||
|
|
||||||
menu[/*LANG*/"Cancel"] = () => showMainMenu();
|
|
||||||
|
|
||||||
if (!newAlarm) {
|
|
||||||
menu[/*LANG*/"Delete"] = function () {
|
|
||||||
alarms.splice(alarmIndex, 1);
|
|
||||||
saveAndReload();
|
|
||||||
showMainMenu();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return E.showMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveTimer(newAlarm, alarmIndex, a, t) {
|
|
||||||
a.timer = require("sched").encodeTime(t);
|
|
||||||
a.t = getCurrentTime() + a.timer;
|
|
||||||
a.last = 0;
|
|
||||||
|
|
||||||
if (newAlarm) {
|
|
||||||
alarms.push(a);
|
|
||||||
} else {
|
|
||||||
alarms[alarmIndex] = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveAndReload();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFirstDayOfWeek(dow, firstDayOfWeek) {
|
|
||||||
if (firstDayOfWeek == 1) {
|
|
||||||
if ((dow & 1) == 1) {
|
|
||||||
// By default 1 = Sunday.
|
|
||||||
// Here the week starts on Monday and Sunday is ON so move Sunday to 128.
|
|
||||||
dow += 127;
|
|
||||||
} else if ((dow & 128) == 128) {
|
|
||||||
dow -= 127;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dow;
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeDOW(dow) {
|
|
||||||
return require("date_utils")
|
|
||||||
.dows(firstDayOfWeek, 2)
|
|
||||||
.map((day, index) => dow & (1 << (index + firstDayOfWeek)) ? day : "_")
|
|
||||||
.join("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableAll(on) {
|
function enableAll(on) {
|
||||||
E.showPrompt(/*LANG*/"Are you sure?", {
|
if (alarms.filter(e => e.on == !on).length == 0) {
|
||||||
title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All"
|
E.showAlert(
|
||||||
}).then((confirm) => {
|
on ? /*LANG*/"Nothing to Enable" : /*LANG*/"Nothing to Disable",
|
||||||
if (confirm) {
|
on ? /*LANG*/"Enable All" : /*LANG*/"Disable All"
|
||||||
alarms.forEach(alarm => alarm.on = on);
|
).then(() => showAdvancedMenu());
|
||||||
saveAndReload();
|
} else {
|
||||||
}
|
E.showPrompt(/*LANG*/"Are you sure?", { title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All" }).then((confirm) => {
|
||||||
|
if (confirm) {
|
||||||
showMainMenu();
|
alarms.forEach(alarm => alarm.on = on);
|
||||||
});
|
saveAndReload();
|
||||||
|
showMainMenu();
|
||||||
|
} else {
|
||||||
|
showAdvancedMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteAll() {
|
function deleteAll() {
|
||||||
E.showPrompt(/*LANG*/"Are you sure?", {
|
if (alarms.length == 0) {
|
||||||
title: /*LANG*/"Delete All"
|
E.showAlert(/*LANG*/"Nothing to delete", /*LANG*/"Delete All").then(() => showAdvancedMenu());
|
||||||
}).then((confirm) => {
|
} else {
|
||||||
if (confirm) {
|
E.showPrompt(/*LANG*/"Are you sure?", {
|
||||||
alarms = [];
|
title: /*LANG*/"Delete All"
|
||||||
saveAndReload();
|
}).then((confirm) => {
|
||||||
}
|
if (confirm) {
|
||||||
|
alarms = [];
|
||||||
showMainMenu();
|
saveAndReload();
|
||||||
});
|
showMainMenu();
|
||||||
|
} else {
|
||||||
|
showAdvancedMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
|
|
|
@ -2,16 +2,29 @@
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Alarms & Timers",
|
"name": "Alarms & Timers",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.26",
|
"version": "0.28",
|
||||||
"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",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": [ "BANGLEJS", "BANGLEJS2" ],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"dependencies": {"scheduler":"type"},
|
"dependencies": { "scheduler":"type" },
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"alarm.app.js","url":"app.js"},
|
{ "name": "alarm.app.js", "url": "app.js" },
|
||||||
{"name":"alarm.img","url":"app-icon.js","evaluate":true},
|
{ "name": "alarm.img", "url": "app-icon.js", "evaluate": true },
|
||||||
{"name":"alarm.wid.js","url":"widget.js"}
|
{ "name": "alarm.wid.js", "url": "widget.js" }
|
||||||
|
],
|
||||||
|
"screenshots": [
|
||||||
|
{ "url": "screenshot-1.png" },
|
||||||
|
{ "url": "screenshot-2.png" },
|
||||||
|
{ "url": "screenshot-3.png" },
|
||||||
|
{ "url": "screenshot-4.png" },
|
||||||
|
{ "url": "screenshot-5.png" },
|
||||||
|
{ "url": "screenshot-6.png" },
|
||||||
|
{ "url": "screenshot-7.png" },
|
||||||
|
{ "url": "screenshot-8.png" },
|
||||||
|
{ "url": "screenshot-9.png" },
|
||||||
|
{ "url": "screenshot-10.png" },
|
||||||
|
{ "url": "screenshot-11.png" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
|
@ -7,3 +7,4 @@
|
||||||
0.06: Option to keep messages after a disconnect (default false) (fix #1186)
|
0.06: Option to keep messages after a disconnect (default false) (fix #1186)
|
||||||
0.07: Include charging state in battery updates to phone
|
0.07: Include charging state in battery updates to phone
|
||||||
0.08: Handling of alarms
|
0.08: Handling of alarms
|
||||||
|
0.09: Alarm vibration, repeat, and auto-snooze now handled by sched
|
||||||
|
|
|
@ -21,7 +21,6 @@ of Gadgetbridge - making your phone make noise so you can find it.
|
||||||
* `Keep Msgs` - default is `Off`. When Gadgetbridge disconnects, should Bangle.js
|
* `Keep Msgs` - default is `Off`. When Gadgetbridge disconnects, should Bangle.js
|
||||||
keep any messages it has received, or should it delete them?
|
keep any messages it has received, or should it delete them?
|
||||||
* `Messages` - launches the messages app, showing a list of messages
|
* `Messages` - launches the messages app, showing a list of messages
|
||||||
* `Alarms` - opens a submenu where you can set default settings for alarms such as vibration pattern, repeat, and auto snooze
|
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
|
|
|
@ -67,17 +67,13 @@
|
||||||
var dow = event.d[j].rep;
|
var dow = event.d[j].rep;
|
||||||
if (!dow) dow = 127; //if no DOW selected, set alarm to all DOW
|
if (!dow) dow = 127; //if no DOW selected, set alarm to all DOW
|
||||||
var last = (event.d[j].h * 3600000 + event.d[j].m * 60000 < currentTime) ? (new Date()).getDate() : 0;
|
var last = (event.d[j].h * 3600000 + event.d[j].m * 60000 < currentTime) ? (new Date()).getDate() : 0;
|
||||||
var a = {
|
var a = require("sched").newDefaultAlarm();
|
||||||
id : "gb"+j,
|
a.id = "gb"+j;
|
||||||
appid : "gbalarms",
|
a.appid = "gbalarms";
|
||||||
on : true,
|
a.on = true;
|
||||||
t : event.d[j].h * 3600000 + event.d[j].m * 60000,
|
a.t = event.d[j].h * 3600000 + event.d[j].m * 60000;
|
||||||
dow : ((dow&63)<<1) | (dow>>6), // Gadgetbridge sends DOW in a different format
|
a.dow = ((dow&63)<<1) | (dow>>6); // Gadgetbridge sends DOW in a different format
|
||||||
last : last,
|
a.last = last;
|
||||||
rp : settings.rp,
|
|
||||||
as : settings.as,
|
|
||||||
vibrate : settings.vibrate
|
|
||||||
};
|
|
||||||
alarms.push(a);
|
alarms.push(a);
|
||||||
}
|
}
|
||||||
sched.setAlarms(alarms);
|
sched.setAlarms(alarms);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "android",
|
"id": "android",
|
||||||
"name": "Android Integration",
|
"name": "Android Integration",
|
||||||
"shortName": "Android",
|
"shortName": "Android",
|
||||||
"version": "0.08",
|
"version": "0.09",
|
||||||
"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",
|
||||||
|
|
|
@ -25,27 +25,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/*LANG*/"Messages" : ()=>load("messages.app.js"),
|
/*LANG*/"Messages" : ()=>load("messages.app.js"),
|
||||||
/*LANG*/"Alarms" : () => E.showMenu({
|
|
||||||
"" : { "title" : /*LANG*/"Alarms" },
|
|
||||||
"< Back" : ()=>E.showMenu(mainmenu),
|
|
||||||
/*LANG*/"Vibrate": require("buzz_menu").pattern(settings.vibrate, v => {settings.vibrate = v; updateSettings();}),
|
|
||||||
/*LANG*/"Repeat": {
|
|
||||||
value: settings.rp,
|
|
||||||
format : v=>v?/*LANG*/"Yes":/*LANG*/"No",
|
|
||||||
onchange: v => {
|
|
||||||
settings.rp = v;
|
|
||||||
updateSettings();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/*LANG*/"Auto snooze": {
|
|
||||||
value: settings.as,
|
|
||||||
format : v=>v?/*LANG*/"Yes":/*LANG*/"No",
|
|
||||||
onchange: v => {
|
|
||||||
settings.as = v;
|
|
||||||
updateSettings();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
E.showMenu(mainmenu);
|
E.showMenu(mainmenu);
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,3 +7,5 @@
|
||||||
0.07: Update to use Bangle.setUI instead of setWatch
|
0.07: Update to use Bangle.setUI instead of setWatch
|
||||||
0.08: Use theme colors, Layout library
|
0.08: Use theme colors, Layout library
|
||||||
0.09: Fix time/date disappearing after fullscreen notification
|
0.09: Fix time/date disappearing after fullscreen notification
|
||||||
|
0.10: Use ClockFace library
|
||||||
|
0.11: Use ClockFace.is12Hour
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* A simple digital clock showing seconds as a bar
|
* A simple digital clock showing seconds as a bar
|
||||||
**/
|
**/
|
||||||
// Check settings for what type our clock should be
|
// Check settings for what type our clock should be
|
||||||
const is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
|
|
||||||
let locale = require("locale");
|
let locale = require("locale");
|
||||||
{ // add some more info to locale
|
{ // add some more info to locale
|
||||||
let date = new Date();
|
let date = new Date();
|
||||||
|
@ -11,13 +10,9 @@ let locale = require("locale");
|
||||||
date.setMonth(1, 3); // februari: months are zero-indexed
|
date.setMonth(1, 3); // februari: months are zero-indexed
|
||||||
const localized = locale.date(date, true);
|
const localized = locale.date(date, true);
|
||||||
locale.dayFirst = /3.*2/.test(localized);
|
locale.dayFirst = /3.*2/.test(localized);
|
||||||
|
locale.hasMeridian = (locale.meridian(date)!=="");
|
||||||
locale.hasMeridian = false;
|
|
||||||
if (typeof locale.meridian==="function") { // function does not exist if languages app is not installed
|
|
||||||
locale.hasMeridian = (locale.meridian(date)!=="");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Bangle.loadWidgets();
|
|
||||||
function renderBar(l) {
|
function renderBar(l) {
|
||||||
if (!this.fraction) {
|
if (!this.fraction) {
|
||||||
// zero-size fillRect stills draws one line of pixels, we don't want that
|
// zero-size fillRect stills draws one line of pixels, we don't want that
|
||||||
|
@ -27,35 +22,9 @@ function renderBar(l) {
|
||||||
g.fillRect(l.x, l.y, l.x+width-1, l.y+l.height-1);
|
g.fillRect(l.x, l.y, l.x+width-1, l.y+l.height-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Layout = require("Layout");
|
|
||||||
const layout = new Layout({
|
|
||||||
type: "v", c: [
|
|
||||||
{
|
|
||||||
type: "h", c: [
|
|
||||||
{id: "time", label: "88:88", type: "txt", font: "6x8:5", bgCol: g.theme.bg}, // size updated below
|
|
||||||
{id: "ampm", label: " ", type: "txt", font: "6x8:2", bgCol: g.theme.bg},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
|
||||||
{height: 40},
|
|
||||||
{id: "date", type: "txt", font: "10%", valign: 1},
|
|
||||||
],
|
|
||||||
}, {lazy: true});
|
|
||||||
// adjustments based on screen size and whether we display am/pm
|
|
||||||
let thickness; // bar thickness, same as time font "pixel block" size
|
|
||||||
if (is12Hour) {
|
|
||||||
// Maximum font size = (<screen width> - <ampm: 2chars * (2*6)px>) / (5chars * 6px)
|
|
||||||
thickness = Math.floor((g.getWidth()-24)/(5*6));
|
|
||||||
} else {
|
|
||||||
layout.ampm.label = "";
|
|
||||||
thickness = Math.floor(g.getWidth()/(5*6));
|
|
||||||
}
|
|
||||||
layout.bar.height = thickness+1;
|
|
||||||
layout.time.font = "6x8:"+thickness;
|
|
||||||
layout.update();
|
|
||||||
|
|
||||||
function timeText(date) {
|
function timeText(date) {
|
||||||
if (!is12Hour) {
|
if (!clock.is12Hour) {
|
||||||
return locale.time(date, true);
|
return locale.time(date, true);
|
||||||
}
|
}
|
||||||
const date12 = new Date(date.getTime());
|
const date12 = new Date(date.getTime());
|
||||||
|
@ -68,7 +37,7 @@ function timeText(date) {
|
||||||
return locale.time(date12, true);
|
return locale.time(date12, true);
|
||||||
}
|
}
|
||||||
function ampmText(date) {
|
function ampmText(date) {
|
||||||
return (is12Hour && locale.hasMeridian)? locale.meridian(date) : "";
|
return (clock.is12Hour && locale.hasMeridian) ? locale.meridian(date) : "";
|
||||||
}
|
}
|
||||||
function dateText(date) {
|
function dateText(date) {
|
||||||
const dayName = locale.dow(date, true),
|
const dayName = locale.dow(date, true),
|
||||||
|
@ -78,31 +47,48 @@ function dateText(date) {
|
||||||
return `${dayName} ${dayMonth}`;
|
return `${dayName} ${dayMonth}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw = function draw(force) {
|
|
||||||
if (!Bangle.isLCDOn()) {return;} // no drawing, also no new update scheduled
|
|
||||||
const date = new Date();
|
|
||||||
layout.time.label = timeText(date);
|
|
||||||
layout.ampm.label = ampmText(date);
|
|
||||||
layout.date.label = dateText(date);
|
|
||||||
const SECONDS_PER_MINUTE = 60;
|
|
||||||
layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
|
|
||||||
if (force) {
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
layout.forgetLazyState();
|
|
||||||
}
|
|
||||||
layout.render();
|
|
||||||
// schedule update at start of next second
|
|
||||||
const millis = date.getMilliseconds();
|
|
||||||
setTimeout(draw, 1000-millis);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Show launcher when button pressed
|
const ClockFace = require("ClockFace"),
|
||||||
Bangle.setUI("clock");
|
clock = new ClockFace({
|
||||||
Bangle.on("lcdPower", function(on) {
|
precision:1,
|
||||||
if (on) {
|
init: function() {
|
||||||
draw(true);
|
const Layout = require("Layout");
|
||||||
}
|
this.layout = new Layout({
|
||||||
});
|
type: "v", c: [
|
||||||
g.reset().clear();
|
{
|
||||||
Bangle.drawWidgets();
|
type: "h", c: [
|
||||||
draw();
|
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col:g.theme.fg, bgCol: g.theme.bg}, // size updated below
|
||||||
|
{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},
|
||||||
|
{height: 40},
|
||||||
|
{id: "date", type: "txt", font: "10%", valign: 1},
|
||||||
|
],
|
||||||
|
}, {lazy: true});
|
||||||
|
// adjustments based on screen size and whether we display am/pm
|
||||||
|
let thickness; // bar thickness, same as time font "pixel block" size
|
||||||
|
if (this.is12Hour) {
|
||||||
|
// Maximum font size = (<screen width> - <ampm: 2chars * (2*6)px>) / (5chars * 6px)
|
||||||
|
thickness = Math.floor((Bangle.appRect.w-24)/(5*6));
|
||||||
|
} else {
|
||||||
|
this.layout.ampm.label = "";
|
||||||
|
thickness = Math.floor(Bangle.appRect.w/(5*6));
|
||||||
|
}
|
||||||
|
this.layout.bar.height = thickness+1;
|
||||||
|
this.layout.time.font = "6x8:"+thickness;
|
||||||
|
this.layout.update();
|
||||||
|
},
|
||||||
|
update: function(date, c) {
|
||||||
|
if (c.m) this.layout.time.label = timeText(date);
|
||||||
|
if (c.h) this.layout.ampm.label = ampmText(date);
|
||||||
|
if (c.d) this.layout.date.label = dateText(date);
|
||||||
|
const SECONDS_PER_MINUTE = 60;
|
||||||
|
if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
|
||||||
|
this.layout.render();
|
||||||
|
},
|
||||||
|
resume: function() {
|
||||||
|
this.layout.forgetLazyState();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
clock.start();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "barclock",
|
"id": "barclock",
|
||||||
"name": "Bar Clock",
|
"name": "Bar Clock",
|
||||||
"version": "0.09",
|
"version": "0.11",
|
||||||
"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"}],
|
||||||
|
|
|
@ -51,3 +51,4 @@
|
||||||
0.45: Fix 0.44 regression (auto-add semi-colon between each boot code chunk)
|
0.45: Fix 0.44 regression (auto-add semi-colon between each boot code chunk)
|
||||||
0.46: Fix no clock found error on Bangle.js 2
|
0.46: Fix no clock found error on Bangle.js 2
|
||||||
0.47: Add polyfill for setUI with an object as an argument (fix regression for 2v12 devices after Layout module changed)
|
0.47: Add polyfill for setUI with an object as an argument (fix regression for 2v12 devices after Layout module changed)
|
||||||
|
0.48: Workaround for BTHRM issues on Bangle.js 1 (write .boot files in chunks)
|
||||||
|
|
|
@ -197,8 +197,18 @@ bootFiles.forEach(bootFile=>{
|
||||||
require('Storage').write('.boot0',"//"+bootFile+"\n",fileOffset);
|
require('Storage').write('.boot0',"//"+bootFile+"\n",fileOffset);
|
||||||
fileOffset+=2+bootFile.length+1;
|
fileOffset+=2+bootFile.length+1;
|
||||||
var bf = require('Storage').read(bootFile);
|
var bf = require('Storage').read(bootFile);
|
||||||
require('Storage').write('.boot0',bf,fileOffset);
|
// we can't just write 'bf' in one go because at least in 2v13 and earlier
|
||||||
fileOffset+=bf.length;
|
// Espruino wants to read the whole file into RAM first, and on Bangle.js 1
|
||||||
|
// it can be too big (especially BTHRM).
|
||||||
|
var bflen = bf.length;
|
||||||
|
var bfoffset = 0;
|
||||||
|
while (bflen) {
|
||||||
|
var bfchunk = Math.min(bflen, 2048);
|
||||||
|
require('Storage').write('.boot0',bf.substr(bfoffset, bfchunk),fileOffset);
|
||||||
|
fileOffset+=bfchunk;
|
||||||
|
bfoffset+=bfchunk;
|
||||||
|
bflen-=bfchunk;
|
||||||
|
}
|
||||||
require('Storage').write('.boot0',";\n",fileOffset);
|
require('Storage').write('.boot0',";\n",fileOffset);
|
||||||
fileOffset+=2;
|
fileOffset+=2;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "boot",
|
"id": "boot",
|
||||||
"name": "Bootloader",
|
"name": "Bootloader",
|
||||||
"version": "0.47",
|
"version": "0.48",
|
||||||
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
||||||
"icon": "bootloader.png",
|
"icon": "bootloader.png",
|
||||||
"type": "bootloader",
|
"type": "bootloader",
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App
|
0.01: New App
|
||||||
0.02: app keeps track of statistics now
|
0.02: app keeps track of statistics now
|
||||||
|
0.03: Fix bug in valid word detection
|
||||||
|
|
|
@ -110,7 +110,12 @@ class Wordle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addGuess(w) {
|
addGuess(w) {
|
||||||
if ((this.words.indexOf(w.toLowerCase())%5)!=0) {
|
let idx = -1;
|
||||||
|
do{
|
||||||
|
idx = this.words.indexOf(w.toLowerCase(), idx+1);
|
||||||
|
}
|
||||||
|
while(idx !== -1 && idx%5 !== 0);
|
||||||
|
if(idx%5 !== 0) {
|
||||||
E.showAlert(w+"\nis not a word", "Invalid word").then(function() {
|
E.showAlert(w+"\nis not a word", "Invalid word").then(function() {
|
||||||
layout = getKeyLayout("");
|
layout = getKeyLayout("");
|
||||||
wordle.render(true);
|
wordle.render(true);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "Bordle",
|
"name": "Bordle",
|
||||||
"shortName":"Bordle",
|
"shortName":"Bordle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.02",
|
"version":"0.03",
|
||||||
"description": "Bangle version of a popular word search game",
|
"description": "Bangle version of a popular word search game",
|
||||||
"supports" : ["BANGLEJS2"],
|
"supports" : ["BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
{ "id": "bowserWF",
|
{
|
||||||
|
"id": "bowserWF",
|
||||||
"name": "Bowser Watchface",
|
"name": "Bowser Watchface",
|
||||||
"shortName":"Bowser Watchface",
|
"shortName":"Bowser Watchface",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "Let bowser show you the time",
|
"description": "Let bowser show you the time",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "",
|
"type": "clock",
|
||||||
"supports" : ["BANGLEJS2"],
|
"tags": "clock",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"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"}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,7 @@
|
||||||
0.03: Adapt colors based on the theme of the user.
|
0.03: Adapt colors based on the theme of the user.
|
||||||
0.04: Steps can be hidden now such that the time is even larger.
|
0.04: Steps can be hidden now such that the time is even larger.
|
||||||
0.05: Included icons for information.
|
0.05: Included icons for information.
|
||||||
0.06: Design and usability improvements.
|
0.06: Design and usability improvements.
|
||||||
|
0.07: Improved positioning.
|
||||||
|
0.08: Select the color of widgets correctly. Additional settings to hide colon.
|
||||||
|
0.09: Larger font size if colon is hidden to improve readability further.
|
|
@ -8,6 +8,7 @@
|
||||||
- Enable / disable lock icon in the settings.
|
- Enable / disable lock icon in the settings.
|
||||||
- If the "sched" app is installed tab top / bottom of the screen to set the timer.
|
- If the "sched" app is installed tab top / bottom of the screen to set the timer.
|
||||||
- The design is adapted to the theme of your bangle.
|
- The design is adapted to the theme of your bangle.
|
||||||
|
- The colon (e.g. 7:35 = 735) can be hidden now in the settings.
|
||||||
|
|
||||||
## Thanks to
|
## Thanks to
|
||||||
<a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a>
|
<a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a>
|
||||||
|
|
|
@ -18,6 +18,7 @@ const H = g.getHeight();
|
||||||
let settings = {
|
let settings = {
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
showLock: true,
|
showLock: true,
|
||||||
|
hideColon: false,
|
||||||
showInfo: 0,
|
showInfo: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,11 +34,25 @@ for (const key in saved_settings) {
|
||||||
|
|
||||||
// Manrope font
|
// Manrope font
|
||||||
Graphics.prototype.setLargeFont = function(scale) {
|
Graphics.prototype.setLargeFont = function(scale) {
|
||||||
// Actual height 49 (50 - 2)
|
// Actual height 48 (49 - 2)
|
||||||
this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAfwAAAAAAAAf/AAAAAAAAf/8AAAAAAAf//wAAAAAAP///AAAAAAP///8AAAAAP////wAAAAP////4AAAAP////8AAAAH////8AAAAH////8AAAAB////8AAAAAH///+AAAAAAf//+AAAAAAB//+AAAAAAAH/+AAAAAAAAf+AAAAAAAAB/AAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///gAAAAAD////4AAAAA/////4AAAAH/////4AAAA//////wAAAH//////gAAA///////AAAH//////+AAA///////4AAD/4AAAH/wAAP+AAAAP/AAB/wAAAAf8AAH/AAAAA/4AAf4AAAAB/gAB/gAAAAH+AAP8AAAAAf4AA/wAAAAB/gAD/AAAAAH+AAP8AAAAAf4AAf4AAAAB/gAB/gAAAAH+AAH+AAAAA/4AAf8AAAAH/AAB/4AAAA/8AAD/4AAAH/wAAP/8AAH/+AAAf//////4AAA///////AAAB//////4AAAD//////AAAAH/////4AAAAP////+AAAAAP////gAAAAAD///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAAAAAAAB/wAAAAAAAAH/AAAAAAAAA/4AAAAAAAAH/gAAAAAAAAf8AAAAAAAAD/gAAAAAAAAP+AAAAAAAAB///////8AAH///////wAAf///////AAB///////8AAH///////wAAf///////AAB///////8AAH///////wAAP///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAfwAAAH8AAAD/AAAB/wAAAf8AAAP/AAAD/wAAB/8AAAf/AAAP/wAAD/8AAB//AAAf/wAAH/8AAD//AAA//gAAf/8AAD/wAAB//wAAf+AAAP//AAB/wAAB//8AAH+AAAP//wAAf4AAB///AAD/AAAP/v8AAP8AAB/8/wAA/wAAP/j/AAD/AAB/8P8AAH+AAH/g/wAAf4AA/8D/AAB/wAH/gP8AAH/AA/+A/wAAf/AP/wD/AAA//D/+AP8AAD////wA/wAAH///+AD/AAAP///wAP8AAAf//+AA/wAAA///wAD/AAAB//+AAP8AAAB//gAA/wAAAB/4AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAD+AAAAHwAAAf4AAAAfwAAB/gAAAB/gAAH+AAAAP/AAAf4AAAA/8AAB/gAAAD/4AAH+ADAAH/wAAf4AeAAP/AAB/gD+AAP8AAH+Af+AA/4AAf4D/4AB/gAB/gP/AAH+AAH+B/8AAf4AAf4P/wAB/gAB/h//AAH+AAH+P/8AAf4AAf5//wAB/gAB/v//gAP+AAH+//+AA/4AAf//f8AH/AAB//5/8B/8AAH//D////gAAf/4P///+AAB//Af///wAAH/4A///+AAAf/AB///wAAB/4AD//+AAAH/AAH//gAAAP4AAD/4AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAA/+AAAAAAAAP/4AAAAAAAH//gAAAAAAB//+AAAAAAAf//4AAAAAAH///gAAAAAB///+AAAAAAf///4AAAAAH//9/gAAAAD///H+AAAAA///wf4AAAAP//8B/gAAAD///AH+AAAA///wAf4AAAH//8AB/gAAAf//AAH+AAAB//gAAf4AAAH/4AAB/gAAAf+AAAH+AAAB/gAf///8AAH4AB////wAAeAAH////AABgAAf///8AAAAAB////wAAAAAH////AAAAAAf///8AAAAAB////wAAAAAH////AAAAAAAAf4AAAAAAAAB/gAAAAAAAAH+AAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAeAAAAAf//AB+AAAH///+AP8AAAf///4A/4AAB////gD/wAAH////Af/gAAf///8B/+AAB////wB/8AAH///+AB/wAAf4Af4AD/gAB/gB/AAP+AAH+AP8AAf4AAf4A/wAB/gAB/gD+AAH+AAH+AP4AAf4AAf4A/gAB/gAB/gD/AAH+AAH+AP8AAf4AAf4A/wAD/gAB/gD/gAf8AAH+AH/AD/wAAf4Af/Af+AAB/gB////4AAH+AD////AAAf4AH///8AAB/gAP///gAAH+AA///8AAAAAAA///AAAAAAAB//4AAAAAAAB/+AAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///gAAAAAB////4AAAAAf////4AAAAH/////4AAAA//////wAAAH//////gAAA///////AAAH//////+AAAf//////4AAD/4D/wH/wAAP+AP8AP/AAB/wB/gAf8AAH/AH8AA/4AAf4A/wAB/gAB/gD/AAH+AAH8AP4AAf4AA/wA/gAB/gAD/AD+AAH+AAH8AP8AAf4AAf4A/wAB/gAB/gD/AAP+AAH+AP+AB/wAAf8Af8AP/AAA/4B/8B/8AAD/gH////gAAP8AP///8AAAfgAf///wAAA8AB///+AAADgAD///wAAAAAAD//+AAAAAAAH//gAAAAAAAH/4AAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/gAAAAAAAAH+AAAAAAAAAf4AAAAAAAAB/gAAAAAAAAH+AAAAAAAAAf4AAAAADgAB/gAAAAA+AAH+AAAAAf4AAf4AAAAH/gAB/gAAAD/+AAH+AAAA//4AAf4AAAf//gAB/gAAH//+AAH+AAD///wAAf4AA///8AAB/gAf//+AAAH+AH///gAAAf4D///wAAAB/g///8AAAAH+f//+AAAAAf////gAAAAB////wAAAAAH///8AAAAAAf//+AAAAAAB///gAAAAAAH//wAAAAAAAf/8AAAAAAAB/+AAAAAAAAH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/wAAAAB/wB//wAAAAf/wf//gAAAD//z///AAAAf/////+AAAD//////8AAAf//////4AAD///////gAAP////w//AAB/+f/8Af8AAH/Af/gA/4AAf4A/8AD/gAB/gB/wAH+AAP8AH+AAf4AA/wAf4AB/gAD/AB/gAH+AAP8AH+AAf4AA/wAf4AB/gAB/gB/wAH+AAH+AP/AAf4AAf8A/+AD/gAB/8f/8Af8AAD////4H/wAAP//////+AAAf//////4AAA///////AAAD//////8AAAH//z///gAAAH/+H//4AAAAH/gH//AAAAAAAAH/wAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAP/wAAAAAAAD//wAAAAAAAf//wAAAAAAH///gABgAAA////AAPAAAD///+AB+AAAf///4AP4AAD////wB/wAAP/AP/AH/AAB/4Af+AP+AAH/AA/4A/4AAf4AB/gB/gAB/gAH+AH+AAP8AAP4Af4AA/wAA/gB/gAD/AAD+AH+AAP8AAP4Af4AA/4AB/gB/gAB/gAH+AH+AAH+AAfwA/4AAf8AD/AH/AAB/4Af4A/8AAD/4H/gP/wAAP//////+AAAf//////wAAA///////AAAB//////4AAAD//////AAAAH/////wAAAAH////+AAAAAH////AAAAAAAf/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf4AP8AAAAAB/gA/wAAAAAH+AD/AAAAAAf4AP8AAAAAB/gA/wAAAAAH+AD/AAAAAAf4AP8AAAAAB/gA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ExwqHCYlJyYoIicoFg=="), 64+(scale<<8)+(1<<16));
|
this.setFontCustom(
|
||||||
|
E.toString(require('heatshrink').decompress(atob('AFcH+AHFh/gA4sf4AHFn+AA4t/E43+AwsB/gHFgf4PH4AMgJ9Ngf/Pot//6bF/59F///PokfA4J9DEgIABEwYkB/7DDEgIlFCoRMDEgQsEDoRLEEgpoBA4JhGOIsHZ40PdwwA/L4SjHNAgGCP4cHA4wWDA4aVCA4gGDA4SNBe4IiBA4MPHYRBBEwScCA4d/EQUBaoRKDA4UBLQYECgb+EAgMHYYcHa4MPHoLBCBgMfYgcfBgM/PIc/BgN/A4YECIIQEDHwkDHwQHDGwQHENQUHA4d/QIQnCRIJJCSgYTCA4hqCA4hqCA4hiCA4ZCEA4RFBGYbrFAHxDGSohdDcgagFAAjPCEzicDToU/A4jPCAwbQCBwgrBgIHEFYKrDWoa7DaggA/AC0PAYV+AYSBCgKpCg4DDVIUfAYZ9BToIDDPoKVBAYfARoQDDXgMPFwTIBdYSYCv4LCv7zCXgYKCXAK8CHoUPXgY9Cn/vEYMPEwX/z46Bj4mBgf+n77CDwX4v54EIIIzCOgX/4I+CAQI9BHYQCCQ4I7CRASDBHYQHCv/Aj4+BGYIeBGAI+Bj/8AIIRBQIZjCRIiWBXgYHCPQgHBBgJ6DA4IEBPQaKBGYQ+BbgiCCAGZFDIIUBaAZBCgYHCQAQTBA4SACUwS8DDYQHBQAbVCQAYwBA4SABgYEBPoQCBFgU/CQWACgRDCHwKVCIYX+aYRDCHwMPAgY+Cn4EDHwX/AgY+B8bEFj/HA4RGCn+f94MBv45Cv+fA4J6C//+j5gBGIMBFoJWBQoRMB8E//4DBHIJcBv4HBEwJUCA4ImCj5MBA4KZCPYQHBZgRBCE4LICvwaCXAYA5PgQAEMIQAEUwQADQAJlCAARlBWYIACT4JtDAAMPA4IWESgg8CAwI+EEoPhHwYlCgY+DEoP4g4+DEoPAh4+CEoReBHwUfLYU/CwgMBXARqBHYQCCGoIjBgI+CgZSCHwcHAYY+Ch4lBJ4IbCjhACPwqUBPwqFCPwhQBIQZ+DOAKVFXooHCXop9DFAi8EFAT0GPoYAygwFEgOATISLDwBWDTQc/A4L6CTQKkCVQX+BYIHBDwX+BYIHBVQX8B4KqD+/wA4aBBj/AgK8CQIIJBA4a/BBIMBAgL/BAgUDYgL/BAII7BAQXgAII7BAQXAYQQxBYARrCMwQ0BAgV/HwYECHwgEBgY+EA4MPGwI8BA4UfGwI8BgYHBPofAQYOHPoeAR4QmBHwQHCEwI+CA4RVBHwQHCaggnBDwQHEHoIAEEQIA6v5NFfgSECBwZtEf4IHFOYQHEj4HGDwYHCDwPgv/jA4UHXQS8E/ED/AHDZ4MPSYKlCv+AYwIHDDwL7EgL7DAgTzCEwIpCeYTZBg4CBeYIJBAgICBFgIJBAgICBeYIEDHII0BAgg+EgI5CMocHGwJBCA4MfGwMD/h/BwF/PoQHC451CJIMDSgIjBA4PAA4QmBA4IhBA4JVBgEMA4bUDV4QeCAAf/HoIAENIIApOoIAEW4QAEW4QAEW4QAEWQRSFNIcDfYQMDny8DO4Q7BAQQjCewh+EHwcPToQ+Dv//ewkHUoI+En68DeIS0EHwMf/46CeYYlCHwQ0BKIY+BGgJ4Dh/nGgZZCAwKPEHYLpFDoKuFGgj4JgY0EHwQ0EYhIA6MAkf+BRBLIa5BQAJSCBgP4R4iVB/YHERoIACA4QGDE4SFBAoV/A4MH/ggBWIL7C8EfVoL4DwBHBFYIHBfYIRBAgT7CDgQEBgP4BgUBEIMDDgIMBgYMBg/gBgS5Ch/ABgUPFIMf4EHA4IEBHwUPCgJGCIIM/CgLgCAQJlBFIQFB44HBEIUBQYc/EIIHDAAIuBA4oeBRoSfBLAIHC/gHBEwIXC+AHBZghHBDwQADj4WCAHEPAwpWBKYYOCLwIHELYJUBghlDA4UcQogHBvgeDD4K0DDwIHBWgQeB4CyBh68CUAMf8DeCdIYHDdIfAfYjxCAgj2BAgbHCvwJCIIYCBBIMDHIX4BgUHFwMD+AMCA4Q0BAgg5CHwxICAQY5BdgQHBEgMDIYV/DgR1CA4PwP4KvDRgIACEYIHFWggABMQQHEZwd/Dwq1DHoTFEdooA/ACrBBcAZmC8DTCAATGBaYR+DwDTCRwbYDAASLBCIIGCFgQRBAG4='))),
|
||||||
|
46,
|
||||||
|
atob("EhooGyUkJiUnISYnFQ=="),
|
||||||
|
63+(scale<<8)+(1<<16)
|
||||||
|
);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Graphics.prototype.setXLargeFont = function(scale) {
|
||||||
|
// Actual height 53 (55 - 3)
|
||||||
|
this.setFontCustom(
|
||||||
|
E.toString(require('heatshrink').decompress(atob('AHM/8AIG/+AA4sD/wQGh/4EWQA/AC8YA40HNA0BRY8/RY0P/6LFgf//4iFA4IiFj4HBEQkHCAQiDHIIZGv4HCFQY5BDAo5CAAIpDDAfACA3wLYv//hsFKYxcCMgoiBOooiBQwwiBS40AHIgA/ACS/DLYjYCBAjQEBAYQDBAgHDUAbyDZQi3CegoHEVQQZFagUfW4Y0DaAgECaIJSEFYMPbIYNDv5ACGAIrBCgJ1EFYILCAAQWCj4zDGgILCegcDEQRNDHIIiCHgZ2BEQShFIqUDFYidCh5ODg4NCn40DAgd/AYR5BDILZEAAIMDAAYVCh7aHdYhKDbQg4Dv7rGBAihFCAwIDCAgA/AB3/eoa7GAAk/dgbVGDJrvCDK67DDIjaGdYpbCdYonCcQjjDEVUBEQ4A/AEMcAYV/NAUHcYUDawd/cYUPRYSmBBgaLBToP8BgYiBSgIiCj4iCg//EQSuDW4IMDVwYiCBgIiBBgrRDCATeBaIYqCv70DCgT4CEQMfIgQZBBoRnDv/3EQIvBDIffEQMHFwReBRYUfOgX/+IiDKIeHEQRRECwUHKwIuB8AiDIoJEBCwZFCv/4HIZaBIgPAEQS2CUYQiCD4SABEQcfOwIZBEQaHBO4RcEAAI/BEQQgBSIQiDTIRZBEQZuBVYQiDHoKWCEQQICFQIiDBAQeCEQQA/AANwA40BLIJ5BO4JWCBAUPAYR5En7RBUIQECN4SYCQQIiEh6CCEQk/BoQiBgYeCBoTrCAgT0CCgIfCFYQiBg4IBGgIiDj6rBg4rCBYLRDFYIiBbYIfBLgQiBIQYiD4JCCLgf/bQIWDBYV/EQV/BYXz/5FBgIiD5//IowZBD4M/NAX/BIPgDIJoC//5GgKUDn//4f/8KLE/wTBAAI8BEQPwj4HBVwYmBDgIZDN4QZCGYKJCHQP/JoSgCBATrCh5dBKITVDG4gICAAbvDAH5SCL4QADK4J5CCAiTCCAp1BCAqCDCAgiGCAIiFCAQiFeoIiFg6/FCAgiECAXnEQgQB/kfEQYQC4F/EQYQCgIiDfoIQBg4iDCAUAEQZUCcgIiDDIIQBEQhuBBoIiENoYiFDwQiECAQiFwEBPQQNCAQKDDEYMDDoMfRh4iGUwqvEESBiBaQ5oEbgr0FNAo+EEIwA+oAHGgJoFRAMHe4L0CAALNBBAT0BfwScDCAXweAL0DWgUPQYQiDwF/QYQiC/zTB+C0FBAL0CEQYIBGgMPCgIxBg4rCJIKsCh5IBBwTPCj4WBgYLBZ4V/MAIiBBQQrBEQYtCBYQiCO4QLFCwgiDIQIiGIoMHEQpFBn5FFD4JoENwRoGDgSUCAoKfBw//DgIiCT4auCFwN/T4RRET4TaCEQKoCDIQiCGgK/DAAQICdYQACHoIqCBAoQFEwIhFAH4AFQIROEj4IGXwIIGNwIACbgIhEBAiRCVwoqDTogHEW4QZFXgIZB/z9Cv49CF4MPBwI0Ca4LlB8ATCJoP4AoINDfQPAg7PBg4cBBwUfD4MfFYILCCwgOCf4QLEwEPCwILCgJaBn4WBBYQxCIQQiD+EDCYI5CBYRQBIo4fBMQIuBC4N/NAv8AoIcBSgU/FYIIBZIYrCW4hOCXIQZCgYUBv7jEh4uBZAscewZ8CgEgUYT0EEoQIBA4gICFQQIEHYQA+KQzdDAArdCAArpCEScHaIQiEvwiGe4QiFUwQiEbgIiFYIL0DEQTkBEQrJEEQc/cYYiCg4HBDIQiCfoRoEHQLaDEQQHBbQYiBCAT8Dn/BCAoXBJYP/OgZKC/6OEEARLCEQZLEEQZLEEQjKFEQI6EEQZLDEQbsGEQLjGYYYA/JIxzEg/AfgJSDAoPgfgiDC8COFAoPnaQj6CAAR+CW4TCFA4i6CDIqhCDIfwHoYHCYIN/GgKuBJ4JDBFYUf/C5CBYIZBv/Ag4ZBg4rBBYQTBAQIcBg4FBn5UBAQUfFwIfCEQeAgYfBAQUBFAKbCAQQiCGwIiE+A2BwBFNwE/AoM/EQJoIWwKCCh4cBFYKUERYV/W46uHFYIZGaJA0B/glBGYT0JIITiEMIJvCFQQAEHYQA/ABBlEOIhdGQAIRFSgQIBgQICn4IB8EAjiBCUYglCbQYeBEoQZCTwM/CYIZD/gEBUwIzBJ4UHYAU/EwIrBh4rCAoIXCn4rBCgUDAQN/FYMfBYIXBCYJnCBYXggf8HgQLCwEPEQQuBgJOECwILDCwgiLHIUHBYJFGD4IxBgYWCn4rBBwJoFDIYNBCgPADgKHBRYfDBQN/GAIrBToTLDVwYACDILiCWAb8DAAYzBYAjTCAAI9BAARNCBAoqCBAgQDFgbYCAH4AufgQACf4T8CAAT/CfgQACBwITCAAYOBCYQioh4iEAHQA=='))),
|
||||||
|
46,
|
||||||
|
atob("FR4uHyopKyksJSssGA=="),
|
||||||
|
70+(scale<<8)+(1<<16)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Graphics.prototype.setMediumFont = function(scale) {
|
Graphics.prototype.setMediumFont = function(scale) {
|
||||||
// Actual height 41 (42 - 2)
|
// Actual height 41 (42 - 2)
|
||||||
|
@ -259,11 +274,12 @@ function draw() {
|
||||||
|
|
||||||
function drawDate(){
|
function drawDate(){
|
||||||
// Draw background
|
// Draw background
|
||||||
var y = H/5*2 + (settings.fullscreen ? 0 : 8);
|
var y = H/5*2;
|
||||||
g.reset().clearRect(0,0,W,W);
|
g.reset().clearRect(0,0,W,W);
|
||||||
|
|
||||||
// Draw date
|
// Draw date
|
||||||
y -= settings.fullscreen ? 8 : 0;
|
y = parseInt(y/2);
|
||||||
|
y += settings.fullscreen ? 2 : 15;
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
var dateStr = date.getDate();
|
var dateStr = date.getDate();
|
||||||
dateStr = ("0" + dateStr).substr(-2);
|
dateStr = ("0" + dateStr).substr(-2);
|
||||||
|
@ -276,14 +292,14 @@ function drawDate(){
|
||||||
var dayW = Math.max(g.stringWidth(dayStr), g.stringWidth(monthStr));
|
var dayW = Math.max(g.stringWidth(dayStr), g.stringWidth(monthStr));
|
||||||
var fullDateW = dateW + 10 + dayW;
|
var fullDateW = dateW + 10 + dayW;
|
||||||
|
|
||||||
g.setFontAlign(-1,1);
|
g.setFontAlign(-1,0);
|
||||||
g.setMediumFont();
|
g.setMediumFont();
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.drawString(dateStr, W/2 - fullDateW / 2, y+5);
|
g.drawString(dateStr, W/2 - fullDateW / 2, y+1);
|
||||||
|
|
||||||
g.setSmallFont();
|
g.setSmallFont();
|
||||||
g.drawString(monthStr, W/2 - fullDateW/2 + 10 + dateW, y+3);
|
g.drawString(dayStr, W/2 - fullDateW/2 + 10 + dateW, y-12);
|
||||||
g.drawString(dayStr, W/2 - fullDateW/2 + 10 + dateW, y-23);
|
g.drawString(monthStr, W/2 - fullDateW/2 + 10 + dateW, y+11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -296,9 +312,16 @@ function drawTime(){
|
||||||
|
|
||||||
// Draw time
|
// Draw time
|
||||||
g.setColor(g.theme.bg);
|
g.setColor(g.theme.bg);
|
||||||
g.setFontAlign(0,-1);
|
g.setFontAlign(0,0);
|
||||||
var timeStr = locale.time(date,1);
|
|
||||||
y += settings.fullscreen ? 14 : 10;
|
var hours = String(date.getHours());
|
||||||
|
var minutes = date.getMinutes();
|
||||||
|
minutes = minutes < 10 ? String("0") + minutes : minutes;
|
||||||
|
var colon = settings.hideColon ? "" : ":";
|
||||||
|
var timeStr = hours + colon + minutes;
|
||||||
|
|
||||||
|
// Set y coordinates correctly
|
||||||
|
y += parseInt((H - y)/2) + 5;
|
||||||
|
|
||||||
var infoEntry = getInfoEntry();
|
var infoEntry = getInfoEntry();
|
||||||
var infoStr = infoEntry[0];
|
var infoStr = infoEntry[0];
|
||||||
|
@ -307,9 +330,13 @@ function drawTime(){
|
||||||
|
|
||||||
// Show large or small time depending on info entry
|
// Show large or small time depending on info entry
|
||||||
if(infoStr == null){
|
if(infoStr == null){
|
||||||
y += 10;
|
if(settings.hideColon){
|
||||||
g.setLargeFont();
|
g.setXLargeFont();
|
||||||
|
} else {
|
||||||
|
g.setLargeFont();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
y -= 15;
|
||||||
g.setMediumFont();
|
g.setMediumFont();
|
||||||
}
|
}
|
||||||
g.drawString(timeStr, W/2, y);
|
g.drawString(timeStr, W/2, y);
|
||||||
|
@ -319,7 +346,7 @@ function drawTime(){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
y += H/5*2-5;
|
y += 35;
|
||||||
g.setFontAlign(0,0);
|
g.setFontAlign(0,0);
|
||||||
g.setSmallFont();
|
g.setSmallFont();
|
||||||
var imgWidth = 0;
|
var imgWidth = 0;
|
||||||
|
@ -370,17 +397,6 @@ function queueDraw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Load clock, widgets and listen for events
|
|
||||||
*/
|
|
||||||
Bangle.loadWidgets();
|
|
||||||
|
|
||||||
// Clear the screen once, at startup and set the correct theme.
|
|
||||||
var bgOrig = g.theme.bg
|
|
||||||
var fgOrig = g.theme.fg
|
|
||||||
g.setTheme({bg:fgOrig,fg:bgOrig}).clear();
|
|
||||||
draw();
|
|
||||||
|
|
||||||
// Stop updates when LCD is off, restart when on
|
// Stop updates when LCD is off, restart when on
|
||||||
Bangle.on('lcdPower',on=>{
|
Bangle.on('lcdPower',on=>{
|
||||||
if (on) {
|
if (on) {
|
||||||
|
@ -446,5 +462,17 @@ E.on("kill", function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Draw clock the first time
|
||||||
|
*/
|
||||||
|
// The upper part is inverse i.e. light if dark and dark if light theme
|
||||||
|
// is enabled. In order to draw the widgets correctly, we invert the
|
||||||
|
// dark/light theme as well as the colors.
|
||||||
|
g.setTheme({bg:g.theme.fg,fg:g.theme.bg, dark:!g.theme.dark}).clear();
|
||||||
|
|
||||||
|
// Load widgets and draw clock the first time
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
draw();
|
||||||
|
|
||||||
// Show launcher when middle button pressed
|
// Show launcher when middle button pressed
|
||||||
Bangle.setUI("clock");
|
Bangle.setUI("clock");
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "bwclk",
|
"id": "bwclk",
|
||||||
"name": "BW Clock",
|
"name": "BW Clock",
|
||||||
"version": "0.06",
|
"version": "0.09",
|
||||||
"description": "BW Clock.",
|
"description": "BW Clock.",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.1 KiB |
|
@ -6,6 +6,7 @@
|
||||||
let settings = {
|
let settings = {
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
showLock: true,
|
showLock: true,
|
||||||
|
hideColon: false,
|
||||||
};
|
};
|
||||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||||
for (const key in saved_settings) {
|
for (const key in saved_settings) {
|
||||||
|
@ -35,6 +36,14 @@
|
||||||
settings.showLock = !settings.showLock;
|
settings.showLock = !settings.showLock;
|
||||||
save();
|
save();
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
'Hide Colon': {
|
||||||
|
value: settings.hideColon,
|
||||||
|
format: () => (settings.hideColon ? 'Yes' : 'No'),
|
||||||
|
onchange: () => {
|
||||||
|
settings.hideColon = !settings.hideColon;
|
||||||
|
save();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,3 +5,5 @@
|
||||||
0.05: Update calendar weekend colors for start on Sunday
|
0.05: Update calendar weekend colors for start on Sunday
|
||||||
0.06: Use larger font for dates
|
0.06: Use larger font for dates
|
||||||
0.07: Fix off-by-one-error on previous month
|
0.07: Fix off-by-one-error on previous month
|
||||||
|
0.08: Do not register as watch, manually start clock on button
|
||||||
|
read start of week from system settings
|
||||||
|
|
|
@ -18,8 +18,7 @@ const blue = "#0000ff";
|
||||||
const yellow = "#ffff00";
|
const yellow = "#ffff00";
|
||||||
|
|
||||||
let settings = require('Storage').readJSON("calendar.json", true) || {};
|
let settings = require('Storage').readJSON("calendar.json", true) || {};
|
||||||
if (settings.startOnSun === undefined)
|
let startOnSun = ((require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0) === 0;
|
||||||
settings.startOnSun = false;
|
|
||||||
if (settings.ndColors === undefined)
|
if (settings.ndColors === undefined)
|
||||||
if (process.env.HWVERSION == 2) {
|
if (process.env.HWVERSION == 2) {
|
||||||
settings.ndColors = true;
|
settings.ndColors = true;
|
||||||
|
@ -50,14 +49,14 @@ function getDowLbls(locale) {
|
||||||
case "de_AT":
|
case "de_AT":
|
||||||
case "de_CH":
|
case "de_CH":
|
||||||
case "de_DE":
|
case "de_DE":
|
||||||
if (settings.startOnSun) {
|
if (startOnSun) {
|
||||||
dowLbls = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"];
|
dowLbls = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"];
|
||||||
} else {
|
} else {
|
||||||
dowLbls = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
dowLbls = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "nl_NL":
|
case "nl_NL":
|
||||||
if (settings.startOnSun) {
|
if (startOnSun) {
|
||||||
dowLbls = ["zo", "ma", "di", "wo", "do", "vr", "za"];
|
dowLbls = ["zo", "ma", "di", "wo", "do", "vr", "za"];
|
||||||
} else {
|
} else {
|
||||||
dowLbls = ["ma", "di", "wo", "do", "vr", "za", "zo"];
|
dowLbls = ["ma", "di", "wo", "do", "vr", "za", "zo"];
|
||||||
|
@ -66,14 +65,14 @@ function getDowLbls(locale) {
|
||||||
case "fr_BE":
|
case "fr_BE":
|
||||||
case "fr_CH":
|
case "fr_CH":
|
||||||
case "fr_FR":
|
case "fr_FR":
|
||||||
if (settings.startOnSun) {
|
if (startOnSun) {
|
||||||
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
|
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
|
||||||
} else {
|
} else {
|
||||||
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
|
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "sv_SE":
|
case "sv_SE":
|
||||||
if (settings.startOnSun) {
|
if (startOnSun) {
|
||||||
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
|
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
|
||||||
} else {
|
} else {
|
||||||
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
|
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
|
||||||
|
@ -81,21 +80,21 @@ function getDowLbls(locale) {
|
||||||
break;
|
break;
|
||||||
case "it_CH":
|
case "it_CH":
|
||||||
case "it_IT":
|
case "it_IT":
|
||||||
if (settings.startOnSun) {
|
if (startOnSun) {
|
||||||
dowLbls = ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"];
|
dowLbls = ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"];
|
||||||
} else {
|
} else {
|
||||||
dowLbls = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"];
|
dowLbls = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "oc_FR":
|
case "oc_FR":
|
||||||
if (settings.startOnSun) {
|
if (startOnSun) {
|
||||||
dowLbls = ["dg", "dl", "dm", "dc", "dj", "dv", "ds"];
|
dowLbls = ["dg", "dl", "dm", "dc", "dj", "dv", "ds"];
|
||||||
} else {
|
} else {
|
||||||
dowLbls = ["dl", "dm", "dc", "dj", "dv", "ds", "dg"];
|
dowLbls = ["dl", "dm", "dc", "dj", "dv", "ds", "dg"];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (settings.startOnSun) {
|
if (startOnSun) {
|
||||||
dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
||||||
} else {
|
} else {
|
||||||
dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
|
dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
|
||||||
|
@ -110,7 +109,7 @@ function drawCalendar(date) {
|
||||||
g.clearRect(0, 0, maxX, maxY);
|
g.clearRect(0, 0, maxX, maxY);
|
||||||
g.setBgColor(bgColorMonth);
|
g.setBgColor(bgColorMonth);
|
||||||
g.clearRect(0, 0, maxX, headerH);
|
g.clearRect(0, 0, maxX, headerH);
|
||||||
if (settings.startOnSun){
|
if (startOnSun){
|
||||||
g.setBgColor(bgColorWeekend);
|
g.setBgColor(bgColorWeekend);
|
||||||
g.clearRect(0, headerH + rowH, colW, maxY);
|
g.clearRect(0, headerH + rowH, colW, maxY);
|
||||||
g.setBgColor(bgColorDow);
|
g.setBgColor(bgColorDow);
|
||||||
|
@ -150,7 +149,7 @@ function drawCalendar(date) {
|
||||||
});
|
});
|
||||||
|
|
||||||
date.setDate(1);
|
date.setDate(1);
|
||||||
const dow = date.getDay() + (settings.startOnSun ? 1 : 0);
|
const dow = date.getDay() + (startOnSun ? 1 : 0);
|
||||||
const dowNorm = dow === 0 ? 7 : dow;
|
const dowNorm = dow === 0 ? 7 : dow;
|
||||||
|
|
||||||
const monthMaxDayMap = {
|
const monthMaxDayMap = {
|
||||||
|
@ -242,5 +241,5 @@ Bangle.on("touch", area => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show launcher when button pressed
|
// Show launcher when button pressed
|
||||||
Bangle.setUI("clock"); // TODO: ideally don't set 'clock' mode
|
setWatch(() => load(), process.env.HWVERSION === 2 ? BTN : BTN3, { repeat: false, edge: "falling" });
|
||||||
// No space for widgets!
|
// No space for widgets!
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "calendar",
|
"id": "calendar",
|
||||||
"name": "Calendar",
|
"name": "Calendar",
|
||||||
"version": "0.07",
|
"version": "0.08",
|
||||||
"description": "Simple calendar",
|
"description": "Simple calendar",
|
||||||
"icon": "calendar.png",
|
"icon": "calendar.png",
|
||||||
"screenshots": [{"url":"screenshot_calendar.png"}],
|
"screenshots": [{"url":"screenshot_calendar.png"}],
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
(function (back) {
|
(function (back) {
|
||||||
var FILE = "calendar.json";
|
var FILE = "calendar.json";
|
||||||
var settings = require('Storage').readJSON(FILE, true) || {};
|
var settings = require('Storage').readJSON(FILE, true) || {};
|
||||||
if (settings.startOnSun === undefined)
|
|
||||||
settings.startOnSun = false;
|
|
||||||
if (settings.ndColors === undefined)
|
if (settings.ndColors === undefined)
|
||||||
if (process.env.HWVERSION == 2) {
|
if (process.env.HWVERSION == 2) {
|
||||||
settings.ndColors = true;
|
settings.ndColors = true;
|
||||||
|
@ -17,14 +15,6 @@
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"": { "title": "Calendar" },
|
"": { "title": "Calendar" },
|
||||||
"< Back": () => back(),
|
"< Back": () => back(),
|
||||||
'Start Sunday': {
|
|
||||||
value: settings.startOnSun,
|
|
||||||
format: v => v ? "Yes" : "No",
|
|
||||||
onchange: v => {
|
|
||||||
settings.startOnSun = v;
|
|
||||||
writeSettings();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'B2 Colors': {
|
'B2 Colors': {
|
||||||
value: settings.ndColors,
|
value: settings.ndColors,
|
||||||
format: v => v ? "Yes" : "No",
|
format: v => v ? "Yes" : "No",
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Banglejs - Touchscreen calibration
|
||||||
|
A simple calibration app for the touchscreen
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Once lauched touch the cross that appear on the screen to make
|
||||||
|
another spawn elsewhere.
|
||||||
|
|
||||||
|
each new touch on the screen will help to calibrate the offset
|
||||||
|
of your finger on the screen. After five or more input, press
|
||||||
|
the button to save the calibration and close the application.
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwkB/4AJ+EPBhQXg+BBDCyJaGGR5zIDBoQEL4QYOLYR3GBIouJR5AYBGBILBU5QMGFwgiFX4wwIEI4XGGBAgHd44+HD44XHNw4XWM5IIHCIoXWV5IXICQgXvLxAAKCYYXh5nMC6n8C4PPC5MAAA8PC4ZxBACAXOI653hU5zvJABASEC5PwHI4XcMBIXICIoXXJBAXHCAwXXJBAXHB5AfGC4ygJEAwXGQ5BoIQxoiDBYgXECwIuIBgb5ECIQJFGBQmCC4QHEDBwAFCxoYICx5ZELZoZJFiIXpA="))
|
|
@ -0,0 +1,85 @@
|
||||||
|
class BanglejsApp {
|
||||||
|
constructor() {
|
||||||
|
this.x = 0;
|
||||||
|
this.y = 0;
|
||||||
|
this.settings = {
|
||||||
|
xoffset: 0,
|
||||||
|
yoffset: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
load_settings() {
|
||||||
|
let settings = require('Storage').readJSON('calibration.json', true) || {active: false};
|
||||||
|
|
||||||
|
// do nothing if the calibration is deactivated
|
||||||
|
if (settings.active === true) {
|
||||||
|
// cancel the calibration offset
|
||||||
|
Bangle.on('touch', function(button, xy) {
|
||||||
|
xy.x += settings.xoffset;
|
||||||
|
xy.y += settings.yoffset;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!settings.xoffset) settings.xoffset = 0;
|
||||||
|
if (!settings.yoffset) settings.yoffset = 0;
|
||||||
|
|
||||||
|
console.log('loaded settings:');
|
||||||
|
console.log(settings);
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
save_settings() {
|
||||||
|
this.settings.active = true;
|
||||||
|
this.settings.reload = false;
|
||||||
|
require('Storage').writeJSON('calibration.json', this.settings);
|
||||||
|
|
||||||
|
console.log('saved settings:');
|
||||||
|
console.log(this.settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
explain() {
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* Present how to use the application
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTarget() {
|
||||||
|
this.x = 16 + Math.floor(Math.random() * (g.getWidth() - 32));
|
||||||
|
this.y = 40 + Math.floor(Math.random() * (g.getHeight() - 80));
|
||||||
|
|
||||||
|
g.clearRect(0, 24, g.getWidth(), g.getHeight() - 24);
|
||||||
|
g.drawLine(this.x, this.y - 5, this.x, this.y + 5);
|
||||||
|
g.drawLine(this.x - 5, this.y, this.x + 5, this.y);
|
||||||
|
g.setFont('Vector', 10);
|
||||||
|
g.drawString('current offset: ' + this.settings.xoffset + ', ' + this.settings.yoffset, 0, 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
setOffset(xy) {
|
||||||
|
this.settings.xoffset = Math.round((this.settings.xoffset + (this.x - Math.floor((this.x + xy.x)/2)))/2);
|
||||||
|
this.settings.yoffset = Math.round((this.settings.yoffset + (this.y - Math.floor((this.y + xy.y)/2)))/2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
E.srand(Date.now());
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
calibration = new BanglejsApp();
|
||||||
|
calibration.load_settings();
|
||||||
|
|
||||||
|
let modes = {
|
||||||
|
mode : 'custom',
|
||||||
|
btn : function(n) {
|
||||||
|
calibration.save_settings(this.settings);
|
||||||
|
load();
|
||||||
|
},
|
||||||
|
touch : function(btn, xy) {
|
||||||
|
calibration.setOffset(xy);
|
||||||
|
calibration.drawTarget();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Bangle.setUI(modes);
|
||||||
|
calibration.drawTarget();
|
|
@ -0,0 +1,14 @@
|
||||||
|
let cal_settings = require('Storage').readJSON("calibration.json", true) || {active: false};
|
||||||
|
Bangle.on('touch', function(button, xy) {
|
||||||
|
// do nothing if the calibration is deactivated
|
||||||
|
if (cal_settings.active === false) return;
|
||||||
|
|
||||||
|
// reload the calibration offset at each touch event /!\ bad for the flash memory
|
||||||
|
if (cal_settings.reload === true) {
|
||||||
|
cal_settings = require('Storage').readJSON("calibration.json", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the calibration offset
|
||||||
|
xy.x += cal_settings.xoffset;
|
||||||
|
xy.y += cal_settings.yoffset;
|
||||||
|
});
|
After Width: | Height: | Size: 451 B |
|
@ -0,0 +1,17 @@
|
||||||
|
{ "id": "calibration",
|
||||||
|
"name": "Touchscreen Calibration",
|
||||||
|
"shortName":"Calibration",
|
||||||
|
"icon": "calibration.png",
|
||||||
|
"version":"1.00",
|
||||||
|
"description": "A simple calibration app for the touchscreen",
|
||||||
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"tags": "tool",
|
||||||
|
"storage": [
|
||||||
|
{"name":"calibration.app.js","url":"app.js"},
|
||||||
|
{"name":"calibration.boot.js","url":"boot.js"},
|
||||||
|
{"name":"calibration.settings.js","url":"settings.js"},
|
||||||
|
{"name":"calibration.img","url":"app-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [{"name":"calibration.json"}]
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
(function(back) {
|
||||||
|
var FILE = "calibration.json";
|
||||||
|
var settings = Object.assign({
|
||||||
|
active: true,
|
||||||
|
}, require('Storage').readJSON(FILE, true) || {});
|
||||||
|
|
||||||
|
function writeSettings() {
|
||||||
|
require('Storage').writeJSON(FILE, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu({
|
||||||
|
"" : { "title" : "Calibration" },
|
||||||
|
"< Back" : () => back(),
|
||||||
|
'Active': {
|
||||||
|
value: !!settings.active,
|
||||||
|
format: v => v? "On":"Off",
|
||||||
|
onchange: v => {
|
||||||
|
settings.active = v;
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: App created
|
|
@ -0,0 +1 @@
|
||||||
|
E.toArrayBuffer(atob("ICABAAAAAAAAAAAAAAAAAAHAAAAP8AAAfn4AA/APwA+DwfAPg8HwD+AH8Az4HzAMPnwwDAfgMAwBgDAMCYAwDA2YMAwhmDAMIZAwDCGDMA2BgzAMgYAwDAGAMA8BgPADwYPAAPGPgAB9ngAAH/gAAAfgAAABgAAAAAAAAAAAAAAAAAA="))
|
|
@ -0,0 +1,108 @@
|
||||||
|
var init_message = true;
|
||||||
|
var acc_data;
|
||||||
|
var die_roll = 1;
|
||||||
|
var selected_die = 0;
|
||||||
|
var roll = 0;
|
||||||
|
const dices = [4, 6, 10, 12, 20];
|
||||||
|
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
|
||||||
|
Bangle.on('touch', function(button, xy) {
|
||||||
|
// Change die if not rolling
|
||||||
|
if(roll < 1){
|
||||||
|
if(selected_die <= 3){
|
||||||
|
selected_die++;
|
||||||
|
}else{
|
||||||
|
selected_die = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Disable initial message
|
||||||
|
init_message = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
function rect(){
|
||||||
|
x1 = g.getWidth()/2 - 35;
|
||||||
|
x2 = g.getWidth()/2 + 35;
|
||||||
|
y1 = g.getHeight()/2 - 35;
|
||||||
|
y2 = g.getHeight()/2 + 35;
|
||||||
|
g.drawRect(x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pentagon(){
|
||||||
|
x1 = g.getWidth()/2;
|
||||||
|
y1 = g.getHeight()/2 - 50;
|
||||||
|
x2 = g.getWidth()/2 - 50;
|
||||||
|
y2 = g.getHeight()/2 - 10;
|
||||||
|
x3 = g.getWidth()/2 - 30;
|
||||||
|
y3 = g.getHeight()/2 + 30;
|
||||||
|
x4 = g.getWidth()/2 + 30;
|
||||||
|
y4 = g.getHeight()/2 + 30;
|
||||||
|
x5 = g.getWidth()/2 + 50;
|
||||||
|
y5 = g.getHeight()/2 - 10;
|
||||||
|
g.drawPoly([x1, y1, x2, y2, x3, y3, x4, y4, x5, y5], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function triangle(){
|
||||||
|
x1 = g.getWidth()/2;
|
||||||
|
y1 = g.getHeight()/2 - 57;
|
||||||
|
x2 = g.getWidth()/2 - 50;
|
||||||
|
y2 = g.getHeight()/2 + 23;
|
||||||
|
x3 = g.getWidth()/2 + 50;
|
||||||
|
y3 = g.getHeight()/2 + 23;
|
||||||
|
g.drawPoly([x1, y1, x2, y2, x3, y3], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawDie(variant) {
|
||||||
|
if(variant == 1){
|
||||||
|
//Rect, 6
|
||||||
|
rect();
|
||||||
|
}else if(variant == 3){
|
||||||
|
//Pentagon, 12
|
||||||
|
pentagon();
|
||||||
|
}else{
|
||||||
|
//Triangle, 4, 10, 20
|
||||||
|
triangle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initMessage(){
|
||||||
|
g.setFont("6x8", 2);
|
||||||
|
g.drawString("Dice-n-Roll", g.getWidth()/2, 20);
|
||||||
|
g.drawString("Shake to roll", g.getWidth()/2, 60);
|
||||||
|
g.drawString("Tap to change", g.getWidth()/2, 80);
|
||||||
|
g.drawString("Tap to start", g.getWidth()/2, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rollDie(){
|
||||||
|
acc_data = Bangle.getAccel();
|
||||||
|
if(acc_data.diff > 0.3){
|
||||||
|
roll = 3;
|
||||||
|
}
|
||||||
|
//Mange the die "roll" by chaning the number a few times
|
||||||
|
if(roll > 0){
|
||||||
|
g.drawString("Rolling!", g.getWidth()/2, 150);
|
||||||
|
die_roll = Math.abs(E.hwRand()) % dices[selected_die] + 1;
|
||||||
|
roll--;
|
||||||
|
}
|
||||||
|
//Draw dice graphics
|
||||||
|
drawDie(selected_die);
|
||||||
|
//Draw dice number
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
g.setFont("Vector", 45);
|
||||||
|
g.drawString(die_roll, g.getWidth()/2, g.getHeight()/2);
|
||||||
|
//Draw selected die in right corner
|
||||||
|
g.setFont("6x8", 2);
|
||||||
|
g.drawString(dices[selected_die], g.getWidth()-15, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
g.clear();
|
||||||
|
if(init_message){
|
||||||
|
initMessage();
|
||||||
|
}else{
|
||||||
|
rollDie();
|
||||||
|
}
|
||||||
|
Bangle.setLCDPower(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var interval = setInterval(main, 300);
|
After Width: | Height: | Size: 637 B |
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,14 @@
|
||||||
|
{ "id": "diceroll",
|
||||||
|
"name": "Dice-n-Roll",
|
||||||
|
"shortName":"Dice-n-Roll",
|
||||||
|
"icon": "app.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "A dice app with a few different dice.",
|
||||||
|
"screenshots": [{"url":"diceroll_screenshot.png"}],
|
||||||
|
"tags": "game",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"diceroll.app.js","url":"app.js"},
|
||||||
|
{"name":"diceroll.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
# dinoClock
|
||||||
|
|
||||||
|
Watchface with T-Rex Dinosaur from Chrome.
|
||||||
|
It displays current temperature and weather.
|
||||||
|
|
||||||
|
**Warning**: Element position and styles can change in the future.
|
||||||
|
|
||||||
|
Based on the [Weather Clock](https://github.com/espruino/BangleApps/tree/master/apps/weatherClock).
|
||||||
|
|
||||||
|
# Requirements
|
||||||
|
|
||||||
|
**This clock requires Gadgetbridge and the weather app in order to get weather data!**
|
||||||
|
|
||||||
|
See the [Bangle.js Gadgetbridge documentation](https://www.espruino.com/Gadgetbridge) for instructions on setting up Gadgetbridge and weather.
|
||||||
|
|
||||||
|

|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
const storage = require('Storage');
|
||||||
|
const locale = require("locale");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// add modifiied 4x5 numeric font
|
||||||
|
(function(graphics) {
|
||||||
|
graphics.prototype.setFont4x5NumPretty = function() {
|
||||||
|
this.setFontCustom(atob("IQAQDJgH4/An4QXr0Fa/BwnwdrcH63BCHwfr8Ha/"),45,atob("AwIEBAQEBAQEBAQEBA=="),5);
|
||||||
|
};
|
||||||
|
})(Graphics);
|
||||||
|
|
||||||
|
// add font for days of the week
|
||||||
|
(function(graphics) {
|
||||||
|
graphics.prototype.setFontDoW = function() {
|
||||||
|
this.setFontCustom(atob("///////ADgB//////+AHAD//////gAAAH//////4D8B+A///////4AcAOAH//////4AcAOAAAAAB//////wA4AcAP//////wAAAAAAAA//////4AcAP//////wA4Af//////gAAAH//////5z85+c/OfnOAA4AcAOAH//////4AcAOAAAAAB//////wcAOAHB//////wAAAAAAAA///////ODnBzg5wc4AAAAD//////84OcH//8/+fAAAAAAAAAAAAA/z/5/8/OfnPz/5/8/wAAAD//////84OcH//////AAAAAAAAAAAAA/z/5/8/OfnPz/5/8/wAAAD//////gBwA///////AAAAAAAAAAAAA"),48,24,13);
|
||||||
|
};
|
||||||
|
})(Graphics);
|
||||||
|
|
||||||
|
|
||||||
|
const SUN = 1;
|
||||||
|
const PART_SUN = 2;
|
||||||
|
const CLOUD = 3;
|
||||||
|
const SNOW = 4;
|
||||||
|
const RAIN = 5;
|
||||||
|
const STORM = 6;
|
||||||
|
const ERR = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Choose weather icon based on weather const
|
||||||
|
Weather icons from https://icons8.com/icon/set/weather/ios-glyphs
|
||||||
|
Error icon from https://icons8.com/icon/set/error-cloud/ios-glyphs
|
||||||
|
**/
|
||||||
|
function weatherIcon(weather) {
|
||||||
|
switch (weather) {
|
||||||
|
case SUN:
|
||||||
|
return atob("Hh4BAAAAAAAMAAAAMAAAAMAAAAMAABgMBgBwADgA4AHAAY/GAAB/gAAD/wAAH/4AAP/8AAP/8AfP/8+fP/8+AP/8AAP/8AAH/4AAD/wAAB/gAAY/GAA4AHABwADgBgMBgAAMAAAAMAAAAMAAAAMAAAAAAAA=");
|
||||||
|
case PART_SUN:
|
||||||
|
return atob("Hh4BAAAAAAAAAAAMAAAAMAAAEMIAAOAcAAGAYAAAeAAAA/AAAB/gAA5/gAA5/g+AB+D/gA4H/wAR//wGD//4OD//4EH//4AH//4Af//+Af//+A////A////A////A///+Af//+AH//4AAAAAAAAAAAAAAAA=");
|
||||||
|
case CLOUD:
|
||||||
|
return atob("Hh4BAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAf+AAA//AAB//gAf//gB///wB///wD///wD///wP///8f///+f///+////////////////////f///+f///+P///8D///wAAAAAAAAAAAAAAAAAAAAAAAAAA=");
|
||||||
|
case SNOW:
|
||||||
|
return atob("Hh4BAAAAAAAAAAAAAAAAAHwAAAf8AAA/+AAH/+AAf//AAf8/AA/8/AB/gHgH/wP4H/wP4P/gH8P/8/8P/8/8P///4H///4B///gAAAAAAMAAAAMAAAB/gGAA/AfgA/AfgB/gfgAMAfgAMAGAAAAAAAAAAAA=");
|
||||||
|
case RAIN:
|
||||||
|
return atob("Hh4BAAAAAAAAAAAAAAAAAHwAAAf8AAA/+AAH/+AAf//AAf//AA///AB///gH///4H///4P///8P///8P///8P///4H///4B///gAAAAAAAAAABgBgABgBgABhhhgABgBgABgBgAAAAAAAAAAAAAAAAAAAAA=");
|
||||||
|
case STORM:
|
||||||
|
return atob("Hh4BAAAAAAAAAAAAAAAAAHwAAAf8AAA/+AAH/+AAf//AAf//AA///AB///gH///4H/x/4P/g/8P/k/8P/E/8P/M/4H+MP4B+cHgAAfgAAA/gABg/AABgHAABgGBgAAGBgAAEBgAAEAAAAAAAAAAAAAAAAAA=");
|
||||||
|
case ERR:
|
||||||
|
default:
|
||||||
|
return atob("Hh4BAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAf+AAA//AAB//gAf//gB///wB/z/wD/z/wD/z/wP/z/8f/z/+f/z/+//z//////////////z//f/z/+f///+P///8D///wAAAAAAAAAAAAAAAAAAAAAAAAAA=");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Choose weather icon to display based on condition.
|
||||||
|
Based on function from the Bangle weather app so it should handle all of the conditions
|
||||||
|
sent from gadget bridge.
|
||||||
|
*/
|
||||||
|
function chooseIcon(condition) {
|
||||||
|
condition = condition.toLowerCase();
|
||||||
|
if (condition.includes("thunderstorm")) return weatherIcon(STORM);
|
||||||
|
if (condition.includes("freezing")||condition.includes("snow")||
|
||||||
|
condition.includes("sleet")) {
|
||||||
|
return weatherIcon(SNOW);
|
||||||
|
}
|
||||||
|
if (condition.includes("drizzle")||
|
||||||
|
condition.includes("shower")) {
|
||||||
|
return weatherIcon(RAIN);
|
||||||
|
}
|
||||||
|
if (condition.includes("rain")) return weatherIcon(RAIN);
|
||||||
|
if (condition.includes("clear")) return weatherIcon(SUN);
|
||||||
|
if (condition.includes("few clouds")) return weatherIcon(PART_SUN);
|
||||||
|
if (condition.includes("scattered clouds")) return weatherIcon(CLOUD);
|
||||||
|
if (condition.includes("clouds")) return weatherIcon(CLOUD);
|
||||||
|
if (condition.includes("mist") ||
|
||||||
|
condition.includes("smoke") ||
|
||||||
|
condition.includes("haze") ||
|
||||||
|
condition.includes("sand") ||
|
||||||
|
condition.includes("dust") ||
|
||||||
|
condition.includes("fog") ||
|
||||||
|
condition.includes("ash") ||
|
||||||
|
condition.includes("squalls") ||
|
||||||
|
condition.includes("tornado")) {
|
||||||
|
return weatherIcon(CLOUD);
|
||||||
|
}
|
||||||
|
return weatherIcon(CLOUD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Choose weather icon to display based on weather conditition code
|
||||||
|
* https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
|
||||||
|
*/
|
||||||
|
function chooseIconByCode(code) {
|
||||||
|
const codeGroup = Math.round(code / 100);
|
||||||
|
switch (codeGroup) {
|
||||||
|
case 2: return weatherIcon(STORM);
|
||||||
|
case 3: return weatherIcon(RAIN);
|
||||||
|
case 5: return weatherIcon(RAIN);
|
||||||
|
case 6: return weatherIcon(SNOW);
|
||||||
|
case 7: return weatherIcon(CLOUD);
|
||||||
|
case 8:
|
||||||
|
switch (code) {
|
||||||
|
case 800: return weatherIcon(SUN);
|
||||||
|
case 801: return weatherIcon(PART_SUN);
|
||||||
|
default: return weatherIcon(CLOUD);
|
||||||
|
}
|
||||||
|
default: return weatherIcon(CLOUD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get weather stored in json file by weather app.
|
||||||
|
*/
|
||||||
|
function getWeather() {
|
||||||
|
let jsonWeather = storage.readJSON('weather.json');
|
||||||
|
return jsonWeather;
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeout used to update every minute
|
||||||
|
var drawTimeout;
|
||||||
|
|
||||||
|
// schedule a draw for the next minute
|
||||||
|
function queueDraw() {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function() {
|
||||||
|
drawTimeout = undefined;
|
||||||
|
draw();
|
||||||
|
},60000-(Date.now()%60000));
|
||||||
|
}
|
||||||
|
|
||||||
|
// only draw the first time
|
||||||
|
function drawBg() {
|
||||||
|
var bgImg = require("heatshrink").decompress(atob("2E7wINKn///+AEaIVUgIUB//wCs/5CtRXrCvMD8AVTg4LFCv4VZ/iSLCrwWMCrMOAQMPCp7cBCojjFCo/xFgIVQgeHCopABCpcH44Vuh/AQQX/wAV7+F/Cq/nCsw/CCqyvRCvgODCqfAgEDCp4QCSIIVQgIOBDQgGDABX/NgIECCp8HCrM/CgP4CqKaCCqSfCCqq1BCqBuB54VqgYVG/gCECp0BwgCDCp8HgYCDCo/wCo0MgHAjACBj7rDABS1Bv4lBv4rPAAsPCo3+gbbPJAIVFiAXMFZ2AUQsAuAQHiOAgJeEA"));
|
||||||
|
g.reset();
|
||||||
|
g.drawImage(bgImg,0,101);
|
||||||
|
}
|
||||||
|
|
||||||
|
function square(x,y,w,e) {
|
||||||
|
g.setColor("#000").fillRect(x,y,x+w,y+w);
|
||||||
|
g.setColor("#fff").fillRect(x+e,y+e,x+w-e,y+w-e);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
var d = new Date();
|
||||||
|
var h = d.getHours(), m = d.getMinutes();
|
||||||
|
h = ("0"+h).substr(-2);
|
||||||
|
m = ("0"+m).substr(-2);
|
||||||
|
|
||||||
|
var day = d.getDate(), mon = d.getMonth(), dow = d.getDay();
|
||||||
|
day = ("0"+day).substr(-2);
|
||||||
|
mon = ("0"+(mon+1)).substr(-2);
|
||||||
|
dow = ((dow+6)%7).toString();
|
||||||
|
date = day+"."+mon;
|
||||||
|
|
||||||
|
var weatherJson = getWeather();
|
||||||
|
var wIcon;
|
||||||
|
var temp;
|
||||||
|
if(weatherJson && weatherJson.weather){
|
||||||
|
var currentWeather = weatherJson.weather;
|
||||||
|
temp = locale.temp(currentWeather.temp-273.15).match(/^(\D*\d*)(.*)$/);
|
||||||
|
const code = currentWeather.code||-1;
|
||||||
|
if (code > 0) {
|
||||||
|
wIcon = chooseIconByCode(code);
|
||||||
|
} else {
|
||||||
|
wIcon = chooseIcon(currentWeather.txt);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
temp = "";
|
||||||
|
wIcon = weatherIcon(ERR);
|
||||||
|
}
|
||||||
|
g.reset();
|
||||||
|
g.clearRect(22,35,153,75);
|
||||||
|
g.setFont("4x5NumPretty",8);
|
||||||
|
g.fillRect(84,42,92,49);
|
||||||
|
g.fillRect(84,60,92,67);
|
||||||
|
g.drawString(h,22,35);
|
||||||
|
g.drawString(m,98,35);
|
||||||
|
|
||||||
|
g.clearRect(22,95,22+4*2*4+2*4,95+2*5);
|
||||||
|
g.setFont("4x5NumPretty",2);
|
||||||
|
g.drawString(date,22,95);
|
||||||
|
|
||||||
|
g.clearRect(22,79,22+24,79+13);
|
||||||
|
g.setFont("DoW");
|
||||||
|
g.drawString(dow,22,79);
|
||||||
|
|
||||||
|
g.drawImage(wIcon,126,81);
|
||||||
|
|
||||||
|
g.clearRect(108,114,176,114+4*5);
|
||||||
|
if (temp != "") {
|
||||||
|
var tempWidth;
|
||||||
|
const mid=126+15;
|
||||||
|
if (temp[1][0]=="-") {
|
||||||
|
// do not account for - when aligning
|
||||||
|
const minusWidth=3*4;
|
||||||
|
tempWidth = minusWidth+(temp[1].length-1)*4*4;
|
||||||
|
x = mid-Math.round((tempWidth-minusWidth)/2)-minusWidth;
|
||||||
|
} else {
|
||||||
|
tempWidth = temp[1].length*4*4;
|
||||||
|
x = mid-Math.round(tempWidth/2);
|
||||||
|
}
|
||||||
|
g.setFont("4x5NumPretty",4);
|
||||||
|
g.drawString(temp[1],x,114);
|
||||||
|
square(x+tempWidth,114,6,2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// queue draw in one minute
|
||||||
|
queueDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
drawBg();
|
||||||
|
Bangle.setUI("clock"); // Show launcher when middle button pressed
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
draw();
|
||||||
|
|
After Width: | Height: | Size: 8.4 KiB |
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwgJC/AAVh/E/hgFC/O/AoMB8EZwc8AoUYgYFBgFgjAXDAowXBAo8B/ARBn4FGAAsBmAFE2ADBhwFEj4VEn+AgPvAontgfwv+ABIMCMwIVCgf4FIWAAoN3sAFCwERoEB0MHwF3gEF0MPwFEAoW/4ALD/4tCg/hAoYhB/5ZDwF+Aok0gEIkEf/4AB8eMBoM2bkw="))
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"id": "dinoClock",
|
||||||
|
"name": "Dino Clock",
|
||||||
|
"description": "Clock with dino from Chrome",
|
||||||
|
"screenshots": [{"url":"screens/screen1.png"}],
|
||||||
|
"icon": "app.png",
|
||||||
|
"version": "0.01",
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock, weather, dino, trex, chrome",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"allow_emulator": true,
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"dinoClock.app.js","url":"app.js"},
|
||||||
|
{"name":"dinoClock.img","url":"icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -11,3 +11,4 @@
|
||||||
0.11: Fix bangle.js 1 white icons not displaying
|
0.11: Fix bangle.js 1 white icons not displaying
|
||||||
0.12: On Bangle 2 change to swiping up/down to move between pages as to match page indicator. Swiping from left to right now loads the clock.
|
0.12: On Bangle 2 change to swiping up/down to move between pages as to match page indicator. Swiping from left to right now loads the clock.
|
||||||
0.13: Added swipeExit setting so that left-right to exit is an option
|
0.13: Added swipeExit setting so that left-right to exit is an option
|
||||||
|
0.14: Don't move pages when doing exit swipe.
|
||||||
|
|
|
@ -29,6 +29,6 @@ Bangle 2:
|
||||||
|
|
||||||
**Touch** - icon to select, scond touch launches app
|
**Touch** - icon to select, scond touch launches app
|
||||||
|
|
||||||
**Swipe Left** - move to next page of app icons
|
**Swipe Left/Up** - move to next page of app icons
|
||||||
|
|
||||||
**Swipe Right** - move to previous page of app icons
|
**Swipe Right/Down** - move to previous page of app icons
|
||||||
|
|
|
@ -93,7 +93,7 @@ Bangle.on("swipe",(dirLeftRight, dirUpDown)=>{
|
||||||
if (dirUpDown==-1||dirLeftRight==-1){
|
if (dirUpDown==-1||dirLeftRight==-1){
|
||||||
++page; if (page>maxPage) page=0;
|
++page; if (page>maxPage) page=0;
|
||||||
drawPage(page);
|
drawPage(page);
|
||||||
} else if (dirUpDown==1||dirLeftRight==1){
|
} else if (dirUpDown==1||(dirLeftRight==1 && !settings.swipeExit)){
|
||||||
--page; if (page<0) page=maxPage;
|
--page; if (page<0) page=maxPage;
|
||||||
drawPage(page);
|
drawPage(page);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "dtlaunch",
|
"id": "dtlaunch",
|
||||||
"name": "Desktop Launcher",
|
"name": "Desktop Launcher",
|
||||||
"version": "0.13",
|
"version": "0.14",
|
||||||
"description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.",
|
"description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.",
|
||||||
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
|
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
|
@ -0,0 +1,33 @@
|
||||||
|
# F9 Lander
|
||||||
|
|
||||||
|
Land a Falcon 9 booster on a drone ship.
|
||||||
|
|
||||||
|
## Game play
|
||||||
|
|
||||||
|
Attempt to land your Falcon 9 booster on a drone ship before running out of fuel.
|
||||||
|
A successful landing requires:
|
||||||
|
* setting down on the ship
|
||||||
|
* the booster has to be mostly vertical
|
||||||
|
* the landing speed cannot be too high
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
|
||||||
|
The angle of the booster is controlled by tilting the watch side-to-side. The
|
||||||
|
throttle level is controlled by tilting the watch forward and back:
|
||||||
|
* screen horizontal (face up) means no throttle
|
||||||
|
* screen vertical corresponds to full throttle
|
||||||
|
|
||||||
|
The fuel burn rate is proportional to the throttle level.
|
||||||
|
|
||||||
|
## Creators
|
||||||
|
Liam Kl. B.
|
||||||
|
|
||||||
|
Marko Kl. B.
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwcA/4AD/P8yVJkgCCye27dt2wRE//kCIuSuwRIBwgCCpwRQpIRRnYRQkmdCIvPCJICBEZ4RG/IRP/15CJ/z5IRPz4RM/gQB/n+BxICCn/z/P/BxQCDz7mIAX4Cq31/CJ+ebpiYE/IR/CNP/5IROnn//4jP5DFQ5sJCKAjPk3oCMMk4QRQAX4Ckn7jBAA/5CK8nCJPJNHA"))
|
|
@ -0,0 +1,150 @@
|
||||||
|
const falcon9 = Graphics.createImage(`
|
||||||
|
xxxxx
|
||||||
|
xxxxx xxxxx
|
||||||
|
x x
|
||||||
|
x x
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxx
|
||||||
|
xxxxxxxxx
|
||||||
|
xx xxxxx xx
|
||||||
|
xx xx`);
|
||||||
|
|
||||||
|
const droneShip = Graphics.createImage(`
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
`);
|
||||||
|
|
||||||
|
const droneX = Math.floor(Math.random()*(g.getWidth()-droneShip.width-40) + 20)
|
||||||
|
const cloudOffs = Math.floor(Math.random()*g.getWidth()/2);
|
||||||
|
|
||||||
|
const oceanHeight = g.getHeight()*0.1;
|
||||||
|
|
||||||
|
const targetY = g.getHeight()-oceanHeight-falcon9.height/2;
|
||||||
|
|
||||||
|
var booster = { x : g.getWidth()/4 + Math.random()*g.getWidth()/2,
|
||||||
|
y : 20,
|
||||||
|
vx : 0,
|
||||||
|
vy : 0,
|
||||||
|
mass : 100,
|
||||||
|
fuel : 100 };
|
||||||
|
|
||||||
|
var exploded = false;
|
||||||
|
var nExplosions = 0;
|
||||||
|
var landed = false;
|
||||||
|
|
||||||
|
const gravity = 4;
|
||||||
|
const dt = 0.1;
|
||||||
|
const fuelBurnRate = 20*(176/g.getHeight());
|
||||||
|
const maxV = 12;
|
||||||
|
|
||||||
|
function flameImageGen (throttle) {
|
||||||
|
var str = " xxx \n xxx \n";
|
||||||
|
str += "xxxxx\n".repeat(throttle);
|
||||||
|
str += " xxx \n x \n";
|
||||||
|
return Graphics.createImage(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawFalcon(x, y, throttle, angle) {
|
||||||
|
g.setColor(1, 1, 1).drawImage(falcon9, x, y, {rotate:angle});
|
||||||
|
if (throttle>0) {
|
||||||
|
var flameImg = flameImageGen(throttle);
|
||||||
|
var r = falcon9.height/2 + flameImg.height/2-1;
|
||||||
|
var xoffs = -Math.sin(angle)*r;
|
||||||
|
var yoffs = Math.cos(angle)*r;
|
||||||
|
if (Math.random()>0.7) g.setColor(1, 0.5, 0);
|
||||||
|
else g.setColor(1, 1, 0);
|
||||||
|
g.drawImage(flameImg, x+xoffs, y+yoffs, {rotate:angle});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBG() {
|
||||||
|
g.setBgColor(0.2, 0.2, 1).clear();
|
||||||
|
g.setColor(0, 0, 1).fillRect(0, g.getHeight()-oceanHeight, g.getWidth()-1, g.getHeight()-1);
|
||||||
|
g.setColor(0.5, 0.5, 1).fillCircle(cloudOffs+34, 30, 15).fillCircle(cloudOffs+60, 35, 20).fillCircle(cloudOffs+75, 20, 10);
|
||||||
|
g.setColor(1, 1, 0).fillCircle(g.getWidth(), 0, 20);
|
||||||
|
g.setColor(1, 1, 1).drawImage(droneShip, droneX, g.getHeight()-oceanHeight-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showFuel() {
|
||||||
|
g.setColor(0, 0, 0).setFont("4x6:2").setFontAlign(-1, -1, 0).drawString("Fuel: "+Math.abs(booster.fuel).toFixed(0), 4, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderScreen(input) {
|
||||||
|
drawBG();
|
||||||
|
showFuel();
|
||||||
|
drawFalcon(booster.x, booster.y, Math.floor(input.throttle*12), input.angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInputs() {
|
||||||
|
var accel = Bangle.getAccel();
|
||||||
|
var a = Math.PI/2 + Math.atan2(accel.y, accel.x);
|
||||||
|
var t = (1+accel.z);
|
||||||
|
if (t > 1) t = 1;
|
||||||
|
if (t < 0) t = 0;
|
||||||
|
if (booster.fuel<=0) t = 0;
|
||||||
|
return {throttle: t, angle: a};
|
||||||
|
}
|
||||||
|
|
||||||
|
function epilogue(str) {
|
||||||
|
g.setFont("Vector", 24).setFontAlign(0, 0, 0).setColor(0, 0, 0).drawString(str, g.getWidth()/2, g.getHeight()/2).flip();
|
||||||
|
g.setFont("Vector", 16).drawString("<= again exit =>", g.getWidth()/2, g.getHeight()/2+20);
|
||||||
|
clearInterval(stepInterval);
|
||||||
|
Bangle.on("swipe", (d) => { if (d>0) load(); else load('f9lander.app.js'); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function gameStep() {
|
||||||
|
if (exploded) {
|
||||||
|
if (nExplosions++ < 15) {
|
||||||
|
var r = Math.random()*25;
|
||||||
|
var x = Math.random()*30 - 15;
|
||||||
|
var y = Math.random()*30 - 15;
|
||||||
|
g.setColor(1, Math.random()*0.5+0.5, 0).fillCircle(booster.x+x, booster.y+y, r);
|
||||||
|
if (nExplosions==1) Bangle.buzz(600);
|
||||||
|
}
|
||||||
|
else epilogue("You crashed!");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var input = getInputs();
|
||||||
|
if (booster.y >= targetY) {
|
||||||
|
// console.log(booster.x + " " + booster.y + " " + booster.vy + " " + droneX + " " + input.angle);
|
||||||
|
if (Math.abs(booster.x-droneX-droneShip.width/2)<droneShip.width/2 && Math.abs(input.angle)<Math.PI/8 && booster.vy<maxV) {
|
||||||
|
renderScreen({angle:0, throttle:0});
|
||||||
|
epilogue("You landed!");
|
||||||
|
}
|
||||||
|
else exploded = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
booster.x += booster.vx*dt;
|
||||||
|
booster.y += booster.vy*dt;
|
||||||
|
booster.vy += gravity*dt;
|
||||||
|
booster.fuel -= input.throttle*dt*fuelBurnRate;
|
||||||
|
booster.vy += -Math.cos(input.angle)*input.throttle*gravity*3*dt;
|
||||||
|
booster.vx += Math.sin(input.angle)*input.throttle*gravity*3*dt;
|
||||||
|
renderScreen(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stepInterval;
|
||||||
|
Bangle.setLCDTimeout(0);
|
||||||
|
renderScreen({angle:0, throttle:0});
|
||||||
|
g.setFont("Vector", 24).setFontAlign(0, 0, 0).setColor(0, 0, 0).drawString("Swipe to start", g.getWidth()/2, g.getHeight()/2);
|
||||||
|
Bangle.on("swipe", () => {
|
||||||
|
stepInterval = setInterval(gameStep, Math.floor(1000*dt));
|
||||||
|
Bangle.removeListener("swipe");
|
||||||
|
});
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 722 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,15 @@
|
||||||
|
{ "id": "f9lander",
|
||||||
|
"name": "Falcon9 Lander",
|
||||||
|
"shortName":"F9lander",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Land a rocket booster",
|
||||||
|
"icon": "f9lander.png",
|
||||||
|
"screenshots" : [ { "url":"f9lander_screenshot1.png" }, { "url":"f9lander_screenshot2.png" }, { "url":"f9lander_screenshot3.png" }],
|
||||||
|
"readme": "README.md",
|
||||||
|
"tags": "game",
|
||||||
|
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"f9lander.app.js","url":"app.js"},
|
||||||
|
{"name":"f9lander.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,2 +1,4 @@
|
||||||
0.01: New Clock Nifty A
|
0.01: New Clock Nifty A
|
||||||
0.02: Shows the current week number (ISO8601), can be disabled via settings ""
|
0.02: Shows the current week number (ISO8601), can be disabled via settings
|
||||||
|
0.03: Call setUI before loading widgets
|
||||||
|
Improve settings page
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
# Nifty-A Clock
|
# Nifty-A Clock
|
||||||
|
|
||||||
Colors are black/white - photos have non correct camera color "blue"
|
Colors are black/white - photos have non correct camera color "blue".
|
||||||
|
|
||||||
## This is the clock
|
This is the clock:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## The week number (ISO8601) can be turned of in settings
|
The week number (ISO8601) can be turned off in settings (default is `On`)
|
||||||
(default is **"On"**)
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const locale = require("locale");
|
const locale = require("locale");
|
||||||
const is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
|
const is12Hour = Object.assign({ "12hour": false }, require("Storage").readJSON("setting.json", true))["12hour"];
|
||||||
const CFG = require('Storage').readJSON("ffcniftya.json", 1) || {showWeekNum: true};
|
const showWeekNum = Object.assign({ showWeekNum: true }, require('Storage').readJSON("ffcniftya.json", true))["showWeekNum"];
|
||||||
|
|
||||||
/* Clock *********************************************/
|
/* Clock *********************************************/
|
||||||
const scale = g.getWidth() / 176;
|
const scale = g.getWidth() / 176;
|
||||||
|
@ -17,16 +17,17 @@ const center = {
|
||||||
y: Math.round(((viewport.height - widget) / 2) + widget),
|
y: Math.round(((viewport.height - widget) / 2) + widget),
|
||||||
}
|
}
|
||||||
|
|
||||||
function ISO8601_week_no(date) { //copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480
|
// copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480
|
||||||
var tdt = new Date(date.valueOf());
|
function ISO8601_week_no(date) {
|
||||||
var dayn = (date.getDay() + 6) % 7;
|
var tdt = new Date(date.valueOf());
|
||||||
tdt.setDate(tdt.getDate() - dayn + 3);
|
var dayn = (date.getDay() + 6) % 7;
|
||||||
var firstThursday = tdt.valueOf();
|
tdt.setDate(tdt.getDate() - dayn + 3);
|
||||||
tdt.setMonth(0, 1);
|
var firstThursday = tdt.valueOf();
|
||||||
if (tdt.getDay() !== 4) {
|
tdt.setMonth(0, 1);
|
||||||
tdt.setMonth(0, 1 + ((4 - tdt.getDay()) + 7) % 7);
|
if (tdt.getDay() !== 4) {
|
||||||
}
|
tdt.setMonth(0, 1 + ((4 - tdt.getDay()) + 7) % 7);
|
||||||
return 1 + Math.ceil((firstThursday - tdt) / 604800000);
|
}
|
||||||
|
return 1 + Math.ceil((firstThursday - tdt) / 604800000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function d02(value) {
|
function d02(value) {
|
||||||
|
@ -59,7 +60,7 @@ function draw() {
|
||||||
g.drawString(year, centerDatesScaleX, center.y - 62 * scale);
|
g.drawString(year, centerDatesScaleX, center.y - 62 * scale);
|
||||||
g.drawString(month, centerDatesScaleX, center.y - 44 * scale);
|
g.drawString(month, centerDatesScaleX, center.y - 44 * scale);
|
||||||
g.drawString(day, centerDatesScaleX, center.y - 26 * scale);
|
g.drawString(day, centerDatesScaleX, center.y - 26 * scale);
|
||||||
if (CFG.showWeekNum) g.drawString(d02(ISO8601_week_no(now)), centerDatesScaleX, center.y + 15 * scale);
|
if (showWeekNum) g.drawString(weekNum, centerDatesScaleX, center.y + 15 * scale);
|
||||||
g.drawString(monthName, centerDatesScaleX, center.y + 48 * scale);
|
g.drawString(monthName, centerDatesScaleX, center.y + 48 * scale);
|
||||||
g.drawString(dayName, centerDatesScaleX, center.y + 66 * scale);
|
g.drawString(dayName, centerDatesScaleX, center.y + 66 * scale);
|
||||||
}
|
}
|
||||||
|
@ -79,7 +80,6 @@ function clearTickTimer() {
|
||||||
function queueNextTick() {
|
function queueNextTick() {
|
||||||
clearTickTimer();
|
clearTickTimer();
|
||||||
tickTimer = setTimeout(tick, 60000 - (Date.now() % 60000));
|
tickTimer = setTimeout(tick, 60000 - (Date.now() % 60000));
|
||||||
// tickTimer = setTimeout(tick, 3000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
|
@ -91,21 +91,16 @@ function tick() {
|
||||||
|
|
||||||
// Clear the screen once, at startup
|
// Clear the screen once, at startup
|
||||||
g.clear();
|
g.clear();
|
||||||
// Start ticking
|
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
// Stop updates when LCD is off, restart when on
|
|
||||||
Bangle.on('lcdPower', (on) => {
|
Bangle.on('lcdPower', (on) => {
|
||||||
if (on) {
|
if (on) {
|
||||||
tick(); // Start ticking
|
tick();
|
||||||
} else {
|
} else {
|
||||||
clearTickTimer(); // stop ticking
|
clearTickTimer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load widgets
|
Bangle.setUI("clock");
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
// Show launcher when middle button pressed
|
|
||||||
Bangle.setUI("clock");
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "ffcniftya",
|
"id": "ffcniftya",
|
||||||
"name": "Nifty-A Clock",
|
"name": "Nifty-A Clock",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "A nifty clock with time and date",
|
"description": "A nifty clock with time and date",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot_nifty.png"}],
|
"screenshots": [{"url":"screenshot_nifty.png"}],
|
||||||
|
|
|
@ -1,23 +1,15 @@
|
||||||
(function(back) {
|
(function (back) {
|
||||||
var FILE = "ffcniftya.json";
|
const settings = Object.assign({ showWeekNum: true }, require("Storage").readJSON("ffcniftya.json", true));
|
||||||
// Load settings
|
|
||||||
var cfg = require('Storage').readJSON(FILE, 1) || { showWeekNum: true };
|
|
||||||
|
|
||||||
function writeSettings() {
|
|
||||||
require('Storage').writeJSON(FILE, cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show the menu
|
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"" : { "title" : "Nifty-A Clock" },
|
"": { "title": "Nifty-A Clock" },
|
||||||
"< Back" : () => back(),
|
"< Back": () => back(),
|
||||||
'week number?': {
|
/*LANG*/"Show Week Number": {
|
||||||
value: cfg.showWeekNum,
|
value: settings.showWeekNum,
|
||||||
format: v => v?"On":"Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
cfg.showWeekNum = v;
|
settings.showWeekNum = v;
|
||||||
writeSettings();
|
require("Storage").writeJSON("ffcniftya.json", settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
0.01: New Clock Nifty B
|
0.01: New Clock Nifty B
|
||||||
0.02: Added configuration
|
0.02: Added configuration
|
||||||
|
0.03: Call setUI before loading widgets
|
||||||
|
Fix bug with black being unselectable
|
||||||
|
Improve settings page
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
# Nifty Series B Clock
|
# Nifty Series B Clock
|
||||||
|
|
||||||
- Display Time and Date
|
- Display Time and Date
|
||||||
- Color Configuration
|
- Colour Configuration
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
const locale = require("locale");
|
const is12Hour = Object.assign({ "12hour": false }, require("Storage").readJSON("setting.json", true))["12hour"];
|
||||||
const storage = require('Storage');
|
const color = Object.assign({ color: 63488 }, require("Storage").readJSON("ffcniftyb.json", true)).color; // Default to RED
|
||||||
|
|
||||||
const is12Hour = (storage.readJSON("setting.json", 1) || {})["12hour"];
|
|
||||||
const color = (storage.readJSON("ffcniftyb.json", 1) || {})["color"] || 63488 /* red */;
|
|
||||||
|
|
||||||
|
|
||||||
/* Clock *********************************************/
|
/* Clock *********************************************/
|
||||||
const scale = g.getWidth() / 176;
|
const scale = g.getWidth() / 176;
|
||||||
|
@ -19,7 +15,7 @@ const center = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function d02(value) {
|
function d02(value) {
|
||||||
return ('0' + value).substr(-2);
|
return ("0" + value).substr(-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEllipse(g) {
|
function renderEllipse(g) {
|
||||||
|
@ -35,8 +31,8 @@ function renderText(g) {
|
||||||
const month = d02(now.getMonth() + 1);
|
const month = d02(now.getMonth() + 1);
|
||||||
const year = now.getFullYear();
|
const year = now.getFullYear();
|
||||||
|
|
||||||
const month2 = locale.month(now, 3);
|
const month2 = require("locale").month(now, 3);
|
||||||
const day2 = locale.dow(now, 3);
|
const day2 = require("locale").dow(now, 3);
|
||||||
|
|
||||||
g.setFontAlign(1, 0).setFont("Vector", 90 * scale);
|
g.setFontAlign(1, 0).setFont("Vector", 90 * scale);
|
||||||
g.drawString(hour, center.x + 32 * scale, center.y - 31 * scale);
|
g.drawString(hour, center.x + 32 * scale, center.y - 31 * scale);
|
||||||
|
@ -96,7 +92,6 @@ function startTick(run) {
|
||||||
stopTick();
|
stopTick();
|
||||||
run();
|
run();
|
||||||
ticker = setTimeout(() => startTick(run), 60000 - (Date.now() % 60000));
|
ticker = setTimeout(() => startTick(run), 60000 - (Date.now() % 60000));
|
||||||
// ticker = setTimeout(() => startTick(run), 3000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init **********************************************/
|
/* Init **********************************************/
|
||||||
|
@ -104,7 +99,7 @@ function startTick(run) {
|
||||||
g.clear();
|
g.clear();
|
||||||
startTick(draw);
|
startTick(draw);
|
||||||
|
|
||||||
Bangle.on('lcdPower', (on) => {
|
Bangle.on("lcdPower", (on) => {
|
||||||
if (on) {
|
if (on) {
|
||||||
startTick(draw);
|
startTick(draw);
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,7 +107,6 @@ Bangle.on('lcdPower', (on) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Bangle.setUI("clock");
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
Bangle.setUI("clock");
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"id": "ffcniftyb",
|
"id": "ffcniftyb",
|
||||||
"name": "Nifty-B Clock",
|
"name": "Nifty-B Clock",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "A nifty clock (series B) with time, date and color configuration",
|
"description": "A nifty clock (series B) with time, date and colour configuration",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -1,49 +1,31 @@
|
||||||
(function (back) {
|
(function (back) {
|
||||||
const storage = require('Storage');
|
const settings = Object.assign({ color: 63488 }, require("Storage").readJSON("ffcniftyb.json", true));
|
||||||
const SETTINGS_FILE = "ffcniftyb.json";
|
|
||||||
|
|
||||||
const colors = {
|
const colors = {
|
||||||
65535: 'White',
|
65535: /*LANG*/"White",
|
||||||
63488: 'Red',
|
63488: /*LANG*/"Red",
|
||||||
65504: 'Yellow',
|
65504: /*LANG*/"Yellow",
|
||||||
2047: 'Cyan',
|
2047: /*LANG*/"Cyan",
|
||||||
2016: 'Green',
|
2016: /*LANG*/"Green",
|
||||||
31: 'Blue',
|
31: /*LANG*/"Blue",
|
||||||
0: 'Black',
|
0: /*LANG*/"Black"
|
||||||
}
|
}
|
||||||
|
|
||||||
function load(settings) {
|
const menu = {};
|
||||||
return Object.assign(settings, storage.readJSON(SETTINGS_FILE, 1) || {});
|
menu[""] = { title: "Nifty-B Clock" };
|
||||||
}
|
menu["< Back"] = back;
|
||||||
|
|
||||||
function save(settings) {
|
Object.keys(colors).forEach(color => {
|
||||||
storage.write(SETTINGS_FILE, settings)
|
var label = colors[color];
|
||||||
}
|
menu[label] = {
|
||||||
|
value: settings.color == color,
|
||||||
const settings = load({
|
onchange: () => {
|
||||||
color: 63488 /* red */,
|
settings.color = color;
|
||||||
|
require("Storage").write("ffcniftyb.json", settings);
|
||||||
|
setTimeout(load, 10);
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const saveColor = (color) => () => {
|
E.showMenu(menu);
|
||||||
settings.color = color;
|
|
||||||
save(settings);
|
|
||||||
back();
|
|
||||||
};
|
|
||||||
|
|
||||||
function showMenu(items, opt) {
|
|
||||||
items[''] = opt || {};
|
|
||||||
items['< Back'] = back;
|
|
||||||
E.showMenu(items);
|
|
||||||
}
|
|
||||||
|
|
||||||
showMenu(
|
|
||||||
Object.keys(colors).reduce((menu, color) => {
|
|
||||||
menu[colors[color]] = saveColor(color);
|
|
||||||
return menu;
|
|
||||||
}, {}),
|
|
||||||
{
|
|
||||||
title: 'Color',
|
|
||||||
selected: Object.keys(colors).indexOf(settings.color)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,4 +6,5 @@
|
||||||
0.06: Fixed issue 1609 added a message popup state handler to control unwanted screen redraw
|
0.06: Fixed issue 1609 added a message popup state handler to control unwanted screen redraw
|
||||||
0.07: Optimized the mover algorithm for efficiency (work in progress)
|
0.07: Optimized the mover algorithm for efficiency (work in progress)
|
||||||
0.08: Bug fix at end of the game with victorious splash and glorious orchestra
|
0.08: Bug fix at end of the game with victorious splash and glorious orchestra
|
||||||
0.09: Added settings menu, removed symbol selection button (*), added highscore reset
|
0.09: Added settings menu, removed symbol selection button (*), added highscore reset
|
||||||
|
0.10: fixed clockmode in settings
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "game1024",
|
{ "id": "game1024",
|
||||||
"name": "1024 Game",
|
"name": "1024 Game",
|
||||||
"shortName" : "1024 Game",
|
"shortName" : "1024 Game",
|
||||||
"version": "0.09",
|
"version": "0.10",
|
||||||
"icon": "game1024.png",
|
"icon": "game1024.png",
|
||||||
"screenshots": [ {"url":"screenshot.png" } ],
|
"screenshots": [ {"url":"screenshot.png" } ],
|
||||||
"readme":"README.md",
|
"readme":"README.md",
|
||||||
|
|
|
@ -32,10 +32,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Exit press:": {
|
"Exit press:": {
|
||||||
value: !settings.debugMode, // ! converts undefined to true
|
value: !settings.clockMode, // ! converts undefined to true
|
||||||
format: v => v?"short":"long",
|
format: v => v?"short":"long",
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.debugMode = v;
|
settings.clockMode = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -67,4 +67,4 @@
|
||||||
}
|
}
|
||||||
// Show the menu
|
// Show the menu
|
||||||
E.showMenu(settingsMenu);
|
E.showMenu(settingsMenu);
|
||||||
})
|
})
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.01: New App! Very limited course support.
|
0.01: New App! Very limited course support.
|
||||||
|
0.02: Course search added to BangleApps page
|
|
@ -7,30 +7,55 @@
|
||||||
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#searchresults {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchresults li:hover {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div class="container">
|
||||||
<input type="text" placeholder="Course ID" id="course_id">
|
<div class="form-group">
|
||||||
<button type="button" onclick="courseSearch();">Search</button>
|
<label class="form-label" for="course_id">Course Search</label>
|
||||||
<p id="status"></p>
|
<div class="input-group">
|
||||||
|
<input class="form-input" type="text" id="course_id" placeholder="Whistling Straits">
|
||||||
|
<button type="button" class="btn btn-primary input-group-btn" onclick="courseSearch();">Search</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="columns" id="searchresults"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
<p id="status">No course loaded, please search for a course then choose 'select'.</p>
|
||||||
<button id="upload" class="btn btn-primary" disabled="true">Upload to Device</button>
|
<button id="upload" class="btn btn-primary" disabled="true">Upload to Device</button>
|
||||||
<button id="download" class="btn btn-primary" disabled="true">Download Course</button>
|
<button id="download" class="btn btn-primary" disabled="true">Download Course</button>
|
||||||
</div>
|
</div>
|
||||||
<p>A course needs a few things to be parsed correctly by this tool.</p>
|
<div>
|
||||||
<ul>
|
<p>A course needs a few things to be parsed correctly by this tool.</p>
|
||||||
<li>See official mapping guidelines <a
|
<ul>
|
||||||
href="https://wiki.openstreetmap.org/wiki/Tag:leisure%3Dgolf_course">here</a>.</li>
|
<li>See official mapping guidelines <a
|
||||||
<li>All holes and features must be within the target course's area.</li>
|
href="https://wiki.openstreetmap.org/wiki/Tag:leisure%3Dgolf_course">here</a>.</li>
|
||||||
<li>Supported features are greens, fairways, tees, bunkers, water hazards and holes.</li>
|
<li>All holes and features must be within the target course's area.</li>
|
||||||
<li>All features for a given hole should have the "ref" tag with the hole number as value. Shared features should
|
<li>Supported features are greens, fairways, tees, bunkers, water hazards and holes.</li>
|
||||||
list ref values separated by ';'. <a href="https://www.openstreetmap.org/way/36896320">example</a>.</li>
|
<li>All features for a given hole should have the "ref" tag with the hole number as value. Shared features
|
||||||
<li>There must be 18 holes and they must have the following tags: handicap, par, ref, dist</li>
|
should
|
||||||
<li>For any mapping assistance or issues, please file in the <a
|
list ref values separated by ';'. <a href="https://www.openstreetmap.org/way/36896320">example</a>.</li>
|
||||||
href="https://github.com/espruino/BangleApps/issues/new?assignees=&labels=bug&template=bangle-bug-report-custom-form.yaml&title=[golfview]+Short+description+of+bug">official
|
<li>There must be 18 holes and they must have the following tags: handicap, par, ref, dist</li>
|
||||||
repo</a></li>
|
<li>For any mapping assistance or issues, please file in the <a
|
||||||
</ul>
|
href="https://github.com/espruino/BangleApps/issues/new?assignees=&labels=bug&template=bangle-bug-report-custom-form.yaml&title=[golfview]+Short+description+of+bug">official
|
||||||
<a href="https://www.openstreetmap.org/way/25447898">Example Course</a>
|
repo</a></li>
|
||||||
<a href="https://www.openstreetmap.org/copyright">© OpenStreetMap contributors</p>
|
</ul>
|
||||||
|
<a href="https://www.openstreetmap.org/way/25447898">Example Course</a>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<a href="https://www.openstreetmap.org/copyright">© OpenStreetMap contributors</p>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="../../core/lib/customize.js"></script>
|
<script src="../../core/lib/customize.js"></script>
|
||||||
|
@ -38,13 +63,47 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const url = "https://overpass-api.de/api/interpreter";
|
const url = "https://overpass-api.de/api/interpreter";
|
||||||
let query = `[out:json][timeout:5];way(25447898);map_to_area ->.golfcourse;way["golf"="hole"](area.golfcourse)->.holes;(relation["golf"="fairway"](area.golfcourse);way["golf"~"^(green|tee|water_hazard|bunker|fairway)"](area.golfcourse);)->.features;.holes out geom;.features out geom;`;
|
const search_url = "https://nominatim.openstreetmap.org/search";
|
||||||
|
let search_query = null;
|
||||||
let course_input = null;
|
let course_input = null;
|
||||||
|
let current_course = null;
|
||||||
|
let search_results = $("#searchresults");
|
||||||
|
|
||||||
|
function buildCourseListElement(title, subtitle, element) {
|
||||||
|
let base_element = $(`<div class="column col-4">
|
||||||
|
<div class="tile">
|
||||||
|
<div class="tile-icon">
|
||||||
|
<figure class="avatar avatar-lg"><img src="./golfview.png" alt="Avatar"></figure>
|
||||||
|
</div>
|
||||||
|
<div class="tile-content">
|
||||||
|
<p class="tile-title">${title}</p>
|
||||||
|
<p class="tile-subtitle">${subtitle}</p>
|
||||||
|
<div class="btn-group btn-group-block">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`);
|
||||||
|
|
||||||
|
// add the buttons
|
||||||
|
base_element.find('.btn-group')
|
||||||
|
.append($('<button>').addClass("btn btn-primary").text("select")
|
||||||
|
.on('click', function () {
|
||||||
|
console.log(element);
|
||||||
|
doQuery(element);
|
||||||
|
}))
|
||||||
|
.append($('<a>').attr('href', `https://www.openstreetmap.org/${element.osm_type}/${element.osm_id}`).attr('target', '_blank')
|
||||||
|
.append('<button>').addClass("btn").text("view")
|
||||||
|
);
|
||||||
|
return base_element
|
||||||
|
}
|
||||||
|
|
||||||
function courseSearch() {
|
function courseSearch() {
|
||||||
let inputVal = document.getElementById("course_id").value;
|
let inputVal = document.getElementById("course_id").value;
|
||||||
query = `[out:json][timeout:5];way(${inputVal});map_to_area ->.golfcourse;way["golf"="hole"](area.golfcourse)->.holes;(relation["golf"="fairway"](area.golfcourse);way["golf"~"^(green|tee|water_hazard|bunker|fairway)"](area.golfcourse);)->.features;.holes out geom;.features out geom;`;
|
search_query = {
|
||||||
doQuery();
|
"format": "jsonv2",
|
||||||
|
"q": inputVal,
|
||||||
|
};
|
||||||
|
doSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
function processFeatures(course_verbose) {
|
function processFeatures(course_verbose) {
|
||||||
|
@ -116,7 +175,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var courses = [];
|
var courses = [];
|
||||||
var course_name = "Davis";
|
|
||||||
$("#upload").click(function () {
|
$("#upload").click(function () {
|
||||||
sendCustomizedApp({
|
sendCustomizedApp({
|
||||||
storage: courses,
|
storage: courses,
|
||||||
|
@ -124,22 +182,45 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#download").click(function () {
|
$("#download").click(function () {
|
||||||
downloadObjectAsJSON(courses[0].content, "golfcourse-" + course_name);
|
downloadObjectAsJSON(courses[0].content, "golfcourse-download");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function testfunc(params) {
|
||||||
|
console.log(params);
|
||||||
|
}
|
||||||
|
|
||||||
// download info from the course
|
// download info from the course
|
||||||
function doQuery() {
|
function doSearch() {
|
||||||
|
$.get(search_url, search_query, function (result) {
|
||||||
|
if (result.length === 0) {
|
||||||
|
$('#status').text("Course not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
search_results.empty();
|
||||||
|
for (let index = 0; index < result.length; index++) {
|
||||||
|
const element = result[index];
|
||||||
|
if (element.type != "golf_course") continue;
|
||||||
|
search_results.append(buildCourseListElement(element.display_name.split(",")[0], element.display_name, element));
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// download info from the course
|
||||||
|
function doQuery(course) {
|
||||||
|
const query = `[out:json][timeout:5];way(${course.osm_id});map_to_area ->.golfcourse;way["golf"="hole"](area.golfcourse)->.holes;(relation["golf"="fairway"](area.golfcourse);way["golf"~"^(green|tee|water_hazard|bunker|fairway)"](area.golfcourse);)->.features;.holes out geom;.features out geom;`;
|
||||||
|
const course_id = course["osm_id"];
|
||||||
$.post(url, query, function (result) {
|
$.post(url, query, function (result) {
|
||||||
if (result.elements.length === 0) {
|
if (result.elements.length === 0) {
|
||||||
$('#status').text("Course not found!");
|
$('#status').text("Course not found!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
course_input = result;
|
console.log(result);
|
||||||
console.log(course_input);
|
out = processFeatures(result.elements);
|
||||||
out = processFeatures(course_input.elements);
|
|
||||||
console.log(out);
|
console.log(out);
|
||||||
courses.push({
|
courses.push({
|
||||||
name: "golfcourse-" + course_name + ".json",
|
name: `golf-${course_id}.json`,
|
||||||
content: JSON.stringify(out),
|
content: JSON.stringify(out),
|
||||||
});
|
});
|
||||||
$('#status').text("Course retrieved!");
|
$('#status').text("Course retrieved!");
|
||||||
|
|
|
@ -54,7 +54,9 @@ function distance(a, b) {
|
||||||
|
|
||||||
|
|
||||||
// golfview.js
|
// golfview.js
|
||||||
let course = require("Storage").readJSON("golfcourse-Davis.json").holes;//TODO use the course ID
|
let courselist = require("Storage").list(/^golf-\d+\.json$/);
|
||||||
|
let course = require("Storage").readJSON(courselist[0]).holes; // TODO use the first course for now
|
||||||
|
|
||||||
let current_hole = 1;
|
let current_hole = 1;
|
||||||
let hole = course[current_hole.toString()];
|
let hole = course[current_hole.toString()];
|
||||||
let user_position = {
|
let user_position = {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "golfview",
|
{ "id": "golfview",
|
||||||
"name": "Golf View",
|
"name": "Golf View",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "This app will provide you with on course data to support your golf game!",
|
"description": "This app will provide you with on course data to support your golf game!",
|
||||||
"icon": "golfview.png",
|
"icon": "golfview.png",
|
||||||
"tags": "outdoors, gps",
|
"tags": "outdoors, gps",
|
||||||
|
|
|
@ -13,3 +13,4 @@
|
||||||
0.12: Add setting for Daily Step Goal
|
0.12: Add setting for Daily Step Goal
|
||||||
0.13: Add support for internationalization
|
0.13: Add support for internationalization
|
||||||
0.14: Move settings
|
0.14: Move settings
|
||||||
|
0.15: Fix charts (fix #1366)
|
||||||
|
|
|
@ -46,7 +46,7 @@ function menuHRM() {
|
||||||
|
|
||||||
function stepsPerHour() {
|
function stepsPerHour() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
let data = new Uint16Array(24);
|
var data = new Uint16Array(24);
|
||||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
|
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
|
||||||
g.clear(1);
|
g.clear(1);
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
@ -57,7 +57,7 @@ function stepsPerHour() {
|
||||||
|
|
||||||
function stepsPerDay() {
|
function stepsPerDay() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
let data = new Uint16Array(31);
|
var data = new Uint16Array(31);
|
||||||
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
|
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
|
||||||
g.clear(1);
|
g.clear(1);
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
@ -68,8 +68,8 @@ function stepsPerDay() {
|
||||||
|
|
||||||
function hrmPerHour() {
|
function hrmPerHour() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
let data = new Uint16Array(24);
|
var data = new Uint16Array(24);
|
||||||
let cnt = new Uint8Array(23);
|
var cnt = new Uint8Array(23);
|
||||||
require("health").readDay(new Date(), h=>{
|
require("health").readDay(new Date(), h=>{
|
||||||
data[h.hr]+=h.bpm;
|
data[h.hr]+=h.bpm;
|
||||||
if (h.bpm) cnt[h.hr]++;
|
if (h.bpm) cnt[h.hr]++;
|
||||||
|
@ -84,8 +84,8 @@ function hrmPerHour() {
|
||||||
|
|
||||||
function hrmPerDay() {
|
function hrmPerDay() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
let data = new Uint16Array(31);
|
var data = new Uint16Array(31);
|
||||||
let cnt = new Uint8Array(31);
|
var cnt = new Uint8Array(31);
|
||||||
require("health").readDailySummaries(new Date(), h=>{
|
require("health").readDailySummaries(new Date(), h=>{
|
||||||
data[h.day]+=h.bpm;
|
data[h.day]+=h.bpm;
|
||||||
if (h.bpm) cnt[h.day]++;
|
if (h.bpm) cnt[h.day]++;
|
||||||
|
@ -100,7 +100,7 @@ function hrmPerDay() {
|
||||||
|
|
||||||
function movementPerHour() {
|
function movementPerHour() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
let data = new Uint16Array(24);
|
var data = new Uint16Array(24);
|
||||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.movement);
|
require("health").readDay(new Date(), h=>data[h.hr]+=h.movement);
|
||||||
g.clear(1);
|
g.clear(1);
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
@ -111,7 +111,7 @@ function movementPerHour() {
|
||||||
|
|
||||||
function movementPerDay() {
|
function movementPerDay() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
let data = new Uint16Array(31);
|
var data = new Uint16Array(31);
|
||||||
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement);
|
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement);
|
||||||
g.clear(1);
|
g.clear(1);
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
@ -183,7 +183,7 @@ function drawBarChart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw a fake 0 height bar if chart_index is outside the bounds of the array
|
// draw a fake 0 height bar if chart_index is outside the bounds of the array
|
||||||
if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len)
|
if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len && chart_max_datum > 0)
|
||||||
bar_top = bar_bot - 100 * (chart_data[chart_index + bar - 1]) / chart_max_datum;
|
bar_top = bar_bot - 100 * (chart_data[chart_index + bar - 1]) / chart_max_datum;
|
||||||
else
|
else
|
||||||
bar_top = bar_bot;
|
bar_top = bar_bot;
|
||||||
|
|