Merge branch 'master' of github.com:espruino/BangleApps

master
Gordon Williams 2022-04-21 10:27:51 +01:00
commit 17eb42403e
42 changed files with 490 additions and 174 deletions

View File

@ -18,3 +18,4 @@
0.17: Moving alarm internals to 'sched' library 0.17: Moving alarm internals to 'sched' library
0.18: Cope with >1 identical alarm at once (#1667) 0.18: Cope with >1 identical alarm at once (#1667)
0.19: Ensure rescheduled alarms that already fired have 'last' reset 0.19: Ensure rescheduled alarms that already fired have 'last' reset
0.20: Use the new 'sched' factories to initialize new alarms/timers

View File

@ -1,28 +1,28 @@
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
var alarms = require("sched").getAlarms();
// An array of alarm objects (see sched/README.md) // An array of alarm objects (see sched/README.md)
let alarms = require("sched").getAlarms();
// time in ms -> { hrs, mins } // time in ms -> { hrs, mins }
function decodeTime(t) { function decodeTime(t) {
t = 0|t; // sanitise t = 0 | t; // sanitise
var hrs = 0|(t/3600000); let hrs = 0 | (t / 3600000);
return { hrs : hrs, mins : Math.round((t-hrs*3600000)/60000) }; return { hrs: hrs, mins: Math.round((t - hrs * 3600000) / 60000) };
} }
// time in { hrs, mins } -> ms // time in { hrs, mins } -> ms
function encodeTime(o) { function encodeTime(o) {
return o.hrs*3600000 + o.mins*60000; return o.hrs * 3600000 + o.mins * 60000;
} }
function formatTime(t) { function formatTime(t) {
var o = decodeTime(t); let o = decodeTime(t);
return o.hrs+":"+("0"+o.mins).substr(-2); return o.hrs + ":" + ("0" + o.mins).substr(-2);
} }
function getCurrentTime() { function getCurrentTime() {
var time = new Date(); let time = new Date();
return ( return (
time.getHours() * 3600000 + time.getHours() * 3600000 +
time.getMinutes() * 60000 + time.getMinutes() * 60000 +
@ -39,7 +39,7 @@ function showMainMenu() {
// Timer img "\0"+atob("DhKBAP////MDDAwwMGGBzgPwB4AeAPwHOBhgwMMzDez////w") // Timer img "\0"+atob("DhKBAP////MDDAwwMGGBzgPwB4AeAPwHOBhgwMMzDez////w")
// Alarm img "\0"+atob("FBSBAABgA4YcMPDGP8Zn/mx/48//PP/zD/8A//AP/wD/8A//AP/wH/+D//w//8AAAADwAAYA") // Alarm img "\0"+atob("FBSBAABgA4YcMPDGP8Zn/mx/48//PP/zD/8A//AP/wD/8A//AP/wH/+D//w//8AAAADwAAYA")
const menu = { const menu = {
'': { 'title': 'Alarm/Timer' }, '': { 'title': /*LANG*/'Alarms&Timers' },
/*LANG*/'< Back' : ()=>{load();}, /*LANG*/'< Back' : ()=>{load();},
/*LANG*/'New Alarm': ()=>editAlarm(-1), /*LANG*/'New Alarm': ()=>editAlarm(-1),
/*LANG*/'New Timer': ()=>editTimer(-1) /*LANG*/'New Timer': ()=>editTimer(-1)
@ -76,13 +76,13 @@ function showMainMenu() {
function editDOW(dow, onchange) { function editDOW(dow, onchange) {
const menu = { const menu = {
'': { 'title': /*LANG*/'Days of Week' }, '': { 'title': /*LANG*/'Days of Week' },
'< Back' : () => onchange(dow) /*LANG*/'< Back' : () => onchange(dow)
}; };
for (var i = 0; i < 7; i++) (i => { for (let i = 0; i < 7; i++) (i => {
var dayOfWeek = require("locale").dow({ getDay: () => i }); let dayOfWeek = require("locale").dow({ getDay: () => i });
menu[dayOfWeek] = { menu[dayOfWeek] = {
value: !!(dow&(1<<i)), value: !!(dow&(1<<i)),
format: v => v ? "Yes" : "No", format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v => v ? dow |= 1<<i : dow &= ~(1<<i), onchange: v => v ? dow |= 1<<i : dow &= ~(1<<i),
}; };
})(i); })(i);
@ -90,23 +90,15 @@ function editDOW(dow, onchange) {
} }
function editAlarm(alarmIndex, alarm) { function editAlarm(alarmIndex, alarm) {
var newAlarm = alarmIndex<0; let newAlarm = alarmIndex < 0;
var a = { let a = require("sched").newDefaultAlarm();
t : 12*3600000, // 12 o clock default
on : true,
rp : false, // repeat not the default
as : false,
dow : 0b1111111,
last : 0,
vibrate : ".."
}
if (!newAlarm) Object.assign(a, alarms[alarmIndex]); if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
if (alarm) Object.assign(a,alarm); if (alarm) Object.assign(a,alarm);
var t = decodeTime(a.t); let t = decodeTime(a.t);
const menu = { const menu = {
'': { 'title': /*LANG*/'Alarm' }, '': { 'title': /*LANG*/'Alarm' },
'< Back' : () => showMainMenu(), /*LANG*/'< Back' : () => showMainMenu(),
/*LANG*/'Hours': { /*LANG*/'Hours': {
value: t.hrs, min : 0, max : 23, wrap : true, value: t.hrs, min : 0, max : 23, wrap : true,
onchange: v => t.hrs=v onchange: v => t.hrs=v
@ -117,23 +109,23 @@ function editAlarm(alarmIndex, alarm) {
}, },
/*LANG*/'Enabled': { /*LANG*/'Enabled': {
value: a.on, value: a.on,
format: v=>v?"On":"Off", format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
onchange: v=>a.on=v onchange: v=>a.on=v
}, },
/*LANG*/'Repeat': { /*LANG*/'Repeat': {
value: a.rp, value: a.rp,
format: v=>v?"Yes":"No", format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v=>a.rp=v onchange: v => a.rp = v
}, },
/*LANG*/'Days': { /*LANG*/'Days': {
value: "SMTWTFS".split("").map((d,n)=>a.dow&(1<<n)?d:".").join(""), value: "SMTWTFS".split("").map((d,n)=>a.dow&(1<<n)?d:".").join(""),
onchange: () => editDOW(a.dow, d=>{a.dow=d;editAlarm(alarmIndex,a)}) onchange: () => editDOW(a.dow, d=>{a.dow=d;editAlarm(alarmIndex,a)})
}, },
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ), /*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
/*LANG*/'Auto snooze': { /*LANG*/'Auto Snooze': {
value: a.as, value: a.as,
format: v=>v?"Yes":"No", format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v=>a.as=v onchange: v => a.as = v
} }
}; };
menu[/*LANG*/"Save"] = function() { menu[/*LANG*/"Save"] = function() {
@ -155,23 +147,15 @@ function editAlarm(alarmIndex, alarm) {
} }
function editTimer(alarmIndex, alarm) { function editTimer(alarmIndex, alarm) {
var newAlarm = alarmIndex<0; let newAlarm = alarmIndex < 0;
var a = { let a = require("sched").newDefaultTimer();
timer : 5*60*1000, // 5 minutes
on : true,
rp : false,
as : false,
dow : 0b1111111,
last : 0,
vibrate : ".."
}
if (!newAlarm) Object.assign(a, alarms[alarmIndex]); if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
if (alarm) Object.assign(a,alarm); if (alarm) Object.assign(a,alarm);
var t = decodeTime(a.timer); let t = decodeTime(a.timer);
const menu = { const menu = {
'': { 'title': /*LANG*/'Timer' }, '': { 'title': /*LANG*/'Timer' },
'< Back' : () => showMainMenu(), /*LANG*/'< Back' : () => showMainMenu(),
/*LANG*/'Hours': { /*LANG*/'Hours': {
value: t.hrs, min : 0, max : 23, wrap : true, value: t.hrs, min : 0, max : 23, wrap : true,
onchange: v => t.hrs=v onchange: v => t.hrs=v
@ -182,8 +166,8 @@ function editTimer(alarmIndex, alarm) {
}, },
/*LANG*/'Enabled': { /*LANG*/'Enabled': {
value: a.on, value: a.on,
format: v=>v?"On":"Off", format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
onchange: v=>a.on=v onchange: v => a.on = v
}, },
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ), /*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
}; };

View File

@ -1,8 +1,8 @@
{ {
"id": "alarm", "id": "alarm",
"name": "Alarm & Timer", "name": "Alarms & Timers",
"shortName": "Alarms", "shortName": "Alarms",
"version": "0.19", "version": "0.20",
"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",

View File

@ -1,2 +1,3 @@
0.01: New App! 0.01: New App!
0.02: Support Bangle.js 2 0.02: Support Bangle.js 2
0.03: Fix bug for Bangle.js 2 where g.flip was not being called.

View File

@ -1,6 +1,3 @@
//g.setTheme({fg : 0xFFFF, fg2 : 0xFFFF,bg2 : 0x0007,fgH : 0xFFFF,bgH : 0x02F7,dark : true});
/* Choozi - Choose people or things at random using Bangle.js. /* Choozi - Choose people or things at random using Bangle.js.
* Inspired by the "Chwazi" Android app * Inspired by the "Chwazi" Android app
* *
@ -133,6 +130,7 @@ function animateChoice(target) {
g.fillCircle(x, y, ballSize); g.fillCircle(x, y, ballSize);
oldx=x; oldx=x;
oldy=y; oldy=y;
g.flip();
} }
} }
@ -154,7 +152,7 @@ function choose() {
// draw the current value of N in the middle of the screen, with // draw the current value of N in the middle of the screen, with
// up/down arrows // up/down arrows
function drawN() { function drawN() {
g.setColor('#000000'); g.setColor(g.theme.fg);
g.setFont("Vector",fontSize); g.setFont("Vector",fontSize);
g.drawString(N,centreX-g.stringWidth(N)/2+4,centreY-fontSize/2); g.drawString(N,centreX-g.stringWidth(N)/2+4,centreY-fontSize/2);
if (N < maxN) if (N < maxN)

View File

@ -1,7 +1,7 @@
{ {
"id": "choozi", "id": "choozi",
"name": "Choozi", "name": "Choozi",
"version": "0.02", "version": "0.03",
"description": "Choose people or things at random using Bangle.js.", "description": "Choose people or things at random using Bangle.js.",
"icon": "app.png", "icon": "app.png",
"tags": "tool", "tags": "tool",

View File

@ -10,3 +10,4 @@
0.09: Fix file naming so months are 1-based (not 0) (fix #1119) 0.09: Fix file naming so months are 1-based (not 0) (fix #1119)
0.10: Adds additional 3 minute setting for HRM 0.10: Adds additional 3 minute setting for HRM
0.11: Pre-minified boot&lib - folds constants and saves RAM 0.11: Pre-minified boot&lib - folds constants and saves RAM
0.12: Add setting for Daily Step Goal

View File

@ -24,6 +24,7 @@ Stores:
* **Off** - Don't turn HRM on, but record heart rate if the HRM was turned on by another app/widget * **Off** - Don't turn HRM on, but record heart rate if the HRM was turned on by another app/widget
* **10 Min** - Turn HRM on every 10 minutes (for each heath entry) and turn it off after 2 minutes, or when a good reading is found * **10 Min** - Turn HRM on every 10 minutes (for each heath entry) and turn it off after 2 minutes, or when a good reading is found
* **Always** - Keep HRM on all the time (more accurate recording, but reduces battery life to ~36 hours) * **Always** - Keep HRM on all the time (more accurate recording, but reduces battery life to ~36 hours)
* **Daily Step Goal** - Default 10000, daily step goal for pedometer apps to use
## Technical Info ## Technical Info

View File

@ -2,8 +2,8 @@ function getSettings() {
return require("Storage").readJSON("health.json",1)||{}; return require("Storage").readJSON("health.json",1)||{};
} }
function setSettings(s) { function setSettings(healthSettings) {
require("Storage").writeJSON("health.json",s); require("Storage").writeJSON("health.json",healthSettings);
} }
function menuMain() { function menuMain() {
@ -22,15 +22,21 @@ function menuMain() {
function menuSettings() { function menuSettings() {
swipe_enabled = false; swipe_enabled = false;
clearButton(); clearButton();
var s=getSettings(); var healthSettings=getSettings();
//print(healthSettings);
E.showMenu({ E.showMenu({
"":{title:"Health Tracking"}, "":{title:"Health Tracking"},
"< Back":()=>menuMain(), "< Back":()=>menuMain(),
"Heart Rt":{ "Heart Rt":{
value : 0|s.hrm, value : 0|healthSettings.hrm,
min : 0, max : 3, min : 0, max : 3,
format : v=>["Off","3 mins","10 mins","Always"][v], format : v=>["Off","3 mins","10 mins","Always"][v],
onchange : v => { s.hrm=v;setSettings(s); } onchange : v => { healthSettings.hrm=v;setSettings(healthSettings); }
},
"Daily Step Goal":{
value : (healthSettings.stepGoal ? healthSettings.stepGoal : 10000),
min : 0, max : 20000, step : 100,
onchange : v => { healthSettings.stepGoal=v;setSettings(healthSettings); }
} }
}); });
} }
@ -199,7 +205,7 @@ function drawBarChart() {
for (bar = 1; bar < 10; bar++) { for (bar = 1; bar < 10; bar++) {
if (bar == 5) { if (bar == 5) {
g.setFont('6x8', 2); g.setFont('6x8', 2);
g.setFontAlign(0,-1) g.setFontAlign(0,-1);
g.setColor(g.theme.fg); g.setColor(g.theme.fg);
g.drawString(chart_label + " " + (chart_index + bar -1) + " " + chart_data[chart_index + bar - 1], g.getWidth()/2, 150); g.drawString(chart_label + " " + (chart_index + bar -1) + " " + chart_data[chart_index + bar - 1], g.getWidth()/2, 150);
g.setColor("#00f"); g.setColor("#00f");

View File

@ -1,7 +1,7 @@
{ {
"id": "health", "id": "health",
"name": "Health Tracking", "name": "Health Tracking",
"version": "0.11", "version": "0.12",
"description": "Logs health data and provides an app to view it", "description": "Logs health data and provides an app to view it",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system,health", "tags": "tool,system,health",

View File

@ -12,3 +12,4 @@
0.11: Merge Bangle.js 1 and 2 launchers, again 0.11: Merge Bangle.js 1 and 2 launchers, again
0.12: Add an option to hide clocks from the app list (fix #1015) 0.12: Add an option to hide clocks from the app list (fix #1015)
Add /*LANG*/ tags for internationalisation Add /*LANG*/ tags for internationalisation
0.13: Add fullscreen mode

View File

@ -2,7 +2,10 @@ var s = require("Storage");
var scaleval = 1; var scaleval = 1;
var vectorval = 20; var vectorval = 20;
var font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2"; var font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
let settings = Object.assign({ showClocks: true }, s.readJSON("launch.json", true) || {}); let settings = Object.assign({
showClocks: true,
fullscreen: false
}, s.readJSON("launch.json", true) || {});
if ("vectorsize" in settings) { if ("vectorsize" in settings) {
vectorval = parseInt(settings.vectorsize); vectorval = parseInt(settings.vectorsize);
@ -44,8 +47,11 @@ function drawApp(i, r) {
} }
g.clear(); g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets(); if (!settings.fullscreen) {
Bangle.loadWidgets();
Bangle.drawWidgets();
}
E.showScroller({ E.showScroller({
h : 64*scaleval, c : apps.length, h : 64*scaleval, c : apps.length,

View File

@ -2,7 +2,7 @@
"id": "launch", "id": "launch",
"name": "Launcher", "name": "Launcher",
"shortName": "Launcher", "shortName": "Launcher",
"version": "0.12", "version": "0.13",
"description": "This is needed to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.", "description": "This is needed to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.",
"icon": "app.png", "icon": "app.png",
"type": "launch", "type": "launch",

View File

@ -1,6 +1,9 @@
// make sure to enclose the function in parentheses // make sure to enclose the function in parentheses
(function(back) { (function(back) {
let settings = Object.assign({ showClocks: true }, require("Storage").readJSON("launch.json", true) || {}); let settings = Object.assign({
showClocks: true,
fullscreen: false
}, require("Storage").readJSON("launch.json", true) || {});
let fonts = g.getFonts(); let fonts = g.getFonts();
function save(key, value) { function save(key, value) {
@ -8,7 +11,7 @@
require("Storage").write("launch.json",settings); require("Storage").write("launch.json",settings);
} }
const appMenu = { const appMenu = {
"": {"title": /*LANG*/"Launcher Settings"}, "": { "title": /*LANG*/"Launcher" },
/*LANG*/"< Back": back, /*LANG*/"< Back": back,
/*LANG*/"Font": { /*LANG*/"Font": {
value: fonts.includes(settings.font)? fonts.indexOf(settings.font) : fonts.indexOf("12x20"), value: fonts.includes(settings.font)? fonts.indexOf(settings.font) : fonts.indexOf("12x20"),
@ -16,15 +19,20 @@
onchange: (m) => {save("font", fonts[m])}, onchange: (m) => {save("font", fonts[m])},
format: v => fonts[v] format: v => fonts[v]
}, },
/*LANG*/"Vector font size": { /*LANG*/"Vector Font Size": {
value: settings.vectorsize || 10, value: settings.vectorsize || 10,
min:10, max: 20,step:1,wrap:true, min:10, max: 20,step:1,wrap:true,
onchange: (m) => {save("vectorsize", m)} onchange: (m) => {save("vectorsize", m)}
}, },
/*LANG*/"Show clocks": { /*LANG*/"Show Clocks": {
value: settings.showClocks == true, value: settings.showClocks == true,
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No", format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: (m) => {save("showClocks", m)} onchange: (m) => { save("showClocks", m) }
},
/*LANG*/"Fullscreen": {
value: settings.fullscreen == true,
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: (m) => { save("fullscreen", m) }
} }
}; };
E.showMenu(appMenu); E.showMenu(appMenu);

View File

@ -18,3 +18,5 @@
0.18: Fullscreen mode can now be enabled or disabled in the settings. 0.18: Fullscreen mode can now be enabled or disabled in the settings.
0.19: Alarms can not go bigger than 100. 0.19: Alarms can not go bigger than 100.
0.20: Use alarm for alarm functionality instead of own implementation. 0.20: Use alarm for alarm functionality instead of own implementation.
0.21: Add custom theming.
0.22: Fix alarm and add build in function for step counting.

View File

@ -1,8 +1,7 @@
# LCARS clock # LCARS clock
A simple LCARS inspired clock. A simple LCARS inspired clock.
Note: To display the steps, the wpedom app is required. To show weather data To show weather data such as temperature, humidity or window you BangleJS must be connected
such as temperature, humidity or window you BangleJS must be connected
with Gadgetbride and the weather app must be installed. To use the timer with Gadgetbride and the weather app must be installed. To use the timer
the "sched" app must be installed on your device. the "sched" app must be installed on your device.
@ -19,6 +18,7 @@ the "sched" app must be installed on your device.
* Tap on top/bottom of screen 1 to activate an alarm. Depends on widtmr. * Tap on top/bottom of screen 1 to activate an alarm. Depends on widtmr.
* The lower orange line indicates the battery level. * The lower orange line indicates the battery level.
* Display graphs (day or month) for steps + hrm on the second screen. * Display graphs (day or month) for steps + hrm on the second screen.
* Customizable theming colors in the settings menu of the app.
## Data that can be configured ## Data that can be configured
* Steps - Steps loaded via the wpedom app. * Steps - Steps loaded via the wpedom app.
@ -43,3 +43,4 @@ Access different screens via tap on the left/ right side of the screen
## Contributors ## Contributors
- [Adam Schmalhofer](https://github.com/adamschmalhofer) - [Adam Schmalhofer](https://github.com/adamschmalhofer)
- [Jon Warrington](https://github.com/BartokW) - [Jon Warrington](https://github.com/BartokW)
- [Ronin Stegner](https://github.com/Ronin0000)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 795 B

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 791 B

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 B

After

Width:  |  Height:  |  Size: 742 B

View File

@ -1,14 +1,17 @@
const TIMER_IDX = "lcars"; const TIMER_IDX = "lcars";
const SETTINGS_FILE = "lcars.setting.json"; const SETTINGS_FILE = "lcars.setting.json";
const locale = require('locale'); const locale = require('locale');
const storage = require('Storage'); const storage = require('Storage')
let settings = { let settings = {
alarm: -1, alarm: -1,
dataRow1: "Steps", dataRow1: "Steps",
dataRow2: "Temp", dataRow2: "HRM",
dataRow3: "Battery", dataRow3: "Battery",
speed: "kph", speed: "kph",
fullscreen: false, fullscreen: false,
themeColor1BG: "#FF9900",
themeColor2BG: "#FF00DC",
themeColor3BG: "#0094FF",
}; };
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) {
@ -18,9 +21,9 @@ for (const key in saved_settings) {
/* /*
* Colors to use * Colors to use
*/ */
let cBlue = "#0094FF"; let color1 = settings.themeColor3BG;
let cOrange = "#FF9900"; let color2 = settings.themeColor1BG;
let cPurple = "#FF00DC"; let color3 = settings.themeColor2BG;
let cWhite = "#FFFFFF"; let cWhite = "#FFFFFF";
let cBlack = "#000000"; let cBlack = "#000000";
let cGrey = "#424242"; let cGrey = "#424242";
@ -33,33 +36,77 @@ let lcarsViewPos = 0;
var plotMonth = false; var plotMonth = false;
/* function convert24to16(input)
* Requirements and globals {
*/ let RGB888 = parseInt(input.replace(/^#/, ''), 16);
let r = (RGB888 & 0xFF0000) >> 16;
let g = (RGB888 & 0xFF00) >> 8;
let b = RGB888 & 0xFF;
r = (r * 249 + 1014) >> 11;
g = (g * 253 + 505) >> 10;
b = (b * 249 + 1014) >> 11;
let RGB565 = 0;
RGB565 = RGB565 | (r << 11);
RGB565 = RGB565 | (g << 5);
RGB565 = RGB565 | b;
return "0x"+RGB565.toString(16);
}
var color1C = convert24to16(color1);
var color2C = convert24to16(color2);
var color3C = convert24to16(color3);
/*
* Requirements and globals
*/
var colorPalette = new Uint16Array([
0x0000, // not used
color2C, // second
color3C, // third
0x0000, // not used
color1C, // first
0x0000, // not used
0x0000, // not used
0x0000, // not used
0x0000, // not used
0x0000, // not used
0x0000, // not used
0x0000, // not used
0x0000, // not used
0x0000, // not used
0x0000, // not used
0x0000 // not used
],0,1);
var bgLeftFullscreen = { var bgLeftFullscreen = {
width : 27, height : 176, bpp : 3, width : 27, height : 176, bpp : 3,
transparent : 0, transparent : 0,
buffer : require("heatshrink").decompress(atob("AAUM2XLlgCCwAJBBAuy4EAmQIF5cggAIGlmwgYIG2XIF42wF4ImGF4ImHJoQmGJoQdJhZNHNY47CgRNGBIJZHHgRiGBIRQ/KH5QCAFCh/eX5Q/KAwdCAGVbtu27YCCoAJBkuWrNlAQRGCiwRDAQPQBIMJCIYCBsAJBgomEtu0WoQmEy1YBIMBHYttIwQ7FyxQ/KHFlFAQ7F2weCHYplKChRTCCg5TCHw5TMAD0GzVp0wCCBBGaBIMaBAtpwECBA2mwEJBAugDgMmCIwJBF5EABAtoeQQvGCYQdPJoI7LMQzTCLJKAGzAJBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A==")) buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAoUP7y/KH5QGDoQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyDCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4"))),
palette: colorPalette
}; };
var bgLeftNotFullscreen = { var bgLeftNotFullscreen = {
width : 27, height : 152, bpp : 3, width : 27, height : 152, bpp : 3,
transparent : 0, transparent : 0,
buffer : require("heatshrink").decompress(atob("AAUM2XLlgCCwAJBBAuy4EAmQIF5cggAIGlmwgYIG2XIF42wF4ImGF4ImHJoQmGJoQdJhZNHNY47CgRNGBIJZHHgRiGBIRQ/KH5QCAGVbtu27YCCoAJBkuWrNlAQRkCiwRDAQPQBIMJCIYCBsAJBgomEtu0WoQmEy1YBIMBHYttIwQ7FyxQ/KHFlFAQ7F2weCHYplKChRTCCg5TCHw5TMAD0GzVp0wCCBBGaBIMaBAtpwECBA2mwEJBAugDgMmCIwJBF5EABAtoeQQvGCYQdPJoI7LMQzTCLJKAGzAJBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A=")) buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A=="))),
palette: colorPalette
}; };
var bgRightFullscreen = { var bgRightFullscreen = {
width : 27, height : 176, bpp : 3, width : 27, height : 176, bpp : 3,
transparent : 0, transparent : 0,
buffer : require("heatshrink").decompress(atob("lmy5YCDBIUyBAmy5AJBhYUG2EAhgIFAQMAgQIGCgQABCg4ABEAwUNFI2AKZHAKZEgGRZTGOIUDQxJxGKH5Q/agwAnUP7y/KH4yGeVYAJrdt23bAQVABIMly1ZsoCCMgUWCIYCB6AJBhIRDAQNgBIMFEwlt2i1CEwmWrAJBgI7FtpGCHYuWKH5QxEwpQDlo7F0A7IqBZBEwo7BCIwCBJo53CJoxiCJpIAdgOmzVpAQR/CgAIEAQJ2CBAoCBBIMmCg1oD4QLGFQUCCjQ+CKYw+CKY4JCKYwoCGRMaGREJDoroCgwdFzBlLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJox/CgAA=")) buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAnUP7y/KH4yGeVYAJ0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkp4CS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A=="))),
palette: colorPalette
}; };
var bgRightNotFullscreen = { var bgRightNotFullscreen = {
width : 27, height : 152, bpp : 3, width : 27, height : 152, bpp : 3,
transparent : 0, transparent : 0,
buffer : require("heatshrink").decompress(atob("lmy5YCDBIUyBAmy5AJBhYUG2EAhgIFAQMAgQIGCgQABCg4ABEAwUNFI2AKZHAKZEgGRZTGOIUDQxJxGKH5Q/agwAxrdt23bAQVABIMly1ZsoCCMgUWCIYCB6AJBhIRDAQNgBIMFEwlt2i1CEwmWrAJBgI7FtpGCHYuWKH5QxEwpQDlo7F0A7IqBZBEwo7BCIwCBJo53CJoxiCJpIAdgOmzVpAQR/CgAIEAQJ2CBAoCBBIMmCg1oD4QLGFQUCCjQ+CKYw+CKY4JCKYwoCGRMaGREJDoroCgwdFzBlLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJox/CgA=")) buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAx0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A="))),
palette: colorPalette
}; };
var bgLeft = settings.fullscreen ? bgLeftFullscreen : bgLeftNotFullscreen; var bgLeft = settings.fullscreen ? bgLeftFullscreen : bgLeftNotFullscreen;
@ -191,7 +238,7 @@ function _drawData(key, y, c){
value = E.getAnalogVRef().toFixed(2) + "V"; value = E.getAnalogVRef().toFixed(2) + "V";
} else if(key == "HRM"){ } else if(key == "HRM"){
value = Math.round(Bangle.getHealthStatus("day").bpm); value = Math.round(Bangle.getHealthStatus("last").bpm);
} else if (key == "TEMP"){ } else if (key == "TEMP"){
var weather = getWeather(); var weather = getWeather();
@ -244,9 +291,11 @@ function drawInfo(){
return; return;
} }
// Draw Infor is called from different sources so
// we have to ensure that the alignment is always the same.
g.setFontAlign(-1, -1, 0); g.setFontAlign(-1, -1, 0);
g.setFontAntonioMedium(); g.setFontAntonioMedium();
g.setColor(cOrange); g.setColor(color2);
g.clearRect(120, 10, g.getWidth(), 75); g.clearRect(120, 10, g.getWidth(), 75);
g.drawString("LCARS", 128, 13); g.drawString("LCARS", 128, 13);
@ -256,7 +305,7 @@ function drawInfo(){
g.drawString("NOCON", 128, 33); g.drawString("NOCON", 128, 33);
} }
if(Bangle.isLocked()){ if(Bangle.isLocked()){
g.setColor(cPurple); g.setColor(color3);
g.drawString("LOCK", 128, 53); g.drawString("LOCK", 128, 53);
} }
} }
@ -287,7 +336,7 @@ function drawState(){
g.drawString("STATUS", 23+26, 108); g.drawString("STATUS", 23+26, 108);
} else { } else {
// Alarm within symbol // Alarm within symbol
g.setColor(cOrange); g.setColor(color2);
g.drawString("ALARM", 23+26, 108); g.drawString("ALARM", 23+26, 108);
g.setColor(cWhite); g.setColor(cWhite);
g.setFontAntonioLarge(); g.setFontAntonioLarge();
@ -302,19 +351,19 @@ function drawPosition0(){
// Draw background image // Draw background image
var offset = settings.fullscreen ? 0 : 24; var offset = settings.fullscreen ? 0 : 24;
g.drawImage(bgLeft, 0, offset); g.drawImage(bgLeft, 0, offset);
drawHorizontalBgLine(cBlue, 25, 120, offset, 4); drawHorizontalBgLine(color1, 25, 120, offset, 4);
drawHorizontalBgLine(cBlue, 130, 176, offset, 4); drawHorizontalBgLine(color1, 130, 176, offset, 4);
drawHorizontalBgLine(cPurple, 20, 70, 80, 4); drawHorizontalBgLine(color3, 20, 70, 80, 4);
drawHorizontalBgLine(cPurple, 80, 176, 80, 4); drawHorizontalBgLine(color3, 80, 176, 80, 4);
drawHorizontalBgLine(cOrange, 35, 110, 87, 4); drawHorizontalBgLine(color2, 35, 110, 87, 4);
drawHorizontalBgLine(cOrange, 120, 176, 87, 4); drawHorizontalBgLine(color2, 120, 176, 87, 4);
// The last line is a battery indicator too // The last line is a battery indicator too
var bat = E.getBattery() / 100.0; var bat = E.getBattery() / 100.0;
var batStart = 19; var batStart = 19;
var batWidth = 172 - batStart; var batWidth = 172 - batStart;
var batX2 = parseInt(batWidth * bat + batStart); var batX2 = parseInt(batWidth * bat + batStart);
drawHorizontalBgLine(cOrange, batStart, batX2, 171, 5); drawHorizontalBgLine(color2, batStart, batX2, 171, 5);
drawHorizontalBgLine(cGrey, batX2, 172, 171, 5); drawHorizontalBgLine(cGrey, batX2, 172, 171, 5);
for(var i=0; i+batStart<=172; i+=parseInt(batWidth/4)){ for(var i=0; i+batStart<=172; i+=parseInt(batWidth/4)){
drawHorizontalBgLine(cBlack, batStart+i, batStart+i+3, 168, 8) drawHorizontalBgLine(cBlack, batStart+i, batStart+i+3, 168, 8)
@ -353,9 +402,9 @@ function drawPosition0(){
// Draw data // Draw data
g.setFontAlign(-1, -1, 0); g.setFontAlign(-1, -1, 0);
g.setColor(cWhite); g.setColor(cWhite);
drawData(settings.dataRow1, 97, cOrange); drawData(settings.dataRow1, 97, color2);
drawData(settings.dataRow2, 122, cPurple); drawData(settings.dataRow2, 122, color3);
drawData(settings.dataRow3, 147, cBlue); drawData(settings.dataRow3, 147, color1);
// Draw state // Draw state
drawState(); drawState();
@ -366,13 +415,13 @@ function drawPosition1(){
var offset = settings.fullscreen ? 0 : 24; var offset = settings.fullscreen ? 0 : 24;
g.drawImage(bgRight, 149, offset); g.drawImage(bgRight, 149, offset);
if(settings.fullscreen){ if(settings.fullscreen){
drawHorizontalBgLine(cBlue, 0, 140, offset, 4); drawHorizontalBgLine(color1, 0, 140, offset, 4);
} }
drawHorizontalBgLine(cPurple, 0, 80, 80, 4); drawHorizontalBgLine(color3, 0, 80, 80, 4);
drawHorizontalBgLine(cPurple, 90, 150, 80, 4); drawHorizontalBgLine(color3, 90, 150, 80, 4);
drawHorizontalBgLine(cOrange, 0, 50, 87, 4); drawHorizontalBgLine(color2, 0, 50, 87, 4);
drawHorizontalBgLine(cOrange, 60, 140, 87, 4); drawHorizontalBgLine(color2, 60, 140, 87, 4);
drawHorizontalBgLine(cOrange, 0, 150, 171, 5); drawHorizontalBgLine(color2, 0, 150, 171, 5);
// Draw steps bars // Draw steps bars
g.setColor(cWhite); g.setColor(cWhite);
@ -511,17 +560,20 @@ function draw(){
* Step counter via widget * Step counter via widget
*/ */
function getSteps() { function getSteps() {
var steps = 0;
try{ try{
if (WIDGETS.wpedom !== undefined) { if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom.getSteps(); steps = WIDGETS.wpedom.getSteps();
} else if (WIDGETS.activepedom !== undefined) { } else if (WIDGETS.activepedom !== undefined) {
return WIDGETS.activepedom.getSteps(); steps = WIDGETS.activepedom.getSteps();
} else {
steps = Bangle.getHealthStatus("day").steps;
} }
} catch(ex) { } catch(ex) {
// In case we failed, we can only show 0 steps. // In case we failed, we can only show 0 steps.
} }
return 0; return steps;
} }
@ -530,38 +582,35 @@ function getWeather(){
try { try {
weatherJson = storage.readJSON('weather.json'); weatherJson = storage.readJSON('weather.json');
var weather = weatherJson.weather;
// Temperature
weather.temp = locale.temp(weather.temp-273.15);
// Humidity
weather.hum = weather.hum + "%";
// Wind
const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
var speedFactor = settings.speed == "kph" ? 1.0 : 1.0 / 1.60934;
weather.wind = Math.round(wind[1] * speedFactor);
return weather
} catch(ex) { } catch(ex) {
// Return default // Return default
} }
if(weatherJson === undefined){ return {
return { temp: " ? ",
temp: "-", hum: " ? ",
hum: "-", txt: " ? ",
txt: "-", wind: " ? ",
wind: "-", wdir: " ? ",
wdir: "-", wrose: " ? "
wrose: "-" };
};
}
var weather = weatherJson.weather;
// Temperature
weather.temp = locale.temp(weather.temp-273.15);
// Humidity
weather.hum = weather.hum + "%";
// Wind
const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
var speedFactor = settings.speed == "kph" ? 1.0 : 1.0 / 1.60934;
weather.wind = Math.round(wind[1] * speedFactor);
return weather
} }
/* /*
* Handle alarm * Handle alarm
*/ */
@ -594,7 +643,7 @@ function increaseAlarm(){
var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0; var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0;
var alarm = require('sched') var alarm = require('sched')
alarm.setAlarm(TIMER_IDX, { alarm.setAlarm(TIMER_IDX, {
timer : (minutes+5)*60*1000, timer : (minutes+5)*60*1000,
}); });
alarm.reload(); alarm.reload();
} catch(ex){ } } catch(ex){ }
@ -609,9 +658,9 @@ function decreaseAlarm(){
alarm.setAlarm(TIMER_IDX, undefined); alarm.setAlarm(TIMER_IDX, undefined);
if(minutes > 0){ if(minutes > 0){
alarm.setAlarm(TIMER_IDX, { alarm.setAlarm(TIMER_IDX, {
timer : minutes*60*1000, timer : minutes*60*1000,
}); });
} }
alarm.reload(); alarm.reload();
@ -642,7 +691,6 @@ Bangle.on('charging',function(charging) {
drawState(); drawState();
}); });
function feedback(){ function feedback(){
Bangle.buzz(40, 0.3); Bangle.buzz(40, 0.3);
} }

View File

@ -5,11 +5,14 @@
const storage = require('Storage') const storage = require('Storage')
let settings = { let settings = {
alarm: -1, alarm: -1,
dataRow1: "Battery", dataRow1: "Steps",
dataRow2: "Steps", dataRow2: "HRM",
dataRow3: "Temp", dataRow3: "Battery",
speed: "kph", speed: "kph",
fullscreen: false, fullscreen: false,
themeColor1BG: "#FF9900",
themeColor2BG: "#FF00DC",
themeColor3BG: "#0094FF",
}; };
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) {
@ -20,8 +23,11 @@
storage.write(SETTINGS_FILE, settings) storage.write(SETTINGS_FILE, settings)
} }
var dataOptions = ["Steps", "Battery", "VREF", "HRM", "Temp", "Humidity", "Wind", "Altitude", "CoreT"]; var dataOptions = ["Steps", "Battery", "VREF", "HRM", "Temp", "Humidity", "Wind", "Altitude", "CoreT"];
var speedOptions = ["kph", "mph"]; var speedOptions = ["kph", "mph"];
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue','Yellow','White'];
var bg_code = ['#00ff00','#FF9900','#0094FF','#FF00DC','#ff0000','#0000ff','#ffef00','#FFFFFF'];
E.showMenu({ E.showMenu({
'': { 'title': 'LCARS Clock' }, '': { 'title': 'LCARS Clock' },
@ -69,6 +75,33 @@
settings.speed = speedOptions[v]; settings.speed = speedOptions[v];
save(); save();
}, },
},
'Theme Color 1': {
value: 0 | bg_code.indexOf(settings.themeColor1BG),
min: 0, max: 7,
format: v => color_options[v],
onchange: v => {
settings.themeColor1BG = bg_code[v];
save();
},
},
'Theme Color 2': {
value: 0 | bg_code.indexOf(settings.themeColor2BG),
min: 0, max: 7,
format: v => color_options[v],
onchange: v => {
settings.themeColor2BG = bg_code[v];
save();
},
},
'Theme Color 3': {
value: 0 | bg_code.indexOf(settings.themeColor3BG),
min: 0, max: 7,
format: v => color_options[v],
onchange: v => {
settings.themeColor3BG = bg_code[v];
save();
},
} }
}); });
}) })

View File

@ -3,7 +3,7 @@
"name": "LCARS Clock", "name": "LCARS Clock",
"shortName":"LCARS", "shortName":"LCARS",
"icon": "lcars.png", "icon": "lcars.png",
"version":"0.20", "version":"0.22",
"readme": "README.md", "readme": "README.md",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"description": "Library Computer Access Retrieval System (LCARS) clock.", "description": "Library Computer Access Retrieval System (LCARS) clock.",

View File

@ -44,3 +44,5 @@
0.29: Fix message list overwrites on Bangle.js 1 (fix #1642) 0.29: Fix message list overwrites on Bangle.js 1 (fix #1642)
0.30: Add new Icons (Youtube, Twitch, MS TODO, Teams, Snapchat, Signal, Post & DHL, Nina, Lieferando, Kalender, Discord, Corona Warn, Bibel) 0.30: Add new Icons (Youtube, Twitch, MS TODO, Teams, Snapchat, Signal, Post & DHL, Nina, Lieferando, Kalender, Discord, Corona Warn, Bibel)
0.31: Option to disable icon flashing 0.31: Option to disable icon flashing
0.32: Added an option to allow quiet mode to override message auto-open
0.33: Timeout from the message list screen if the message being displayed is removed and there is a timer going

View File

@ -470,8 +470,6 @@ function checkMessages(options) {
// no new messages - go to clock? // no new messages - go to clock?
if (options.clockIfAllRead && newMessages.length==0) if (options.clockIfAllRead && newMessages.length==0)
return load(); return load();
// we don't have to time out of this screen...
cancelReloadTimeout();
active = "main"; active = "main";
// Otherwise show a menu // Otherwise show a menu
E.showScroller({ E.showScroller({

View File

@ -56,9 +56,16 @@ exports.pushMessage = function(event) {
} }
// otherwise load messages/show widget // otherwise load messages/show widget
var loadMessages = Bangle.CLOCK || event.important; var loadMessages = Bangle.CLOCK || event.important;
// first, buzz
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet; var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
var unlockWatch = (require('Storage').readJSON('messages.settings.json',1)||{}).unlockWatch; var appSettings = require('Storage').readJSON('messages.settings.json',1)||{};
var unlockWatch = appSettings.unlockWatch;
var quietNoAutOpn = appSettings.quietNoAutOpn;
delete appSettings;
// don't auto-open messages in quiet mode if quietNoAutOpn is true
if(quiet && quietNoAutOpn) {
loadMessages = false;
}
// first, buzz
if (!quiet && loadMessages && global.WIDGETS && WIDGETS.messages){ if (!quiet && loadMessages && global.WIDGETS && WIDGETS.messages){
WIDGETS.messages.buzz(); WIDGETS.messages.buzz();
if(unlockWatch != false){ if(unlockWatch != false){

View File

@ -1,7 +1,7 @@
{ {
"id": "messages", "id": "messages",
"name": "Messages", "name": "Messages",
"version": "0.31", "version": "0.33",
"description": "App to display notifications from iOS and Gadgetbridge/Android", "description": "App to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",

View File

@ -53,6 +53,11 @@
format: v => v?/*LANG*/'Yes':/*LANG*/'No', format: v => v?/*LANG*/'Yes':/*LANG*/'No',
onchange: v => updateSetting("flash", v) onchange: v => updateSetting("flash", v)
}, },
/*LANG*/'Quiet mode disables auto-open': {
value: !!settings().quietNoAutOpn,
format: v => v?/*LANG*/'Yes':/*LANG*/'No',
onchange: v => updateSetting("quietNoAutOpn", v)
},
}; };
E.showMenu(mainmenu); E.showMenu(mainmenu);
}) })

View File

@ -2,3 +2,4 @@
0.02: Fix scheduling of other alarms if there is a pending alarm from the past (fix #1667) 0.02: Fix scheduling of other alarms if there is a pending alarm from the past (fix #1667)
0.03: Fix `getTimeToAlarm` for a timer already used at same day, don't set `last` for timers. 0.03: Fix `getTimeToAlarm` for a timer already used at same day, don't set `last` for timers.
0.04: Fix `getTimeToAlarm` to check for next dow if alarm.t lower currentTime. 0.04: Fix `getTimeToAlarm` to check for next dow if alarm.t lower currentTime.
0.05: Export new functions (`newDefaultAlarm/Timer`), add Settings page

View File

@ -8,8 +8,17 @@ Other apps can use this to provide alarm functionality.
App App
--- ---
The Alarm app allows you to add/modify any running timers. The **Alarms & Timers** app allows you to add/modify any running alarms and timers.
Global Settings
---------------
- `Unlock at Buzz` - If `Yes` the alarm/timer will unlock the watch
- `Default Auto Snooze` - Default _Auto Snooze_ value for newly created alarms (_Alarms_ only)
- `Default Snooze` - Default _Snooze_ value for newly created alarms/timers
- `Buzz Count` - The number of buzzes before the watch goes silent
- `Buzz Interval` - The interval between one buzz and the next
- `Default Alarm/Timer Pattern` - Default vibration pattern for newly created alarms/timers
Internals / Library Internals / Library
------------------- -------------------
@ -53,21 +62,27 @@ use too much RAM.
It can be used as follows: It can be used as follows:
``` ```
// add/update an existing alarm // Get a new alarm with default values
let alarm = require("sched").newDefaultAlarm();
// Get a new timer with default values
let timer = require("sched").newDefaultTimer();
// Add/update an existing alarm
require("sched").setAlarm("mytimer", { require("sched").setAlarm("mytimer", {
msg : "Wake up", msg : "Wake up",
timer : 10*60*1000, // 10 Minutes timer : 10 * 60 * 1000 // 10 minutes
}); });
// Ensure the widget and alarm timer updates to schedule the new alarm properly // Ensure the widget and alarm timer updates to schedule the new alarm properly
require("sched").reload(); require("sched").reload();
// Get the time to the next alarm for us // Get the time to the next alarm for us
var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm("mytimer")); let timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm("mytimer"));
// timeToNext===undefined if no alarm or alarm disabled // timeToNext === undefined if no alarm or alarm disabled
// delete an alarm // Delete an alarm
require("sched").setAlarm("mytimer", undefined); require("sched").setAlarm("mytimer", undefined);
// reload after deleting... // Reload after deleting
require("sched").reload(); require("sched").reload();
// Or add an alarm that runs your own code - in this case // Or add an alarm that runs your own code - in this case
@ -76,12 +91,15 @@ require("sched").reload();
require("sched").setAlarm("customrunner", { require("sched").setAlarm("customrunner", {
appid : "myapp", appid : "myapp",
js : "load('setting.app.js')", js : "load('setting.app.js')",
timer : 1*60*1000, // 1 Minute timer : 1 * 60 * 1000 // 1 minute
}); });
// If you have been specifying `appid` you can also find any alarms that // If you have been specifying `appid` you can also find any alarms that
// your app has created with the following: // your app has created with the following:
require("sched").getAlarms().filter(a=>a.appid=="myapp"); require("sched").getAlarms().filter(a => a.appid == "myapp");
// Get the scheduler settings
let settings = require("sched").getSettings();
``` ```
If your app requires alarms, you can specify that the alarms app needs to If your app requires alarms, you can specify that the alarms app needs to

View File

@ -52,3 +52,58 @@ exports.reload = function() {
Bangle.drawWidgets(); Bangle.drawWidgets();
} }
}; };
// Factory that creates a new alarm with default values
exports.newDefaultAlarm = function () {
const settings = exports.getSettings();
let alarm = {
t: 12 * 3600000, // Default to 12:00
on: true,
rp: false, // repeat not the default
as: settings.defaultAutoSnooze || false,
dow: 0b1111111,
last: 0,
vibrate: settings.defaultAlarmPattern,
};
delete settings;
return alarm;
}
// Factory that creates a new timer with default values
exports.newDefaultTimer = function () {
const settings = exports.getSettings();
let timer = {
timer: 5 * 60 * 1000, // 5 minutes
on: true,
rp: false,
as: false,
dow: 0b1111111,
last: 0,
vibrate: settings.defaultTimerPattern
}
delete settings;
return timer;
};
// Return the scheduler settings
exports.getSettings = function () {
return Object.assign(
{
unlockAtBuzz: false,
defaultSnoozeMillis: 600000, // 10 minutes
defaultAutoSnooze: false,
buzzCount: 10,
buzzIntervalMillis: 3000, // 3 seconds
defaultAlarmPattern: "..",
defaultTimerPattern: ".."
},
require("Storage").readJSON("sched.settings.json", true) || {}
);
}
// Write the updated settings back to storage
exports.setSettings = function(settings) {
require("Storage").writeJSON("sched.settings.json", settings);
};

View File

@ -1,7 +1,7 @@
{ {
"id": "sched", "id": "sched",
"name": "Scheduler", "name": "Scheduler",
"version": "0.04", "version": "0.05",
"description": "Scheduling library for alarms and timers", "description": "Scheduling library for alarms and timers",
"icon": "app.png", "icon": "app.png",
"type": "scheduler", "type": "scheduler",
@ -12,7 +12,8 @@
{"name":"sched.boot.js","url":"boot.js"}, {"name":"sched.boot.js","url":"boot.js"},
{"name":"sched.js","url":"sched.js"}, {"name":"sched.js","url":"sched.js"},
{"name":"sched.img","url":"app-icon.js","evaluate":true}, {"name":"sched.img","url":"app-icon.js","evaluate":true},
{"name":"sched","url":"lib.js"} {"name":"sched","url":"lib.js"},
{"name":"sched.settings.js","url":"settings.js"}
], ],
"data": [{"name":"sched.json"}] "data": [{"name":"sched.json"}, {"name":"sched.settings.json"}]
} }

View File

@ -18,7 +18,9 @@ function formatTime(t) {
} }
function showAlarm(alarm) { function showAlarm(alarm) {
var msg = ""; const settings = require("sched").getSettings();
let msg = "";
msg += alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t); msg += alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t);
if (alarm.msg) { if (alarm.msg) {
msg += "\n"+alarm.msg; msg += "\n"+alarm.msg;
@ -28,9 +30,12 @@ function showAlarm(alarm) {
else else
msg = atob("AC0swgF97///RcEpMlVVVVVVf9VVVVVVVVX/9VVf9VVf/1VVV///1Vf9VX///VVX///VWqqlV///1Vf//9aqqqqpf//9V///2qqqqqqn///V///6qqqqqqr///X//+qqoAAKqqv//3//6qoAAAAKqr//3//qqAAAAAAqq//3/+qoAADwAAKqv/3/+qgAADwAACqv/3/aqAAADwAAAqp/19qoAAADwAAAKqfV1qgAAADwAAACqXVWqgAAADwAAACqlVWqAAAADwAAAAqlVWqAAAADwAAAAqlVWqAAAADwAAAAqlVaoAAAADwAAAAKpVaoAAAADwAAAAKpVaoAAAADwAAAAKpVaoAAAAOsAAAAKpVaoAAAAOsAAAAKpVaoAAAAL/AAAAKpVaoAAAAgPwAAAKpVaoAAACAD8AAAKpVWqAAAIAA/AAAqlVWqAAAgAAPwAAqlVWqAACAAADwAAqlVWqgAIAAAAAACqlVVqgAgAAAAAACqVVVqoAAAAAAAAKqVVVaqAAAAAAAAqpVVVWqgAAAAAACqlVVVWqoAAAAAAKqlVVVVqqAAAAAAqqVVVVVaqoAAAAKqpVVVVVeqqoAAKqqtVVVVV/6qqqqqqr/VVVVX/2qqqqqqn/1VVVf/VaqqqqpV/9VVVf9VVWqqlVVf9VVVf1VVVVVVVVX9VQ==")+" "+msg; msg = atob("AC0swgF97///RcEpMlVVVVVVf9VVVVVVVVX/9VVf9VVf/1VVV///1Vf9VX///VVX///VWqqlV///1Vf//9aqqqqpf//9V///2qqqqqqn///V///6qqqqqqr///X//+qqoAAKqqv//3//6qoAAAAKqr//3//qqAAAAAAqq//3/+qoAADwAAKqv/3/+qgAADwAACqv/3/aqAAADwAAAqp/19qoAAADwAAAKqfV1qgAAADwAAACqXVWqgAAADwAAACqlVWqAAAADwAAAAqlVWqAAAADwAAAAqlVWqAAAADwAAAAqlVaoAAAADwAAAAKpVaoAAAADwAAAAKpVaoAAAADwAAAAKpVaoAAAAOsAAAAKpVaoAAAAOsAAAAKpVaoAAAAL/AAAAKpVaoAAAAgPwAAAKpVaoAAACAD8AAAKpVWqAAAIAA/AAAqlVWqAAAgAAPwAAqlVWqAACAAADwAAqlVWqgAIAAAAAACqlVVqgAgAAAAAACqVVVqoAAAAAAAAKqVVVaqAAAAAAAAqpVVVWqgAAAAAACqlVVVWqoAAAAAAKqlVVVVqqAAAAAAqqVVVVVaqoAAAAKqpVVVVVeqqoAAKqqtVVVVV/6qqqqqqr/VVVVX/2qqqqqqn/1VVVf/VaqqqqpV/9VVVf9VVWqqlVVf9VVVf1VVVVVVVVX9VQ==")+" "+msg;
} }
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
var buzzCount = 10;
let buzzCount = settings.buzzCount;
E.showPrompt(msg,{ E.showPrompt(msg,{
title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!", title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!",
buttons : {/*LANG*/"Snooze":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins buttons : {/*LANG*/"Snooze":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins
@ -38,7 +43,7 @@ function showAlarm(alarm) {
buzzCount = 0; buzzCount = 0;
if (sleep) { if (sleep) {
if(alarm.ot===undefined) alarm.ot = alarm.t; if(alarm.ot===undefined) alarm.ot = alarm.t;
alarm.t += 10*60*1000; // 10 minutes alarm.t += settings.defaultSnoozeMillis;
} else { } else {
if (!alarm.timer) alarm.last = (new Date()).getDate(); if (!alarm.timer) alarm.last = (new Date()).getDate();
if (alarm.ot!==undefined) { if (alarm.ot!==undefined) {
@ -51,24 +56,35 @@ function showAlarm(alarm) {
require("sched").setAlarms(alarms); require("sched").setAlarms(alarms);
load(); load();
}); });
function buzz() { function buzz() {
require("buzz").pattern(alarm.vibrate===undefined?"..":alarm.vibrate).then(function() { if (settings.unlockAtBuzz) {
if (buzzCount--) Bangle.setLocked(false);
setTimeout(buzz, 3000); }
else if(alarm.as) { // auto-snooze
buzzCount = 10; require("buzz").pattern(alarm.vibrate === undefined ? ".." : alarm.vibrate).then(() => {
setTimeout(buzz, 600000); if (buzzCount--) {
setTimeout(buzz, settings.buzzIntervalMillis);
} else if (alarm.as) { // auto-snooze
buzzCount = settings.buzzCount;
setTimeout(buzz, settings.defaultSnoozeMillis);
} }
}); });
} }
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return;
if ((require("Storage").readJSON("setting.json", 1) || {}).quiet > 1)
return;
buzz(); buzz();
} }
// Check for alarms // Check for alarms
var alarms = require("sched").getAlarms(); let alarms = require("sched").getAlarms();
var active = require("sched").getActiveAlarms(alarms); let active = require("sched").getActiveAlarms(alarms);
if (active.length) // if there's an alarm, show it if (active.length) {
// if there's an alarm, show it
showAlarm(active[0]); showAlarm(active[0]);
else // otherwise just go back to default app } else {
// otherwise just go back to default app
setTimeout(load, 100); setTimeout(load, 100);
}

72
apps/sched/settings.js Normal file
View File

@ -0,0 +1,72 @@
(function (back) {
let settings = require("sched").getSettings();
E.showMenu({
"": { "title": /*LANG*/"Scheduler" },
/*LANG*/"< Back": () => back(),
/*LANG*/"Unlock at Buzz": {
value: settings.unlockAtBuzz,
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v => {
settings.unlockAtBuzz = v;
require("sched").setSettings(settings);
}
},
/*LANG*/"Default Auto Snooze": {
value: settings.defaultAutoSnooze,
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v => {
settings.defaultAutoSnooze = v;
require("sched").setSettings(settings);
}
},
/*LANG*/"Default Snooze": {
value: settings.defaultSnoozeMillis / 60000,
min: 5,
max: 30,
step: 5,
format: v => v + /*LANG*/" min",
onchange: v => {
settings.defaultSnoozeMillis = v * 60000;
require("sched").setSettings(settings);
}
},
/*LANG*/"Buzz Count": {
value: settings.buzzCount,
min: 5,
max: 15,
step: 1,
onchange: v => {
settings.buzzCount = v;
require("sched").setSettings(settings);
}
},
/*LANG*/"Buzz Interval": {
value: settings.buzzIntervalMillis / 1000,
min: 1,
max: 5,
step: 1,
format: v => v + /*LANG*/"s",
onchange: v => {
settings.buzzIntervalMillis = v * 1000;
require("sched").setSettings(settings);
}
},
/*LANG*/"Default Alarm Pattern": require("buzz_menu").pattern(settings.defaultAlarmPattern, v => {
settings.defaultAlarmPattern = v;
require("sched").setSettings(settings);
}),
/*LANG*/"Default Timer Pattern": require("buzz_menu").pattern(settings.defaultTimerPattern, v => {
settings.defaultTimerPattern = v;
require("sched").setSettings(settings);
})
});
});

1
apps/widstep/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New widget

9
apps/widstep/README.md Normal file
View File

@ -0,0 +1,9 @@
# Step counter widget
This is my step counter. There are many like it, but this one is mine.
A pedometer widget designed to be as narrow as possible, but still easy to read, by sacrificing accuracy and only showing to the nearest 100 steps (0.1k).
Shows a subtle fill colour in the background for progress to the goal. The goal is picked up from the health tracker settings.
![](widstep-light.png)
![](widstep-dark.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,17 @@
{
"id": "widstep",
"name": "Step counter widget",
"version": "0.01",
"description": "Step counter widget, narrow but clearly readable",
"readme": "README.md",
"icon": "icons8-winter-boots-48.png",
"screenshots": [{"url":"widstep-light.png"},{"url":"widstep-dark.png"}],
"type": "widget",
"tags": "widget,health",
"supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : {"health":"app"},
"allow_emulator":false,
"storage": [
{"name":"widstep.wid.js","url":"widstep.wid.js"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,23 @@
let wsSettingsGoal = (require('Storage').readJSON("health.json", 1) || {}).stepGoal || 10000;
Bangle.on('step', function(s) { WIDGETS["widstep"].draw(); });
Bangle.on('lcdPower', function(on) {
if (on) WIDGETS["widstep"].draw();
});
WIDGETS["widstep"]={area:"tl", sortorder:-1, width:28,
draw:function() {
if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off
var steps = Bangle.getHealthStatus("day").steps;
g.reset();
g.setColor(g.theme.bg);
g.fillRect(this.x, this.y, this.x + this.width, this.y + 23);
g.setColor(g.theme.dark ? '#00f' : '#0ff');
var progress = this.width * Math.min(steps/wsSettingsGoal, 1);
g.fillRect(this.x+1, this.y+1, this.x + progress -1, this.y + 23);
g.setColor(g.theme.fg);
g.setFontAlign(0, -1);
var steps_k = (steps/1000).toFixed(1) + 'k';
g.setFont('6x15').drawString(steps_k, this.x+this.width/2, this.y + 10);
g.setFont('4x6').drawString('steps', this.x+this.width/2, this.y + 2);
}
};

View File

@ -81,7 +81,7 @@ a.btn.btn-link.dropdown-toggle {
min-height: 8em; min-height: 8em;
} }
.tile-content { position: relative; word-break: break-all; } .tile-content { position: relative; }
.link-github { .link-github {
position:absolute; position:absolute;
top: 36px; top: 36px;