-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2964 from tinxx/master
add edgeclk
- Loading branch information
Showing
10 changed files
with
464 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
0.01: Initial release. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Edge Clock | ||
|
||
![Screenshot](screenshot.png) | ||
![Screenshot](screenshot2.png) | ||
![Screenshot](screenshot3.png) | ||
|
||
Tinxx presents you a clock with as many straight edges as possible to allow for a crisp look and perfect readability. | ||
It comes with a custom font to display weekday, date, time, and steps. Also displays battery percentage while charging. | ||
There are three progress bars that indicate day of the week, time of the day, and daily step goal. | ||
The watch face is monochrome and allows for applying your favorite color scheme. | ||
|
||
The appearance is highly configurable. In the settings menu you can: | ||
- De-/activate a buzz when the charger is connected while the watch face is active. | ||
- Decide if month or day should be displayed first. | ||
- Switch between 24h and 12h clock. | ||
- Hide or display seconds.* | ||
- Show AM/PM in place of the seconds. | ||
- Set the daily step goal. | ||
- En- or disable the individual progress bars. | ||
- Set if your week should start with Monday or Sunday (for week progress bar). | ||
|
||
*) Hiding seconds should further reduce power consumption as the draw interval is prolonged as well. | ||
|
||
The clock implements Fast Loading for faster switching to and fro. |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,306 @@ | ||
{ | ||
/* Configuration | ||
------------------------------------------------------------------------------*/ | ||
|
||
const settings = Object.assign({ | ||
buzzOnCharge: true, | ||
monthFirst: true, | ||
twentyFourH: true, | ||
showAmPm: false, | ||
showSeconds: true, | ||
stepGoal: 10000, | ||
stepBar: true, | ||
weekBar: true, | ||
mondayFirst: true, | ||
dayBar: true, | ||
}, require('Storage').readJSON('edgeclk.settings.json', true) || {}); | ||
|
||
|
||
/* Runtime Variables | ||
------------------------------------------------------------------------------*/ | ||
|
||
let startTimeout; | ||
let drawInterval; | ||
|
||
let lcdPower = true; | ||
let charging = Bangle.isCharging(); | ||
|
||
const font = atob('AA////wDwDwDwD////AAAAAAAAwAwA////AAAAAAAA8/8/wzwzwzwz/z/zAAAA4H4HwDxjxjxj////AAAA/w/wAwAwD/D/AwAwAAAA/j/jxjxjxjxjx/x/AAAA////xjxjxjxjx/x/AAAAwAwAwAwAwA////AAAAAA////xjxjxjxj////AAAA/j/jxjxjxjxj////AAAAAAAAAAMMMMAAAAAAAAAAAAAAABMOMMAAAAAAAAAABgBgDwDwGYGYMMMMAAAAAAGYGYGYGYGYGYAAAAAAMMMMGYGYDwDwBgBgAAAA4A4Ax7x7xgxg/g/gAAAA//gBv9shshv9gF/7AAAA////wwwwwwww////AAAA////xjxjxjxj////AAAA////wDwDwDwD4H4HAAAA////wDwDwD4Hf+P8AAAA////xjxjxjxjwDwDAAAA////xgxgxgxgwAwAAAAA////wDwDwzwz4/4/AAAA////BgBgBgBg////AAAAAAwDwD////wDwDAAAAAAAAwPwPwDwD////AAAAAA////DwH4OccO4HwDAAAA////ADADADADADADAAAA////YAGAGAYA////AAAA////MADAAwAM////AAAA////wDwDwDwD////AAAA////xgxgxgxg/g/gAAAA/+/+wGwOwOwO////AAAA////xgxgxwx8/v/jAAAA/j/jxjxjxjxjx/x/AAAAwAwAwA////wAwAwAAAAA////ADADADAD////AAAA/w/8AOAHAHAO/8/wAAAA////AGAYAYAG////AAAAwD4PecH4H4ec4PwDAAAAwA4AeBH/H/eA4AwAAAAAwPwfw7xzzj3D+D8DAAAAAAAAAAAA////wDAAAAAAAAAABgBgBgBgAAAAAAAAAAwD////AAAAAAAAAAAAAwDwPA8A8APADwAwAAAAAAAAAAAAAAAAAAAAAA'); | ||
|
||
const iconSize = [19, 26]; | ||
const plugIcon = atob('ExoBBxwA44AccAOOAHHAf/8P/+H//D//h//w//4P/4H/8B/8Af8ABwAA4AAcAAOAAHAADgABwAA4AAcAAOAAHAA='); | ||
const stepIcon1 = atob('ExoBAfAAPgAHwAD4AB8AAAAB/wD/8D//Bn9wz+cZ/HM/hmfwAP4AAAAD+AD/gBxwB48A4OA8HgcBwcAcOAOGADA='); | ||
const stepIcon2 = atob('ExoBAfAAPgMHwfD4dx8ccAcH/8B/8Af8AH8AD+AB/AA/gAfwAP4AAAAD+AD/gBxwB48A4OA8HgcBwcAcOAOGADA='); | ||
|
||
|
||
/* Draw Functions | ||
------------------------------------------------------------------------------*/ | ||
|
||
const drawAll = function () { | ||
const date = new Date(); | ||
|
||
drawDate(date); | ||
if (settings.showSeconds) drawSecs(date); | ||
drawTime(date); | ||
drawLower(); | ||
}; | ||
|
||
const drawLower = function (stepsOnlyCount) { | ||
if (charging) { | ||
drawCharge(); | ||
} else { | ||
drawSteps(stepsOnlyCount); | ||
} | ||
}; | ||
|
||
const drawDate = function (date) { | ||
const top = 30; | ||
g.reset(); | ||
|
||
// weekday | ||
g.setFontCustom(font, 48, 10, 512 + 12); // double size (1<<9) | ||
g.setFontAlign(-1, -1); // left top | ||
g.drawString(date.toString().slice(0,3).toUpperCase(), 0, top + 12, true); | ||
|
||
// date | ||
g.setFontAlign(1, -1); // right top | ||
// Note: to save space first and last two lines of ASCII are left out. | ||
// That is why '-' is assigned to '\' and ' ' (space) to '_'. | ||
if (settings.monthFirst) { | ||
g.drawString((date.getMonth()+1).toString().padStart(2, '_') | ||
+ '\\' | ||
+ date.getDate().toString().padStart(2, 0), | ||
g.getWidth(), top + 12, true); | ||
} else { | ||
g.drawString('_' | ||
+ date.getDate().toString().padStart(2, 0) | ||
+ '\\' | ||
+ (date.getMonth()+1).toString(), | ||
g.getWidth(), top + 12, true); | ||
} | ||
|
||
// line/progress bar | ||
if (settings.weekBar) { | ||
let weekday = date.getDay(); | ||
if (settings.mondayFirst) { | ||
if (weekday === 0) { weekday = 7; } | ||
} else { | ||
weekday += 1; | ||
} | ||
drawBar(top, weekday/7); | ||
} else { | ||
drawLine(top); | ||
} | ||
}; | ||
|
||
const drawTime = function (date) { | ||
const top = 72; | ||
g.reset(); | ||
|
||
const h = date.getHours(); | ||
g.setFontCustom(font, 48, 10, 1024 + 12); // triple size (2<<9) | ||
g.setFontAlign(-1, -1); // left top | ||
g.drawString((settings.twentyFourH ? h : (h % 12 || 12)).toString().padStart(2, 0), | ||
0, top+12, true); | ||
g.setFontAlign(0, -1); // center top | ||
g.drawString(':', g.getWidth()/2, top+12, false); | ||
const m = date.getMinutes(); | ||
g.setFontAlign(1, -1); // right top | ||
g.drawString(m.toString().padStart(2, 0), | ||
g.getWidth(), top+12, true); | ||
|
||
if (settings.showAmPm) { | ||
g.setFontCustom(font, 48, 10, 512 + 12); // double size (1<<9) | ||
g.setFontAlign(1, 1); // right bottom | ||
g.drawString(h < 12 ? 'AM' : 'PM', g.getWidth(), g.getHeight() - 1, true); | ||
} | ||
|
||
if (settings.dayBar) { | ||
drawBar(top, (h*60+m)/1440); | ||
} else { | ||
drawLine(top); | ||
} | ||
}; | ||
|
||
const drawSecs = function (date) { | ||
g.reset(); | ||
g.setFontCustom(font, 48, 10, 512 + 12); // double size (1<<9) | ||
g.setFontAlign(1, 1); // right bottom | ||
g.drawString(date.getSeconds().toString().padStart(2, 0), g.getWidth(), g.getHeight() - 1, true); | ||
}; | ||
|
||
const drawSteps = function (onlyCount) { | ||
g.reset(); | ||
g.setFontCustom(font, 48, 10, 512 + 12); // double size (1<<9) | ||
g.setFontAlign(-1, 1); // left bottom | ||
|
||
const steps = Bangle.getHealthStatus('day').steps; | ||
g.drawString(steps.toString().padEnd(5, '_'), iconSize[0] + 6, g.getHeight() - 1, true); | ||
|
||
if (onlyCount === true) { | ||
return; | ||
} | ||
|
||
const progress = steps / settings.stepGoal; | ||
if (settings.stepBar) { | ||
drawBar(g.getHeight() - 38, progress); | ||
} else { | ||
drawLine(g.getHeight() - 38); | ||
} | ||
|
||
// icon | ||
if (progress < 1) { | ||
g.drawImage(stepIcon1, 0, g.getHeight() - iconSize[1]); | ||
} else { | ||
g.drawImage(stepIcon2, 0, g.getHeight() - iconSize[1]); | ||
} | ||
}; | ||
|
||
const drawCharge = function () { | ||
g.reset(); | ||
g.setFontCustom(font, 48, 10, 512 + 12); // double size (1<<9) | ||
g.setFontAlign(-1, 1); // left bottom | ||
|
||
const charge = E.getBattery(); | ||
g.drawString(charge.toString().padEnd(5, '_'), iconSize[0] + 6, g.getHeight() - 1, true); | ||
|
||
drawBar(g.getHeight() - 38, charge / 100); | ||
g.drawImage(plugIcon, 0, g.getHeight() - 26); | ||
}; | ||
|
||
const drawBar = function (top, progress) { | ||
g.drawRect(0, top, g.getWidth()-1, top + 5); | ||
g.drawRect(1, top+1, g.getWidth()-2, top + 4); | ||
const barLen = progress > 1 ? g.getWidth() : g.getWidth() * progress; | ||
g.drawLine(2, top+2, barLen, top + 2); | ||
g.drawLine(2, top+3, barLen, top + 3); | ||
}; | ||
|
||
const drawLine = function (top) { | ||
const width = g.getWidth(); | ||
g.drawLine(0, top+2, width, top + 2); | ||
g.drawLine(0, top+3, width, top + 3); | ||
}; | ||
|
||
|
||
/* Event Handlers | ||
------------------------------------------------------------------------------*/ | ||
|
||
const onSecondInterval = function () { | ||
const date = new Date(); | ||
drawSecs(date); | ||
if (date.getSeconds() === 0) { | ||
onMinuteInterval(); | ||
} | ||
}; | ||
|
||
const onMinuteInterval = function () { | ||
const date = new Date(); | ||
drawTime(date); | ||
drawLower(true); | ||
}; | ||
|
||
const onMinuteIntervalStarter = function () { | ||
drawInterval = setInterval(onMinuteInterval, 60000); | ||
startTimeout = null; | ||
onMinuteInterval(); | ||
}; | ||
|
||
const onLcdPower = function (on) { | ||
lcdPower = on; | ||
if (on) { | ||
drawAll(); | ||
startTimers(); | ||
} else { | ||
stopTimers(); | ||
} | ||
}; | ||
|
||
const onMidnight = function () { | ||
if (!lcdPower) return; | ||
drawDate(new Date()); | ||
// Lower part (steps/charge) will be updated every minute. | ||
// However, to save power while on battery only step count will get updated. | ||
// This will update icon and progress bar as well: | ||
if (!charging) drawSteps(); | ||
}; | ||
|
||
const onHealth = function () { | ||
if (!lcdPower || charging) return; | ||
// This will update progress bar and icon: | ||
drawSteps(); | ||
}; | ||
|
||
const onLock = function (locked) { | ||
if (locked) return; | ||
drawLower(); | ||
}; | ||
|
||
const onCharging = function (isCharging) { | ||
charging = isCharging; | ||
if (isCharging && settings.buzzOnCharge) Bangle.buzz(); | ||
if (!lcdPower) return; | ||
drawLower(); | ||
}; | ||
|
||
|
||
/* Lifecycle Functions | ||
------------------------------------------------------------------------------*/ | ||
|
||
const registerEvents = function () { | ||
// This is for original Bangle.js; version two has always-on display: | ||
Bangle.on('lcdPower', onLcdPower); | ||
|
||
// Midnight event is triggered qhen health data is reset and a new day begins: | ||
Bangle.on('midnight', onMidnight); | ||
|
||
// Health data is published via 10 mins interval: | ||
Bangle.on('health', onHealth); | ||
|
||
// Lock event signals screen (un)lock: | ||
Bangle.on('lock', onLock); | ||
|
||
// Charging event signals when charging status changes: | ||
Bangle.on('charging', onCharging); | ||
}; | ||
|
||
const deregisterEvents = function () { | ||
Bangle.removeListener('lcdPower', onLcdPower); | ||
Bangle.removeListener('midnight', onMidnight); | ||
Bangle.removeListener('health', onHealth); | ||
Bangle.removeListener('lock', onLock); | ||
Bangle.removeListener('charging', onCharging); | ||
}; | ||
|
||
const startTimers = function () { | ||
if (drawInterval) return; | ||
if (settings.showSeconds) { | ||
drawInterval = setInterval( onSecondInterval, 1000); | ||
} else { | ||
startTimeout = setTimeout(onMinuteIntervalStarter, (60 - new Date().getSeconds()) * 1000); | ||
} | ||
}; | ||
|
||
const stopTimers = function () { | ||
if (startTimeout) clearTimeout(startTimeout); | ||
if (!drawInterval) return; | ||
clearInterval(drawInterval); | ||
drawInterval = null; | ||
}; | ||
|
||
|
||
/* Startup Process | ||
------------------------------------------------------------------------------*/ | ||
|
||
g.clear(); | ||
drawAll(); | ||
startTimers(); | ||
registerEvents(); | ||
|
||
Bangle.setUI({mode: 'clock', remove: function() { | ||
stopTimers(); | ||
deregisterEvents(); | ||
}}); | ||
Bangle.loadWidgets(); | ||
Bangle.drawWidgets(); | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"id": "edgeclk", | ||
"name": "Edge Clock", | ||
"shortName": "Edge Clock", | ||
"version": "0.01", | ||
"description": "Crisp clock with perfect readability.", | ||
"readme": "README.md", | ||
"icon": "app.png", | ||
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot2.png"}, {"url":"screenshot3.png"}], | ||
"type": "clock", | ||
"tags": "clock", | ||
"supports": ["BANGLEJS2"], | ||
"allow_emulator": true, | ||
"storage": [ | ||
{"name":"edgeclk.app.js", "url": "app.js"}, | ||
{"name":"edgeclk.settings.js", "url": "settings.js"}, | ||
{"name":"edgeclk.img", "url": "app-icon.js", "evaluate": true} | ||
], | ||
"data": [{"name":"edgeclk.settings.json"}] | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.