Merge branch 'master' into update_long_date_sv_se_locale

master
Gordon Williams 2022-01-10 08:40:34 +00:00 committed by GitHub
commit 0f6c2a554e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 973 additions and 374 deletions

View File

@ -1717,17 +1717,18 @@
{
"id": "wohrm",
"name": "Workout HRM",
"version": "0.08",
"version": "0.09",
"description": "Workout heart rate monitor notifies you with a buzz if your heart rate goes above or below the set limits.",
"icon": "app.png",
"type": "app",
"tags": "hrm,workout",
"supports": ["BANGLEJS"],
"supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"screenshots": [{"url":"bangle1-workout-HRM-screenshot.png"}],
"storage": [
{"name":"wohrm.app.js","url":"app.js"},
{"name":"wohrm.settings.js","url":"settings.js"},
{"name":"wohrm.img","url":"app-icon.js","evaluate":true}
]
},
@ -1893,13 +1894,15 @@
{
"id": "widhwt",
"name": "Hand Wash Timer",
"version": "0.01",
"description": "Swipe your wrist over the watch face to start your personal Bangle.js hand wash timer for 35 sec. Start washing after the short buzz and stop after the long buzz.",
"version": "0.02",
"description": "On Bangle.js 1 swipe your wrist over the watch face to start your personal Bangle.js 1 hand wash timer. On Bangle.js2 the Pattern Launcher is recommended to start the timer. Start washing after the short buzz and stop after the long buzz 35sec. later.",
"icon": "widget.png",
"type": "widget",
"tags": "widget,tool",
"supports": ["BANGLEJS"],
"allow_emulator": true,
"supports": ["BANGLEJS", "BANGLEJS2"],
"storage": [
{"name":"widhwt.app.js","url":"app.js"},
{"name":"widhwt.wid.js","url":"widget.js"}
]
},
@ -3794,7 +3797,7 @@
{
"id": "simplest",
"name": "Simplest Clock",
"version": "0.03",
"version": "0.05",
"description": "The simplest working clock, acts as a tutorial piece",
"icon": "simplest.png",
"screenshots": [{"url":"screenshot_simplest.png"}],
@ -4215,7 +4218,7 @@
"id": "pastel",
"name": "Pastel Clock",
"shortName": "Pastel",
"version": "0.10",
"version": "0.11",
"description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times",
"icon": "pastel.png",
"dependencies": {"mylocation":"app", "widpedom":"app","weather":"app"},
@ -4242,7 +4245,7 @@
{
"id": "antonclk",
"name": "Anton Clock",
"version": "0.04",
"version": "0.05",
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
"readme":"README.md",
"icon": "app.png",
@ -4805,7 +4808,7 @@
{
"id": "menuwheel",
"name": "Wheel Menus",
"version": "0.01",
"version": "0.02",
"description": "Replace Bangle.js 2's menus with a version that contains variable-size text and a back button",
"readme": "README.md",
"icon": "icon.png",
@ -5437,7 +5440,7 @@
},
{
"id": "flipper",
"name": "flipper",
"name": "Flipper",
"version": "0.01",
"description": "Switch between dark and light theme and vice versa, combine with pattern launcher and swipe to flip.",
"readme":"README.md",
@ -5465,5 +5468,22 @@
{"name":"ruuviwatch.app.js","url":"ruuviwatch.app.js"},
{"name":"ruuviwatch.img","url":"ruuviwatch.app-icon.js","evaluate":true}
]
},
{
"id": "limelight",
"name": "Limelight",
"version": "0.01",
"description": "Simple analogue clock (with configurable fonts) based on the work of @Andreas_Rozek (Simple_Clock)",
"icon": "limelight.png",
"readme":"README.md",
"screenshots": [{"url":"screenshot_limelight.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"limelight.app.js","url":"limelight.app.js"},
{"name":"limelight.settings.js","url":"limelight.settings.js"},
{"name":"limelight.img","url":"limelight.icon.js","evaluate":true}
]
}
]

View File

@ -2,3 +2,6 @@
0.02: Load widgets after setUI so widclk knows when to hide
0.03: Clock now shows day of week under date.
0.04: Clock can optionally show seconds, date optionally in ISO-8601 format, weekdays and uppercase configurable, too.
0.05: Clock can optionally show ISO-8601 calendar weeknumber (default: Off)
when weekday name "Off": week #:<num>
when weekday name "On": weekday name is cut at 6th position and .#<week num> is added

View File

@ -40,8 +40,7 @@ The main menu contains several settings covering Anton clock in general.
* **Show Weekday** - Weekday is shown in the time presentation without seconds.
Weekday name depends on the current locale.
If seconds are shown, the weekday is never shown as there is not enough space on the watch face.
* **Uppercase** - Weekday name and month name in the long format are converted to upper case letters.
This can improve readability.
**Show Weeknumber** - Weeknumber (ISO-8601) is shown.
* **Vector font** - Use the built-in vector font for dates and weekday.
This can improve readability.
Otherwise, a scaled version of the built-in 6x8 pixels font is used.

View File

@ -19,6 +19,7 @@ var secondsWithColon;
var dateOnMain;
var dateOnSecs;
var weekDay;
var calWeek;
var upperCase;
var vectorFont;
@ -29,22 +30,25 @@ var secondsScreen = true;
var isBangle1 = (g.getWidth() == 240);
/* For development purposes
//For development purposes
/*
require('Storage').writeJSON(SETTINGSFILE, {
secondsMode: "Always", // "Never", "Unlocked", "Always"
secondsMode: "Unlocked", // "Never", "Unlocked", "Always"
secondsColoured: true,
secondsWithColon: true,
dateOnMain: "Long", // "Short", "Long", "ISO8601"
dateOnSecs: "Year", // "No", "Year", "Weekday", LEGACY: true/false
weekDay: true,
calWeek: true,
upperCase: true,
vectorFont: true,
});
/* */
*/
/* OR (also for development purposes)
// OR (also for development purposes)
/*
require('Storage').erase(SETTINGSFILE);
/* */
*/
// Helper method for loading the settings
function def(value, def) {
@ -60,6 +64,7 @@ function loadSettings() {
dateOnMain = def(settings.dateOnMain, "Long");
dateOnSecs = def(settings.dateOnSecs, "Year");
weekDay = def(settings.weekDay, true);
calWeek = def(settings.calWeek, false);
upperCase = def(settings.upperCase, true);
vectorFont = def(settings.vectorFont, false);
@ -99,6 +104,18 @@ function isoStr(date) {
return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).substr(-2) + "-" + ("0" + date.getDate()).substr(-2);
}
function ISO8601calWeek(date) { //copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480
var tdt = new Date(date.valueOf());
var dayn = (date.getDay() + 6) % 7;
tdt.setDate(tdt.getDate() - dayn + 3);
var firstThursday = tdt.valueOf();
tdt.setMonth(0, 1);
if (tdt.getDay() !== 4) {
tdt.setMonth(0, 1 + ((4 - tdt.getDay()) + 7) % 7);
}
return 1 + Math.ceil((firstThursday - tdt) / 604800000);
}
function doColor() {
return !isBangle1 && !Bangle.isLocked() && secondsColoured;
}
@ -169,11 +186,14 @@ function draw() {
else
g.setFont("6x8", 2);
g.drawString(dateStr, x, y);
if (weekDay) {
var dowStr = require("locale").dow(date);
if (weekDay || calWeek) {
var dowwumStr = require("locale").dow(date);
dowwumStr = "thursday";
if (calWeek)
dowwumStr = (weekDay ? dowwumStr.substr(0,Math.min(dowwumStr.length,6)) + (dowwumStr.length>=6 ? "." : "") : "week ") + "#" + ISO8601calWeek(date);
if (upperCase)
dowStr = dowStr.toUpperCase();
g.drawString(dowStr, x, y + (vectorFont ? 26 : 16));
dowwumStr = dowwumStr.toUpperCase();
g.drawString(dowwumStr, x, y + (vectorFont ? 26 : 16));
}
}

View File

@ -47,6 +47,14 @@
writeSettings();
}
},
"Show Weeknumber": {
value: (settings.weekNum !== undefined ? settings.weekNum : true),
format: v => v ? "On" : "Off",
onchange: v => {
settings.weekNum = v;
writeSettings();
}
},
"Uppercase": {
value: (settings.upperCase !== undefined ? settings.upperCase : false),
format: v => v ? "On" : "Off",

1
apps/limelight/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: first release

19
apps/limelight/README.md Normal file
View File

@ -0,0 +1,19 @@
# Limelight
*Simple configurable analogue clock based on the work of @Andreas_Rozek [Simple_Clock](https://github.com/espruino/BangleApps/tree/master/apps/simple_clock)*
![](screenshot_limelight.png)
* Selection of different fonts
* Settings menu where you can select font, or switch to Vector font and try a range of sizes
* Reduction by 100 lines of code, demonstrating that there is no need for a custom widget draw method
* Full screen option (widgets are loaded but not displayed)
![](screenshot_gochihand.png)
![](screenshot_monoton.png)
![](screenshot_grenadier.png)
Many thanks for @Andreas_Rozek for his pioneering work on building an analogue clock toolkit for the Bangle 2.
Limelight Written by: [Hugh Barney](https://github.com/hughbarney) For support and discussion please post in the [Bangle JS
Forum](http://forum.espruino.com/microcosms/1424/)

View File

@ -0,0 +1,263 @@
/*
* Limelight analoguce clock with bolted hands
* Based on the work of @Andreas_Rozek
* [Simple_Clock](https://github.com/espruino/BangleApps/tree/master/apps/simple_clock)
*
* . Demonstrates simpler approach to establishing the available size of the appRect in relation
* to widgets, avoids having to take on the responsibility for managing the widget draw.
* . Demonstrates a settings menu and various configuration options
* . Demonstrates fullscreen verses, widgets and app area.
*
*/
g.clear();
const SETTINGS_FILE = "limelight.json";
var UPDATE_PERIOD;
var drawTimeout;
function loadSettings() {
settings = require("Storage").readJSON(SETTINGS_FILE,1)||{};
settings.secondhand = settings.secondhand||false;
settings.font = settings.font||"Limelight";
settings.vector = settings.vector||false;
settings.fullscreen = settings.fullscreen||false;
settings.vector_size = settings.vector_size||42;
UPDATE_PERIOD = (settings.secondhand ? 1000 : 60000);
}
loadSettings();
// if we are not full screen then load and draw the widgets so that Bangle.appRect gets set
if (!settings.fullscreen) {
Bangle.loadWidgets();
Bangle.drawWidgets();
}
// fonts.google.com
Graphics.prototype.setFontLimelight = function(scale) {
// Actual height 28 (28 - 1)
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAeAAAAAD8AAAAAf4AAAAB/gAAAAH+AAAAAf4AAAAB/gAAAAD8AAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAPwAAAAH8AAAAD+AAAAD+AAAAB/AAAAA/gAAAAfwAAAAD4AAAAAMAAAAAAAAAAAAAAAAAAAAA/gAAAA//wAAAP//wAAB///wAAP///gAA///+AAH///8AAf///4AD////gAP///+AA////4AD////gAMAAAGAAwAAAYADAAABgAMAAAGAAwAAAwABgAADAAHAAAYAAOAADgAAeAA8AAAfh/AAAAf/wAAAAHgAAAAAAAAAAGAAAAAAYAAAAABAAAAAAMAAAAAAwAAAAAD///+AAf///4AB////gAH///+AAf///4AD////gAP///+AA////4AH////gAf///+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAeAAOAAH4AAwAA/gAGAAP+AAYAB/4ADAAf/gAMAD/+AAwAf/4ADAH//gAMA//+AAwH//4ADB//9gAOP//GAA///wYAD//+BgAH//gGAAf/8AYAA//ABgAB/4AGAAD+AAYAADAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAHAAAwAAGAAHAAAMAAYAAAwADAEABgAMAwAGAAwDAAYADAMABgAMAwAGAAwDAAYAD////gAP///+AA////4AD////gAP///8AAf///wAB/7//AAD/H/4AAP4f/AAAPA/4AAAAA+AAAAAAAAAAAAAAAAAAAGAAAAAB8AAAAAPwAAAADzAAAAAcMAAAAHgwAAAA8DAAAAHAMAAAB4AwAAAOADAAADwAMAAAcAAwAAD///+AA////4AD////gAP///+AA////4AD////gAP///+AA////4AD////gAP///+AAAAAMAAAAAAwAAAAADAAAAAAcAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAHAAAH4AOAAP/gAcAA+GAAwADAwABgAMDAAGAAwMAAYADAwABgAMDAAGAAwMAAYADA///gAMD//+AAwP//4ADA///AAMB//8AAwH//wADAP/+AAIAf/wAAAA/+AAAAB/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAD/8AAAA//8AAAH//4AAB///wAAH///gAA////AAH///8AAf///4AD////gAP///+AAwAAAYADAYABgAMBgAGAAwGAAYADAYABgAMBgAGAAwGAAwABgYADAAHAwAYAAMDgHAAAAHh4AAAAP/AAAAAHgAAAAAAAAAAAAAAAA+AAAAAD4AAAAAMAAAAAAwAAAAADAAAAAAMAA/+AAwB//4ADB///gAM///+AA////4AD////gAP///+AA////4AD////gAP///+AA////4AD//+AAAP/wAAAA/wAAAAD4AAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAAA/g/wAAH/GDgAA/+wGAAD//AMAAf/4AwAB//gBgAP//AGAAx/+AYADD/4BgAMH/wGAAwP/AYACA/+BgAMB/8GAAwD/wYADAP/jgAMBf/+AAYH//wABgz//AADHH/4AAH4f/gAAOA/8AAAAB/AAAAAAwAAAAAAAAAAAAAAAAAeAAAAAH/AAAAB8eAAAAGAcBgAAwAwHAAGABgMAAYAGAYADAAIBgAMAAwGAAwADAYADAAYBgAMAAgGAAwAAAYAD////gAP///+AA////wAB////AAH///4AAP///AAA///8AAA///AAAB//4AAAB/+AAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAeAAAD8D8AAAf4f4AAB/h/gAAH+H+AAAf4f4AAB/h/gAAD8D8AAAHgHgAAAAAAAAAAAAAAA="), 46, atob("DQ0aExgZHRkbGBsbDQ=="), 40+(scale<<8)+(1<<16));
}
// fonts.google.com
Graphics.prototype.setFontGochiHand = function(scale) {
// Actual height 29 (31 - 3)
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAB4AAAAAD4AAAAAB4AAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAH/gAAAD//gAAD///gAD///+AAH///AAAH//gAAAH/wAAAAHwAAAAAAAAwAAAAAP+AAAAA//gAAAB//wAAAD//4AAAD8P4AAAHwD8AAAHgB8AAAPgA8AAAPAA8AAAPAA8AAAPAA8AAAPgA8AAAPgA8AAAPgB8AAAHwB4AAAH4D4AAAD+PwAAAD//gAAAB//gAAAA/+AAAAAP8AAAAAAAAAAAAAAAAAAAcAAAAAA8AAAAAB8AAAAAD4AAAAAD4AAAAAHwAAAAAHgAAAAAPgAAAAAPgAAAAAf/AAAAAf//wAAAP//wAAAH//wAAAAf/wAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAA8AAAeAB8AAA+AD8AAB+AH8AAB4AP8AAD4AP8AADwAf8AADwB+8AAD4D88AAD4H48AAD//w+AAB//g+AAB//A+AAA/8A+AAAPwA+AAAAAA+AAAAAAcAAAAAAIAAAAAAAAAA8AAAAAB8AAAAAB8AAAAAB4AHgAAD4AHwAAD4AH4AADwPH8AADwfB8AADwfA8AAD4fA+AAD4fA+AAB//A+AAB//A+AAA//A8AAA//x8AAAPP/8AAAAH/4AAAAD/wAAAAB/gAAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAH8AAAAAP+AAAAA/+AAAAB/+AAAAD8+AAAAP4+AAAB/weAAAD/weAAAD/8eAAAB///AAAA///AAAAH//8AAAAf//AAAAD//AAAAAf/AAAAAf+AAAAAfAAAAAAMAAAAAAAAAAAAAAAAAAPA/gAAAfh/wAAA/x/4AAA/x/4AAB/4/8AAB74B8AAB58A8AAB58A+AAB5+A+AAB4+A+AAB4+A+AAB4fA+AAB4fg+AAB4Pg8AAB4P58AAB4H/4AAB4D/4AAB4D/wAAAQA/gAAAAAAAAAAAAAAAAAAAAAAAAAf8AAAAB/+AAAAD//gAAAH//gAAAPwfwAAAPgf4AAAfAf4AAAeA/8AAAeA98AAAeA88AAAfB48AAAfB48AAAPB48AAAOB48AAAAB98AAAAB/8AAAAA/4AAAAA/wAAAAAfgAAA8AAAAAA8BAAAAA8HgAAAA8HgAAAA8HgAAAA8HgAAAA8HgAAAA+HgAAAA+HgAAAA+HgAAAAfHgAAAAf//+AAAf//+AAAP//+AAAH//8AAAAPwAAAAAHgAAAAAHwAAAAAHwAAAAAHwAAAAADwAAAAADgAAAAAAAAAAAAAAAAAAAB/AAAAP3/wAAAf//wAAA///4AAA//D8AAB9+B8AAB4+A8AAB4+A+AAB4+A+AAB4+A+AAB8+A+AAB8+A+AAA/+A8AAA//A8AAAf/x8AAAP//4AAAH//wAAAAD/gAAAAB/AAAAAAAAAAAAAAAAAAD/AAAAAD/gAAAAH/gAAAAP/wAAAAPHwAAAAPDwAAAAeDwAAAAeDwAAAAeDwAAAAeHwAAAAeHgAAAAePgAAAAefAAAAAf/AAAAAf///gAAf///wAAP///wAAP///gAAH8AAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8BwAAAA8B4AAAA+D4AAAA8B4AAAAcB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="), 46, atob("DQoYERUWFBYVFhcVDQ=="), 42+(scale<<8)+(1<<16));
}
// free for commercial use
// https://www.1001fonts.com/search.html?search=Grenadier+NF
Graphics.prototype.setFontGrenadierNF = function(scale) {
// Actual height 39 (39 - 1)
g.setFontCustom(atob("AAAAAAAAAAAAB4AAAAAAPAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAEAAAAAAPgAAAAA/8AAAAB//gAAAD//4AAAP//wAAAf//AAAB//+AAAD//8AAAB//wAAAAP/gAAAAB+AAAAAAMAAAAAAAAAAAAAAAAB4AAAAAD/8AAAAB//4AAAA///wAAAP8D/AAAD8AD8AAA/AAPwAAPgAAfAAD4AAB8AAeAAAHgAHwAAA+AA8AAADwAHgAAAeAB4AAAB4APAAAAPAB4AAAB4APAAAAPAB4AAAB4APAAAAPAB4AAAB4APAAAAPAB4AAAB4APgAAAfAA8AAADwAHwAAA+AAeAAAHgAD4AAB8AAPgAAfAAA+AAHwAAD8AD8AAAP8D/AAAA///wAAAD//8AAAAH/+AAAAAD8AAAAAAAAAAAAAAAAAAABAAAAAAAcAAAAAAHwAAAAAB8AAAAAAfgAAAAAH/////AB/////4AP/////AB/////4AAAAAAAAAAAAAAAAAAAAADAAAAAAA4APAAAAPAB4AAAD4APAAAA/AB4AAAP4APAAAD/AB8AAA/4AHgAAPvAA8AAD54ADwAB+PAAfAAfh4AB8AH4PAAPwD+B4AA///gPAAD//wB4AAH/4APAAAP8AAAAAAAAAAAAAAAAAAAPAAAAHAB4AAAB4APAAAAPAB4PAAB4APB4AAPAB+/gAB4AH/8AAfAAf/wADwAB/fAA+AABD8APgAAAPwH4AAAA//+AAAAD//gAAAAH/4AAAAAP8AAAAAAAAAAAAAAAAAAAAAAYAAAAAAPAAAAAAH4AAAAAD/AAAAAB/4AAAAA//AAAAAf94AAAAH+PAAAAD/B4AAAB/gPAAAA/wB4AAAf8APAAAP////4AH/////AD/////4AAAAAPAAAAAAA4AAAAAAAAAAAAAAAAAAAIAAPAAAPAAB4AAf4AAPAA//gAB4AP/8AAPAB/3gAB4APg8AAfAB4DwADwAPAfAA+AB4B8APgAPAP4H4AB4A//+AAPAB//gAB4AH/4AAAAAP8AAAAAAAAAAAAAB4AAAAAD/4AAAAA//wAAAAf//AAAAP+H8AAAH+AHwAAB/gAfAAA/4AB4AAf+AAPAAP/wAA8AH+eAAHgB/ngAA8APw8AAHgB4HgAA8AMA8AAHgAADwAA8AAAeAAPgAAD4AB4AAAPgAfAAAA+AHwAAAH4D8AAAAf//AAAAB//wAAAAD/8AAAAAH8AAAAAAAAAAAAAAAAAAAAAAAYAAAAAAfAB4AAAP4APAAAH/AB4AAH/gAPAAD/wAB4AD/wAAPAB/4AAB4A/8AAAPA/8AAAB4f+AAAAPP+AAAAB//AAAAAP/gAAAAB/gAAAAAPwAAAAAB4AAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAD/gAAAAB//AAAAAf/+AAAAH//4AAAB+AfgAA+fAB8AAP/wAHwAD/+AAeAA//gAB4APj8AAPAB4PAAB4APB4AAPAB4PAAB4APB4AAPAB8fgAB4AH/8AAPAAf/wADwAB/eAA+AADj4APgAAAPwD8AAAA///AAAAD//wAAAAP/4AAAAAf8AAAAAAAAAAAB4AAAAAB/4AAAAA//wAAAAP//AAAAD+H8AAAA/AHwAAAHgAfAAAB8AB4AAAPAAHgAADwAA8AAAeAAHgBADwAAeA4AeAADwfADwAAeP4AeAAD3+ADwAA9/gAfAAH/wAB4AB/4AAPgAf8AAA+AH+AAAD8B/gAAAP//wAAAA//4AAAAD/8AAAAAD+AAAAAAAAAAAAAAAAAAAAAAAeB4AAAADwPAAAAAeB4AAAAAAAAAAAAAAAA=="), 46, atob("Bg4kChURExEaFBoaBg=="), 45+(scale<<8)+(1<<16));
}
// fonts.google.com
Graphics.prototype.setFontMonoton = function(scale) {
// Actual height 38 (37 - 0)
g.setFontCustom(atob("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"), 46, atob("ChIiERcYGRwfGSAfCw=="), 40+(scale<<8)+(1<<16));
}
/*
* If only 1 widget is loaded at the top, then Bangle.appRect changes
* to report as if widgets were loaded at the bottom as well. The
* other option would be for Bangle.appRect to adjust for different
* combinations EG: no widgets, wigets on top, widgets on bottom and
* widgets on top and bottom areas, but it does not at present.
*
* Example of Bangle.appRect with 3 widges on the top, note h = 152, not 176
* ={ x: 0, y: 24, w: 176, h: 152, x2: 175, y2: 175 }
*
* With the example below we are going assume that the bottom widget
* space is not used.
*
*/
const CenterX = g.getWidth()/2;
const CenterY = (g.getHeight()/2) + (Bangle.appRect.y/2);
const outerRadius = (g.getHeight() - Bangle.appRect.y)/2;
if (settings.fullscreen) {
Bangle.loadWidgets();
/*
* We load the widgets as some like widpedom accumualte the step count.
* we are not drawing the widgets as we are taking over the whole screen
* so we will blank out the draw() functions of each widget and change the
* widgets area to the top bar doesn't get cleared.
*/
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
}
function debug(o) {
//console.log(o);
}
debug("limelight.app.js");
debug("CenterX=" + CenterX);
debug("CenterY=" + CenterY);
debug("outerRadius=" + outerRadius);
debug("y12=" + (CenterY - outerRadius));
debug("y6=" + (CenterY + outerRadius));
let HourHandLength = outerRadius * 0.5;
let HourHandWidth = 2*5, halfHourHandWidth = HourHandWidth/2;
let MinuteHandLength = outerRadius * 0.7;
let MinuteHandWidth = 2*3, halfMinuteHandWidth = MinuteHandWidth/2;
let SecondHandLength = outerRadius * 0.9;
let SecondHandOffset = halfHourHandWidth + 10;
let outerBoltRadius = halfHourHandWidth + 2, innerBoltRadius = outerBoltRadius - 4;
let HandOffset = outerBoltRadius + 4;
let twoPi = 2*Math.PI, deg2rad = Math.PI/180;
let Pi = Math.PI;
let halfPi = Math.PI/2;
let sin = Math.sin, cos = Math.cos;
let sine = [0, sin(30*deg2rad), sin(60*deg2rad), 1];
let HandPolygon = [
-sine[3],-sine[0], -sine[2],-sine[1], -sine[1],-sine[2], -sine[0],-sine[3],
sine[0],-sine[3], sine[1],-sine[2], sine[2],-sine[1], sine[3],-sine[0],
sine[3], sine[0], sine[2], sine[1], sine[1], sine[2], sine[0], sine[3],
-sine[0], sine[3], -sine[1], sine[2], -sine[2], sine[1], -sine[3], sine[0],
];
let HourHandPolygon = new Array(HandPolygon.length);
for (let i = 0, l = HandPolygon.length; i < l; i+=2) {
HourHandPolygon[i] = halfHourHandWidth*HandPolygon[i];
HourHandPolygon[i+1] = halfHourHandWidth*HandPolygon[i+1];
if (i < l/2) { HourHandPolygon[i+1] -= HourHandLength; }
if (i > l/2) { HourHandPolygon[i+1] += HandOffset; }
}
let MinuteHandPolygon = new Array(HandPolygon.length);
for (let i = 0, l = HandPolygon.length; i < l; i+=2) {
MinuteHandPolygon[i] = halfMinuteHandWidth*HandPolygon[i];
MinuteHandPolygon[i+1] = halfMinuteHandWidth*HandPolygon[i+1];
if (i < l/2) { MinuteHandPolygon[i+1] -= MinuteHandLength; }
if (i > l/2) { MinuteHandPolygon[i+1] += HandOffset; }
}
/**** transforme polygon ****/
let transformedPolygon = new Array(HandPolygon.length);
function transformPolygon (originalPolygon, OriginX,OriginY, Phi) {
let sPhi = sin(Phi), cPhi = cos(Phi), x,y;
for (let i = 0, l = originalPolygon.length; i < l; i+=2) {
x = originalPolygon[i];
y = originalPolygon[i+1];
transformedPolygon[i] = OriginX + x*cPhi + y*sPhi;
transformedPolygon[i+1] = OriginY + x*sPhi - y*cPhi;
}
}
/**** draw clock hands ****/
function drawClockHands () {
let now = new Date();
let Hours = now.getHours() % 12;
let Minutes = now.getMinutes();
let Seconds = now.getSeconds();
let HoursAngle = (Hours+(Minutes/60))/12 * twoPi - Pi;
let MinutesAngle = (Minutes/60) * twoPi - Pi;
let SecondsAngle = (Seconds/60) * twoPi - Pi;
g.setColor(g.theme.fg);
transformPolygon(HourHandPolygon, CenterX,CenterY, HoursAngle);
g.fillPoly(transformedPolygon);
transformPolygon(MinuteHandPolygon, CenterX,CenterY, MinutesAngle);
g.fillPoly(transformedPolygon);
let sPhi = Math.sin(SecondsAngle), cPhi = Math.cos(SecondsAngle);
if (settings.secondhand) {
g.setColor(g.theme.fg2);
g.drawLine(
CenterX + SecondHandOffset*sPhi,
CenterY - SecondHandOffset*cPhi,
CenterX - SecondHandLength*sPhi,
CenterY + SecondHandLength*cPhi
);
}
g.setColor(g.theme.fg);
g.fillCircle(CenterX,CenterY, outerBoltRadius);
g.setColor(g.theme.bg);
g.drawCircle(CenterX,CenterY, outerBoltRadius);
g.fillCircle(CenterX,CenterY, innerBoltRadius);
}
function setNumbersFont() {
if (settings.vector) {
g.setFont('Vector', settings.vector_size);
return;
}
if (settings.font == "GochiHand")
g.setFontGochiHand();
else if (settings.font == "Grenadier")
g.setFontGrenadierNF();
else if (settings.font == "Monoton")
g.setFontMonoton();
else
g.setFontLimelight();
}
function drawNumbers() {
g.setColor(g.theme.fg);
setNumbersFont();
g.setFontAlign(0,-1);
g.drawString('12', CenterX, CenterY - outerRadius);
g.setFontAlign(1,0);
g.drawString('3', CenterX + outerRadius, CenterY);
g.setFontAlign(0,1);
g.drawString('6', CenterX, CenterY + outerRadius);
g.setFontAlign(-1,0);
g.drawString('9', CenterX - outerRadius,CenterY);
}
function draw() {
g.setColor(g.theme.bg);
g.fillRect(Bangle.appRect);
drawClockHands();
drawNumbers();
queueDraw();
}
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, UPDATE_PERIOD - (Date.now() % UPDATE_PERIOD));
}
// Stop updates when LCD is off, restart when on
Bangle.on('lcdPower',on=>{
if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
Bangle.setUI('clock');
draw();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("lksgIqngf/wAFC//+AgUch/4AgMBwAQEh/8Dgf/4AKOEAQKCAYUB//gAoU/DQkPBQYVBGx5SDBQIbDBR0GEAlgFYcHGwh4B+CDHRwL04"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

View File

@ -0,0 +1,78 @@
(function(back) {
const SETTINGS_FILE = "limelight.json";
// initialize with default settings...
let s = {
'vector_size': 42,
'vector': false,
'font': "Limelight",
'secondhand': false,
'fullscreen': false
}
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
const storage = require('Storage')
let settings = storage.readJSON(SETTINGS_FILE, 1) || {}
const saved = settings || {}
// copy settings into variable
for (const key in saved) {
s[key] = saved[key]
}
function save() {
settings = s
storage.write(SETTINGS_FILE, settings)
}
var font_options = ["Limelight","GochiHand","Grenadier","Monoton"];
E.showMenu({
'': { 'title': 'Limelight Clock' },
'< Back': back,
'Full Screen': {
value: s.fullscreen,
format: () => (s.fullscreen ? 'Yes' : 'No'),
onchange: () => {
s.fullscreen = !s.fullscreen;
save();
},
},
'Font': {
value: 0 | font_options.indexOf(s.font),
min: 0, max: 3,
format: v => font_options[v],
onchange: v => {
s.font = font_options[v];
save();
},
},
'Vector Font': {
value: s.vector,
format: () => (s.vector ? 'Yes' : 'No'),
onchange: () => {
s.vector = !s.vector;
save();
},
},
'Vector Size': {
value: s.vector_size,
min: 24,
max: 56,
step: 6,
onchange: v => {
s.vector_size = v;
save();
}
},
'Second Hand': {
value: s.secondhand,
format: () => (s.secondhand ? 'Yes' : 'No'),
onchange: () => {
s.secondhand = !s.secondhand;
save();
},
}
});
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -15,3 +15,4 @@
0.13: Now use shorter de_DE date format to more closely match other languages for size
0.14: Added some first translations for Messages in nl_NL
0.15: Fixed sv_SE formatting, long date does not work well for Bangle.js2
Added Swedish localisation with English text

View File

@ -283,6 +283,24 @@ var locales = {
day: "söndag,måndag,tisdag,onsdag,torsdag,fredag,lördag",
trans: { yes: "ja", Yes: "Ja", no: "nej", No: "Nej", ok: "ok", on: "on", off: "off" }
},
"en_SE": { // Swedish localisation with English text
lang: "en_SE",
decimal_point: ",",
thousands_sep: ".",
currency_symbol: "kr",
int_curr_symbol: "SKR",
speed: 'kmh',
distance: { "0": "m", "1": "km" },
temperature: '°C',
ampm: { 0: "", 1: "" },
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
datePattern: { 0: "%B %d %Y", "1": "%Y-%m-%d" }, // March 1 2020 // 2020-03-01
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
month: "January,February,March,April,May,June,July,August,September,October,November,December",
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
// No translation for english...
},
"en_NZ": {
lang: "en_NZ",
decimal_point: ".",

View File

@ -1 +1,2 @@
0.01: New menu!
0.02: Clean up touch handler in setUI

View File

@ -1,8 +1,5 @@
E.showMenu = function(items) {
g.clearRect(Bangle.appRect); // clear screen if no menu supplied
// clean up back button listener
if (Bangle.backHandler) Bangle.removeListener('touch', Bangle.backHandler)
delete Bangle.backHandler;
if (!items) {
Bangle.setUI();
return;
@ -206,8 +203,13 @@ E.showMenu = function(items) {
if (b===1) back();
}
}
// note: backHandler is cleaned up at the top of this file
Bangle.on('touch', Bangle.backHandler);
}
return l;
};
// setUI now also needs to clear up our back button touch handler
Bangle.setUI = (old => function() {
if (Bangle.backHandler) Bangle.removeListener("touch", Bangle.backHandler);
delete Bangle.backHandler;
return old.apply(this, arguments);
})(Bangle.setUI);

View File

@ -8,3 +8,4 @@
0.08: Added dependancy on MyLocation
0.09: Added dependancy on Pedometer Widget
0.10: Added Weather line, fixed issues on a Bangle 1, update every minute
0.11: Changed cycle on minute to prevInfo to avoid the 2nd one being the blank line

View File

@ -255,7 +255,7 @@ function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
nextInfo();
prevInfo();
draw();
}, 60000 - (Date.now() % 60000));
}

View File

@ -1,3 +1,6 @@
0.01: Modified for use with new bootloader and firmware
0.02: Use Bangle.setUI for button/launcher handling
0.03: Fix display for Bangle 2
0.04: Use queueDraw(), update every minute, respect theme, use Lato font
0.05: Decided against custom font as it inceases the code size
minimalism is useful when narrowing down issues

View File

@ -1,28 +1,55 @@
const h = g.getHeight();
const w = g.getWidth();
function draw() {
var d = new Date();
var da = d.toString().split(" ");
var time = da[4].substr(0,5);
var date = new Date();
var timeStr = require("locale").time(date,1);
g.reset();
g.clearRect(0, 30, w, 99);
g.setFontAlign(0, -1);
g.setFont("Vector", w/3);
g.drawString(time, w/2, 40);
g.setColor(g.theme.bg);
g.fillRect(Bangle.appRect);
g.setFont('Vector', w/3);
g.setFontAlign(0, 0);
g.setColor(g.theme.fg);
g.drawString(timeStr, w/2, h/2);
queueDraw();
}
// handle switch display on by pressing BTN1
Bangle.on('lcdPower', function(on) {
if (on) draw();
// 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));
}
// Stop updates when LCD is off, restart when on
Bangle.on('lcdPower',on=>{
if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
g.clear();
// Show launcher when middle button pressed
//Bangle.setUI("clock");
// use clockupdown as it tests for issue #1249
Bangle.setUI("clockupdown", btn=> {
draw();
});
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
setInterval(draw, 15000); // refresh every 15s
draw();
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -1 +1,2 @@
0.01: New Widget!
0.02: Ported to Bangle.js2

13
apps/widhwt/app.js Normal file
View File

@ -0,0 +1,13 @@
// Replace the "Loading..." box
// with our own message
g.clearRect(38, 68, 138, 108);
g.drawRect(38, 68, 138, 108);
g.setFontVector(13);
g.setFontAlign(0, 0, 0);
g.drawString("Wash...", g.getWidth()/2, g.getHeight()/2);
Bangle.buzz();
setTimeout(() => {
Bangle.buzz(1E3, 1);
setTimeout(() => load(), 2E3);
}, 35E3);

View File

@ -6,9 +6,7 @@
g.reset().setColor(color).drawImage(require("heatshrink").decompress(atob("jEYwIKHgwCBhwCBh4CEggPCkACBmAXDBwVZ+EB+F4gEsjl8EgMP+EChk/gEMh+ehkA+YIBxwxBnF/4HggH/wEAj0AA==")), this.x + 1, 0);
}
WIDGETS["widhwt"] = { area: "tr", width: 26, draw: draw };
Bangle.on('swipe', function() {
function startTimer() {
color = 0x41f;
Bangle.buzz();
Bangle.drawWidgets();
@ -17,6 +15,14 @@
Bangle.buzz(1E3, 1);
Bangle.drawWidgets();
}, 35E3);
}
});
if (process.env.HWVERSION == 1) {
WIDGETS["widhwt"] = {
area: "tr",
width: 26,
draw: draw,
};
Bangle.on('swipe', startTimer);
}
})();

View File

@ -5,4 +5,7 @@
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
0.07: Home button fixed and README added
0.08: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799)
0.08: tag HRM power requests to allow this to work alongside other widgets/apps (fix #799)
0.09: Ported to Bangle.js2
Home returns to clock, instead of menu
Add settings

View File

@ -8,6 +8,9 @@ and will notify you with a buzz whenever your heart rate falls below or jumps ab
[Try it out](https://www.espruino.com/ide/emulator.html?codeurl=https://raw.githubusercontent.com/msdeibel/BangleApps/master/apps/wohrm/app.js&upload) using the [online Espruino emulator](https://www.espruino.com/ide/emulator.html).
## Setting the limits
Use the settings menu to set the limits. On the Bangle.js1 these can in addition be set with the buttons:
For setting the lower limit press button 4 (left part of the watch's touch screen).
Then adjust the value with the buttons 1 (top) and 3 (bottom) of the watch.
@ -22,7 +25,7 @@ the received value: For 85% and above the bars are green, between 84% and 50% th
and below 50% they turn red.
## Closing the app
Pressing button 2 (middle) will switch off the HRM of the watch and return you to the launcher.
Pressing middle button will switch off the HRM of the watch and return you to the launcher.
# HRM usage
The HRM is switched on when the app is started. It stays switch on while the app is running, even

View File

@ -4,14 +4,16 @@ const Setter = {
UPPER: 'upper',
LOWER: 'lower'
};
const SETTINGS_FILE = "wohrm.setting.json";
var settings = require('Storage').readJSON(SETTINGS_FILE, 1) || {
upperLimit: 130,
lowerLimit: 100
};
const shortBuzzTimeInMs = 80;
const longBuzzTimeInMs = 400;
let upperLimit = 130;
let upperLimitChanged = true;
let lowerLimit = 100;
let lowerLimitChanged = true;
let limitSetter = Setter.NONE;
@ -23,54 +25,115 @@ let confidenceChanged = true;
let setterHighlightTimeout;
function renderUpperLimitBackground() {
g.setColor(1,0,0);
g.fillRect(125,40, 210, 70);
g.fillRect(180,70, 210, 200);
const isB1 = process.env.HWVERSION==1;
const upperLshape = isB1 ? {
right: 125,
left: 210,
bottom: 40,
top: 210,
rectWidth: 30,
cornerRoundness: 5,
orientation: -1,
color: '#f00'
} : {
right: Bangle.appRect.x2-100,
left: Bangle.appRect.x2,
bottom: 24,
top: Bangle.appRect.y2,
rectWidth: 26,
cornerRoundness: 4,
orientation: -1, // rotated 180°
color: '#f00'
};
//Round top left corner
g.fillEllipse(115,40,135,70);
const lowerLshape = {
left: isB1 ? 10 : Bangle.appRect.x,
right: 100,
bottom: upperLshape.top,
top: upperLshape.bottom,
rectWidth: upperLshape.rectWidth,
cornerRoundness: upperLshape.cornerRoundness,
orientation: 1,
color: '#00f'
};
//Round top right corner
g.setColor(0,0,0);
g.fillRect(205,40, 210, 45);
g.setColor(1,0,0);
g.fillEllipse(190,40,210,50);
const centerBar = {
minY: (upperLshape.bottom + upperLshape.top - upperLshape.rectWidth)/2,
maxY: (upperLshape.bottom + upperLshape.top + upperLshape.rectWidth)/2,
confidenceWidth: isB1 ? 10 : 8,
minX: isB1 ? 55 : upperLshape.rectWidth + 14,
maxX: isB1 ? 165 : Bangle.appRect.x2 - upperLshape.rectWidth - 14
};
//Round inner corner
g.fillRect(174,71, 179, 76);
g.setColor(0,0,0);
g.fillEllipse(160,71,179,82);
const fontSizes = isB1 ? {
limits: 13,
heartRate: 24
} : {
limits: 12,
heartRate: 20
};
//Round bottom
g.setColor(1,0,0);
g.fillEllipse(180,190, 210, 210);
function fillEllipse(x, y, x2, y2) {
g.fillEllipse(Math.min(x, x2),
Math.min(y, y2),
Math.max(x, x2),
Math.max(y, y2));
}
function renderLowerLimitBackground() {
g.setColor(0,0,1);
g.fillRect(10, 180, 100, 210);
g.fillRect(10, 50, 40, 180);
/**
* @param p.left: the X coordinate of the left side of the L in its orientation
* @param p.right: the X coordinate of the right side of the L in its orientation
* @param p.top: the Y coordinate of the top side of the L in its orientation
* @param p.bottom: the Y coordinate of the bottom side of the L in its orientation
* @param p.strokeWidth: how thick we draw the letter.
* @param p.cornerRoundness: how much the corners should be rounded
* @param p.orientation: 1 == turned 0°; -1 == turned 180°
* @param p.color: the color to draw the shape
*/
function renderLshape(p) {
g.setColor(p.color);
//Rounded top
g.setColor(0,0,1);
g.fillEllipse(10,40, 40, 60);
g.fillRect(p.right, p.bottom, p.left, p.bottom-p.orientation*p.rectWidth);
g.fillRect(p.left+p.orientation*p.rectWidth,
p.bottom-p.orientation*p.rectWidth,
p.left,
p.top+p.orientation*p.cornerRoundness*2);
//Round bottom right corner
g.setColor(0,0,1);
g.fillEllipse(90,180,110,210);
//Round end of small line
fillEllipse(p.right+p.orientation*p.cornerRoundness*2,
p.bottom,
p.right-p.orientation*p.cornerRoundness*2,
p.bottom-p.orientation*p.rectWidth);
//Round outer corner
g.setColor(g.theme.bg);
g.fillRect(p.left+p.orientation*p.cornerRoundness,
p.bottom,
p.left,
p.bottom-p.orientation*p.cornerRoundness);
g.setColor(p.color);
fillEllipse(p.left+p.orientation*p.cornerRoundness*4,
p.bottom,
p.left,
p.bottom-p.orientation*p.cornerRoundness*2);
//Round inner corner
g.setColor(0,0,1);
g.fillRect(40,175,45,180);
g.setColor(0,0,0);
g.fillEllipse(41,170,60,179);
g.fillRect(p.left+p.orientation*(p.rectWidth+p.cornerRoundness+1),
p.bottom-p.orientation*(p.rectWidth+1),
p.left+p.orientation*(p.rectWidth+1),
p.bottom-p.orientation*(p.rectWidth+p.cornerRoundness-1));
g.setColor(g.theme.bg);
fillEllipse(p.left+p.orientation*(p.rectWidth+p.cornerRoundness*4),
p.bottom-p.orientation*(p.rectWidth+1),
p.left+p.orientation*(p.rectWidth+1),
p.bottom-p.orientation*(p.rectWidth+p.cornerRoundness*3-1));
//Round bottom left corner
g.setColor(0,0,0);
g.fillRect(10,205, 15, 210);
g.setColor(0,0,1);
g.fillEllipse(10,200,30,210);
//Round end of long line
g.setColor(p.color);
fillEllipse(p.left+p.orientation*p.rectWidth,
p.top+p.orientation*p.cornerRoundness*4,
p.left,
p.top);
}
function drawTrainingHeartRate() {
@ -91,16 +154,17 @@ function drawTrainingHeartRate() {
function renderUpperLimit() {
if(!upperLimitChanged) { return; }
g.setColor(1,0,0);
g.fillRect(125,40, 210, 70);
renderLshape(upperLshape);
if(limitSetter === Setter.UPPER){
g.setColor(255,255, 0);
g.setColor(1,1,0);
} else {
g.setColor(255,255,255);
g.setColor(g.theme.fg);
}
g.setFontVector(13);
g.drawString("Upper: " + upperLimit, 125, 50);
g.setFontVector(fontSizes.limits).setFontAlign(-1, 0, 0);
g.drawString("Upper: " + settings.upperLimit,
upperLshape.right,
upperLshape.bottom+upperLshape.rectWidth/2);
upperLimitChanged = false;
}
@ -108,13 +172,17 @@ function renderUpperLimit() {
function renderCurrentHeartRate() {
if(!hrChanged) { return; }
g.setColor(255,255,255);
g.fillRect(55, 110, 165, 150);
g.setColor(g.theme.fg);
g.fillRect(centerBar.minX, centerBar.minY,
centerBar.maxX, centerBar.maxY);
g.setColor(0,0,0);
g.setFontVector(24);
g.setFontAlign(1, -1, 0);
g.drawString(currentHeartRate, 130, 117);
g.setColor(g.theme.bg);
g.setFontVector(fontSizes.heartRate);
g.setFontAlign(1, 0, 0);
g.drawString(currentHeartRate,
Math.max(upperLshape.right+upperLshape.cornerRoundness,
lowerLshape.right-lowerLshape.cornerRoundness),
(centerBar.minY+centerBar.maxY)/2);
//Reset alignment to defaults
g.setFontAlign(-1, -1, 0);
@ -125,16 +193,17 @@ function renderCurrentHeartRate() {
function renderLowerLimit() {
if(!lowerLimitChanged) { return; }
g.setColor(0,0,1);
g.fillRect(10, 180, 100, 210);
renderLshape(lowerLshape);
if(limitSetter === Setter.LOWER){
g.setColor(255,255, 0);
g.setColor(1,1,0);
} else {
g.setColor(255,255,255);
g.setColor(g.theme.fg);
}
g.setFontVector(13);
g.drawString("Lower: " + lowerLimit, 20,190);
g.setFontVector(fontSizes.limits).setFontAlign(-1, 0, 0);
g.drawString("Lower: " + settings.lowerLimit,
lowerLshape.left + lowerLshape.rectWidth/2,
lowerLshape.bottom - lowerLshape.rectWidth/2);
lowerLimitChanged = false;
}
@ -143,26 +212,26 @@ function renderConfidenceBars(){
if(!confidenceChanged) { return; }
if(hrConfidence >= 85){
g.setColor(0, 255, 0);
g.setColor(0, 1, 0);
} else if (hrConfidence >= 50) {
g.setColor(255, 255, 0);
g.setColor(1, 1, 0);
} else if(hrConfidence >= 0){
g.setColor(255, 0, 0);
g.setColor(1, 0, 0);
} else {
g.setColor(255, 255, 255);
g.setColor(g.theme.fg);
}
g.fillRect(45, 110, 55, 150);
g.fillRect(165, 110, 175, 150);
g.fillRect(centerBar.minX-centerBar.confidenceWidth, centerBar.minY, centerBar.minX, centerBar.maxY);
g.fillRect(centerBar.maxX, centerBar.minY, centerBar.maxX+centerBar.confidenceWidth, centerBar.maxY);
confidenceChanged = false;
}
function renderPlusMinusIcons() {
if (limitSetter === Setter.NONE) {
g.setColor(0, 0, 0);
g.setColor(g.theme.bg);
} else {
g.setColor(1, 1, 1);
g.setColor(g.theme.fg);
}
g.setFontVector(14);
@ -190,13 +259,13 @@ function buzz() {
// Do not buzz if not confident
if(hrConfidence < 85) { return; }
if(currentHeartRate > upperLimit)
if(currentHeartRate > settings.upperLimit)
{
Bangle.buzz(shortBuzzTimeInMs);
setTimeout(() => { Bangle.buzz(shortBuzzTimeInMs); }, shortBuzzTimeInMs * 2);
}
if(currentHeartRate < lowerLimit)
if(currentHeartRate < settings.lowerLimit)
{
Bangle.buzz(longBuzzTimeInMs);
}
@ -255,11 +324,11 @@ function incrementLimit() {
resetHighlightTimeout();
if (limitSetter === Setter.UPPER) {
upperLimit++;
settings.upperLimit++;
renderUpperLimit();
upperLimitChanged = true;
} else if(limitSetter === Setter.LOWER) {
lowerLimit++;
settings.lowerLimit++;
renderLowerLimit();
lowerLimitChanged = true;
}
@ -269,11 +338,11 @@ function decrementLimit(){
resetHighlightTimeout();
if (limitSetter === Setter.UPPER) {
upperLimit--;
settings.upperLimit--;
renderUpperLimit();
upperLimitChanged = true;
} else if(limitSetter === Setter.LOWER) {
lowerLimit--;
settings.lowerLimit--;
renderLowerLimit();
lowerLimitChanged = true;
}
@ -289,17 +358,18 @@ function resetHighlightTimeout() {
function switchOffApp(){
Bangle.setHRMPower(0,"wohrm");
Bangle.showLauncher();
load();
}
Bangle.on('lcdPower', (on) => {
g.clear();
if (on) {
Bangle.drawWidgets();
if (typeof(BTN5) !== typeof(undefined)) {
renderHomeIcon();
renderLowerLimitBackground();
renderUpperLimitBackground();
}
renderLshape(lowerLshape);
renderLshape(upperLshape);
lowerLimitChanged = true;
upperLimitChanged = true;
drawTrainingHeartRate();
@ -309,19 +379,22 @@ Bangle.on('lcdPower', (on) => {
Bangle.setHRMPower(1,"wohrm");
Bangle.on('HRM', onHrm);
setWatch(incrementLimit, BTN1, {edge:"rising", debounce:50, repeat:true});
setWatch(decrementLimit, BTN3, {edge:"rising", debounce:50, repeat:true});
setWatch(setLimitSetterToLower, BTN4, {edge:"rising", debounce:50, repeat:true});
setWatch(setLimitSetterToUpper, BTN5, { edge: "rising", debounce: 50, repeat: true });
setWatch(switchOffApp, BTN2, {edge:"falling", debounce:50, repeat:true});
g.setTheme({bg:"#000",fg:"#fff",dark:true});
g.reset();
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
renderHomeIcon();
renderLowerLimitBackground();
renderUpperLimitBackground();
if (typeof(BTN5) !== typeof(undefined)) {
renderHomeIcon();
setWatch(incrementLimit, BTN1, {edge:"rising", debounce:50, repeat:true});
setWatch(decrementLimit, BTN3, {edge:"rising", debounce:50, repeat:true});
setWatch(setLimitSetterToLower, BTN4, {edge:"rising", debounce:50, repeat:true});
setWatch(setLimitSetterToUpper, BTN5, { edge: "rising", debounce: 50, repeat: true });
setWatch(switchOffApp, BTN2, {edge:"falling", debounce:50, repeat:true});
} else {
setWatch(switchOffApp, BTN1, {edge:"falling", debounce:50, repeat:true});
}
setInterval(drawTrainingHeartRate, 1000);

35
apps/wohrm/settings.js Normal file
View File

@ -0,0 +1,35 @@
(function menu(back) {
const SETTINGS_FILE = "wohrm.setting.json";
// initialize with default settings...
const storage = require('Storage');
var settings = storage.readJSON(SETTINGS_FILE, 1) || {
upperLimit: 130,
lowerLimit: 100
};
function save() {
storage.write(SETTINGS_FILE, settings);
}
E.showMenu({
'': { 'title': 'Workout HRM' },
'< Back': back,
'Upper limit': {
value: settings.upperLimit,
min: 100, max: 200,
onchange: v => {
settings.upperLimit = v;
save();
}
},
'Lower limit': {
value: settings.lowerLimit,
min: 50, max: 150,
onchange: v => {
settings.lowerLimit = v;
save();
}
}
});
})