Merge branch 'master' into jekyll-apps.json

pull/1221/head
Adam Schmalhofer 2022-01-15 11:08:53 +01:00
commit 3f95330025
21 changed files with 419 additions and 68 deletions

1
apps/acmaze/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

17
apps/acmaze/README.md Normal file
View File

@ -0,0 +1,17 @@
# AccelaMaze
Tilt the watch to roll a ball through a maze.
![Screenshot](screenshot.png)
## Usage
* Use the menu to select difficulty level (or exit).
* Wait until the maze gets generated and a red ball appears.
* Tilt the watch to get the ball into the green cell.
At any time you can click the button to return to the menu.
## Creator
[Nimrod Kerrett](https://zzzen.com)

1
apps/acmaze/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwggaXh3M53/AA3yl4IHn//+EM5nMAoIX/C4RfCC4szmcxC4QFBAAUxC4UPAwIOB+YCCiMRkAFCkIGBAAQfBC4IUEAQhHIAAQX/C5EDmcyCgUTAoYXDR4kzC4UBPoKVB+YFFAQSPBiAKBiCnDGoZECABDUCa4YX/C5qPBQwoXGkczmC/FQYSSCVQSSCEwQOCC4hKFX4QXCd5YX/C4qMEmQXITAinDPoIADTwSPFkKMBX47RGI47XIC/4XCgZ9DQYYABmKYBmIXFkczmEBRIK/CQYQIBkECSoiSCA4MQa5pEFd6IX/RgMyC6H/QASVCRIS/EAQrXFJQoX/C6kDRQIXCiYFD+QFBmIUCkYFD+CJBiSPCRwIFFSoQFCiF3u9wI4gAO+wXW+IXygAAW"))

276
apps/acmaze/app.js Normal file
View File

@ -0,0 +1,276 @@
const MARGIN = 25;
const WALL_RIGHT = 1, WALL_DOWN = 2;
const STATUS_GENERATING = 0, STATUS_PLAYING = 1,
STATUS_SOLVED = 2, STATUS_ABORTED = -1;
function Maze(n) {
this.n = n;
this.status = STATUS_GENERATING;
this.wall_length = Math.floor((g.getHeight()-2*MARGIN)/n);
this.total_length = this.wall_length*n;
this.margin = Math.floor((g.getHeight()-this.total_length)/2);
this.ball_x = 0;
this.ball_y = 0;
this.clearScreen = function() {
g.clearRect(
0, this.margin,
g.getWidth(), this.margin+this.total_length
);
};
this.clearScreen();
g.setColor(g.theme.fg);
for (let i=0; i<=n; i++) {
g.drawRect(
this.margin, this.margin+i*this.wall_length,
g.getWidth()-this.margin, this.margin+i*this.wall_length
);
g.drawRect(
this.margin+i*this.wall_length, this.margin,
this.margin+i*this.wall_length, g.getHeight() - this.margin
);
}
this.walls = new Uint8Array(n*n);
this.groups = new Uint8Array(n*n);
for (let cell = 0; cell<n*n; cell++) {
this.walls[cell] = WALL_RIGHT|WALL_DOWN;
this.groups[cell] = cell;
}
let from_group, to_group;
let ngroups = n*n;
while (--ngroups) {
// Abort if BTN1 pressed [grace period for menu]
// (for some reason setWatch() fails inside constructor)
if (ngroups<n*n-4 && digitalRead(BTN1)) {
aborting = true;
return;
}
from_group = to_group = -1;
while (from_group<0) {
if (Math.random()<0.5) { // try to break a wall right
let r = Math.floor(Math.random()*n);
let c = Math.floor(Math.random()*(n-1));
let cell = r*n+c;
if (this.groups[cell]!=this.groups[cell+1]) {
this.walls[cell] &= ~WALL_RIGHT;
g.clearRect(
this.margin+(c+1)*this.wall_length,
this.margin+r*this.wall_length+1,
this.margin+(c+1)*this.wall_length,
this.margin+(r+1)*this.wall_length-1
);
g.flip(); // show progress.
from_group = this.groups[cell];
to_group = this.groups[cell+1];
}
} else { // try to break a wall down
let r = Math.floor(Math.random()*(n-1));
let c = Math.floor(Math.random()*n);
let cell = r*n+c;
if (this.groups[cell]!=this.groups[cell+n]) {
this.walls[cell] &= ~WALL_DOWN;
g.clearRect(
this.margin+c*this.wall_length+1,
this.margin+(r+1)*this.wall_length,
this.margin+(c+1)*this.wall_length-1,
this.margin+(r+1)*this.wall_length
);
from_group = this.groups[cell];
to_group = this.groups[cell+n];
}
}
}
for (let cell = 0; cell<n*n; cell++) {
if (this.groups[cell]==from_group) {
this.groups[cell] = to_group;
}
}
}
this.clearScreen = function() {
g.clearRect(
0, MARGIN, g.getWidth(), g.getHeight()-MARGIN-1
);
};
this.clearCell = function(r, c) {
if (!r && !c) {
g.setColor("#ffff00");
} else if (r==this.n-1 && c==this.n-1) {
g.setColor("#00ff00");
} else {
g.setColor(g.theme.bg);
}
g.fillRect(
this.margin+this.wall_length*c+1,
this.margin+this.wall_length*r+1,
this.margin+this.wall_length*(c+1),
this.margin+this.wall_length*(r+1)
);
g.setColor(g.theme.fg);
if (this.walls[r*n+c]&WALL_RIGHT) {
g.fillRect(
this.margin+this.wall_length*(c+1),
this.margin+this.wall_length*r,
this.margin+this.wall_length*(c+1),
this.margin+this.wall_length*(r+1)
);
}
if (this.walls[r*n+c]&WALL_DOWN) {
g.fillRect(
this.margin+this.wall_length*c,
this.margin+this.wall_length*(r+1),
this.margin+this.wall_length*(c+1),
this.margin+this.wall_length*(r+1)
);
}
};
this.drawBall = function(x, y) {
g.setColor("#ff0000");
g.fillEllipse(
this.margin+x+1,
this.margin+y+1,
this.margin+x+this.wall_length-1,
this.margin+y+this.wall_length-1
);
g.setColor(g.theme.fg);
};
this.move = function(dx, dy) {
let next_x = this.ball_x,
next_y = this.ball_y,
ball_r = Math.floor(this.ball_y/this.wall_length),
ball_c = Math.floor(this.ball_x/this.wall_length);
if (this.ball_x%this.wall_length) {
if (dx) {
next_x += dx;
} else {
return false;
}
} else if (this.ball_y%this.wall_length) {
if (dy) {
next_y += dy;
} else {
return false;
}
} else { // exactly in a cell. Check walls
if (dy<0 && ball_r>0 && !(this.walls[n*(ball_r-1)+ball_c]&WALL_DOWN)) {
next_y--;
} else if (dy>0 && ball_r<(this.n-1) && !(this.walls[n*ball_r+ball_c]&WALL_DOWN)) {
next_y++;
} else if (dx<0 && ball_c>0 && !(this.walls[n*ball_r+ball_c-1]&WALL_RIGHT)) {
next_x--;
} else if (dx>0 && ball_c<(this.n-1) && !(this.walls[n*ball_r+ball_c]&WALL_RIGHT)) {
next_x++;
} else {
return false;
}
}
this.clearCell(ball_r, ball_c);
if (this.ball_x%this.wall_length) {
this.clearCell(ball_r, ball_c+1);
}
if (this.ball_y%this.wall_length) {
this.clearCell(ball_r+1, ball_c);
}
this.ball_x = next_x;
this.ball_y = next_y;
this.drawBall(this.ball_x, this.ball_y);
if (this.ball_x==(n-1)*this.wall_length && this.ball_y==(n-1)*this.wall_length) {
this.status = STATUS_SOLVED;
}
return true;
};
this.try_move_horizontally = function(accel_x) {
if (accel_x>0.15) {
return this.move(-1, 0);
} else if (accel_x<-0.15) {
return this.move(1, 0);
}
return false;
};
this.try_move_vertically = function(accel_y) {
if (accel_y<-0.15) {
return this.move(0,1);
} else if (accel_y>0.15) {
return this.move(0,-1);
}
return false;
};
this.tick = function() {
accel = Bangle.getAccel();
if (this.ball_x%this.wall_length) {
this.try_move_horizontally(accel.x);
} else if (this.ball_y%this.wall_length) {
this.try_move_vertically(accel.y);
} else {
if (Math.abs(accel.x)>Math.abs(accel.y)) { // prefer horizontally
if (!this.try_move_horizontally(accel.x)) {
this.try_move_vertically(accel.y);
}
} else { // prefer vertically
if (!this.try_move_vertically(accel.y)) {
this.try_move_horizontally(accel.x);
}
}
}
};
this.clearCell(0,0);
this.clearCell(n-1,n-1);
this.drawBall(0,0);
this.status = STATUS_PLAYING;
}
function timeToText(t) { // Courtesy of stopwatch app
let hrs = Math.floor(t/3600000);
let mins = Math.floor(t/60000)%60;
let secs = Math.floor(t/1000)%60;
let tnth = Math.floor(t/100)%10;
let text;
if (hrs === 0)
text = ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2) + "." + tnth;
else
text = ("0"+hrs) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
return text;
}
let aborting = false;
let start_time = 0;
let duration = 0;
let maze=null;
let mazeMenu = {
"": { "title": "Maze size", "selected": 1 },
"Easy (8x8)": function() { E.showMenu(); maze = new Maze(8); },
"Medium (10x10)": function() { E.showMenu(); maze = new Maze(10); },
"Hard (14x14)": function() { E.showMenu(); maze = new Maze(14); },
"< Exit": function() { setTimeout(load, 100); } // timeout voodoo prevents deadlock
};
g.clear(true);
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setLocked(false);
Bangle.setLCDTimeout(0);
E.showMenu(mazeMenu);
let maze_interval = setInterval(
function() {
if (maze) {
if (digitalRead(BTN1) || maze.status==STATUS_ABORTED) {
console.log(`aborting ${start_time}`);
maze = null;
start_time = duration = 0;
aborting = false;
setTimeout(function() {E.showMenu(mazeMenu); }, 100);
return;
}
if (!start_time) {
start_time = Date.now();
}
if (maze.status==STATUS_PLAYING) {
maze.tick();
}
if (maze.status==STATUS_SOLVED && !duration) {
duration = Date.now()-start_time;
g.setFontAlign(0,0).setColor(g.theme.fg);
g.setFont("Vector",18);
g.drawString(`Solved in\n ${timeToText(duration)} \nClick to play again`, g.getWidth()/2, g.getHeight()/2, true);
}
}
}, 25);

BIN
apps/acmaze/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
apps/acmaze/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -1 +1,4 @@
0.01: New App! 0.01: New App!
0.02: Add sit ups
Add more feedback to the user about the exercises
Clean up code

View File

@ -2,7 +2,7 @@
Can automatically track exercises while wearing the Bangle.js watch. Can automatically track exercises while wearing the Bangle.js watch.
Currently only push ups and curls are supported. Currently only push ups, curls and sit ups are supported.
## Disclaimer ## Disclaimer
@ -23,7 +23,7 @@ Press stop to end your exercise.
## TODO ## TODO
* Add other exercise types: * Add other exercise types:
* Rope jumps * Rope jumps
* Sit ups * Star jumps
* ... * ...
* Save exercise summaries to file system * Save exercise summaries to file system
* Configure daily goal for exercises * Configure daily goal for exercises

View File

@ -25,22 +25,32 @@ let exerciseType = {
const exerciseTypes = [{ const exerciseTypes = [{
"id": "pushup", "id": "pushup",
"name": "push ups", "name": "push ups",
"useYaxe": true, "useYaxis": true,
"useZaxe": false, "useZaxis": false,
"thresholdY": 2500, "threshold": 2500,
"thresholdMinTime": 1400, // mininmal time between two push ups in ms "thresholdMinTime": 800, // mininmal time between two push ups in ms
"thresholdMaxTime": 5000, // maximal time between two push ups in ms "thresholdMaxTime": 5000, // maximal time between two push ups in ms
"thresholdMinDurationTime": 700, // mininmal duration of half a push ups in ms "thresholdMinDurationTime": 600, // mininmal duration of half a push up in ms
}, },
{ {
"id": "curl", "id": "curl",
"name": "curls", "name": "curls",
"useYaxe": true, "useYaxis": true,
"useZaxe": false, "useZaxis": false,
"thresholdY": 2500, "threshold": 2500,
"thresholdMinTime": 1000, // mininmal time between two curls in ms "thresholdMinTime": 800, // mininmal time between two curls in ms
"thresholdMaxTime": 5000, // maximal time between two curls in ms "thresholdMaxTime": 5000, // maximal time between two curls in ms
"thresholdMinDurationTime": 500, // mininmal duration of half a push ups in ms "thresholdMinDurationTime": 500, // mininmal duration of half a curl in ms
},
{
"id": "situp",
"name": "sit ups",
"useYaxis": false,
"useZaxis": true,
"threshold": 3500,
"thresholdMinTime": 800, // mininmal time between two sit ups in ms
"thresholdMaxTime": 5000, // maximal time between two sit ups in ms
"thresholdMinDurationTime": 500, // mininmal duration of half a sit up in ms
} }
]; ];
let exerciseCounter = 0; let exerciseCounter = 0;
@ -66,7 +76,7 @@ function showMainMenu() {
}; };
exerciseTypes.forEach(function(et) { exerciseTypes.forEach(function(et) {
menu["Do " + et.name] = function() { menu[et.name] = function() {
exerciseType = et; exerciseType = et;
E.showMenu(); E.showMenu();
startTraining(); startTraining();
@ -81,8 +91,8 @@ function showMainMenu() {
value: exerciseCounter + " " + exerciseType.name value: exerciseCounter + " " + exerciseType.name
}; };
} }
menu.Exit = function() { menu.exit = function() {
load(); load();
}; };
E.showMenu(menu); E.showMenu(menu);
@ -91,11 +101,11 @@ function showMainMenu() {
function accelHandler(accel) { function accelHandler(accel) {
if (!exerciseType) return; if (!exerciseType) return;
const t = Math.round(new Date().getTime()); // time in ms const t = Math.round(new Date().getTime()); // time in ms
const y = exerciseType.useYaxe ? accel.y * 8192 : 0; const y = exerciseType.useYaxis ? accel.y * 8192 : 0;
const z = exerciseType.useZaxe ? accel.z * 8192 : 0; const z = exerciseType.useZaxis ? accel.z * 8192 : 0;
//console.log(t, y, z); //console.log(t, y, z);
if (exerciseType.useYaxe) { if (exerciseType.useYaxis) {
while (historyY.length > avgSize) while (historyY.length > avgSize)
historyY.shift(); historyY.shift();
@ -109,7 +119,7 @@ function accelHandler(accel) {
} }
} }
if (exerciseType.useYaxe) { if (exerciseType.useZaxis) {
while (historyZ.length > avgSize) while (historyZ.length > avgSize)
historyZ.shift(); historyZ.shift();
@ -124,72 +134,64 @@ function accelHandler(accel) {
} }
// slope for Y // slope for Y
if (exerciseType.useYaxe) { if (exerciseType.useYaxis) {
let l = historyAvgY.length; let l = historyAvgY.length;
if (l > 1) { if (l > 1) {
const p1 = historyAvgY[l - 2]; const p1 = historyAvgY[l - 2];
const p2 = historyAvgY[l - 1]; const p2 = historyAvgY[l - 1];
const slopeY = (p2[1] - p1[1]) / (p2[0] / 1000 - p1[0] / 1000); const slopeY = (p2[1] - p1[1]) / (p2[0] / 1000 - p1[0] / 1000);
// we use this data for exercises which can be detected by using Y axis data // we use this data for exercises which can be detected by using Y axis data
switch (exerciseType.id) { isValidExercise(slopeY, t);
case "pushup":
isValidYAxisExercise(slopeY, t);
break;
case "curl":
isValidYAxisExercise(slopeY, t);
break;
}
} }
} }
// slope for Z // slope for Z
if (exerciseType.useZaxe) { if (exerciseType.useZaxis) {
l = historyAvgZ.length; l = historyAvgZ.length;
if (l > 1) { if (l > 1) {
const p1 = historyAvgZ[l - 2]; const p1 = historyAvgZ[l - 2];
const p2 = historyAvgZ[l - 1]; const p2 = historyAvgZ[l - 1];
const slopeZ = (p2[1] - p1[1]) / (p2[0] - p1[0]); const slopeZ = (p2[1] - p1[1]) / (p2[0] / 1000 - p1[0] / 1000);
historyAvgZ.shift(); // we use this data for some exercises which can be detected by using Z axis data
historySlopeZ.push([p2[0] - p1[0], slopeZ]); isValidExercise(slopeZ, t);
// TODO: we can use this data for some exercises which can be detected by using Z axis data
} }
} }
} }
/* /*
* Check if slope value of Y-axis data looks like an exercise * Check if slope value of Y-axis or Z-axis data (depending on exercise type) looks like an exercise
* *
* In detail we look for slop values which are bigger than the configured Y threshold for the current exercise * In detail we look for slop values which are bigger than the configured threshold for the current exercise type
* Then we look for two consecutive slope values of which one is above 0 and the other is below zero. * Then we look for two consecutive slope values of which one is above 0 and the other is below zero.
* If we find one pair of these values this could be part of one exercise. * If we find one pair of these values this could be part of one exercise.
* Then we look for a pair of values which cross the zero from the otherwise direction * Then we look for a pair of values which cross the zero from the otherwise direction
*/ */
function isValidYAxisExercise(slopeY, t) { function isValidExercise(slope, t) {
if (!exerciseType) return; if (!exerciseType) return;
const thresholdY = exerciseType.thresholdY; const threshold = exerciseType.threshold;
const historySlopeValues = exerciseType.useYaxis ? historySlopeY : historySlopeZ;
const thresholdMinTime = exerciseType.thresholdMinTime; const thresholdMinTime = exerciseType.thresholdMinTime;
const thresholdMaxTime = exerciseType.thresholdMaxTime; const thresholdMaxTime = exerciseType.thresholdMaxTime;
const thresholdMinDurationTime = exerciseType.thresholdMinDurationTime; const thresholdMinDurationTime = exerciseType.thresholdMinDurationTime;
const exerciseName = exerciseType.name; const exerciseName = exerciseType.name;
if (Math.abs(slopeY) >= thresholdY) {
historyAvgY.shift();
historySlopeY.push([t, slopeY]);
//console.log(t, Math.abs(slopeY));
const lSlopeY = historySlopeY.length; if (Math.abs(slope) >= threshold) {
if (lSlopeY > 1) { historySlopeValues.push([t, slope]);
const p1 = historySlopeY[lSlopeY - 1][1]; //console.log(t, Math.abs(slope));
const p2 = historySlopeY[lSlopeY - 2][1];
const lSlopeHistory = historySlopeValues.length;
if (lSlopeHistory > 1) {
const p1 = historySlopeValues[lSlopeHistory - 1][1];
const p2 = historySlopeValues[lSlopeHistory - 2][1];
if (p1 > 0 && p2 < 0) { if (p1 > 0 && p2 < 0) {
if (lastZeroPassCameFromPositive == false) { if (lastZeroPassCameFromPositive == false) {
lastExerciseHalfCompletionTime = t; lastExerciseHalfCompletionTime = t;
//console.log(t, exerciseName + " half complete..."); console.log(t, exerciseName + " half complete...");
layout.progress.label = "½"; layout.progress.label = "½";
layout.recording.label = "TRAINING";
g.clear(); g.clear();
layout.render(); layout.render();
} }
@ -201,7 +203,7 @@ function isValidYAxisExercise(slopeY, t) {
if (lastZeroPassCameFromPositive == true) { if (lastZeroPassCameFromPositive == true) {
const tDiffLastExercise = t - lastExerciseCompletionTime; const tDiffLastExercise = t - lastExerciseCompletionTime;
const tDiffStart = t - tStart; const tDiffStart = t - tStart;
//console.log(t, exerciseName + " maybe complete?", Math.round(tDiffLastExercise), Math.round(tDiffStart)); console.log(t, exerciseName + " maybe complete?", Math.round(tDiffLastExercise), Math.round(tDiffStart));
// check minimal time between exercises: // check minimal time between exercises:
if ((lastExerciseCompletionTime <= 0 && tDiffStart >= thresholdMinTime) || tDiffLastExercise >= thresholdMinTime) { if ((lastExerciseCompletionTime <= 0 && tDiffStart >= thresholdMinTime) || tDiffLastExercise >= thresholdMinTime) {
@ -219,22 +221,36 @@ function isValidYAxisExercise(slopeY, t) {
layout.count.label = exerciseCounter; layout.count.label = exerciseCounter;
layout.progress.label = ""; layout.progress.label = "";
layout.recording.label = "Good!";
g.clear(); g.clear();
layout.render(); layout.render();
if (settings.buzz) if (settings.buzz)
Bangle.buzz(100, 0.4); Bangle.buzz(200, 0.5);
} else { } else {
//console.log(t, exerciseName + " to quick for duration time threshold!"); console.log(t, exerciseName + " too quick for duration time threshold!"); // thresholdMinDurationTime
lastExerciseCompletionTime = t; lastExerciseCompletionTime = t;
layout.recording.label = "Go slower!";
g.clear();
layout.render();
} }
} else { } else {
//console.log(t, exerciseName + " to slow for time threshold!"); console.log(t, exerciseName + " top slow for time threshold!"); // thresholdMaxTime
lastExerciseCompletionTime = t; lastExerciseCompletionTime = t;
layout.recording.label = "Go faster!";
g.clear();
layout.render();
} }
} else { } else {
//console.log(t, exerciseName + " to quick for time threshold!"); console.log(t, exerciseName + " too quick for time threshold!"); // thresholdMinTime
lastExerciseCompletionTime = t; lastExerciseCompletionTime = t;
layout.recording.label = "Go slower!";
g.clear();
layout.render();
} }
} }
@ -267,6 +283,7 @@ function startTraining() {
if (recordActive) return; if (recordActive) return;
g.clear(1); g.clear(1);
reset(); reset();
Bangle.setLCDTimeout(0); // force LCD on
Bangle.setHRMPower(1, "banglexercise"); Bangle.setHRMPower(1, "banglexercise");
if (!hrtValue) hrtValue = "..."; if (!hrtValue) hrtValue = "...";
@ -285,7 +302,7 @@ function startTraining() {
type: "txt", type: "txt",
id: "count", id: "count",
font: exerciseCounter < 100 ? "6x8:9" : "6x8:8", font: exerciseCounter < 100 ? "6x8:9" : "6x8:8",
label: 10, label: exerciseCounter,
pad: 5 pad: 5
}, },
{ {
@ -337,11 +354,16 @@ function startTraining() {
layout.render(); layout.render();
Bangle.setPollInterval(80); // 12.5 Hz Bangle.setPollInterval(80); // 12.5 Hz
Bangle.on('accel', accelHandler);
tStart = new Date().getTime(); tStart = new Date().getTime();
recordActive = true; recordActive = true;
if (settings.buzz) if (settings.buzz)
Bangle.buzz(200, 1); Bangle.buzz(200, 1);
// delay start a little bit
setTimeout(() => {
Bangle.on('accel', accelHandler);
}, 1000);
} }
function stopTraining() { function stopTraining() {

View File

@ -0,0 +1,21 @@
{ "id": "banglexercise",
"name": "BanglExercise",
"shortName":"BanglExercise",
"version":"0.02",
"description": "Can automatically track exercises while wearing the Bangle.js watch.",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"type": "app",
"tags": "sport",
"supports" : ["BANGLEJS2"],
"allow_emulator":true,
"readme": "README.md",
"storage": [
{"name":"banglexercise.app.js","url":"app.js"},
{"name":"banglexercise.img","url":"app-icon.js","evaluate":true},
{"name":"banglexercise.settings.js","url":"settings.js"}
],
"data": [
{"name":"banglexercise.json"}
]
}

View File

@ -9,4 +9,5 @@
0.09: Tab anywhere to open the launcher. 0.09: Tab anywhere to open the launcher.
0.10: Removed swipes to be compatible with the Pattern Launcher. Stability improvements. 0.10: Removed swipes to be compatible with the Pattern Launcher. Stability improvements.
0.11: Show the gadgetbridge weather temperature (settings). 0.11: Show the gadgetbridge weather temperature (settings).
0.12: Added humidity to data. 0.12: Added humidity to data.
0.13: Improved battery visualization.

View File

@ -20,7 +20,7 @@ let cOrange = "#FF9900";
let cPurple = "#FF00DC"; let cPurple = "#FF00DC";
let cWhite = "#FFFFFF"; let cWhite = "#FFFFFF";
let cBlack = "#000000"; let cBlack = "#000000";
let cGrey = "#9E9E9E"; let cGrey = "#424242";
/* /*
* Global lcars variables * Global lcars variables
@ -143,7 +143,7 @@ function printData(key, y, c){
} else if (key == "HUMIDITY"){ } else if (key == "HUMIDITY"){
text = "HUM"; text = "HUM";
var weather = getWeather(); var weather = getWeather();
value = parseInt(weather.hum) + "%"; value = weather.hum + "%";
} else if(key == "CORET"){ } else if(key == "CORET"){
value = locale.temp(parseInt(E.getTemperature())); value = locale.temp(parseInt(E.getTemperature()));
@ -242,9 +242,14 @@ function drawPosition0(){
// 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 batX2 = parseInt((172 - 35) * bat + 35); var batStart = 19;
drawHorizontalBgLine(cOrange, 35, batX2-5, 171, 5); var batWidth = 172 - batStart;
drawHorizontalBgLine(cGrey, batX2+5, 172, 171, 5); var batX2 = parseInt(batWidth * bat + batStart);
drawHorizontalBgLine(cOrange, batStart, batX2, 171, 5);
drawHorizontalBgLine(cGrey, batX2, 172, 171, 5);
for(var i=0; i+batStart<=172; i+=parseInt(batWidth/4)){
drawHorizontalBgLine(cBlack, batStart+i, batStart+i+3, 168, 8)
}
// Draw Infos // Draw Infos
drawInfo(); drawInfo();

View File

@ -3,7 +3,7 @@
"name": "LCARS Clock", "name": "LCARS Clock",
"shortName":"LCARS", "shortName":"LCARS",
"icon": "lcars.png", "icon": "lcars.png",
"version":"0.12", "version":"0.13",
"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.",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,3 +1,4 @@
0.01: First commit 0.01: First commit
0.02: Handle new firmwares with 'lock' event 0.02: Handle new firmwares with 'lock' event
0.03: Don't try to be fancy - just bail out on firmwares without a lock event 0.03: Don't try to be fancy - just bail out on firmwares without a lock event
0.04: Set sortorder to -1 so that widget always takes up the furthest left position

View File

@ -1,12 +1,13 @@
{ {
"id": "widlock", "id": "widlock",
"name": "Lock Widget", "name": "Lock Widget",
"version": "0.03", "version": "0.04",
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked", "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,lock", "tags": "widget,lock",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"sortorder": -1,
"storage": [ "storage": [
{"name":"widlock.wid.js","url":"widget.js"} {"name":"widlock.wid.js","url":"widget.js"}
] ]

View File

@ -1 +1,2 @@
0.01: First release 0.01: First release
0.02: Size widget after step count is reset

View File

@ -4,7 +4,7 @@
"shortName":"Simple Pedometer", "shortName":"Simple Pedometer",
"icon": "screenshot_widpa.png", "icon": "screenshot_widpa.png",
"screenshots": [{"url":"screenshot_widpa.png"}], "screenshots": [{"url":"screenshot_widpa.png"}],
"version":"0.01", "version":"0.02",
"type": "widget", "type": "widget",
"supports": ["BANGLEJS", "BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md", "readme": "README.md",

View File

@ -6,7 +6,7 @@ WIDGETS["widpa"]={area:"tl",width:13,draw:function() {
if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off
var steps = Bangle.getHealthStatus("day").steps; var steps = Bangle.getHealthStatus("day").steps;
var w = 1 + (steps.toString().length)*12; var w = 1 + (steps.toString().length)*12;
if (w > this.width) {this.width = w; setTimeout(() => Bangle.drawWidgets(),10); return;} if (w != this.width) {this.width = w; setTimeout(() => Bangle.drawWidgets(),10); return;}
g.reset(); g.reset();
g.setColor(g.theme.bg); g.setColor(g.theme.bg);
g.fillRect(this.x, this.y, this.x + this.width, this.y + 23); g.fillRect(this.x, this.y, this.x + this.width, this.y + 23);

View File

@ -1 +1,2 @@
0.01: First release 0.01: First release
0.02: Fixed widget id to wibpb, Size widget after step count is reset

View File

@ -1,13 +1,13 @@
// on.step version // on.step version
Bangle.on('step', function(s) { WIDGETS["bata"].draw(); }); Bangle.on('step', function(s) { WIDGETS["bata"].draw(); });
Bangle.on('lcdPower', function(on) { Bangle.on('lcdPower', function(on) {
if (on) WIDGETS["bata"].draw(); if (on) WIDGETS["widpb"].draw();
}); });
WIDGETS["bata"]={area:"tl",width:13,draw:function() { WIDGETS["widpb"]={area:"tl",width:13,draw:function() {
if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off
var steps = Bangle.getHealthStatus("day").steps; var steps = Bangle.getHealthStatus("day").steps;
var w = 1 + (steps.toString().length)*12; var w = 1 + (steps.toString().length)*12;
if (w > this.width) {this.width = w; setTimeout(() => Bangle.drawWidgets(),10); return;} if (w != this.width) {this.width = w; setTimeout(() => Bangle.drawWidgets(),10); return;}
g.reset(); g.reset();
g.setColor(g.theme.bg); g.setColor(g.theme.bg);
g.fillRect(this.x, this.y, this.x + this.width, this.y + 23); // erase background g.fillRect(this.x, this.y, this.x + this.width, this.y + 23); // erase background