Merge branch 'master' into lcd-brightness-setting
commit
2dfbbfd64e
13
apps.json
13
apps.json
|
|
@ -27,7 +27,7 @@
|
|||
{ "id": "daysl",
|
||||
"name": "Days left",
|
||||
"icon": "app.png",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "Shows you the days left until a certain date. Date can be set with a settings app and is written to a file.",
|
||||
"tags": "",
|
||||
"allow_emulator":false,
|
||||
|
|
@ -118,11 +118,12 @@
|
|||
{ "id": "setting",
|
||||
"name": "Settings",
|
||||
"icon": "settings.png",
|
||||
"version":"0.09",
|
||||
"version":"0.10",
|
||||
"description": "A menu for setting up Bangle.js",
|
||||
"tags": "tool,system",
|
||||
"storage": [
|
||||
{"name":"setting.app.js","url":"settings.js"},
|
||||
{"name":"setting.boot.js","url":"boot.js"},
|
||||
{"name":"setting.json","url":"settings-default.json","evaluate":true},
|
||||
{"name":"setting.img","url":"settings-icon.js","evaluate":true}
|
||||
],
|
||||
|
|
@ -160,7 +161,7 @@
|
|||
{ "id": "aclock",
|
||||
"name": "Analog Clock",
|
||||
"icon": "clock-analog.png",
|
||||
"version":"0.10",
|
||||
"version": "0.11",
|
||||
"description": "An Analog Clock",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
|
|
@ -339,7 +340,7 @@
|
|||
"name": "Battery Level Widget (with percentage)",
|
||||
"shortName": "Battery Widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.07",
|
||||
"version":"0.08",
|
||||
"description": "Show the current battery level and charging status in the top right of the clock, with charge percentage",
|
||||
"tags": "widget,battery",
|
||||
"type":"widget",
|
||||
|
|
@ -871,7 +872,7 @@
|
|||
{ "id": "wohrm",
|
||||
"name": "Workout HRM",
|
||||
"icon": "app.png",
|
||||
"version":"0.05",
|
||||
"version":"0.06",
|
||||
"description": "Workout heart rate monitor notifies you with a buzz if your heart rate goes above or below the set limits.",
|
||||
"tags": "hrm,workout",
|
||||
"type": "app",
|
||||
|
|
@ -1030,7 +1031,7 @@
|
|||
"name": "Touch Launcher",
|
||||
"shortName":"Menu",
|
||||
"icon": "app.png",
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"description": "Touch enable left to right launcher.",
|
||||
"tags": "tool,system,launcher",
|
||||
"type":"launch",
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@
|
|||
0.08: make dots bigger and date more readable
|
||||
0.09: center date, remove box around it, internal refactor to remove redundant code.
|
||||
0.10: remove debug, refactor seconds to show elapsed secs each time app is displayed
|
||||
0.11: shift face down for widget area, maximize face size, 0 pad single digit date, use locale for date
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// eliminate ide undefined errors
|
||||
let g;
|
||||
let Bangle;
|
||||
|
||||
|
|
@ -5,15 +6,18 @@ let Bangle;
|
|||
const locale = require('locale');
|
||||
const p = Math.PI / 2;
|
||||
const pRad = Math.PI / 180;
|
||||
const faceWidth = 100; // watch face radius
|
||||
const faceWidth = 100; // watch face radius (240/2 - 24px for widget area)
|
||||
const widgetHeight=24+1;
|
||||
let timer = null;
|
||||
let currentDate = new Date();
|
||||
const centerPx = g.getWidth() / 2;
|
||||
const centerX = g.getWidth() / 2;
|
||||
const centerY = (g.getWidth() / 2) + widgetHeight/2;
|
||||
|
||||
|
||||
const seconds = (angle) => {
|
||||
const a = angle * pRad;
|
||||
const x = centerPx + Math.sin(a) * faceWidth;
|
||||
const y = centerPx - Math.cos(a) * faceWidth;
|
||||
const x = centerX + Math.sin(a) * faceWidth;
|
||||
const y = centerY - Math.cos(a) * faceWidth;
|
||||
|
||||
// if 15 degrees, make hour marker larger
|
||||
const radius = (angle % 15) ? 2 : 4;
|
||||
|
|
@ -25,14 +29,14 @@ const hand = (angle, r1, r2) => {
|
|||
const r3 = 3;
|
||||
|
||||
g.fillPoly([
|
||||
Math.round(centerPx + Math.sin(a) * r1),
|
||||
Math.round(centerPx - Math.cos(a) * r1),
|
||||
Math.round(centerPx + Math.sin(a + p) * r3),
|
||||
Math.round(centerPx - Math.cos(a + p) * r3),
|
||||
Math.round(centerPx + Math.sin(a) * r2),
|
||||
Math.round(centerPx - Math.cos(a) * r2),
|
||||
Math.round(centerPx + Math.sin(a - p) * r3),
|
||||
Math.round(centerPx - Math.cos(a - p) * r3)
|
||||
Math.round(centerX + Math.sin(a) * r1),
|
||||
Math.round(centerY - Math.cos(a) * r1),
|
||||
Math.round(centerX + Math.sin(a + p) * r3),
|
||||
Math.round(centerY - Math.cos(a + p) * r3),
|
||||
Math.round(centerX + Math.sin(a) * r2),
|
||||
Math.round(centerY - Math.cos(a) * r2),
|
||||
Math.round(centerX + Math.sin(a - p) * r3),
|
||||
Math.round(centerY - Math.cos(a - p) * r3)
|
||||
]);
|
||||
};
|
||||
|
||||
|
|
@ -54,6 +58,7 @@ const drawAll = () => {
|
|||
seconds((360 * i) / 60);
|
||||
}
|
||||
onSecond();
|
||||
|
||||
};
|
||||
|
||||
const resetSeconds = () => {
|
||||
|
|
@ -88,8 +93,8 @@ const drawDate = () => {
|
|||
// console.log(`${dayString}|${dateString}`);
|
||||
// center date
|
||||
const l = (g.getWidth() - g.stringWidth(dateDisplay)) / 2;
|
||||
const t = centerPx + 37;
|
||||
g.drawString(dateDisplay, l, t);
|
||||
const t = centerY + 37;
|
||||
g.drawString(dateDisplay, l, t, true);
|
||||
// console.log(l, t);
|
||||
};
|
||||
const onMinute = () => {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New Widget!
|
||||
0.02: Improved calculation, new image for app
|
||||
0.03: Improved display of number
|
||||
|
|
|
|||
|
|
@ -1,39 +1,84 @@
|
|||
const storage = require('Storage');
|
||||
let settings;
|
||||
let height = 23;
|
||||
let width = 34;
|
||||
|
||||
var debug = 0; //1 = show debug info
|
||||
|
||||
//write settings to file
|
||||
function updateSettings() {
|
||||
storage.write('daysleft.json', settings);
|
||||
}
|
||||
|
||||
//Define standard settings
|
||||
function resetSettings() {
|
||||
settings = {
|
||||
day : 17,
|
||||
month : 6,
|
||||
year: 2020
|
||||
};
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
settings = storage.readJSON('daysleft.json',1); //read storage
|
||||
if (!settings) resetSettings(); //if settings file was not found, set to standard
|
||||
|
||||
var dd = settings.day,
|
||||
mm = settings.month-1, //-1 because month is zero-based
|
||||
yy = settings.year;
|
||||
|
||||
const oneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
|
||||
const targetDate = new Date(yy, mm, dd); //is 00:00
|
||||
const today = new Date(); //includes current time
|
||||
|
||||
const currentYear = today.getFullYear();
|
||||
const currentMonth = today.getMonth();
|
||||
const currentDay = today.getDate();
|
||||
const todayMorning = new Date (currentYear, currentMonth, currentDay, 0, 0, 0); //create date object with today, but 00:00:00
|
||||
|
||||
const diffDays = (targetDate - todayMorning) / oneDay; //calculate day difference
|
||||
|
||||
function drawWidget() {
|
||||
if (debug == 1) g.drawRect(this.x,this.y,this.x+width,this.y+height); //draw rectangle around widget area
|
||||
g.reset();
|
||||
|
||||
//define font size and string position
|
||||
//small if number has more than 3 digits (positive number)
|
||||
if (diffDays >= 1000) {
|
||||
g.setFont("6x8", 1);
|
||||
g.drawString(diffDays,this.x+10,this.y+7);
|
||||
}
|
||||
|
||||
function resetSettings() {
|
||||
settings = {
|
||||
day : 17,
|
||||
month : 6,
|
||||
year: 2020
|
||||
};
|
||||
updateSettings();
|
||||
//large if number has 3 digits (positive number)
|
||||
if (diffDays <= 999 && diffDays >= 100) {
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString(diffDays,this.x,this.y+4);
|
||||
}
|
||||
//large if number has 2 digits (positive number)
|
||||
if (diffDays <= 99 && diffDays >= 10) {
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString(diffDays,this.x+6,this.y+4);
|
||||
}
|
||||
//large if number has 1 digit (positive number)
|
||||
if (diffDays <= 9 && diffDays >= 0) {
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString(diffDays,this.x+13,this.y+4);
|
||||
}
|
||||
//large if number has 1 digit (negative number)
|
||||
if (diffDays <= -1 && diffDays >= -9) {
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString(diffDays,this.x+5,this.y+4);
|
||||
}
|
||||
//large if number has 2 digits (negative number)
|
||||
if (diffDays <= -10 && diffDays >= -99) {
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString(diffDays,this.x,this.y+4);
|
||||
}
|
||||
//large if number has 3 digits or more (negative number)
|
||||
if (diffDays <= -100) {
|
||||
g.setFont("6x8", 1);
|
||||
g.drawString(diffDays,this.x,this.y+7);
|
||||
}
|
||||
}
|
||||
|
||||
settings = storage.readJSON('daysleft.json',1);
|
||||
if (!settings) resetSettings();
|
||||
|
||||
var dd = settings.day,
|
||||
mm = settings.month-1, //month is zero-based
|
||||
yy = settings.year;
|
||||
|
||||
const oneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
|
||||
const targetDate = new Date(yy, mm, dd);
|
||||
const today = new Date();
|
||||
|
||||
//create date object with today, but 00:00:00
|
||||
const currentYear = today.getFullYear();
|
||||
const currentMonth = today.getMonth();
|
||||
const currentDay = today.getDate();
|
||||
const todayMorning = new Date (currentYear, currentMonth, currentDay, 0, 0, 0);
|
||||
|
||||
const diffDays = (targetDate - todayMorning) / oneDay;
|
||||
|
||||
WIDGETS["daysl"]={area:"tl",width:40,draw:function(){
|
||||
g.setFont("6x8", 1);
|
||||
g.drawString(diffDays,this.x+12,this.y+12);
|
||||
}};
|
||||
//draw widget
|
||||
WIDGETS["daysl"]={area:"tl",width:width,draw:drawWidget};
|
||||
|
|
@ -6,4 +6,5 @@
|
|||
0.07: Added vibrate as beep workaround
|
||||
0.08: Add support for app/widget settings
|
||||
0.09: Move Welcome into App/widget settings
|
||||
0.10: Adds LCD brightness setting
|
||||
0.10: Added LCD wake-up settings
|
||||
Adds LCD brightness setting
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
(() => {
|
||||
var settings = require('Storage').readJSON('setting.json', true);
|
||||
if (settings != undefined) {
|
||||
Bangle.setOptions(settings.options);
|
||||
}
|
||||
})()
|
||||
|
|
@ -11,4 +11,15 @@
|
|||
"12hour" : false, // 12 or 24 hour clock?
|
||||
// welcomed : undefined/true (whether welcome app should show)
|
||||
brightness: 1, // LCD brightness from 0 to 1
|
||||
options: {
|
||||
wakeOnBTN1: true,
|
||||
wakeOnBTN2: true,
|
||||
wakeOnBTN3: true,
|
||||
wakeOnFaceUp: false,
|
||||
wakeOnTouch: false,
|
||||
wakeOnTwist: true,
|
||||
twistThreshold: 819.2,
|
||||
twistMaxY: -800,
|
||||
twistTimeout: 1000
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,21 @@ function updateSettings() {
|
|||
storage.write('setting.json', settings);
|
||||
}
|
||||
|
||||
function updateOptions() {
|
||||
updateSettings();
|
||||
Bangle.setOptions(settings.options)
|
||||
}
|
||||
|
||||
function gToInternal(g) {
|
||||
// converts g to Espruino internal unit
|
||||
return g * 8192;
|
||||
}
|
||||
|
||||
function internalToG(u) {
|
||||
// converts Espruino internal unit to g
|
||||
return u / 8192
|
||||
}
|
||||
|
||||
function resetSettings() {
|
||||
settings = {
|
||||
ble: true, // Bluetooth enabled by default
|
||||
|
|
@ -18,23 +33,34 @@ function resetSettings() {
|
|||
vibrate: true, // Vibration enabled by default. App must support
|
||||
beep: "vib", // Beep enabled by default. App must support
|
||||
timezone: 0, // Set the timezone for the device
|
||||
HID : false, // BLE HID mode, off by default
|
||||
HID: false, // BLE HID mode, off by default
|
||||
clock: null, // a string for the default clock's name
|
||||
"12hour" : false, // 12 or 24 hour clock?
|
||||
brightness: 1, // LCD brightness from 0 to 1
|
||||
// welcomed : undefined/true (whether welcome app should show)
|
||||
options: {
|
||||
wakeOnBTN1: true,
|
||||
wakeOnBTN2: true,
|
||||
wakeOnBTN3: true,
|
||||
wakeOnFaceUp: false,
|
||||
wakeOnTouch: false,
|
||||
wakeOnTwist: true,
|
||||
twistThreshold: 819.2,
|
||||
twistMaxY: -800,
|
||||
twistTimeout: 1000
|
||||
}
|
||||
};
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
settings = storage.readJSON('setting.json',1);
|
||||
settings = storage.readJSON('setting.json', 1);
|
||||
if (!settings) resetSettings();
|
||||
|
||||
const boolFormat = v => v ? "On" : "Off";
|
||||
|
||||
function showMainMenu() {
|
||||
var beepV = [ false,true,"vib" ];
|
||||
var beepN = [ "Off","Piezo","Vibrate" ];
|
||||
var beepV = [false, true, "vib"];
|
||||
var beepN = ["Off", "Piezo", "Vibrate"];
|
||||
const mainmenu = {
|
||||
'': { 'title': 'Settings' },
|
||||
'Make Connectable': makeConnectable,
|
||||
|
|
@ -85,9 +111,9 @@ function showMainMenu() {
|
|||
}
|
||||
},
|
||||
'Beep': {
|
||||
value: 0|beepV.indexOf(settings.beep),
|
||||
min:0,max:2,
|
||||
format: v=>beepN[v],
|
||||
value: 0 | beepV.indexOf(settings.beep),
|
||||
min: 0, max: 2,
|
||||
format: v => beepN[v],
|
||||
onchange: v => {
|
||||
settings.beep = beepV[v];
|
||||
if (v==1) { analogWrite(D18,0.5,{freq:2000});setTimeout(()=>D18.reset(),200); } // piezo
|
||||
|
|
@ -103,7 +129,7 @@ function showMainMenu() {
|
|||
updateSettings();
|
||||
if (settings.vibrate) {
|
||||
VIBRATE.write(1);
|
||||
setTimeout(()=>VIBRATE.write(0), 10);
|
||||
setTimeout(() => VIBRATE.write(0), 10);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -118,14 +144,101 @@ function showMainMenu() {
|
|||
}
|
||||
},
|
||||
'Set Time': showSetTimeMenu,
|
||||
'LCD Wake-Up': showWakeUpMenu,
|
||||
'App/widget settings': showAppSettingsMenu,
|
||||
'Reset Settings': showResetMenu,
|
||||
'Turn Off': Bangle.off,
|
||||
'< Back': ()=> {load();}
|
||||
'< Back': () => { load(); }
|
||||
};
|
||||
return E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
function showWakeUpMenu() {
|
||||
const wakeUpMenu = {
|
||||
'': { 'title': 'LCD Wake-Up' },
|
||||
'< Back': showMainMenu,
|
||||
'Wake On BTN1': {
|
||||
value: settings.options.wakeOnBTN1,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnBTN1 = !settings.options.wakeOnBTN1;
|
||||
updateOptions();
|
||||
}
|
||||
},
|
||||
'Wake On BTN2': {
|
||||
value: settings.options.wakeOnBTN2,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnBTN2 = !settings.options.wakeOnBTN2;
|
||||
updateOptions();
|
||||
}
|
||||
},
|
||||
'Wake On BTN3': {
|
||||
value: settings.options.wakeOnBTN3,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnBTN3 = !settings.options.wakeOnBTN3;
|
||||
updateOptions();
|
||||
}
|
||||
},
|
||||
'Wake on FaceUp': {
|
||||
value: settings.options.wakeOnFaceUp,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnFaceUp = !settings.options.wakeOnFaceUp;
|
||||
updateOptions();
|
||||
}
|
||||
},
|
||||
'Wake on Touch': {
|
||||
value: settings.options.wakeOnTouch,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnTouch = !settings.options.wakeOnTouch;
|
||||
updateOptions();
|
||||
}
|
||||
},
|
||||
'Wake On Twist': {
|
||||
value: settings.options.wakeOnTwist,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.options.wakeOnTwist = !settings.options.wakeOnTwist;
|
||||
updateOptions();
|
||||
}
|
||||
},
|
||||
'Twist Threshold': {
|
||||
value: internalToG(settings.options.twistThreshold),
|
||||
min: -0.5,
|
||||
max: 0.5,
|
||||
step: 0.01,
|
||||
onchange: v => {
|
||||
settings.options.twistThreshold = gToInternal(v || 0.1);
|
||||
updateOptions();
|
||||
}
|
||||
},
|
||||
'Twist Max Y': {
|
||||
value: settings.options.twistMaxY,
|
||||
min: -1500,
|
||||
max: 1500,
|
||||
step: 100,
|
||||
onchange: v => {
|
||||
settings.options.twistMaxY = v || -800;
|
||||
updateOptions();
|
||||
}
|
||||
},
|
||||
'Twist Timeout': {
|
||||
value: settings.options.twistTimeout,
|
||||
min: 0,
|
||||
max: 2000,
|
||||
step: 100,
|
||||
onchange: v => {
|
||||
settings.options.twistTimeout = v || 1000;
|
||||
updateOptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
return E.showMenu(wakeUpMenu)
|
||||
}
|
||||
|
||||
function showLocaleMenu() {
|
||||
const localemenu = {
|
||||
'': { 'title': 'Locale' },
|
||||
|
|
@ -142,7 +255,7 @@ function showLocaleMenu() {
|
|||
},
|
||||
'Clock Style': {
|
||||
value: !!settings["12hour"],
|
||||
format : v => v?"12hr":"24hr",
|
||||
format: v => v ? "12hr" : "24hr",
|
||||
onchange: v => {
|
||||
settings["12hour"] = v;
|
||||
updateSettings();
|
||||
|
|
@ -170,33 +283,33 @@ function showResetMenu() {
|
|||
}
|
||||
|
||||
function makeConnectable() {
|
||||
try { NRF.wake(); } catch(e) {}
|
||||
try { NRF.wake(); } catch (e) { }
|
||||
Bluetooth.setConsole(1);
|
||||
var name="Bangle.js "+NRF.getAddress().substr(-5).replace(":","");
|
||||
E.showPrompt(name+"\nStay Connectable?",{title:"Connectable"}).then(r=>{
|
||||
if (settings.ble!=r) {
|
||||
var name = "Bangle.js " + NRF.getAddress().substr(-5).replace(":", "");
|
||||
E.showPrompt(name + "\nStay Connectable?", { title: "Connectable" }).then(r => {
|
||||
if (settings.ble != r) {
|
||||
settings.ble = r;
|
||||
updateSettings();
|
||||
}
|
||||
if (!r) try { NRF.sleep(); } catch(e) {}
|
||||
if (!r) try { NRF.sleep(); } catch (e) { }
|
||||
showMainMenu();
|
||||
});
|
||||
}
|
||||
function showClockMenu() {
|
||||
var clockApps = require("Storage").list(/\.info$/).map(app=>{
|
||||
var clockApps = require("Storage").list(/\.info$/).map(app => {
|
||||
try { return require("Storage").readJSON(app); }
|
||||
catch (e) {}
|
||||
}).filter(app=>app.type=="clock").sort((a, b) => a.sortorder - b.sortorder);
|
||||
catch (e) { }
|
||||
}).filter(app => app.type == "clock").sort((a, b) => a.sortorder - b.sortorder);
|
||||
const clockMenu = {
|
||||
'': {
|
||||
'title': 'Select Clock',
|
||||
},
|
||||
'< Back': showMainMenu,
|
||||
};
|
||||
clockApps.forEach((app,index) => {
|
||||
clockApps.forEach((app, index) => {
|
||||
var label = app.name;
|
||||
if ((!settings.clock && index === 0) || (settings.clock === app.src)) {
|
||||
label = "* "+label;
|
||||
label = "* " + label;
|
||||
}
|
||||
clockMenu[label] = () => {
|
||||
if (settings.clock !== app.src) {
|
||||
|
|
@ -207,7 +320,7 @@ function showClockMenu() {
|
|||
};
|
||||
});
|
||||
if (clockApps.length === 0) {
|
||||
clockMenu["No Clocks Found"] = () => {};
|
||||
clockMenu["No Clocks Found"] = () => { };
|
||||
}
|
||||
return E.showMenu(clockMenu);
|
||||
}
|
||||
|
|
@ -219,7 +332,7 @@ function showSetTimeMenu() {
|
|||
const timemenu = {
|
||||
'': {
|
||||
'title': 'Set Time',
|
||||
'predraw': function() {
|
||||
'predraw': function () {
|
||||
d = new Date();
|
||||
timemenu.Hour.value = d.getHours();
|
||||
timemenu.Minute.value = d.getMinutes();
|
||||
|
|
@ -238,7 +351,7 @@ function showSetTimeMenu() {
|
|||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setHours(v);
|
||||
setTime(d.getTime()/1000);
|
||||
setTime(d.getTime() / 1000);
|
||||
}
|
||||
},
|
||||
'Minute': {
|
||||
|
|
@ -249,7 +362,7 @@ function showSetTimeMenu() {
|
|||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setMinutes(v);
|
||||
setTime(d.getTime()/1000);
|
||||
setTime(d.getTime() / 1000);
|
||||
}
|
||||
},
|
||||
'Second': {
|
||||
|
|
@ -260,7 +373,7 @@ function showSetTimeMenu() {
|
|||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setSeconds(v);
|
||||
setTime(d.getTime()/1000);
|
||||
setTime(d.getTime() / 1000);
|
||||
}
|
||||
},
|
||||
'Date': {
|
||||
|
|
@ -271,7 +384,7 @@ function showSetTimeMenu() {
|
|||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setDate(v);
|
||||
setTime(d.getTime()/1000);
|
||||
setTime(d.getTime() / 1000);
|
||||
}
|
||||
},
|
||||
'Month': {
|
||||
|
|
@ -282,7 +395,7 @@ function showSetTimeMenu() {
|
|||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setMonth(v - 1);
|
||||
setTime(d.getTime()/1000);
|
||||
setTime(d.getTime() / 1000);
|
||||
}
|
||||
},
|
||||
'Year': {
|
||||
|
|
@ -293,16 +406,16 @@ function showSetTimeMenu() {
|
|||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setFullYear(v);
|
||||
setTime(d.getTime()/1000);
|
||||
setTime(d.getTime() / 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
return E.showMenu(timemenu);
|
||||
}
|
||||
|
||||
function showAppSettingsMenu(){
|
||||
function showAppSettingsMenu() {
|
||||
let appmenu = {
|
||||
'': {'title': 'App Settings'},
|
||||
'': { 'title': 'App Settings' },
|
||||
'< Back': showMainMenu,
|
||||
}
|
||||
const apps = storage.list(/\.info$/)
|
||||
|
|
@ -310,10 +423,10 @@ function showAppSettingsMenu(){
|
|||
.filter(app => app && app.settings)
|
||||
.sort((a, b) => a.sortorder - b.sortorder)
|
||||
if (apps.length === 0) {
|
||||
appmenu['No app has settings'] = () => {};
|
||||
appmenu['No app has settings'] = () => { };
|
||||
}
|
||||
apps.forEach(function (app) {
|
||||
appmenu[app.name] = () => {showAppSettings(app)};
|
||||
appmenu[app.name] = () => { showAppSettings(app) };
|
||||
})
|
||||
E.showMenu(appmenu)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Add swipe support and doucle tap to run application
|
||||
0.03: Close launcher when lcd turn off
|
||||
0.03: Close launcher when lcd turn off
|
||||
0.04: Complete rewrite to add animation and loop ( issue #210 )
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
Bangle.setLCDMode("120x120");
|
||||
g.clear();
|
||||
g.flip();
|
||||
|
||||
const Storage = require("Storage");
|
||||
|
||||
|
|
@ -14,99 +16,144 @@ function getApps(){
|
|||
});
|
||||
}
|
||||
|
||||
const selected = 0;
|
||||
const apps = getApps();
|
||||
const HEIGHT = g.getHeight();
|
||||
const WIDTH = g.getWidth();
|
||||
const HALF = WIDTH/2;
|
||||
const ANIMATION_FRAME = 3;
|
||||
const ANIMATION_STEP = HALF / ANIMATION_FRAME;
|
||||
|
||||
function prev(){
|
||||
if (selected>=0) {
|
||||
selected--;
|
||||
}
|
||||
drawMenu();
|
||||
function getPosition(index){
|
||||
return (index*HALF);
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (selected+1<apps.length) {
|
||||
selected++;
|
||||
}
|
||||
drawMenu();
|
||||
let current_app = 0;
|
||||
let target = 0;
|
||||
let slideOffset = 0;
|
||||
|
||||
const back = {
|
||||
name: 'BACK',
|
||||
back: true
|
||||
};
|
||||
|
||||
const apps = [back].concat(getApps());
|
||||
apps.push(back);
|
||||
|
||||
function noIcon(x, y, size){
|
||||
const half = size/2;
|
||||
g.setColor(1,1,1);
|
||||
g.setFontAlign(-0,0);
|
||||
const fontSize = Math.floor(size / 30 * 2);
|
||||
g.setFont('6x8', fontSize);
|
||||
if(fontSize) g.drawString('-?-', x+1.5, y);
|
||||
g.drawRect(x-half, y-half, x+half, y+half);
|
||||
}
|
||||
|
||||
function drawIcons(offset){
|
||||
apps.forEach((app, i) => {
|
||||
const x = getPosition(i) + HALF - offset;
|
||||
const y = HALF - (HALF*0.3);//-(HALF*0.7);
|
||||
let diff = (x - HALF);
|
||||
if(diff < 0) diff *=-1;
|
||||
let size = 30;
|
||||
if((diff*0.5) < size) size -= (diff*0.5);
|
||||
else size = 0;
|
||||
|
||||
const scale = size / 30;
|
||||
if(size){
|
||||
let c = size / 30 * 2;
|
||||
c = c -1;
|
||||
if(c < 0) c = 0;
|
||||
|
||||
if(app.back){
|
||||
g.setFont('6x8', 1);
|
||||
g.setFontAlign(0, -1);
|
||||
g.setColor(c,c,c);
|
||||
g.drawString('Back', HALF, HALF);
|
||||
return;
|
||||
}
|
||||
// icon
|
||||
const icon = app.icon ? Storage.read(app.icon) : null;
|
||||
if(icon){
|
||||
try {
|
||||
g.drawImage(icon, x-(scale*24), y-(scale*24), { scale: scale });
|
||||
} catch(e){
|
||||
noIcon(x, y, size);
|
||||
}
|
||||
}else{
|
||||
noIcon(x, y, size);
|
||||
}
|
||||
//text
|
||||
g.setFont('6x8', 1);
|
||||
g.setFontAlign(0, -1);
|
||||
g.setColor(c,c,c);
|
||||
g.drawString(app.name, HALF, HEIGHT - (HALF*0.7));
|
||||
|
||||
const type = app.type ? app.type : 'App';
|
||||
const version = app.version ? app.version : '0.00';
|
||||
const info = type+' v'+version;
|
||||
g.setFontAlign(0,1);
|
||||
g.setFont('4x6', 0.25);
|
||||
g.setColor(c,c,c);
|
||||
g.drawString(info, HALF, 110, { scale: scale });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function draw(ignoreLoop){
|
||||
g.clear();
|
||||
drawIcons(slideOffset);
|
||||
g.flip();
|
||||
if(slideOffset == target) return;
|
||||
if(slideOffset < target) slideOffset+= ANIMATION_STEP;
|
||||
else if(slideOffset > target) slideOffset -= ANIMATION_STEP;
|
||||
if(!ignoreLoop) draw();
|
||||
}
|
||||
|
||||
function animateTo(index){
|
||||
target = getPosition(index);
|
||||
draw();
|
||||
}
|
||||
function goTo(index){
|
||||
current_app = index;
|
||||
target = getPosition(index);
|
||||
slideOffset = target;
|
||||
draw(true);
|
||||
}
|
||||
|
||||
goTo(1);
|
||||
|
||||
function prev(){
|
||||
if(current_app == 0) goTo(apps.length-1);
|
||||
current_app -= 1;
|
||||
if(current_app < 0) current_app = 0;
|
||||
animateTo(current_app);
|
||||
}
|
||||
|
||||
function next(){
|
||||
if(current_app == apps.length-1) goTo(0);
|
||||
current_app += 1;
|
||||
if(current_app > apps.length-1) current_app = apps.length-1;
|
||||
animateTo(current_app);
|
||||
}
|
||||
|
||||
function run() {
|
||||
if(selected < 0) return load();
|
||||
if (!apps[selected].src) return;
|
||||
if (Storage.read(apps[selected].src)===undefined) {
|
||||
const app = apps[current_app];
|
||||
if(app.back) return load();
|
||||
if (Storage.read(app.src)===undefined) {
|
||||
E.showMessage("App Source\nNot found");
|
||||
setTimeout(drawMenu, 2000);
|
||||
setTimeout(draw, 2000);
|
||||
} else {
|
||||
E.showMessage("Loading...");
|
||||
load(apps[selected].src);
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentApp(){
|
||||
return apps[selected];
|
||||
}
|
||||
|
||||
function getNextApp(){
|
||||
return apps[selected+1];
|
||||
}
|
||||
|
||||
function drawFallbackIcon(){
|
||||
g.setColor(1,1,1);
|
||||
g.fillRect(72, 40, 168, 136);
|
||||
g.setColor(0,0,0);
|
||||
g.setFont('6x8', 8);
|
||||
g.drawString('?', 124, 88);
|
||||
}
|
||||
|
||||
function drawArrow(x, y, size, dir){
|
||||
size = size || 10;
|
||||
dir = dir || 1;
|
||||
g.moveTo(x, y).lineTo(x+(size*dir), y-size).lineTo(x+(size*dir),y+size).lineTo(x, y);
|
||||
}
|
||||
|
||||
function drawMenu(){
|
||||
|
||||
if(selected < 0){
|
||||
Bangle.setLCDMode();
|
||||
g.clear();
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont('6x8', 2);
|
||||
g.drawString('Back', 120, 120);
|
||||
drawArrow(220, 120, 10, -1);
|
||||
return;
|
||||
g.flip();
|
||||
E.showMessage("Loading...");
|
||||
load(app.src);
|
||||
}
|
||||
|
||||
const app = getCurrentApp();
|
||||
g.clear();
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont('6x8', 2);
|
||||
if(!app) return g.drawString('???', 120, 120);
|
||||
g.drawString(app.name, 120, 160);
|
||||
if (app.icon) icon = Storage.read(app.icon);
|
||||
if (icon) try {g.drawImage(icon, 120-48, 40, { scale: 2 });} catch(e){ drawFallbackIcon(); }
|
||||
else drawFallbackIcon();
|
||||
|
||||
g.setFont('6x8', 1);
|
||||
|
||||
const type = app.type ? app.type : 'App';
|
||||
const version = app.version ? app.version : '0.00';
|
||||
const info = type+' v'+version;
|
||||
g.setFontAlign(-1,1);
|
||||
g.drawString(info, 20, 220);
|
||||
|
||||
const count = (selected+1)+'/'+apps.length;
|
||||
g.setFontAlign(1,1);
|
||||
g.drawString(count, 220, 220);
|
||||
|
||||
drawArrow(20, 120, 10, 1);
|
||||
if(getNextApp()) drawArrow(220, 120, 10, -1);
|
||||
}
|
||||
|
||||
drawMenu();
|
||||
|
||||
// Physical buttons
|
||||
setWatch(prev, BTN1, {repeat:true});
|
||||
setWatch(next, BTN3, {repeat:true});
|
||||
setWatch(prev, BTN1, { repeat: true });
|
||||
setWatch(next, BTN3, { repeat: true });
|
||||
setWatch(run, BTN2, {repeat:true,edge:"falling"});
|
||||
|
||||
// Screen event
|
||||
|
|
@ -129,6 +176,7 @@ Bangle.on('swipe', dir => {
|
|||
else next();
|
||||
});
|
||||
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
// close launcher when lcd is off
|
||||
Bangle.on('lcdPower', on => {
|
||||
if(!on) return load();
|
||||
});
|
||||
|
|
@ -4,3 +4,4 @@
|
|||
0.05: Change color depending on battery level, cloned from widbat
|
||||
0.06: Show battery percentage as text
|
||||
0.07: Add settings: percentage/color/charger icon
|
||||
0.08: Draw percentage as inverted on monochrome battery
|
||||
|
|
|
|||
|
|
@ -66,19 +66,26 @@ function draw() {
|
|||
g.fillRect(x+s-3,y+10,x+s,y+14);
|
||||
const l = E.getBattery(),
|
||||
c = levelColor(l);
|
||||
g.setColor(c).fillRect(x+4,y+6,x+4+l*(s-12)/100,y+17);
|
||||
const xl = x+4+l*(s-12)/100
|
||||
g.setColor(c).fillRect(x+4,y+6,xl,y+17);
|
||||
g.setColor(-1);
|
||||
if (!setting('percentage')) {
|
||||
return;
|
||||
}
|
||||
g.setFontAlign(-1,-1);
|
||||
let gfx = g
|
||||
if (setting('color') === 'Monochrome') {
|
||||
// draw text inverted on battery level
|
||||
gfx = Graphics.createCallback(240, 240, 1,
|
||||
(x,y) => {g.setPixel(x,y,x<=xl?0:-1)})
|
||||
}
|
||||
gfx.setFontAlign(-1,-1);
|
||||
if (l >= 100) {
|
||||
g.setFont('4x6', 2);
|
||||
g.drawString(l, x + 6, y + 7);
|
||||
gfx.setFont('4x6', 2);
|
||||
gfx.drawString(l, x + 6, y + 7);
|
||||
} else {
|
||||
if (l < 10) x+=6;
|
||||
g.setFont('6x8', 2);
|
||||
g.drawString(l, x + 6, y + 4);
|
||||
gfx.setFont('6x8', 2);
|
||||
gfx.drawString(l, x + 6, y + 4);
|
||||
}
|
||||
}
|
||||
// reload widget, e.g. when settings have changed
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Optimized rendering for the background
|
||||
0.04: Only buzz on high confidence (>85%)
|
||||
0.05: Improved buzz timing and rendering
|
||||
0.06: Removed debug outputs, fixed rendering for upper limit, improved rendering for +/- icons, changelog version order fixed
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
/* eslint-disable no-undef */
|
||||
const Setter = {
|
||||
NONE: "none",
|
||||
UPPER: 'upper',
|
||||
LOWER: 'lower'
|
||||
NONE: "none",
|
||||
UPPER: 'upper',
|
||||
LOWER: 'lower'
|
||||
};
|
||||
|
||||
|
||||
const shortBuzzTimeInMs = 80;
|
||||
const longBuzzTimeInMs = 400;
|
||||
|
||||
|
|
@ -77,11 +77,11 @@ function drawTrainingHeartRate() {
|
|||
//Only redraw if the display is on
|
||||
if (Bangle.isLCDOn()) {
|
||||
renderUpperLimit();
|
||||
|
||||
|
||||
renderCurrentHeartRate();
|
||||
|
||||
|
||||
renderLowerLimit();
|
||||
|
||||
|
||||
renderConfidenceBars();
|
||||
}
|
||||
|
||||
|
|
@ -90,24 +90,24 @@ function drawTrainingHeartRate() {
|
|||
|
||||
function renderUpperLimit() {
|
||||
if(!upperLimitChanged) { return; }
|
||||
|
||||
|
||||
g.setColor(1,0,0);
|
||||
g.fillRect(125,40, 210, 70);
|
||||
|
||||
|
||||
if(limitSetter === Setter.UPPER){
|
||||
g.setColor(255,255, 0);
|
||||
} else {
|
||||
g.setColor(255,255,255);
|
||||
}
|
||||
g.setFontVector(13);
|
||||
g.drawString("Upper : " + upperLimit, 130,50);
|
||||
|
||||
g.drawString("Upper: " + upperLimit, 125, 50);
|
||||
|
||||
upperLimitChanged = false;
|
||||
}
|
||||
|
||||
|
||||
function renderCurrentHeartRate() {
|
||||
if(!hrChanged) { return; }
|
||||
|
||||
|
||||
g.setColor(255,255,255);
|
||||
g.fillRect(55, 110, 165, 150);
|
||||
|
||||
|
|
@ -121,27 +121,27 @@ function renderCurrentHeartRate() {
|
|||
|
||||
hrChanged = false;
|
||||
}
|
||||
|
||||
|
||||
function renderLowerLimit() {
|
||||
if(!lowerLimitChanged) { return; }
|
||||
|
||||
|
||||
g.setColor(0,0,1);
|
||||
g.fillRect(10, 180, 100, 210);
|
||||
|
||||
|
||||
if(limitSetter === Setter.LOWER){
|
||||
g.setColor(255,255, 0);
|
||||
} else {
|
||||
g.setColor(255,255,255);
|
||||
}
|
||||
g.setFontVector(13);
|
||||
g.drawString("Lower : " + lowerLimit, 20,190);
|
||||
|
||||
g.drawString("Lower: " + lowerLimit, 20,190);
|
||||
|
||||
lowerLimitChanged = false;
|
||||
}
|
||||
|
||||
|
||||
function renderConfidenceBars(){
|
||||
if(!confidenceChanged) { return; }
|
||||
|
||||
|
||||
if(hrConfidence >= 85){
|
||||
g.setColor(0, 255, 0);
|
||||
} else if (hrConfidence >= 50) {
|
||||
|
|
@ -157,42 +157,51 @@ function renderConfidenceBars(){
|
|||
|
||||
confidenceChanged = false;
|
||||
}
|
||||
|
||||
function renderButtonIcons() {
|
||||
g.setColor(255,255,255);
|
||||
|
||||
function renderPlusMinusIcons() {
|
||||
if (limitSetter === Setter.NONE) {
|
||||
g.setColor(0, 0, 0);
|
||||
} else {
|
||||
g.setColor(1, 1, 1);
|
||||
}
|
||||
|
||||
g.setFontVector(14);
|
||||
|
||||
//+ for Btn1
|
||||
g.drawString("+", 222,50);
|
||||
g.drawString("+", 222, 50);
|
||||
|
||||
//- for Btn3
|
||||
g.drawString("-", 222,165);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function renderHomeIcon() {
|
||||
//Home for Btn2
|
||||
g.setColor(1, 1, 1);
|
||||
g.drawLine(220, 118, 227, 110);
|
||||
g.drawLine(227, 110, 234, 118);
|
||||
|
||||
g.drawPoly([222,117,222,125,232,125,232,117], false);
|
||||
g.drawRect(226,120,229,125);
|
||||
|
||||
//- for Btn3
|
||||
g.drawString("-", 222,165);
|
||||
}
|
||||
|
||||
function buzz()
|
||||
{
|
||||
|
||||
function buzz() {
|
||||
// Do not buzz if not confident
|
||||
if(hrConfidence < 85) { return; }
|
||||
|
||||
if(currentHeartRate > upperLimit)
|
||||
{
|
||||
Bangle.buzz(shortBuzzTimeInMs);
|
||||
setTimeout(() => { Bangle.buzz(shortBuzzTimeInMs); }, shortBuzzTimeInMs * 2);
|
||||
Bangle.buzz(shortBuzzTimeInMs);
|
||||
setTimeout(() => { Bangle.buzz(shortBuzzTimeInMs); }, shortBuzzTimeInMs * 2);
|
||||
}
|
||||
|
||||
if(currentHeartRate < lowerLimit)
|
||||
{
|
||||
Bangle.buzz(longBuzzTimeInMs);
|
||||
Bangle.buzz(longBuzzTimeInMs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onHrm(hrm){
|
||||
if(currentHeartRate !== hrm.bpm){
|
||||
currentHeartRate = hrm.bpm;
|
||||
|
|
@ -204,97 +213,93 @@ function onHrm(hrm){
|
|||
confidenceChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setLimitSetterToLower() {
|
||||
resetHighlightTimeout();
|
||||
|
||||
limitSetter = Setter.LOWER;
|
||||
console.log("Limit setter is lower");
|
||||
|
||||
|
||||
upperLimitChanged = true;
|
||||
lowerLimitChanged = true;
|
||||
|
||||
|
||||
renderUpperLimit();
|
||||
renderLowerLimit();
|
||||
renderPlusMinusIcons();
|
||||
}
|
||||
|
||||
|
||||
function setLimitSetterToUpper() {
|
||||
resetHighlightTimeout();
|
||||
|
||||
limitSetter = Setter.UPPER;
|
||||
console.log("Limit setter is upper");
|
||||
|
||||
|
||||
upperLimitChanged = true;
|
||||
lowerLimitChanged = true;
|
||||
|
||||
|
||||
renderLowerLimit();
|
||||
renderUpperLimit();
|
||||
renderPlusMinusIcons();
|
||||
}
|
||||
|
||||
|
||||
function setLimitSetterToNone() {
|
||||
limitSetter = Setter.NONE;
|
||||
console.log("Limit setter is none");
|
||||
|
||||
|
||||
upperLimitChanged = true;
|
||||
lowerLimitChanged = true;
|
||||
|
||||
|
||||
renderLowerLimit();
|
||||
renderUpperLimit();
|
||||
renderPlusMinusIcons();
|
||||
}
|
||||
|
||||
function incrementLimit(){
|
||||
|
||||
function incrementLimit() {
|
||||
resetHighlightTimeout();
|
||||
|
||||
if (limitSetter === Setter.UPPER) {
|
||||
upperLimit++;
|
||||
renderUpperLimit();
|
||||
console.log("Upper limit: " + upperLimit);
|
||||
upperLimitChanged = true;
|
||||
} else if(limitSetter === Setter.LOWER) {
|
||||
lowerLimit++;
|
||||
renderLowerLimit();
|
||||
console.log("Lower limit: " + lowerLimit);
|
||||
lowerLimitChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function decrementLimit(){
|
||||
resetHighlightTimeout();
|
||||
|
||||
if (limitSetter === Setter.UPPER) {
|
||||
upperLimit--;
|
||||
renderUpperLimit();
|
||||
console.log("Upper limit: " + upperLimit);
|
||||
upperLimitChanged = true;
|
||||
} else if(limitSetter === Setter.LOWER) {
|
||||
lowerLimit--;
|
||||
renderLowerLimit();
|
||||
console.log("Lower limit: " + lowerLimit);
|
||||
lowerLimitChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function resetHighlightTimeout() {
|
||||
if (setterHighlightTimeout) {
|
||||
clearTimeout(setterHighlightTimeout);
|
||||
}
|
||||
|
||||
|
||||
setterHighlightTimeout = setTimeout(setLimitSetterToNone, 2000);
|
||||
}
|
||||
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
function switchOffApp(){
|
||||
Bangle.setHRMPower(0);
|
||||
Bangle.showLauncher();
|
||||
}
|
||||
|
||||
|
||||
// special function to handle display switch on
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
g.clear();
|
||||
if (on) {
|
||||
Bangle.drawWidgets();
|
||||
renderButtonIcons();
|
||||
// call your app function here
|
||||
|
||||
renderHomeIcon();
|
||||
renderLowerLimitBackground();
|
||||
renderUpperLimitBackground();
|
||||
lowerLimitChanged = true;
|
||||
|
|
@ -302,10 +307,10 @@ Bangle.on('lcdPower', (on) => {
|
|||
drawTrainingHeartRate();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Bangle.setHRMPower(1);
|
||||
Bangle.on('HRM', onHrm);
|
||||
|
||||
|
||||
setWatch(incrementLimit, BTN1, {edge:"rising", debounce:50, repeat:true});
|
||||
setWatch(switchOffApp, BTN2, {edge:"rising", debounce:50, repeat:true});
|
||||
setWatch(decrementLimit, BTN3, {edge:"rising", debounce:50, repeat:true});
|
||||
|
|
@ -317,7 +322,7 @@ Bangle.loadWidgets();
|
|||
Bangle.drawWidgets();
|
||||
//drawTrainingHeartRate();
|
||||
|
||||
renderButtonIcons();
|
||||
renderHomeIcon();
|
||||
renderLowerLimitBackground();
|
||||
renderUpperLimitBackground();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue