mirror of https://github.com/espruino/BangleApps
Refactoring + added timer which can easily be started / stopped.
parent
ea028454af
commit
cc64bfdd6a
|
@ -6,17 +6,19 @@ It looks like an analog clock, but its not! It shows the time digital - check th
|
||||||
The red hand shows the number of steps (0 = 0°, 2.5k = 90°, 5k = 180°, ...) and the
|
The red hand shows the number of steps (0 = 0°, 2.5k = 90°, 5k = 180°, ...) and the
|
||||||
black one the battery level (100% = 0°, 75% = 270°, 50% = 180°, ...).
|
black one the battery level (100% = 0°, 75% = 270°, 50% = 180°, ...).
|
||||||
The selected theme is also respected. Note that this watch face is in fullscreen
|
The selected theme is also respected. Note that this watch face is in fullscreen
|
||||||
mode - widgets are still loaded in background...
|
mode, but widgets are still loaded in background.
|
||||||
|
|
||||||
## Other features
|
## Other features
|
||||||
- If you have done more than 10k steps, the red hand and icon will turn green.
|
- Set a timer - simply touch top (+5min.) or bottom (-5 min.).
|
||||||
|
- If the weather is available through the weather app, the outside temp. will be shown.
|
||||||
- If the battery is charged, the icons will change.
|
- If the battery is charged, the icons will change.
|
||||||
- Shows outside temperature if gadgetbridge is connected with weather.
|
- If you have done more than 10k steps, the red hand and icon will turn green.
|
||||||
- Shows current lock status of your bangle.
|
- Shows current lock status of your bangle va a colored dot in the middle.
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
# Thanks
|
# Thanks
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
const locale = require('locale');
|
/**
|
||||||
|
* NOT ANALOG CLOCK
|
||||||
|
*/
|
||||||
|
|
||||||
|
const locale = require('locale');
|
||||||
|
const storage = require('Storage')
|
||||||
|
const SETTINGS_FILE = "notanalog.setting.json";
|
||||||
|
let settings = {
|
||||||
|
alarm: -1,
|
||||||
|
};
|
||||||
|
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||||
|
for (const key in saved_settings) {
|
||||||
|
settings[key] = saved_settings[key]
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set some important constants such as width, height and center
|
* Set some important constants such as width, height and center
|
||||||
|
@ -10,10 +22,31 @@ var cx = W/2;
|
||||||
var cy = H/2;
|
var cy = H/2;
|
||||||
var drawTimeout;
|
var drawTimeout;
|
||||||
|
|
||||||
|
var state = {
|
||||||
|
color: "#ff0000",
|
||||||
|
steps: 0,
|
||||||
|
maxSteps: 10000,
|
||||||
|
bat: 0,
|
||||||
|
has_weather: false,
|
||||||
|
temp: "-"
|
||||||
|
}
|
||||||
|
|
||||||
var chargeImg = {
|
var chargeImg = {
|
||||||
width : 32, height : 32, bpp : 1,
|
width : 32, height : 32, bpp : 1,
|
||||||
transparent : 0,
|
transparent : 0,
|
||||||
buffer : require("heatshrink").decompress(atob("AAMMAYUeAYUzAYVjAYXGAQMPxwDBj8cAYM73ADBuPwEAPg8E+gHAuFzgOAnHjAYMd40BwOPxkBwfHjEBw1j2ADBsPgBYPAAYIYBsEDFoPgg+AgPAg/AgeAhgJBgEYuf+AYM//BhBnAYBh1wKQXAAYM5wADBBQQoBAYQOCgA="))
|
buffer : E.toArrayBuffer(atob("AAAMAAAAHgAAADMAAABjAAAAxgAAD44AAB8cAAA7uAAAcfAMAODgPgDAcHMBgDjjAYAdxgGBj4wBg8cYAYZjsAGGYeABg8DgAQGAYAMAAOAHgAHAB8ADgAzgBwAYc/4AGD/4AAw4AAAOcAAAH8AAADmAAABwAAAA4AAAAMAAAAA="))
|
||||||
|
};
|
||||||
|
|
||||||
|
var alarmImg = {
|
||||||
|
width : 32, height : 32, bpp : 1,
|
||||||
|
transparent : 0,
|
||||||
|
buffer : E.toArrayBuffer(atob("AA/wAAAP8AAAD/AAAAGAAAABgAAAA8AABh/4YAd//uAH+B/gA+AHwAOAAcAHAPDgDgD4cA4A/HAcAP44HAD+OBwA/zgYAP8YGAD/GBj//xgc//84HH/+OBx//jgOP/xwDh/4cAcP8OAHg8HgA8ADwAHwD4AA//8AAD/8AAAP8AA="))
|
||||||
|
};
|
||||||
|
|
||||||
|
var stepsImg = {
|
||||||
|
width : 32, height : 32, bpp : 1,
|
||||||
|
transparent : 0,
|
||||||
|
buffer : E.toArrayBuffer(atob("AcAAAAPwAAAH8AAAB/gAAAf4AAAH/AAAD/wAAAf8AAAH/AfAB/wP4Af8H+AH/B/gB/wf4AP8P+AD+D/gAfg/4AGAP+AAPD/gAPw/4AD+P+AAfj/AAH4/wAB+H8AAPAeAAAAwAAAAPgAAAH8AAAB/AAAAfgAAAH4AAAA8AAAAOAA="))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,6 +116,23 @@ function drawBackground() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawState(){
|
||||||
|
g.setColor(state.color);
|
||||||
|
g.setFontAlign(1,0,0);
|
||||||
|
|
||||||
|
// Draw alarm
|
||||||
|
var highPrioImg = isAlarmEnabled() ? alarmImg :
|
||||||
|
Bangle.isCharging() ? chargeImg : undefined;
|
||||||
|
|
||||||
|
// As default, we draw weather if available, otherwise the steps symbol is shown.
|
||||||
|
if(!highPrioImg && state.has_weather){
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.drawString(state.temp, cx+cx/2+15, cy+cy/2+10);
|
||||||
|
} else {
|
||||||
|
var img = highPrioImg ? highPrioImg : stepsImg;
|
||||||
|
g.drawImage(img, cx+cx/2 - img.width/2, cy+cy/2 - img.height/2+5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function drawData() {
|
function drawData() {
|
||||||
g.setFontAlign(0,0,0);
|
g.setFontAlign(0,0,0);
|
||||||
|
@ -90,41 +140,23 @@ function drawData() {
|
||||||
|
|
||||||
// Set hand functions
|
// Set hand functions
|
||||||
var drawBatteryHand = g.drawRotRect.bind(g,6,12,R-38);
|
var drawBatteryHand = g.drawRotRect.bind(g,6,12,R-38);
|
||||||
var drawStepsHand = g.drawRotRect.bind(g,5,12,R-24);
|
var drawDataHand = g.drawRotRect.bind(g,5,12,R-24);
|
||||||
|
|
||||||
// Draw state
|
|
||||||
g.setColor(g.theme.fg);
|
|
||||||
if(Bangle.isCharging()){
|
|
||||||
g.drawImage(chargeImg, cx+cx/2 - chargeImg.width/2, cy+cy/2 - chargeImg.height/2+5);
|
|
||||||
} else {
|
|
||||||
dataStr = "B-JS";
|
|
||||||
try {
|
|
||||||
weather = require('weather').get();
|
|
||||||
if (weather === undefined){
|
|
||||||
dataStr = "-";
|
|
||||||
} else {
|
|
||||||
dataStr = locale.temp(Math.round(weather.temp-273.15));
|
|
||||||
}
|
|
||||||
} catch(ex) {
|
|
||||||
|
|
||||||
}
|
|
||||||
g.setFontAlign(1,0,0);
|
|
||||||
g.drawString(dataStr, cx+cx/2+15, cy+cy/2+10);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Draw battery hand
|
// Draw battery hand
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
g.setFontAlign(0,0,0);
|
g.setFontAlign(0,0,0);
|
||||||
var bat = E.getBattery();
|
drawBatteryHand(parseInt(state.bat*360/100));
|
||||||
var maxBat = 100;
|
|
||||||
drawBatteryHand(parseInt(bat*360/maxBat));
|
|
||||||
|
|
||||||
// Draw step hand and icon
|
// Draw data hand - depending on state
|
||||||
var steps = getSteps();
|
g.setColor(state.color);
|
||||||
var maxSteps = 10000;
|
if(isAlarmEnabled()){
|
||||||
var stepsColor = steps > 10000 ? "#00ff00" : "#ff0000";
|
var alrm = getAlarmMinutes();
|
||||||
g.setColor(stepsColor);
|
drawDataHand(parseInt(alrm*360/60));
|
||||||
drawStepsHand(parseInt(steps*360/maxSteps));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default are the steps
|
||||||
|
drawDataHand(parseInt(state.steps*360/state.maxSteps));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,15 +182,6 @@ function drawTime(){
|
||||||
var m2 = m < 10 ? m : m - m1*10;
|
var m2 = m < 10 ? m : m - m1*10;
|
||||||
g.drawString(m2, cx, H-posY);
|
g.drawString(m2, cx, H-posY);
|
||||||
g.drawString(m1, posX-1, cy+5);
|
g.drawString(m1, posX-1, cy+5);
|
||||||
|
|
||||||
// Connect
|
|
||||||
var rP = 24;
|
|
||||||
var w = 4;
|
|
||||||
g.setColor(1,0,0);
|
|
||||||
for(var dy=-w;dy <= w; dy += 1){
|
|
||||||
g.drawLine(cx+rP, posY+rP/2+dy+5, W-posX-rP, cy-rP);
|
|
||||||
g.drawLine(posX-2+rP, cy+rP/2+dy+5, cx-rP, H-posY+2-rP);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,28 +202,62 @@ function drawLock(){
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.fillCircle(cx, cy, 7);
|
g.fillCircle(cx, cy, 7);
|
||||||
|
|
||||||
var c = Bangle.isLocked() ? "#ff0000" : g.theme.bg;
|
var c = Bangle.isLocked() ? state.color : g.theme.bg;
|
||||||
g.setColor(c);
|
g.setColor(c);
|
||||||
g.fillCircle(cx, cy, 4);
|
g.fillCircle(cx, cy, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleState(){
|
||||||
|
// Set battery
|
||||||
|
state.bat = E.getBattery();
|
||||||
|
|
||||||
|
// Set steps
|
||||||
|
state.steps = getSteps();
|
||||||
|
state.maxSteps = 10000;
|
||||||
|
|
||||||
|
// Set theme color
|
||||||
|
state.color = isAlarmEnabled() ? "#FF6A00" :
|
||||||
|
state.steps > state.maxSteps ? "#00ff00" :
|
||||||
|
"#ff0000";
|
||||||
|
|
||||||
|
// Set weather
|
||||||
|
state.has_weather = true;
|
||||||
|
try {
|
||||||
|
weather = require('weather').get();
|
||||||
|
if (weather === undefined){
|
||||||
|
state.temp = "-";
|
||||||
|
} else {
|
||||||
|
state.temp = locale.temp(Math.round(weather.temp-273.15));
|
||||||
|
}
|
||||||
|
} catch(ex) {
|
||||||
|
state.has_weather = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function draw(){
|
function draw(){
|
||||||
// Clear watch face
|
// Execute handlers
|
||||||
g.reset();
|
handleState();
|
||||||
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
handleAlarm();
|
||||||
|
|
||||||
// Draw again
|
// Clear watch face
|
||||||
g.setColor(1,1,1);
|
g.reset();
|
||||||
|
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
||||||
|
|
||||||
drawBackground();
|
// Draw again
|
||||||
drawLock();
|
g.setColor(1,1,1);
|
||||||
drawDate();
|
|
||||||
drawData();
|
|
||||||
drawTime();
|
|
||||||
|
|
||||||
// Queue draw in one minute
|
drawBackground();
|
||||||
queueDraw();
|
drawLock();
|
||||||
|
drawDate();
|
||||||
|
drawState();
|
||||||
|
drawData();
|
||||||
|
drawTime();
|
||||||
|
|
||||||
|
// Queue draw in one minute
|
||||||
|
queueDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -208,12 +265,12 @@ function draw(){
|
||||||
* Listeners
|
* Listeners
|
||||||
*/
|
*/
|
||||||
Bangle.on('lcdPower',on=>{
|
Bangle.on('lcdPower',on=>{
|
||||||
if (on) {
|
if (on) {
|
||||||
draw();
|
draw();
|
||||||
} else { // stop draw timer
|
} else { // stop draw timer
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Bangle.on('charging',function(charging) {
|
Bangle.on('charging',function(charging) {
|
||||||
|
@ -224,6 +281,25 @@ Bangle.on('lock', function(isLocked) {
|
||||||
drawLock();
|
drawLock();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Bangle.on('touch', function(btn, e){
|
||||||
|
var upper = parseInt(g.getHeight() * 0.2);
|
||||||
|
var lower = g.getHeight() - upper;
|
||||||
|
|
||||||
|
var is_upper = e.y < upper;
|
||||||
|
var is_lower = e.y > lower;
|
||||||
|
|
||||||
|
if(is_upper){
|
||||||
|
feedback();
|
||||||
|
increaseAlarm();
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_lower){
|
||||||
|
feedback();
|
||||||
|
decreaseAlarm();
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -238,6 +314,74 @@ function queueDraw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle alarm
|
||||||
|
*/
|
||||||
|
function getCurrentTimeInMinutes(){
|
||||||
|
return Math.floor(Date.now() / (1000*60));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAlarmEnabled(){
|
||||||
|
return settings.alarm >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAlarmMinutes(){
|
||||||
|
var currentTime = getCurrentTimeInMinutes();
|
||||||
|
return settings.alarm - currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAlarm(){
|
||||||
|
if(!isAlarmEnabled()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getAlarmMinutes() > 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alarm
|
||||||
|
var t = 300;
|
||||||
|
Bangle.buzz(t, 1)
|
||||||
|
.then(() => new Promise(resolve => setTimeout(resolve, t)))
|
||||||
|
.then(() => Bangle.buzz(t, 1))
|
||||||
|
.then(() => new Promise(resolve => setTimeout(resolve, t)))
|
||||||
|
.then(() => Bangle.buzz(t, 1))
|
||||||
|
.then(() => new Promise(resolve => setTimeout(resolve, t)))
|
||||||
|
.then(() => Bangle.buzz(t, 1))
|
||||||
|
.then(() => new Promise(resolve => setTimeout(resolve, 5E3)))
|
||||||
|
.then(() => {
|
||||||
|
// Update alarm state to disabled
|
||||||
|
settings.alarm = -1;
|
||||||
|
storage.writeJSON(SETTINGS_FILE, settings);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function increaseAlarm(){
|
||||||
|
if(isAlarmEnabled()){
|
||||||
|
settings.alarm += 5;
|
||||||
|
} else {
|
||||||
|
settings.alarm = getCurrentTimeInMinutes() + 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.writeJSON(SETTINGS_FILE, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function decreaseAlarm(){
|
||||||
|
if(isAlarmEnabled() && (settings.alarm-5 > getCurrentTimeInMinutes())){
|
||||||
|
settings.alarm -= 5;
|
||||||
|
} else {
|
||||||
|
settings.alarm = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.writeJSON(SETTINGS_FILE, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
function feedback(){
|
||||||
|
Bangle.buzz(40, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lets start widgets, listen for btn etc.
|
* Lets start widgets, listen for btn etc.
|
||||||
*/
|
*/
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
Loading…
Reference in New Issue