Merge branch 'master' of github.com:espruino/BangleApps into pushups
commit
f0c7d311c7
|
|
@ -21,7 +21,6 @@
|
|||
'< Back': back,
|
||||
'Full Screen': {
|
||||
value: settings.fullscreen,
|
||||
format: () => (settings.fullscreen ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.fullscreen = !settings.fullscreen;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
'< Back': back,
|
||||
'Buzz': {
|
||||
value: "buzz" in settings ? settings.buzz : false,
|
||||
format: () => (settings.buzz ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.buzz = !settings.buzz;
|
||||
save('buzz', settings.buzz);
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
0.01: Added app
|
||||
0.02: Removed unneeded squares
|
||||
0.03: Added settings with fullscreen option
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
var settings = Object.assign({
|
||||
fullscreen: false,
|
||||
}, require('Storage').readJSON("binaryclk.json", true) || {});
|
||||
|
||||
function draw() {
|
||||
var dt = new Date();
|
||||
var h = dt.getHours(), m = dt.getMinutes();
|
||||
|
|
@ -11,10 +15,14 @@ function draw() {
|
|||
g.clearRect(Bangle.appRect);
|
||||
|
||||
let i = 0;
|
||||
var gap = 8;
|
||||
var mgn = 20;
|
||||
if (settings.fullscreen) {
|
||||
gap = 12;
|
||||
mgn = 0;
|
||||
}
|
||||
const sq = 29;
|
||||
const gap = 8;
|
||||
const mgn = 20;
|
||||
const pos = sq + gap;
|
||||
var pos = sq + gap;
|
||||
|
||||
for (let r = 3; r >= 0; r--) {
|
||||
for (let c = 0; c < 4; c++) {
|
||||
|
|
@ -26,14 +34,15 @@ function draw() {
|
|||
}
|
||||
i++;
|
||||
}
|
||||
g.clearRect(mgn/2 + gap, mgn + gap, mgn/2 + gap + sq, mgn + 2 * gap + 2 * sq);
|
||||
g.clearRect(mgn/2 + 3 * gap + 2 * sq, mgn + gap, mgn/2 + 3 * gap + 3 * sq, mgn + gap + sq);
|
||||
g.clearRect(mgn/2 + gap, mgn + gap, mgn/2 + gap + sq, mgn + 2 * gap + 2 * sq);
|
||||
g.clearRect(mgn/2 + 3 * gap + 2 * sq, mgn + gap, mgn/2 + 3 * gap + 3 * sq, mgn + gap + sq);
|
||||
}
|
||||
|
||||
|
||||
g.clear();
|
||||
draw();
|
||||
var secondInterval = setInterval(draw, 60000);
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
if (!settings.fullscreen) {
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "binaryclk",
|
||||
"name": "Bin Clock",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "Clock face to show binary time in 24 hr format",
|
||||
"icon": "app-icon.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
|
|
@ -11,6 +11,8 @@
|
|||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"binaryclk.app.js","url":"app.js"},
|
||||
{"name":"binaryclk.settings.js","url":"settings.js"},
|
||||
{"name":"binaryclk.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
],
|
||||
"data": [{"name":"binaryclk.json"}]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
(function(back) {
|
||||
var FILE = "binaryclk.json";
|
||||
var settings = Object.assign({
|
||||
fullscreen: false,
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
E.showMenu({
|
||||
"" : { "title" : "Bin Clock" },
|
||||
"< Back" : () => back(),
|
||||
'Fullscreen': {
|
||||
value: settings.fullscreen,
|
||||
onchange: v => {
|
||||
settings.fullscreen = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
||||
|
|
@ -32,7 +32,6 @@
|
|||
},
|
||||
'Show Lock': {
|
||||
value: settings.showLock,
|
||||
format: () => (settings.showLock ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.showLock = !settings.showLock;
|
||||
save();
|
||||
|
|
@ -40,7 +39,6 @@
|
|||
},
|
||||
'Hide Colon': {
|
||||
value: settings.hideColon,
|
||||
format: () => (settings.hideColon ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.hideColon = !settings.hideColon;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
},
|
||||
'Show Lock': {
|
||||
value: settings.showLock,
|
||||
format: () => (settings.showLock ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.showLock = !settings.showLock;
|
||||
save();
|
||||
|
|
@ -40,7 +39,6 @@
|
|||
},
|
||||
'Hide Colon': {
|
||||
value: settings.hideColon,
|
||||
format: () => (settings.hideColon ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.hideColon = !settings.hideColon;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -17,3 +17,4 @@
|
|||
0.15: Edit holidays on device in settings
|
||||
0.16: Add menu to fast open settings to edit holidays
|
||||
Display Widgets in menus
|
||||
0.17: Load holidays before events so the latter is not overpainted
|
||||
|
|
|
|||
|
|
@ -43,24 +43,24 @@ const dowLbls = function() {
|
|||
}();
|
||||
|
||||
const loadEvents = () => {
|
||||
// add holidays & other events
|
||||
events = (require("Storage").readJSON("calendar.days.json",1) || []).map(d => {
|
||||
const date = new Date(d.date);
|
||||
const o = {date: date, msg: d.name, type: d.type};
|
||||
if (d.repeat) {
|
||||
o.repeat = d.repeat;
|
||||
}
|
||||
return o;
|
||||
});
|
||||
// all alarms that run on a specific date
|
||||
events = (require("Storage").readJSON("sched.json",1) || []).filter(a => a.on && a.date).map(a => {
|
||||
events = events.concat((require("Storage").readJSON("sched.json",1) || []).filter(a => a.on && a.date).map(a => {
|
||||
const date = new Date(a.date);
|
||||
const time = timeutils.decodeTime(a.t);
|
||||
date.setHours(time.h);
|
||||
date.setMinutes(time.m);
|
||||
date.setSeconds(time.s);
|
||||
return {date: date, msg: a.msg, type: "e"};
|
||||
});
|
||||
// add holidays & other events
|
||||
(require("Storage").readJSON("calendar.days.json",1) || []).forEach(d => {
|
||||
const date = new Date(d.date);
|
||||
const o = {date: date, msg: d.name, type: d.type};
|
||||
if (d.repeat) {
|
||||
o.repeat = d.repeat;
|
||||
}
|
||||
events.push(o);
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
const loadSettings = () => {
|
||||
|
|
@ -280,14 +280,12 @@ const showMenu = function() {
|
|||
setUI();
|
||||
},
|
||||
/*LANG*/"Exit": () => load(),
|
||||
/*LANG*/"Settings": () => {
|
||||
const appSettings = eval(require('Storage').read('calendar.settings.js'));
|
||||
appSettings(() => {
|
||||
/*LANG*/"Settings": () =>
|
||||
eval(require('Storage').read('calendar.settings.js'))(() => {
|
||||
loadSettings();
|
||||
loadEvents();
|
||||
showMenu();
|
||||
});
|
||||
},
|
||||
}),
|
||||
};
|
||||
if (require("Storage").read("alarm.app.js")) {
|
||||
menu[/*LANG*/"Launch Alarms"] = () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.16",
|
||||
"version": "0.17",
|
||||
"description": "Monthly calendar, displays holidays uploaded from the web interface and scheduled events.",
|
||||
"icon": "calendar.png",
|
||||
"screenshots": [{"url":"screenshot_calendar.png"}],
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
},
|
||||
/*LANG*/'show widgets': {
|
||||
value: !!settings.showWidgets,
|
||||
format: () => (settings.showWidgets ? 'Yes' : 'No'),
|
||||
onchange: x => save('showWidgets', x),
|
||||
},
|
||||
/*LANG*/'update interval': {
|
||||
|
|
@ -45,7 +44,6 @@
|
|||
},
|
||||
/*LANG*/'show big weather': {
|
||||
value: !!settings.showBigWeather,
|
||||
format: () => (settings.showBigWeather ? 'Yes' : 'No'),
|
||||
onchange: x => save('showBigWeather', x),
|
||||
},
|
||||
/*LANG*/'colorize icons': ()=>showCircleMenus()
|
||||
|
|
@ -87,8 +85,7 @@
|
|||
const colorizeIconKey = circleName + "colorizeIcon";
|
||||
menu[/*LANG*/'circle ' + circleId] = {
|
||||
value: settings[colorizeIconKey] || false,
|
||||
format: () => (settings[colorizeIconKey]? /*LANG*/'Yes': /*LANG*/'No'),
|
||||
onchange: x => save(colorizeIconKey, x),
|
||||
onchange: x => save(colorizeIconKey, x),
|
||||
};
|
||||
}
|
||||
E.showMenu(menu);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
'': { 'title': 'CLI complete clk' },
|
||||
'Show battery': {
|
||||
value: "battery" in settings ? settings.battery : false,
|
||||
format: () => (settings.battery ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.battery = !settings.battery;
|
||||
save('battery', settings.battery);
|
||||
|
|
@ -27,7 +26,6 @@
|
|||
},
|
||||
'Show weather': {
|
||||
value: "weather" in settings ? settings.weather : false,
|
||||
format: () => (settings.weather ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.weather = !settings.weather;
|
||||
save('weather', settings.weather);
|
||||
|
|
@ -35,7 +33,6 @@
|
|||
},
|
||||
'Show steps': {
|
||||
value: "steps" in settings ? settings.steps : false,
|
||||
format: () => (settings.steps ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.steps = !settings.steps;
|
||||
save('steps', settings.steps);
|
||||
|
|
@ -43,7 +40,6 @@
|
|||
},
|
||||
'Show heartrate': {
|
||||
value: "heartrate" in settings ? settings.heartrate : false,
|
||||
format: () => (settings.heartrate ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.heartrate = !settings.heartrate;
|
||||
save('heartrate', settings.heartrate);
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@
|
|||
0.08: Catch and discard swipe events on fw2v19 and up (as well as some cutting
|
||||
edge 2v18 ones), allowing compatability with the Back Swipe app.
|
||||
0.09: Fix colors settings, where color was stored as string instead of the expected int.
|
||||
0.10: Fix touch region for letters
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ exports.input = function(options) {
|
|||
"ram";
|
||||
// ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
// Choose character by draging along red rectangle at bottom of screen
|
||||
if (event.y >= ( (R.y+R.h) - 12 )) {
|
||||
if (event.y >= ( (R.y+R.h) - 26 )) {
|
||||
// Translate x-position to character
|
||||
if (event.x < ABCPADDING) { abcHL = 0; }
|
||||
else if (event.x >= 176-ABCPADDING) { abcHL = 25; }
|
||||
|
|
@ -139,7 +139,7 @@ exports.input = function(options) {
|
|||
|
||||
// 12345678901234567890
|
||||
// Choose number or puctuation by draging on green rectangle
|
||||
else if ((event.y < ( (R.y+R.h) - 12 )) && (event.y > ( (R.y+R.h) - 52 ))) {
|
||||
else if ((event.y < ( (R.y+R.h) - 26 )) && (event.y > ( (R.y+R.h) - 52 ))) {
|
||||
// Translate x-position to character
|
||||
if (event.x < NUMPADDING) { numHL = 0; }
|
||||
else if (event.x > 176-NUMPADDING) { numHL = NUM.length-1; }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{ "id": "dragboard",
|
||||
"name": "Dragboard",
|
||||
"version":"0.09",
|
||||
"version":"0.10",
|
||||
"description": "A library for text input via swiping keyboard",
|
||||
"icon": "app.png",
|
||||
"type":"textinput",
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
0.01: attempt to import
|
||||
0.02: Make it possible for Fastload Utils to fastload into this app.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// App Forge
|
||||
|
||||
"Bangle.loadWidgets()"; // Facilitates fastloading to this app via Fastload Utils, while still not loading widgets on standard `load` calls.
|
||||
|
||||
st = require('Storage');
|
||||
|
||||
l = /^a\..*\.js$/;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{ "id": "forge",
|
||||
"name": "App Forge",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "Easy way to run development versions of your apps",
|
||||
"icon": "app.png",
|
||||
"readme": "README.md",
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
'< Back': back,
|
||||
'Show Widgets': {
|
||||
value: settings.showWidgets,
|
||||
format: () => (settings.showWidgets ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.showWidgets = !settings.showWidgets;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ function handleUpload() {
|
|||
storage:[
|
||||
{name:"RAM", content:hexJS},
|
||||
]
|
||||
});
|
||||
}, { noFinish: true });
|
||||
}
|
||||
|
||||
document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@
|
|||
|
||||
/*LANG*/"Step Goal Notification": {
|
||||
value: "stepGoalNotification" in settings ? settings.stepGoalNotification : false,
|
||||
format: () => (settings.stepGoalNotification ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.stepGoalNotification = !settings.stepGoalNotification;
|
||||
setSettings();
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ var bg_code = [
|
|||
},
|
||||
'Full Screen': {
|
||||
value: settings.fullscreen,
|
||||
format: () => (settings.fullscreen ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.fullscreen = !settings.fullscreen;
|
||||
save();
|
||||
|
|
@ -120,7 +119,6 @@ var bg_code = [
|
|||
},
|
||||
'Disable alarm functionality': {
|
||||
value: settings.disableAlarms,
|
||||
format: () => (settings.disableAlarms ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.disableAlarms = !settings.disableAlarms;
|
||||
save();
|
||||
|
|
@ -128,7 +126,6 @@ var bg_code = [
|
|||
},
|
||||
'Disable data pages functionality': {
|
||||
value: settings.disableData,
|
||||
format: () => (settings.disableData ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.disableData = !settings.disableData;
|
||||
save();
|
||||
|
|
@ -136,7 +133,6 @@ var bg_code = [
|
|||
},
|
||||
'Random colors on open': {
|
||||
value: settings.randomColors,
|
||||
format: () => (settings.randomColors ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.randomColors = !settings.randomColors;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -27,13 +27,12 @@
|
|||
}
|
||||
|
||||
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();
|
||||
|
|
@ -50,7 +49,6 @@
|
|||
},
|
||||
'Vector Font': {
|
||||
value: s.vector,
|
||||
format: () => (s.vector ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.vector = !s.vector;
|
||||
save();
|
||||
|
|
@ -68,7 +66,6 @@
|
|||
},
|
||||
'Second Hand': {
|
||||
value: s.secondhand,
|
||||
format: () => (s.secondhand ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.secondhand = !s.secondhand;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
0.1: init app
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Paul Spenke
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Line Clock
|
||||
|
||||
This app displays a simple, different looking, analog clock. It considers the
|
||||
currently configured "theme" (and may therefore look different than shown in
|
||||
the screenshot on your watch depending on which theme you prefer).
|
||||
|
||||

|
||||
|
||||
## License
|
||||
|
||||
[MIT License](LICENSE)
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgYMJh/4AgUD+AeKgIRDj/+n41O/4RQABcfIJYAEKZgAkL4U/8ARNBwIRP/+AGx6YBPSH/4ASPh/A/hfDAAZAHg/8gP/LguSoARHEwIRFiVJkDCFjgRHgEJkg4CcwQjIAAMEHAUDCoIRB46kIHAkH//xLIw4I8eAnCNKHAYAO/xxEABg4ByASPHAkBKAbUE/5xGhP//wRFv4RDOIYIB//ACQr1FHAIRJAA0TCAP/ZwIALgYRJVowRCj/4BIkBLIgABgRHC/KqFaI4RC5MkJBlPR4UECJizJJwoAKCKImVQAwAJv0HL5S6CbwIjLCKMAn4RDh0/LMKMhWaYAKA="))
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
|
|
@ -0,0 +1,287 @@
|
|||
const handWidth = 6;
|
||||
const hourRadius = 4;
|
||||
const hourWidth = 8;
|
||||
const hourLength = 40;
|
||||
const hourSLength = 20;
|
||||
const radius = 220;
|
||||
const lineOffset = 115;
|
||||
const hourOffset = 32;
|
||||
const numberOffset = 85;
|
||||
const numberSize = 22;
|
||||
|
||||
const storage = require('Storage');
|
||||
|
||||
const SETTINGS_FILE = "line_clock.setting.json";
|
||||
|
||||
let initialSettings = {
|
||||
showLock: true,
|
||||
showMinute: true,
|
||||
};
|
||||
|
||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || initialSettings;
|
||||
for (const key in saved_settings) {
|
||||
initialSettings[key] = saved_settings[key];
|
||||
}
|
||||
|
||||
let gWidth = g.getWidth(), gCenterX = gWidth/2;
|
||||
let gHeight = g.getHeight(), gCenterY = gHeight/2;
|
||||
|
||||
let currentTime = new Date();
|
||||
let currentHour = currentTime.getHours();
|
||||
let currentMinute = currentTime.getMinutes();
|
||||
|
||||
let drawTimeout;
|
||||
|
||||
function imgLock() {
|
||||
return {
|
||||
width : 16, height : 16, bpp : 1,
|
||||
transparent : 0,
|
||||
buffer : E.toArrayBuffer(atob("A8AH4A5wDDAYGBgYP/w//D/8Pnw+fD58Pnw//D/8P/w="))
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the angle of the hour hand for the current time.
|
||||
*
|
||||
* @returns {number} The angle of the hour hand in degrees.
|
||||
*/
|
||||
function getHourHandAngle() {
|
||||
let hourHandAngle = 30 * currentHour;
|
||||
hourHandAngle += 0.5 * currentMinute;
|
||||
return hourHandAngle;
|
||||
}
|
||||
|
||||
let hourAngle = getHourHandAngle();
|
||||
|
||||
/**
|
||||
* Converts degrees to radians.
|
||||
*
|
||||
* @param {number} degrees - The degrees to be converted to radians.
|
||||
* @return {number} - The equivalent value in radians.
|
||||
*/
|
||||
function degreesToRadians(degrees) {
|
||||
return degrees * (Math.PI / 180);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates an array of points around a given angle and radius.
|
||||
*
|
||||
* @param {Array} points - The array of points to be rotated.
|
||||
* @param {number} angle - The angle in degrees to rotate the points.
|
||||
* @param {number} rad - The radius to offset the rotation.
|
||||
* @returns {Array} - The array of rotated points.
|
||||
*/
|
||||
function rotatePoints(points, angle, rad) {
|
||||
const ang = degreesToRadians(angle);
|
||||
const hAng = degreesToRadians(hourAngle);
|
||||
const rotatedPoints = [];
|
||||
points.map(function(point) {
|
||||
return {
|
||||
x: point.x * Math.cos(ang) - point.y * Math.sin(ang),
|
||||
y: point.x * Math.sin(ang) + point.y * Math.cos(ang)
|
||||
};
|
||||
}).forEach(function(point) {
|
||||
rotatedPoints.push(point.x + gCenterX - (rad * Math.sin(hAng)));
|
||||
rotatedPoints.push(point.y + gCenterY + (rad * Math.cos(hAng)));
|
||||
});
|
||||
return rotatedPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a hand on the canvas.
|
||||
*
|
||||
* @function drawHand
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function drawHand() {
|
||||
g.setColor(0xF800);
|
||||
const halfWidth = handWidth / 2;
|
||||
|
||||
const points = [{
|
||||
x: -halfWidth,
|
||||
y: -gHeight
|
||||
}, {
|
||||
x: halfWidth,
|
||||
y: -gHeight
|
||||
}, {
|
||||
x: halfWidth,
|
||||
y: gHeight
|
||||
}, {
|
||||
x: -halfWidth,
|
||||
y: gHeight
|
||||
}];
|
||||
|
||||
g.fillPolyAA(rotatePoints(points, hourAngle, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the hour coordinates for a given small flag.
|
||||
* @param {boolean} small - Determines if the flag is small.
|
||||
* @returns {Array} - An array of hour coordinates.
|
||||
*/
|
||||
function getHourCoordinates(small) {
|
||||
const dist = small ? (hourSLength - hourLength) : 0;
|
||||
const halfWidth = hourWidth / 2;
|
||||
const gh = gHeight + lineOffset;
|
||||
return [{
|
||||
x: -halfWidth,
|
||||
y: -gh - dist
|
||||
}, {
|
||||
x: halfWidth,
|
||||
y: -gh - dist
|
||||
}, {
|
||||
x: halfWidth,
|
||||
y: -gh + hourLength
|
||||
}, {
|
||||
x: -halfWidth,
|
||||
y: -gh + hourLength
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the given time to the hour dot on the clock face.
|
||||
*
|
||||
* @param {number} a - The time value to assign to the hour dot.
|
||||
* @return {void}
|
||||
*/
|
||||
function hourDot(a) {
|
||||
const h = gHeight + lineOffset;
|
||||
const rotatedPoints = rotatePoints(
|
||||
[{
|
||||
x: 0,
|
||||
y: -h + hourLength - (hourRadius / 2)
|
||||
}], a, radius
|
||||
);
|
||||
g.fillCircle(rotatedPoints[0], rotatedPoints[1], hourRadius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an hour into a number and display it on the clock face.
|
||||
*
|
||||
* @param {number} a - The hour to be converted (between 0 and 360 degrees).
|
||||
*/
|
||||
function hourNumber(a) {
|
||||
const h = gHeight + lineOffset;
|
||||
const rotatedPoints = rotatePoints(
|
||||
[{
|
||||
x: 0,
|
||||
y: -h + hourLength + hourOffset
|
||||
}], a, radius
|
||||
);
|
||||
g.drawString(String(a / 30), rotatedPoints[0], rotatedPoints[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a number on the display.
|
||||
*
|
||||
* @param {number} n - The number to be drawn.
|
||||
* @return {void}
|
||||
*/
|
||||
function drawNumber(n) {
|
||||
const h = gHeight + lineOffset;
|
||||
const halfWidth = handWidth / 2;
|
||||
const rotatedPoints = rotatePoints(
|
||||
[{
|
||||
x: 0,
|
||||
y: -h + hourLength + numberOffset
|
||||
}], hourAngle, radius
|
||||
);
|
||||
g.setColor(0xF800);
|
||||
g.fillCircle(rotatedPoints[0], rotatedPoints[1], numberSize+ halfWidth);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillCircle(rotatedPoints[0], rotatedPoints[1], numberSize - halfWidth);
|
||||
g.setColor(g.theme.fg);
|
||||
g.setFont("Vector:"+numberSize);
|
||||
g.drawString(String(n), rotatedPoints[0], rotatedPoints[1]);
|
||||
}
|
||||
|
||||
const hourPoints = getHourCoordinates(false);
|
||||
const hourSPoints = getHourCoordinates(true);
|
||||
|
||||
/**
|
||||
* Draws an hour on a clock face.
|
||||
*
|
||||
* @param {number} h - The hour to be drawn on the clock face.
|
||||
* @return {undefined}
|
||||
*/
|
||||
function drawHour(h) {
|
||||
if (h === 0) { h= 12; }
|
||||
if (h === 13) { h= 1; }
|
||||
g.setColor(g.theme.fg);
|
||||
g.setFont("Vector:32");
|
||||
const a = h * 30;
|
||||
g.fillPolyAA(rotatePoints(hourPoints, a, radius));
|
||||
g.fillPolyAA(rotatePoints(hourSPoints, a + 15, radius));
|
||||
hourNumber(a);
|
||||
hourDot(a + 5);
|
||||
hourDot(a + 10);
|
||||
hourDot(a + 20);
|
||||
hourDot(a + 25);
|
||||
}
|
||||
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
function lockListenerBw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}
|
||||
Bangle.on('lock', lockListenerBw);
|
||||
|
||||
Bangle.setUI({
|
||||
mode : "clock",
|
||||
// TODO implement https://www.espruino.com/Bangle.js+Fast+Load
|
||||
// remove : function() {
|
||||
// Bangle.removeListener('lock', lockListenerBw);
|
||||
// if (drawTimeout) clearTimeout(drawTimeout);
|
||||
// drawTimeout = undefined;
|
||||
// }
|
||||
});
|
||||
|
||||
/**
|
||||
* Draws a clock on the canvas using the current time.
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
function draw() {
|
||||
queueDraw();
|
||||
currentTime = new Date();
|
||||
currentHour = currentTime.getHours();
|
||||
if (currentHour > 12) {
|
||||
currentHour -= 12;
|
||||
}
|
||||
currentMinute = currentTime.getMinutes();
|
||||
|
||||
hourAngle = getHourHandAngle();
|
||||
|
||||
g.clear();
|
||||
g.setFontAlign(0, 0);
|
||||
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(0, 0, gWidth, gHeight);
|
||||
|
||||
if(initialSettings.showLock && Bangle.isLocked()){
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawImage(imgLock(), gWidth-16, 2);
|
||||
}
|
||||
|
||||
drawHour(currentHour);
|
||||
drawHour(currentHour-1);
|
||||
drawHour(currentHour+1);
|
||||
|
||||
|
||||
drawHand();
|
||||
|
||||
if(initialSettings.showMinute){
|
||||
drawNumber(currentMinute);
|
||||
}
|
||||
}
|
||||
|
||||
draw();
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{ "id": "line_clock",
|
||||
"name": "Line Clock",
|
||||
"shortName":"Line Clock",
|
||||
"version":"0.1",
|
||||
"description": "a readable analog clock",
|
||||
"icon": "app-icon.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"app-screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"line_clock.app.js","url":"app.js"},
|
||||
{"name":"line_clock.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"line_clock.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data":[{"name":"line_clock.setting.json"}]
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "line_clock.setting.json";
|
||||
|
||||
// initialize with default settings...
|
||||
const storage = require('Storage')
|
||||
let settings = {
|
||||
showLock: true,
|
||||
showMinute: true,
|
||||
};
|
||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||
for (const key in saved_settings) {
|
||||
settings[key] = saved_settings[key]
|
||||
}
|
||||
|
||||
function save() {
|
||||
storage.write(SETTINGS_FILE, settings)
|
||||
}
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'Line Clock' },
|
||||
'< Back': back,
|
||||
'Show Lock': {
|
||||
value: settings.showLock,
|
||||
onchange: () => {
|
||||
settings.showLock = !settings.showLock;
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Show Minute': {
|
||||
value: settings.showMinute,
|
||||
onchange: () => {
|
||||
settings.showMinute = !settings.showMinute;
|
||||
save();
|
||||
},
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
@ -32,7 +32,6 @@
|
|||
},
|
||||
'Show Lock': {
|
||||
value: settings.showLock,
|
||||
format: () => (settings.showLock ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.showLock = !settings.showLock;
|
||||
save();
|
||||
|
|
@ -40,7 +39,6 @@
|
|||
},
|
||||
'Hide Colon': {
|
||||
value: settings.hideColon,
|
||||
format: () => (settings.hideColon ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.hideColon = !settings.hideColon;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
'< Back': back,
|
||||
'Show Widgets': {
|
||||
value: settings.showWidgets,
|
||||
format: () => (settings.showWidgets ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.showWidgets = !settings.showWidgets;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue'];
|
||||
var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f'];
|
||||
var theme_options = ['System', 'Light', 'Dark'];
|
||||
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'Pebble Clock' },
|
||||
/*LANG*/'< Back': back,
|
||||
|
|
@ -48,7 +48,6 @@
|
|||
},
|
||||
/*LANG*/'Show Lock': {
|
||||
value: settings.showlock,
|
||||
format: () => (settings.showlock ? /*LANG*/'Yes' : /*LANG*/'No'),
|
||||
onchange: () => {
|
||||
settings.showlock = !settings.showlock;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -21,42 +21,34 @@
|
|||
},
|
||||
/*LANG*/'Red': {
|
||||
value: !!settings.colorRed,
|
||||
format: () => (settings.colorRed ? 'Yes' : 'No'),
|
||||
onchange: x => save('colorRed', x),
|
||||
},
|
||||
/*LANG*/'Green': {
|
||||
value: !!settings.colorGreen,
|
||||
format: () => (settings.colorGreen ? 'Yes' : 'No'),
|
||||
onchange: x => save('colorGreen', x),
|
||||
},
|
||||
/*LANG*/'Blue': {
|
||||
value: !!settings.colorBlue,
|
||||
format: () => (settings.colorBlue ? 'Yes' : 'No'),
|
||||
onchange: x => save('colorBlue', x),
|
||||
},
|
||||
/*LANG*/'Magenta': {
|
||||
value: !!settings.colorMagenta,
|
||||
format: () => (settings.colorMagenta ? 'Yes' : 'No'),
|
||||
onchange: x => save('colorMagenta', x),
|
||||
},
|
||||
/*LANG*/'Cyan': {
|
||||
value: !!settings.colorCyan,
|
||||
format: () => (settings.colorCyan ? 'Yes' : 'No'),
|
||||
onchange: x => save('colorCyan', x),
|
||||
},
|
||||
/*LANG*/'Yellow': {
|
||||
value: !!settings.colorYellow,
|
||||
format: () => (settings.colorYellow ? 'Yes' : 'No'),
|
||||
onchange: x => save('colorYellow', x),
|
||||
},
|
||||
/*LANG*/'Black': {
|
||||
value: !!settings.colorBlack,
|
||||
format: () => (settings.colorBlack ? 'Yes' : 'No'),
|
||||
onchange: x => save('colorBlack', x),
|
||||
},
|
||||
/*LANG*/'White': {
|
||||
value: !!settings.colorWhite,
|
||||
format: () => (settings.colorWhite ? 'Yes' : 'No'),
|
||||
onchange: x => save('colorWhite', x),
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
0.01: New App
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AFW7AAgThDBQeNCaYaSDg4TTFygcFCaYuWDgYTTFyHB5AmRGCARK4XJF7bXR5PQF9vQ6/CF8COL6/XYDgwERxYvBYEKOM67AhLQwEDFwTAGYywvIXIIhCAgPQSILAE4RmWF5AnBMAQECA4gJDMCovDGAfBQ4PR4K+C4RiCZAr3UFwgvDJ4IAB5IrEYAgJBHwaoF6HQNRIvFEQItDXYaTEYAQ+EAogADZZIvFGAYTBAgIBBFQg0CRwQREF5wuGeQpNDQYSUDAYYyFR5guIF4jyCdQ3CMYY+DNwTtRMBSJCAwgyBNAI+HFygwEQoJ4EQwQpEHwwuVGAhPFGwIABFIY+GFywwDdoTAFe4ZgCFzjDFSAy4NFyowIF4PIF0gwHXAKYGFz4wHXBgubGAxeOFzT1KFsowQF0AwNF0QxKFsoxIEj/XGBgQDCJYrODQICHEgQDGAQoSECwhaNCoYJDAwgoHBxBfVJgpfJGIgOHL5haGPgoBDTxIBHAH4AkA=="))
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
var option = null;
|
||||
|
||||
//debugging or analysis files
|
||||
//var logfile = require("Storage").open("HRV_log.csv", "w");
|
||||
|
||||
var logfile = require("Storage").open("HRV_logs.csv", "a");
|
||||
|
||||
var csv = [
|
||||
"time",
|
||||
"sample count",
|
||||
"HR",
|
||||
"SDNN",
|
||||
"RMSSD",
|
||||
"Temp",
|
||||
"movement"
|
||||
];
|
||||
logfile.write(csv.join(",")+"\n");
|
||||
|
||||
var debugging = true;
|
||||
var samples = 0; // how many samples have we connected?
|
||||
var collectData = false; // are we currently collecting data?
|
||||
|
||||
var BPM_array = [];
|
||||
var raw_HR_array = new Float32Array(1536);
|
||||
var alternate_array = new Float32Array(3072);
|
||||
var pulse_array = [];
|
||||
var cutoff_threshold = 0.5;
|
||||
var sample_frequency = 51.6;
|
||||
var gap_threshold = 0.15;
|
||||
var movement = 0;
|
||||
|
||||
var px = g.getWidth()/2;
|
||||
var py = g.getHeight()/2;
|
||||
var accel; // interval for acceleration logging
|
||||
|
||||
function storeMyData(data, file_type) { "ram"
|
||||
log = raw_HR_array;
|
||||
// shift elements backwards - note the 4, because a Float32 is 4 bytes
|
||||
log.set(new Float32Array(log.buffer, 4 /*bytes*/));
|
||||
// add ad final element
|
||||
log[log.length - 1] = data;
|
||||
}
|
||||
|
||||
function average(samples) {
|
||||
return E.sum(samples) / samples.length; // faster builtin
|
||||
/* var sum = 0;
|
||||
for (var i = 0; i < samples.length; i++) {
|
||||
sum += parseFloat(samples[i]);
|
||||
}
|
||||
var avg = sum / samples.length;
|
||||
return avg;*/
|
||||
}
|
||||
|
||||
function StandardDeviation (array) {
|
||||
const n = array.length;
|
||||
const mean = E.sum(array) / n; //array.reduce((a, b) => a + b) / n;
|
||||
//return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n);
|
||||
return Math.sqrt(E.variance(array, mean));
|
||||
}
|
||||
|
||||
function turn_off() {
|
||||
Bangle.setHRMPower(0);
|
||||
|
||||
|
||||
g.clear();
|
||||
g.drawString("processing 1/5", px, py);
|
||||
|
||||
rolling_average(raw_HR_array,5);
|
||||
g.clear();
|
||||
g.drawString("processing 2/5", px, py);
|
||||
|
||||
upscale();
|
||||
g.clear();
|
||||
g.drawString("processing 3/5", px, py);
|
||||
|
||||
rolling_average(alternate_array,5);
|
||||
g.clear();
|
||||
g.drawString("processing 4/5", px, py);
|
||||
|
||||
apply_cutoff();
|
||||
find_peaks();
|
||||
|
||||
g.clear();
|
||||
g.drawString("processing 5/5", px, py);
|
||||
|
||||
calculate_HRV();
|
||||
}
|
||||
|
||||
function bernstein(A, B, C, D, E, t) { "ram"
|
||||
s = 1 - t;
|
||||
x = (A * Math.pow(s, 4)) + (B * 4 * Math.pow(s, 3) * t) + (C * 6 * s * s * t * t)
|
||||
+ (D * 4 * s * Math.pow(t, 3)) + (E * Math.pow(t, 4));
|
||||
return x;
|
||||
}
|
||||
|
||||
function upscale() { "ram"
|
||||
var index = 0;
|
||||
for (let i = raw_HR_array.length - 1; i > 5; i -= 5) {
|
||||
p0 = raw_HR_array[i];
|
||||
p1 = raw_HR_array[i - 1];
|
||||
p2 = raw_HR_array[i - 2];
|
||||
p3 = raw_HR_array[i - 3];
|
||||
p4 = raw_HR_array[i - 4];
|
||||
for (let T = 0; T < 100; T += 10) {
|
||||
x = T / 100;
|
||||
D = bernstein(p0, p1, p2, p3, p4, x);
|
||||
alternate_array[index] = D;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rolling_average(values, count) { "ram"
|
||||
var temp_array = [];
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
temp_array = [];
|
||||
for (let x = 0; x < count; x++)
|
||||
temp_array.push(values[i + x]);
|
||||
values[i] = average(temp_array);
|
||||
}
|
||||
}
|
||||
|
||||
function apply_cutoff() { "ram"
|
||||
var x;
|
||||
for (let i = 0; i < alternate_array.length; i++) {
|
||||
x = alternate_array[i];
|
||||
if (x < cutoff_threshold)
|
||||
x = cutoff_threshold;
|
||||
alternate_array[i] = x;
|
||||
}
|
||||
}
|
||||
|
||||
function find_peaks() { "ram"
|
||||
var previous;
|
||||
var previous_slope = 0;
|
||||
var slope;
|
||||
var gap_size = 0;
|
||||
var temp_array = [];
|
||||
|
||||
for (let i = 0; i < alternate_array.length; i++) {
|
||||
if (previous == null)
|
||||
previous = alternate_array[i];
|
||||
slope = alternate_array[i] - previous;
|
||||
if (slope * previous_slope < 0) {
|
||||
if (gap_size > 30) {
|
||||
pulse_array.push(gap_size);
|
||||
gap_size = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
gap_size++;
|
||||
}
|
||||
previous_slope = slope;
|
||||
previous = alternate_array[i];
|
||||
}
|
||||
}
|
||||
|
||||
function RMSSD(samples){ "ram"
|
||||
var sum = 0;
|
||||
var square = 0;
|
||||
var data = [];
|
||||
var value = 0;
|
||||
|
||||
for (let i = 0; i < samples.length-1; i++) {
|
||||
value = Math.abs(samples[i]-samples[i+1])*((1 / (sample_frequency * 2)) * 1000);
|
||||
data.push(value);
|
||||
}
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
square = data[i] * data[i];
|
||||
Math.round(square);
|
||||
sum += square;
|
||||
}
|
||||
|
||||
var meansquare = sum/data.length;
|
||||
var RMS = Math.sqrt(meansquare);
|
||||
RMS = parseInt(RMS);
|
||||
return RMS;
|
||||
}
|
||||
|
||||
function calculate_HRV() {
|
||||
var gap_average = average(pulse_array);
|
||||
var temp_array = [];
|
||||
var gap_max = (1 + gap_threshold) * gap_average;
|
||||
var gap_min = (1 - gap_threshold) * gap_average;
|
||||
for (let i = 0; i < pulse_array.length; i++) {
|
||||
if (pulse_array[i] > gap_min && pulse_array[i] < gap_max)
|
||||
temp_array.push(pulse_array[i]);
|
||||
}
|
||||
gap_average = average(temp_array);
|
||||
var calculatedHR = (sample_frequency*60)/(gap_average/2);
|
||||
if(option == 0)
|
||||
Bangle.setLCDPower(1);
|
||||
g.clear();
|
||||
//var display_stdv = StandardDeviation(pulse_array).toFixed(1);
|
||||
var SDNN = (StandardDeviation(temp_array) * (1 / (sample_frequency * 2) * 1000)).toFixed(0);
|
||||
var RMS_SD = RMSSD(temp_array);
|
||||
g.drawString("SDNN:" + SDNN
|
||||
+"\nRMSSD:" + RMS_SD
|
||||
+ "\nHR:" + calculatedHR.toFixed(0)
|
||||
+"\nSample Count:" + temp_array.length, px, py);
|
||||
Bangle.setLCDPower(1);
|
||||
if(option == 0) { // single run
|
||||
Bangle.buzz(500,1);
|
||||
option = null;
|
||||
drawButtons();
|
||||
} else {
|
||||
var csv = [
|
||||
0|getTime(),
|
||||
temp_array.length,
|
||||
calculatedHR.toFixed(0),
|
||||
SDNN,
|
||||
RMS_SD,
|
||||
E.getTemperature(),
|
||||
movement.toFixed(5)
|
||||
];
|
||||
logfile.write(csv.join(",")+"\n");
|
||||
|
||||
|
||||
turn_on();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function btn3Pressed() {
|
||||
if(option === null){
|
||||
logfile.write(""); //reset HRV log
|
||||
g.clear();
|
||||
g.drawString("continuous mode", px, py);
|
||||
option = 1;
|
||||
|
||||
turn_on();
|
||||
}
|
||||
}
|
||||
|
||||
function turn_on() {
|
||||
BPM_array = [];
|
||||
pulse_array = [];
|
||||
samples = 0;
|
||||
if (accel) clearInterval(accel);
|
||||
movement = 0;
|
||||
accel = setInterval(function () {
|
||||
movement = movement + Bangle.getAccel().diff;
|
||||
}, 1000);
|
||||
Bangle.setHRMPower(1);
|
||||
collectData = true;
|
||||
}
|
||||
|
||||
function drawButtons() {
|
||||
g.setColor("#00ff7f");
|
||||
g.setFont("6x8", 2);
|
||||
g.setFontAlign(-1,1);
|
||||
g.drawString("start recording HRV", 120, 210);
|
||||
g.setColor("#ffffff");
|
||||
g.setFontAlign(0, 0);
|
||||
}
|
||||
|
||||
g.clear();
|
||||
|
||||
drawButtons();
|
||||
|
||||
g.setFont("6x8", 2);
|
||||
g.setColor("#ffffff");
|
||||
g.setFontAlign(0, 0); // center font
|
||||
|
||||
setWatch(btn3Pressed, BTN3, {repeat:true});
|
||||
|
||||
|
||||
|
||||
Bangle.on('HRM-raw', function (e) {
|
||||
if (!collectData) return;
|
||||
storeMyData(e.raw, 0);
|
||||
if (!(samples & 7)) {
|
||||
Bangle.setLCDPower(1);
|
||||
g.clearRect(0, py-10, g.getWidth(), py+22);
|
||||
if (samples < 100)
|
||||
g.drawString("setting up...\nremain still " + samples + "%", px, py, true);
|
||||
else
|
||||
g.drawString("logging: " + (samples*100/raw_HR_array.length).toFixed(0) + "%", px, py, true);
|
||||
}
|
||||
if (samples > raw_HR_array.length) {
|
||||
collectData = false;
|
||||
turn_off();
|
||||
}
|
||||
samples++;
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{ "id": "stressless",
|
||||
"name": "Stressless",
|
||||
"shortName":"Stressless",
|
||||
"icon": "stressless.png",
|
||||
"version":"0.01",
|
||||
"description": "This is a heart activity tracker for PIIS stressless project",
|
||||
"tags": "health",
|
||||
"supports": ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"stressless.app.js","url":"app.js"},
|
||||
{"name":"stressless.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -21,7 +21,6 @@
|
|||
},
|
||||
'Hide Widget': {
|
||||
value: "hide" in settings ? settings.hide : false,
|
||||
format: () => (settings.hide ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.hide = !settings.hide
|
||||
save('hide', settings.hide);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@
|
|||
},
|
||||
'Hide Widget': {
|
||||
value: s.hide,
|
||||
format: () => (s.hide ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.hide = !s.hide;
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
},
|
||||
'Show Progress': {
|
||||
value: s.progress,
|
||||
format: () => (s.progress ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.progress = !s.progress
|
||||
save();
|
||||
|
|
@ -45,7 +44,6 @@
|
|||
},
|
||||
'Large Digits': {
|
||||
value: s.large,
|
||||
format: () => (s.large ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.large = !s.large
|
||||
save();
|
||||
|
|
@ -53,7 +51,6 @@
|
|||
},
|
||||
'Hide Widget': {
|
||||
value: s.hide,
|
||||
format: () => (s.hide ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.hide = !s.hide
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -256,9 +256,17 @@ apps.forEach((app,appIdx) => {
|
|||
if (a>=0 && b>=0 && a<b)
|
||||
WARN(`Clock ${app.id} file calls loadWidgets before setUI (clock widget/etc won't be aware a clock app is running)`, {file:appDirRelative+file.url, line : fileContents.substr(0,a).split("\n").length});
|
||||
}
|
||||
// if settings, suggest adding to datafiles
|
||||
if (/\.settings?\.js$/.test(file.name) && (!app.data || app.data.every(d => !d.name || !d.name.endsWith(".json")))) {
|
||||
WARN(`App ${app.id} has a setting file but no corresponding data entry (add \`"data":[{"name":"${app.id}.settings.json"}]\`)`, {file:appDirRelative+file.url});
|
||||
// if settings
|
||||
if (/\.settings?\.js$/.test(file.name)) {
|
||||
// suggest adding to datafiles
|
||||
if (!app.data || app.data.every(d => !d.name || !d.name.endsWith(".json"))) {
|
||||
WARN(`App ${app.id} has a setting file but no corresponding data entry (add \`"data":[{"name":"${app.id}.settings.json"}]\`)`, {file:appDirRelative+file.url});
|
||||
}
|
||||
// check for manual boolean formatter
|
||||
const m = fileContents.match(/format: *\(\) *=>.*["'](yes|on)["']/i);
|
||||
if (m) {
|
||||
WARN(`Settings for ${app.id} has a boolean formatter - this is handled automatically, the line can be removed`, {file:appDirRelative+file.url, line: fileContents.substr(0, m.index).split("\n").length});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const key in file) {
|
||||
|
|
|
|||
2
core
2
core
|
|
@ -1 +1 @@
|
|||
Subproject commit c97b7851f50cfff4e898c2264a337a17085ce463
|
||||
Subproject commit e6a65a8cb20a730f75bbbab549c602300e69e8c4
|
||||
Loading…
Reference in New Issue