Merge branch 'master' into icon-notifs
|
@ -3,3 +3,4 @@
|
|||
0.03: Do not alarm while charging
|
||||
0.04: Obey system quiet mode
|
||||
0.05: Battery optimisation, add the pause option, bug fixes
|
||||
0.06: Add a temperature threshold to detect (and not alert) if the BJS isn't worn. Better support for the peoples using the app at night
|
||||
|
|
|
@ -11,4 +11,5 @@ Different settings can be personalized:
|
|||
- Dismiss delay: Delay added before the next alert if the alert is dismissed. From 5 to 60 min
|
||||
- Pause delay: Same as Dismiss delay but longer (usefull for meetings and such). From 30 to 240 min
|
||||
- Min steps: Minimal amount of steps to count as an activity
|
||||
- Temp Threshold: Temperature threshold to determine if the watch is worn
|
||||
|
||||
|
|
|
@ -2,17 +2,17 @@ function run() {
|
|||
if (isNotWorn()) return;
|
||||
let now = new Date();
|
||||
let h = now.getHours();
|
||||
let health = Bangle.getHealthStatus("day");
|
||||
|
||||
if (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour) {
|
||||
if (isDuringAlertHours(h)) {
|
||||
let health = Bangle.getHealthStatus("day");
|
||||
if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed
|
||||
|| health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch
|
||||
activityreminder_data.stepsOnDate = health.steps;
|
||||
activityreminder_data.stepsDate = now;
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
/* todo in a futur release
|
||||
add settimer to trigger like 10 secs after the stepsDate + minSteps
|
||||
cancel all other timers of this app
|
||||
Add settimer to trigger like 30 secs after going in this part cause the person have been walking
|
||||
(pass some argument to run() to handle long walks and not triggering so often)
|
||||
*/
|
||||
}
|
||||
|
||||
|
@ -24,22 +24,42 @@ function run() {
|
|||
}
|
||||
|
||||
function isNotWorn() {
|
||||
// todo in a futur release check temperature and mouvement in a futur release
|
||||
return Bangle.isCharging();
|
||||
return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature());
|
||||
}
|
||||
|
||||
function isDuringAlertHours(h) {
|
||||
if(activityreminder_settings.startHour < activityreminder_settings.endHour){ // not passing through midnight
|
||||
return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour)
|
||||
} else{ // passing through midnight
|
||||
return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour)
|
||||
}
|
||||
}
|
||||
|
||||
Bangle.on('midnight', function() {
|
||||
/*
|
||||
Usefull trick to have the app working smothly for people using it at night
|
||||
*/
|
||||
let now = new Date();
|
||||
let h = now.getHours();
|
||||
if (activityreminder_settings.enabled && isDuringAlertHours(h)){
|
||||
// updating only the steps and keeping the original stepsDate on purpose
|
||||
activityreminder_data.stepsOnDate = 0;
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
}
|
||||
});
|
||||
|
||||
const activityreminder = require("activityreminder");
|
||||
const activityreminder_settings = activityreminder.loadSettings();
|
||||
if (activityreminder_settings.enabled) {
|
||||
const activityreminder_data = activityreminder.loadData();
|
||||
if(activityreminder_data.firstLoad){
|
||||
activityreminder_data.firstLoad =false;
|
||||
activityreminder_data.firstLoad = false;
|
||||
activityreminder.saveData(activityreminder_data);
|
||||
}
|
||||
setInterval(run, 60000);
|
||||
/* todo in a futur release
|
||||
increase setInterval time to something that is still sensible (5 mins ?)
|
||||
add settimer to trigger like 10 secs after the stepsDate + minSteps
|
||||
cancel all other timers of this app
|
||||
when we added a settimer
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ exports.loadSettings = function () {
|
|||
maxInnactivityMin: 30,
|
||||
dismissDelayMin: 15,
|
||||
pauseDelayMin: 120,
|
||||
minSteps: 50
|
||||
minSteps: 50,
|
||||
tempThreshold: 27
|
||||
}, storage.readJSON("activityreminder.s.json", true) || {});
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Activity Reminder",
|
||||
"shortName":"Activity Reminder",
|
||||
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
||||
"version":"0.05",
|
||||
"version":"0.06",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "tool,activity",
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
},
|
||||
'Pause delay': {
|
||||
value: settings.pauseDelayMin,
|
||||
min: 30, max: 240,
|
||||
min: 30, max: 240, step: 5,
|
||||
onchange: v => {
|
||||
settings.pauseDelayMin = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
|
@ -66,11 +66,20 @@
|
|||
},
|
||||
'Min steps': {
|
||||
value: settings.minSteps,
|
||||
min: 10, max: 500,
|
||||
min: 10, max: 500, step: 10,
|
||||
onchange: v => {
|
||||
settings.minSteps = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
},
|
||||
'Temp Threshold': {
|
||||
value: settings.tempThreshold,
|
||||
min: 20, max: 40, step: 0.5,
|
||||
format: v => v + "°C",
|
||||
onchange: v => {
|
||||
settings.tempThreshold = v;
|
||||
activityreminder.writeSettings(settings);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
|
@ -29,3 +29,4 @@
|
|||
0.27: New UI!
|
||||
0.28: Fix bug with alarms not firing when configured to fire only once
|
||||
0.29: Fix wrong 'dow' handling in new timer if first day of week is Monday
|
||||
0.30: Fix "Enable All"
|
||||
|
|
|
@ -86,7 +86,8 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
|||
const menu = {
|
||||
"": { "title": isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm" },
|
||||
"< Back": () => {
|
||||
saveAlarm(alarm, alarmIndex, time);
|
||||
prepareAlarmForSave(alarm, alarmIndex, time);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
},
|
||||
/*LANG*/"Hour": {
|
||||
|
@ -144,7 +145,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
|||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function saveAlarm(alarm, alarmIndex, time) {
|
||||
function prepareAlarmForSave(alarm, alarmIndex, time) {
|
||||
alarm.t = require("time_utils").encodeTime(time);
|
||||
alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0;
|
||||
|
||||
|
@ -153,8 +154,6 @@ function saveAlarm(alarm, alarmIndex, time) {
|
|||
} else {
|
||||
alarms[alarmIndex] = alarm;
|
||||
}
|
||||
|
||||
saveAndReload();
|
||||
}
|
||||
|
||||
function saveAndReload() {
|
||||
|
@ -251,7 +250,8 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
|||
const menu = {
|
||||
"": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" },
|
||||
"< Back": () => {
|
||||
saveTimer(timer, timerIndex, time);
|
||||
prepareTimerForSave(timer, timerIndex, time);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
},
|
||||
/*LANG*/"Hours": {
|
||||
|
@ -293,7 +293,7 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
|||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function saveTimer(timer, timerIndex, time) {
|
||||
function prepareTimerForSave(timer, timerIndex, time) {
|
||||
timer.timer = require("time_utils").encodeTime(time);
|
||||
timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer;
|
||||
timer.last = 0;
|
||||
|
@ -303,8 +303,6 @@ function saveTimer(timer, timerIndex, time) {
|
|||
} else {
|
||||
alarms[timerIndex] = timer;
|
||||
}
|
||||
|
||||
saveAndReload();
|
||||
}
|
||||
|
||||
function showAdvancedMenu() {
|
||||
|
@ -327,7 +325,16 @@ function enableAll(on) {
|
|||
} else {
|
||||
E.showPrompt(/*LANG*/"Are you sure?", { title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All" }).then((confirm) => {
|
||||
if (confirm) {
|
||||
alarms.forEach(alarm => alarm.on = on);
|
||||
alarms.forEach((alarm, i) => {
|
||||
alarm.on = on;
|
||||
if (on) {
|
||||
if (alarm.timer) {
|
||||
prepareTimerForSave(alarm, i, require("time_utils").decodeTime(alarm.timer))
|
||||
} else {
|
||||
prepareAlarmForSave(alarm, i, require("time_utils").decodeTime(alarm.t))
|
||||
}
|
||||
}
|
||||
});
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
} else {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "alarm",
|
||||
"name": "Alarms & Timers",
|
||||
"shortName": "Alarms",
|
||||
"version": "0.29",
|
||||
"version": "0.30",
|
||||
"description": "Set alarms and timers on your Bangle",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm,widget",
|
||||
|
|
|
@ -9,3 +9,5 @@
|
|||
0.09: Fix time/date disappearing after fullscreen notification
|
||||
0.10: Use ClockFace library
|
||||
0.11: Use ClockFace.is12Hour
|
||||
0.12: Add settings to hide date,widgets
|
||||
0.13: Add font setting
|
||||
|
|
|
@ -4,3 +4,7 @@ A simple digital clock showing seconds as a horizontal bar.
|
|||
| 24hr style | 12hr style |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
## Settings
|
||||
* `Show date`: display date at the bottom of screen
|
||||
* `Font`: choose between bitmap or vector fonts
|
|
@ -51,24 +51,25 @@ function dateText(date) {
|
|||
const ClockFace = require("ClockFace"),
|
||||
clock = new ClockFace({
|
||||
precision:1,
|
||||
settingsFile:'barclock.settings.json',
|
||||
init: function() {
|
||||
const Layout = require("Layout");
|
||||
this.layout = new Layout({
|
||||
type: "v", c: [
|
||||
{
|
||||
type: "h", c: [
|
||||
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col:g.theme.fg, bgCol: g.theme.bg}, // size updated below
|
||||
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col:g.theme.fg, bgCol: g.theme.bg}, // updated below
|
||||
{id: "ampm", label: " ", type: "txt", font: "6x8:2", col:g.theme.fg, bgCol: g.theme.bg},
|
||||
],
|
||||
},
|
||||
{id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
||||
{height: 40},
|
||||
{id: "date", type: "txt", font: "10%", valign: 1},
|
||||
this.showDate ? {height: 40} : {},
|
||||
this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {},
|
||||
],
|
||||
}, {lazy: true});
|
||||
// adjustments based on screen size and whether we display am/pm
|
||||
let thickness; // bar thickness, same as time font "pixel block" size
|
||||
if (this.is12Hour) {
|
||||
if (this.is12Hour && locale.hasMeridian) {
|
||||
// Maximum font size = (<screen width> - <ampm: 2chars * (2*6)px>) / (5chars * 6px)
|
||||
thickness = Math.floor((Bangle.appRect.w-24)/(5*6));
|
||||
} else {
|
||||
|
@ -76,13 +77,23 @@ const ClockFace = require("ClockFace"),
|
|||
thickness = Math.floor(Bangle.appRect.w/(5*6));
|
||||
}
|
||||
this.layout.bar.height = thickness+1;
|
||||
this.layout.time.font = "6x8:"+thickness;
|
||||
if (this.font===1) { // vector
|
||||
const B2 = process.env.HWVERSION>1;
|
||||
if (this.is12Hour && locale.hasMeridian) {
|
||||
this.layout.time.font = "Vector:"+(B2 ? 50 : 60);
|
||||
this.layout.ampm.font = "Vector:"+(B2 ? 20 : 40);
|
||||
} else {
|
||||
this.layout.time.font = "Vector:"+(B2 ? 60 : 80);
|
||||
}
|
||||
} else {
|
||||
this.layout.time.font = "6x8:"+thickness;
|
||||
}
|
||||
this.layout.update();
|
||||
},
|
||||
update: function(date, c) {
|
||||
if (c.m) this.layout.time.label = timeText(date);
|
||||
if (c.h) this.layout.ampm.label = ampmText(date);
|
||||
if (c.d) this.layout.date.label = dateText(date);
|
||||
if (c.d && this.showDate) this.layout.date.label = dateText(date);
|
||||
const SECONDS_PER_MINUTE = 60;
|
||||
if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
|
||||
this.layout.render();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "barclock",
|
||||
"name": "Bar Clock",
|
||||
"version": "0.11",
|
||||
"version": "0.13",
|
||||
"description": "A simple digital clock showing seconds as a bar",
|
||||
"icon": "clock-bar.png",
|
||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
||||
|
@ -12,6 +12,10 @@
|
|||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"barclock.app.js","url":"clock-bar.js"},
|
||||
{"name":"barclock.settings.js","url":"settings.js"},
|
||||
{"name":"barclock.img","url":"clock-bar-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [
|
||||
{"name":"barclock.settings.json"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
(function(back) {
|
||||
let s = require('Storage').readJSON("barclock.settings.json", true) || {};
|
||||
|
||||
function saver(key) {
|
||||
return value => {
|
||||
s[key] = value;
|
||||
require('Storage').writeJSON("barclock.settings.json", s);
|
||||
}
|
||||
}
|
||||
|
||||
const fonts = [/*LANG*/"Bitmap",/*LANG*/"Vector"];
|
||||
const menu = {
|
||||
"": {"title": /*LANG*/"Bar Clock"},
|
||||
/*LANG*/"< Back": back,
|
||||
/*LANG*/"Show date": require("ClockFace_menu").showDate(s.showDate, saver('showDate')),
|
||||
/*LANG*/"Load widgets": require("ClockFace_menu").loadWidgets(s.loadWidgets, saver('loadWidgets')),
|
||||
/*LANG*/"Font": {
|
||||
value: s.font|0,
|
||||
min:0,max:1,wrap:true,
|
||||
format:v=>fonts[v],
|
||||
onchange:saver('font'),
|
||||
},
|
||||
};
|
||||
|
||||
E.showMenu(menu);
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
0.01: Please forgive me
|
||||
0.02: Now tells time!
|
||||
0.03: Interaction
|
||||
0.04: Shows day of week
|
||||
0.05: Shows day of month
|
||||
0.06: Updates every 5 minutes when locked, or when unlock occurs. Also shows nr of steps.
|
||||
0.07: Step count resets at midnight
|
||||
0.08: Step count stored in memory to survive reloads. Now shows step count daily and since last reboot.
|
||||
0.09: NOW it really should reset daily (instead of every other day...)
|
|
@ -0,0 +1,23 @@
|
|||
# Barcode clockwatchface
|
||||
|
||||
A scannable EAN-8 compatible clockwatchface for your Bangle 2
|
||||
|
||||
The format of the bars are
|
||||
|
||||
`||HHmm||MMwc||`
|
||||
|
||||
* Left section: HHmm
|
||||
* H: Hours
|
||||
* m: Minutes
|
||||
* Right section: MM9c
|
||||
* M: Day of month
|
||||
* w: Day of week
|
||||
* c: Calculated EAN-8 digit checksum
|
||||
|
||||
Apart from that
|
||||
|
||||
* The upper left section displays total number of steps per day
|
||||
* The upper right section displays total number of steps from last boot ("stepuptime")
|
||||
* The face updates every 5 minutes or on demant by pressing the hardware button
|
||||
|
||||
This clockwathface is aware of theme choice, so it will adapt to Light/Dark themes.
|
|
@ -0,0 +1,428 @@
|
|||
/* Sizes */
|
||||
let checkBarWidth = 10;
|
||||
let checkBarHeight = 140;
|
||||
|
||||
let digitBarWidth = 14;
|
||||
let digitBarHeight = 100;
|
||||
|
||||
let textBarWidth = 56;
|
||||
let textBarHeight = 20;
|
||||
|
||||
let textWidth = 14;
|
||||
let textHeight = 20;
|
||||
|
||||
/* Offsets */
|
||||
var startOffsetX = 17;
|
||||
var startOffsetY = 30;
|
||||
|
||||
let startBarOffsetX = startOffsetX;
|
||||
let startBarOffsetY = startOffsetY;
|
||||
|
||||
let upperTextBarLeftOffsetX = startBarOffsetX + checkBarWidth;
|
||||
let upperTextBarLeftOffsetY = startOffsetY;
|
||||
|
||||
let midBarOffsetX = upperTextBarLeftOffsetX + textBarWidth;
|
||||
let midBarOffsetY = startOffsetY;
|
||||
|
||||
let upperTextBarRightOffsetX = midBarOffsetX + checkBarWidth;
|
||||
let upperTextBarRightOffsetY = startOffsetY;
|
||||
|
||||
let endBarOffsetX = upperTextBarRightOffsetX + textBarWidth;
|
||||
let endBarOffsetY = startOffsetY;
|
||||
|
||||
let leftBarsStartX = startBarOffsetX + checkBarWidth;
|
||||
let leftBarsStartY = upperTextBarLeftOffsetY + textBarHeight;
|
||||
|
||||
let rightBarsStartX = midBarOffsetX + checkBarWidth;
|
||||
let rightBarsStartY = upperTextBarRightOffsetY + textBarHeight;
|
||||
|
||||
/* Utilities */
|
||||
let stepCount = require("Storage").readJSON("stepCount",1);
|
||||
if(stepCount === undefined) stepCount = 0;
|
||||
let intCaster = num => Number(num);
|
||||
|
||||
var drawTimeout;
|
||||
|
||||
function renderWatch(l) {
|
||||
g.setFont("4x6",2);
|
||||
|
||||
// work out how to display the current time
|
||||
|
||||
var d = new Date();
|
||||
var h = d.getHours(), m = d.getMinutes();
|
||||
var time = h + ":" + ("0"+m).substr(-2);
|
||||
//var month = ("0" + (d.getMonth()+1)).slice(-2);
|
||||
var dayOfMonth = ('0' + d.getDate()).slice(-2);
|
||||
var dayOfWeek = d.getDay() || 7;
|
||||
var concatTime = ("0"+h).substr(-2) + ("0"+m).substr(-2) + dayOfMonth + dayOfWeek;
|
||||
|
||||
const chars = String(concatTime).split("").map((concatTime) => {
|
||||
return Number(concatTime);
|
||||
});
|
||||
const checkSum = calculateChecksum(chars);
|
||||
concatTime += checkSum;
|
||||
|
||||
drawCheckBar(startBarOffsetX, startBarOffsetY);
|
||||
|
||||
drawLDigit(chars[0], 0, leftBarsStartY);
|
||||
drawLDigit(chars[1], 1, leftBarsStartY);
|
||||
drawLDigit(chars[2], 2, leftBarsStartY);
|
||||
drawLDigit(chars[3], 3, leftBarsStartY);
|
||||
|
||||
g.drawString(getStepCount(), startOffsetX + checkBarWidth + 3, startOffsetY + 4);
|
||||
g.drawString(concatTime.substring(0,4), startOffsetX + checkBarWidth + 3, startOffsetY + textBarHeight + digitBarHeight + 6);
|
||||
|
||||
drawCheckBar(midBarOffsetX, midBarOffsetY);
|
||||
|
||||
drawRDigit(chars[4], 0, rightBarsStartY);
|
||||
drawRDigit(chars[5], 1, rightBarsStartY);
|
||||
drawRDigit(chars[6], 2, rightBarsStartY);
|
||||
drawRDigit(checkSum, 3, rightBarsStartY);
|
||||
|
||||
g.drawString(Bangle.getStepCount(), midBarOffsetX + checkBarWidth + 3, startOffsetY + 4);
|
||||
g.drawString(concatTime.substring(4), midBarOffsetX + checkBarWidth + 3, startOffsetY + textBarHeight + digitBarHeight + 6);
|
||||
|
||||
drawCheckBar(endBarOffsetX, endBarOffsetY);
|
||||
|
||||
// schedule a draw for the next minute
|
||||
if (drawTimeout) {
|
||||
clearTimeout(drawTimeout);
|
||||
}
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
layout.render(layout.watch);
|
||||
}, (1000 * 60 * 5) - (Date.now() % (1000 * 60 * 5)));
|
||||
}
|
||||
|
||||
function drawLDigit(digit, index, offsetY) {
|
||||
switch(digit) {
|
||||
case 0:
|
||||
drawLZeroWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 1:
|
||||
drawLOneWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 2:
|
||||
drawLTwoWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 3:
|
||||
drawLThreeWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 4:
|
||||
drawLFourWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 5:
|
||||
drawLFiveWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 6:
|
||||
drawLSixWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 7:
|
||||
drawLSevenWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 8:
|
||||
drawLEightWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 9:
|
||||
drawLNineWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function drawRDigit(digit, index, offsetY) {
|
||||
switch(digit) {
|
||||
case 0:
|
||||
drawRZeroWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 1:
|
||||
drawROneWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 2:
|
||||
drawRTwoWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 3:
|
||||
drawRThreeWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 4:
|
||||
drawRFourWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 5:
|
||||
drawRFiveWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 6:
|
||||
drawRSixWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 7:
|
||||
drawRSevenWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 8:
|
||||
drawREightWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
case 9:
|
||||
drawRNineWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
LEAN
|
||||
|
||||
01234567890123
|
||||
xxxx xx
|
||||
xx xxxx
|
||||
xxxxxxxx xx
|
||||
xx xxxx
|
||||
xxxx xx
|
||||
xx xxxxxxxx
|
||||
xxxxxx xxxx
|
||||
xxxx xxxxxx
|
||||
xx xxxx
|
||||
xxxx xx
|
||||
*/
|
||||
function drawLOneWithOffset(offset, offsetY) {
|
||||
let barOneX = 4;
|
||||
let barTwoX = 12;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+3+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("1",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
function drawLTwoWithOffset(offset, offsetY) {
|
||||
let barOneX = 4;
|
||||
let barTwoX = 10;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+3+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("2",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
function drawLThreeWithOffset(offset, offsetY) {
|
||||
let barOneX = 2;
|
||||
let barTwoX = 12;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+7+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("3",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
function drawLFourWithOffset(offset, offsetY) {
|
||||
let barOneX = 2;
|
||||
let barTwoX = 10;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+3+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("4",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
function drawLFiveWithOffset(offset, offsetY) {
|
||||
let barOneX = 2;
|
||||
let barTwoX = 12;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+3+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("5",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
function drawLSixWithOffset(offset, offsetY) {
|
||||
let barOneX = 2;
|
||||
let barTwoX = 6;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+7+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("6",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
function drawLSevenWithOffset(offset, offsetY) {
|
||||
let barOneX = 2;
|
||||
let barTwoX = 10;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+5+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+3+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("7",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
function drawLEightWithOffset(offset, offsetY) {
|
||||
let barOneX = 2;
|
||||
let barTwoX = 8;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+3+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+5+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("8",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
function drawLNineWithOffset(offset, offsetY) {
|
||||
let barOneX = 6;
|
||||
let barTwoX = 10;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+3+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("9",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
function drawLZeroWithOffset(offset, offsetY) {
|
||||
let barOneX = 6;
|
||||
let barTwoX = 12;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+3+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("0",offset+3,offsetY+digitHeight+5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
REAN
|
||||
|
||||
01234567890123
|
||||
xxxx xxxx
|
||||
xxxx xxxx
|
||||
xx xx
|
||||
xx xxxxxx
|
||||
xx xxxxxx
|
||||
xx xx
|
||||
xx xx
|
||||
xx xx
|
||||
xxxxxx xx
|
||||
xxxxxx xx
|
||||
|
||||
*/
|
||||
function drawROneWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 8;
|
||||
g.fillRect(offset+barOneX,offsetY+0,offset+barOneX+3,offsetY+digitBarHeight);
|
||||
g.fillRect(offset+barTwoX,offsetY+0,offset+barTwoX+3,offsetY+digitBarHeight);
|
||||
//g.drawString("1",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawRTwoWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 6;
|
||||
g.fillRect(offset+barOneX,offsetY+0,offset+barOneX+3,offsetY+digitBarHeight);
|
||||
g.fillRect(offset+barTwoX,offsetY+0,offset+barTwoX+3,offsetY+digitBarHeight);
|
||||
//g.drawString("2",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawRThreeWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 10;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("3",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawRFourWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 4;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+5+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("4",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawRFiveWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 6;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+5+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("5",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawRSixWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 4;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("6",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawRSevenWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 8;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("7",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawREightWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 6;
|
||||
g.fillRect(offset+barOneX,offsetY+0,offset+barOneX+1,offsetY+digitBarHeight);
|
||||
g.fillRect(offset+barTwoX,offsetY+0,offset+barTwoX+1,offsetY+digitBarHeight);
|
||||
//g.drawString("8",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawRNineWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 8;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+5+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("9",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawRZeroWithOffset(offset, offsetY) {
|
||||
let barOneX = 0;
|
||||
let barTwoX = 10;
|
||||
g.fillRect(barOneX+offset,offsetY+0,barOneX+5+offset,offsetY+digitBarHeight);
|
||||
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
|
||||
//g.drawString("0",offset+2,offsetY+textHeight+5);
|
||||
}
|
||||
|
||||
function drawCheckBar(offsetX, offsetY) {
|
||||
const barOneX = offsetX+2;
|
||||
const barOneWidth = 1;
|
||||
const barTwoX = offsetX+6;
|
||||
const barTwoWidth = 1;
|
||||
g.fillRect(barOneX,offsetY,barOneX+barOneWidth,offsetY+checkBarHeight);
|
||||
g.fillRect(barTwoX,offsetY,barTwoX+barTwoWidth,offsetY+checkBarHeight);
|
||||
}
|
||||
|
||||
function calculateChecksum(digits) {
|
||||
let oddSum = digits[6] + digits[4] + digits[2] + digits[0];
|
||||
let evenSum = digits[5] + digits[3] + digits[1];
|
||||
|
||||
let checkSum = (10 - ((3 * oddSum + evenSum) % 10)) % 10;
|
||||
|
||||
return checkSum;
|
||||
}
|
||||
|
||||
function storeStepCount() {
|
||||
stepCount = Bangle.getStepCount();
|
||||
require("Storage").writeJSON("stepCount",stepCount);
|
||||
}
|
||||
|
||||
function getStepCount() {
|
||||
let accumulatedSteps = Bangle.getStepCount();
|
||||
if(accumulatedSteps <= stepCount) {
|
||||
return 0;
|
||||
}
|
||||
return accumulatedSteps - stepCount;
|
||||
}
|
||||
|
||||
function resetAtMidnight() {
|
||||
let now = new Date();
|
||||
let night = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate(), // the next day, ...
|
||||
23, 58, 0 // ...at 00:00:00 hours
|
||||
);
|
||||
let msToMidnight = night.getTime() - now.getTime();
|
||||
|
||||
setTimeout(function() {
|
||||
storeStepCount(); // <-- This is the function being called at midnight.
|
||||
resetAtMidnight(); // Then, reset again next midnight.
|
||||
}, msToMidnight);
|
||||
}
|
||||
|
||||
resetAtMidnight();
|
||||
|
||||
// The layout, referencing the custom renderer
|
||||
var Layout = require("Layout");
|
||||
var layout = new Layout( {
|
||||
type:"v", c: [
|
||||
{type:"custom", render:renderWatch, id:"watch", bgCol:g.theme.bg, fillx:1, filly:1 }
|
||||
]
|
||||
});
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
Bangle.setUI("clock");
|
||||
layout.render();
|
||||
|
||||
Bangle.on('lock', function(locked) {
|
||||
if(!locked) {
|
||||
layout.render();
|
||||
}
|
||||
});
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -0,0 +1 @@
|
|||
E.toArrayBuffer(atob("MDAE///+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifrV6mlp+rXYiFXZr8k4q8r+if/////+if7+///u//79ie7/7//u///+if/////+ifnP6t+378r+ienvyf/K/7v+if/////+ifrfx6/I78j+ibe/+e/W75n+if/////+iee/+t/Z77f+iervyv/r/7v+if//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////"))
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,16 @@
|
|||
{ "id": "barcode",
|
||||
"name": "Barcode clock",
|
||||
"shortName":"Barcode clock",
|
||||
"icon": "barcode.icon.png",
|
||||
"version":"0.09",
|
||||
"description": "EAN-8 compatible barcode clock.",
|
||||
"tags": "barcode,ean,ean-8,watchface,clock,clockface",
|
||||
"type": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"barcode.app.js","url":"barcode.app.js"},
|
||||
{"name":"barcode.img","url":"barcode.icon.js","evaluate":true}
|
||||
],
|
||||
"readme":"README.md",
|
||||
"screenshots": [{"url":"barcode.clock.png"}]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
0.01: Initial version
|
||||
0.02: setTimeout bug fix; no leading zero on date; lightmode; 12 hour format; cleanup
|
||||
0.03: Internationalisation; bug fix - battery icon responds promptly to charging state
|
|
@ -0,0 +1,14 @@
|
|||
# Big Digit Clock
|
||||
|
||||
There are a number of big digit clocks available for the Bangle, but this is
|
||||
the first which shows all the essential information that a clock needs to show
|
||||
in a manner that is easy to read by those with poor eyesight.
|
||||
|
||||
The clock shows the time-of-day, the day-of-week and the day-of-month, as well
|
||||
as an easy-to-see icon showing the current charge on the battery.
|
||||
|
||||

|
||||
|
||||
## Creator
|
||||
|
||||
Created by [Deirdre O'Byrne](https://github.com/deirdreobyrne)
|
|
@ -0,0 +1,91 @@
|
|||
// <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap" rel="stylesheet">
|
||||
|
||||
Graphics.prototype.setFontOpenSans = function(scale) {
|
||||
// Actual height 48 (50 - 3)
|
||||
this.setFontCustom(
|
||||
atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAAP8AAAAAAAAB/wAAAAAAAAH/gAAAAAAAAf+AAAAAAAAB/4AAAAAAAAH/gAAAAAAAAf8AAAAAAAAA/wAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAPAAAAAAAAAH8AAAAAAAAD/wAAAAAAAA//AAAAAAAAf/8AAAAAAAP//wAAAAAAH///AAAAAAB///4AAAAAA///8AAAAAAf///AAAAAAH///gAAAAAD///wAAAAAB///4AAAAAAf//+AAAAAAP///AAAAAAH///gAAAAAA///4AAAAAAD//8AAAAAAAP/+AAAAAAAA//gAAAAAAAD/wAAAAAAAAP4AAAAAAAAA8AAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/gAAAAAAAP///AAAAAAH////gAAAAD/////gAAAAf/////gAAAH//////AAAA//////+AAAD//////8AAAf//////4AAD//4AH//gAAP/gAAAf/AAA/4AAAAf8AAH/AAAAA/4AAf4AAAAB/gAB/AAAAAD+AAH8AAAAAP4AAfwAAAAA/gAB/AAAAAD+AAH8AAAAAP4AAf4AAAAB/gAB/gAAAAH+AAD/gAAAB/wAAP/gAAAf/AAA//4AAf/8AAB///////gAAD//////8AAAH//////wAAAP/////+AAAAf/////wAAAA/////8AAAAAf////AAAAAAf///gAAAAAAB//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAHgAAAAAAAAA/AAAAAAAAAH+AAAAAAAAA/4AAAAAAAAD/AAAAAAAAAf8AAAAAAAAD/gAAAAAAAAf8AAAAAAAAB/gAAAAAAAAP8AAAAAAAAB/wAAAAAAAAP///////AAA///////8AAD///////wAAP///////AAA///////8AAD///////wAAP///////AAA///////8AAD///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAD8AAAOAAAAAfwAAB+AAAAD/AAAH8AAAAf8AAA/wAAAH/wAAH/AAAA//AAAf4AAAH/8AAD/gAAA//wAAP8AAAH//AAA/gAAA//8AAD+AAAH//wAAf4AAA/9/AAB/AAAH/n8AAH8AAA/8fwAAfwAAH/h/AAB/AAA/8H8AAH8AAH/gfwAAfwAA/8B/AAB/gAH/gH8AAH+AB/8AfwAAP8Af/gB/AAA////8AH8AAD////gAfwAAH///8AB/AAAf///gAH8AAA///8AAfwAAB///gAB/AAAD//4AAH8AAAH/+AAAfwAAAH/gAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAYAAAAB/gAAB4AAAAD+AAAPwAAAAP4AAA/gAAAAfwAAH+AAAAB/AAAf4AAAAH8AAD/AB+AAfwAAP8AH4AA/gAA/gAfgAD+AAH+AB+AAP4AAfwAH4AA/gAB/AAfgAD+AAH8AB+AAP4AAfwAH4AA/gAB/AA/wAD+AAH8AD/AAP4AAfwAP8AA/gAB/AA/wAD+AAH+AH/AAf4AAf4Af+AB/AAA/wH/8AP8AAD////4D/wAAP//+////AAAf//7///4AAB///P///gAAD//8f//8AAAP//h///gAAAf/8D//+AAAA//gH//wAAAA/4AP/8AAAAAAAAP/AAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAAAAAAA/wAAAAAAAAH/AAAAAAAAB/8AAAAAAAAP/wAAAAAAAD//AAAAAAAAf/8AAAAAAAH//wAAAAAAA/+/AAAAAAAP/z8AAAAAAB/8PwAAAAAAf/g/AAAAAAD/4D8AAAAAAf/APwAAAAAH/4A/AAAAAA/+AD8AAAAAP/wAPwAAAAB/8AA/AAAAAf/gAD8AAAAD/4AAPwAAAA//AAA/AAAAD///////wAAP///////AAA///////8AAD///////wAAP///////AAA///////8AAD///////wAAP///////AAAAAAAA/AAAAAAAAAD8AAAAAAAAAPwAAAAAAAAA/AAAAAAAAAD8AAAAAAAAAPwAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAQAB/gAAAD//gAD+AAA////AAP8AAD///8AAfwAAP///wAB/AAA////AAH8AAD///8AAf4AAP///wAA/gAA////AAD+AAD/+H8AAP4AAP4AfwAA/gAA/gB+AAD+AAD+AH4AAP4AAP4AfwAA/gAA/gB/AAD+AAD+AH8AAP4AAP4AfwAB/gAA/gB/AAH+AAD+AH+AA/wAAP4Af8AD/AAA/gB/4A/8AAD+AD////gAAP4AP///+AAA/gAf///wAAD+AB////AAAP4AD///4AAA/gAH///AAAAAAAP//4AAAAAAAf/+AAAAAAAAf/gAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//AAAAAAAf///gAAAAAP////gAAAAD/////gAAAAf/////AAAAD/////+AAAA//////8AAAD//////4AAAf/8/4//gAAD/8H+Af/AAAP/AfgAf8AAB/wD+AA/wAAH+APwAB/gAA/wB/AAD+AAD+AH4AAP4AAP4AfgAA/gAB/gB+AAD+AAH8AH4AAP4AAfwAfgAA/gAB/AB/AAH+AAH8AH8AAf4AAfwAf4AD/AAB/AB/4A/8AAH8AH////wAAfwAP///+AAB/AA////4AAH8AB////AAAfwAD///4AAA/AAH///AAAAAAAP//4AAAAAAAP/+AAAAAAAAP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AAAAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AAAAAAAAAP4AAAAADAAA/gAAAAB8AAD+AAAAAfwAAP4AAAAH/AAA/gAAAB/8AAD+AAAAf/wAAP4AAAP//AAA/gAAD//8AAD+AAA///wAAP4AAP//8AAA/gAD///AAAD+AB///wAAAP4Af//4AAAA/gH//+AAAAD+B///gAAAAP4f//wAAAAA/v//8AAAAAD////AAAAAAP///wAAAAAA///4AAAAAAD//+AAAAAAAP//gAAAAAAA//wAAAAAAAD/8AAAAAAAAP/AAAAAAAAA/wAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAH/wAAAAH8AB//gAAAB/8AP//gAAAf/8B//+AAAB//4P//8AAAP//w///4AAB///n///gAAH//+////AAA/////gf8AAD/h//4AfwAAP4B//AB/gAB/gD/4AD+AAH8AP/gAP4AAfwAf8AA/gAB/AA/wAB+AAH4AD/AAH4AAfwAP8AAfgAB/AB/4AD+AAH8AH/gAP4AAfwA//AA/gAA/gH/+AH+AAD/h//8AfwAAP//+/4D/AAAf//7///8AAB///H///gAAD//4P//+AAAP//g///wAAAf/8B//+AAAA//gD//wAAAA/4AH/+AAAAAAAAH/wAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAAAAAAD//AAAAAAAA///AAAAAAAH//+AAPwAAB///8AA/gAAP///4AD+AAA////wAP4AAH////AA/gAAf///8AD+AAD/4B/4AP4AAP+AB/gA/gAA/gAD+AD+AAH+AAP4AP4AAfwAAfgA/gAB/AAB+AD+AAH8AAH4AP4AAfwAAfgB/AAB/AAB+AH8AAH8AAH4A/wAAf4AA/AH+AAA/gAD8A/4AAD/gAfwH/gAAP/AD+B/8AAAf/g/w//gAAB//////+AAAD//////wAAAH/////+AAAAP/////wAAAAf////8AAAAA/////AAAAAA////wAAAAAAf//4AAAAAAAH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAfgAAAAP8AAD/AAAAA/4AAf8AAAAH/gAB/4AAAAf+AAH/gAAAB/4AAf+AAAAH/gAB/4AAAAf+AAH/AAAAA/wAAP8AAAAB+AAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=='),
|
||||
46,
|
||||
atob("EhklJSUlJSUlJSUlEg=="),
|
||||
64+(scale<<8)+(1<<16)
|
||||
);
|
||||
};
|
||||
|
||||
var drawTimeout;
|
||||
|
||||
// schedule a draw for the next minute
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function () {
|
||||
draw();
|
||||
}, 60300 - (Date.now() % 60000)); // We aim for 300ms into the next minute to ensure we make it!
|
||||
}
|
||||
|
||||
function draw() {
|
||||
var date = new Date();
|
||||
var h = date.getHours(),
|
||||
m = date.getMinutes();
|
||||
var d = date.getDate(),
|
||||
w = date.getDay(); // d=1..31; w=0..6
|
||||
const level = E.getBattery();
|
||||
const width = level + (level/2);
|
||||
var is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
|
||||
var dows = require("date_utils").dows(0,1);
|
||||
|
||||
g.reset();
|
||||
g.clear();
|
||||
|
||||
g.setFontOpenSans();
|
||||
g.setFontAlign(0, -1);
|
||||
if (is12Hour) {
|
||||
if (h > 12) h -= 12;
|
||||
if (h == 0) h = 12;
|
||||
g.drawString(h + ":" + ("0"+m).substr(-2), g.getWidth() / 2, 30);
|
||||
} else {
|
||||
g.drawString(("0"+h).substr(-2) + ":" + ("0"+m).substr(-2), g.getWidth() / 2, 30);
|
||||
}
|
||||
g.setFontAlign(1, -1);
|
||||
g.drawString(d, g.getWidth() -6, 98);
|
||||
g.setFont('Vector', 52);
|
||||
g.setFontAlign(-1, -1);
|
||||
g.drawString(dows[w].slice(0,2).toUpperCase(), 6, 103);
|
||||
|
||||
g.fillRect(9,159,166,171);
|
||||
g.fillRect(167,163,170,167);
|
||||
if (Bangle.isCharging()) {
|
||||
g.setColor(1,1,0);
|
||||
} else if (level > 40) {
|
||||
g.setColor(0,1,0);
|
||||
} else {
|
||||
g.setColor(1,0,0);
|
||||
}
|
||||
g.fillRect(12,162,12+width,168);
|
||||
if (level < 100) {
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(12+width+1,162,162,168);
|
||||
}
|
||||
|
||||
g.setColor(0, 1, 0);
|
||||
g.fillRect(0, 90, g.getWidth(), 94);
|
||||
|
||||
// widget redraw
|
||||
Bangle.drawWidgets();
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
Bangle.on('lcdPower', on => {
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.on('charging', (charging) => {
|
||||
draw();
|
||||
});
|
||||
|
||||
Bangle.loadWidgets();
|
||||
draw();
|
||||
|
||||
Bangle.setUI("clock");
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgJC/AAMD4F4AgN4g/D/4FB/E/AoUH/F/AoOAh4FCz4FD4EPAoUHAoOHwAFDx/AAoUfAol/g4RD/w1Cg/B/AFD4fwn4XC4fg8/wAoPH//P7AFE9wFE8YFEEwcf4+BwAFBiACBAoUwAQPAAQMgAQNAArIjFF4sYgEBAoUIAoIRChi3B8AFBg8Ah/wAoIVBjH8ZAXguF+AoSDBn7WEh4FEg4"))
|
After Width: | Height: | Size: 7.9 KiB |
|
@ -0,0 +1,17 @@
|
|||
{ "id": "bigdclock",
|
||||
"name": "Big digit clock containing just the essentials",
|
||||
"shortName":"Big digit clk",
|
||||
"version":"0.03",
|
||||
"description": "A clock containing just the essentials, made as easy to read as possible for those of us that need glasses. It contains the time, the day-of-week, the day-of-month, and the current battery state-of-charge.",
|
||||
"icon": "bigdclock.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"allow_emulator":true,
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"screenshots": [ { "url":"screenshot.png" } ],
|
||||
"storage": [
|
||||
{"name":"bigdclock.app.js","url":"bigdclock.app.js"},
|
||||
{"name":"bigdclock.img","url":"bigdclock.icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 3.1 KiB |
|
@ -7,3 +7,4 @@
|
|||
0.07: Fix off-by-one-error on previous month
|
||||
0.08: Do not register as watch, manually start clock on button
|
||||
read start of week from system settings
|
||||
0.09: Fix scope of let variables
|
||||
|
|
|
@ -16,6 +16,12 @@ const white = "#ffffff";
|
|||
const red = "#d41706";
|
||||
const blue = "#0000ff";
|
||||
const yellow = "#ffff00";
|
||||
let bgColor = color4;
|
||||
let bgColorMonth = color1;
|
||||
let bgColorDow = color2;
|
||||
let bgColorWeekend = color3;
|
||||
let fgOtherMonth = gray1;
|
||||
let fgSameMonth = white;
|
||||
|
||||
let settings = require('Storage').readJSON("calendar.json", true) || {};
|
||||
let startOnSun = ((require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0) === 0;
|
||||
|
@ -27,19 +33,12 @@ if (settings.ndColors === undefined)
|
|||
}
|
||||
|
||||
if (settings.ndColors === true) {
|
||||
let bgColor = white;
|
||||
let bgColorMonth = blue;
|
||||
let bgColorDow = black;
|
||||
let bgColorWeekend = yellow;
|
||||
let fgOtherMonth = blue;
|
||||
let fgSameMonth = black;
|
||||
} else {
|
||||
let bgColor = color4;
|
||||
let bgColorMonth = color1;
|
||||
let bgColorDow = color2;
|
||||
let bgColorWeekend = color3;
|
||||
let fgOtherMonth = gray1;
|
||||
let fgSameMonth = white;
|
||||
bgColor = white;
|
||||
bgColorMonth = blue;
|
||||
bgColorDow = black;
|
||||
bgColorWeekend = yellow;
|
||||
fgOtherMonth = blue;
|
||||
fgSameMonth = black;
|
||||
}
|
||||
|
||||
function getDowLbls(locale) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.08",
|
||||
"version": "0.09",
|
||||
"description": "Simple calendar",
|
||||
"icon": "calendar.png",
|
||||
"screenshots": [{"url":"screenshot_calendar.png"}],
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
1.00: New App!
|
||||
1.01: Use fractional numbers and scale the points to keep working consistently on whole screen
|
|
@ -6,6 +6,7 @@ A simple calibration app for the touchscreen
|
|||
Once lauched touch the cross that appear on the screen to make
|
||||
another spawn elsewhere.
|
||||
|
||||
each new touch on the screen will help to calibrate the offset
|
||||
of your finger on the screen. After five or more input, press
|
||||
the button to save the calibration and close the application.
|
||||
Each new touch on the screen will help to calibrate the offset
|
||||
of your finger on the screen. After four or more inputs, press
|
||||
the button to save the calibration and close the application. Quality
|
||||
of the calibration gets better with every touch on a cross.
|
||||
|
|
|
@ -1,40 +1,60 @@
|
|||
class BanglejsApp {
|
||||
constructor() {
|
||||
this.maxSamples = 16;
|
||||
this.target = {
|
||||
xMin: Math.floor(0.1 * g.getWidth()),
|
||||
xMax: Math.floor(0.9 * g.getWidth()),
|
||||
yMin: Math.floor(0.1 * g.getHeight()),
|
||||
yMax: Math.floor(0.9 * g.getHeight()),
|
||||
};
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.step = 0;
|
||||
this.settings = {
|
||||
xoffset: 0,
|
||||
yoffset: 0,
|
||||
xoffset: [0],
|
||||
yoffset: [0],
|
||||
xMaxActual: [this.target.xMax],
|
||||
yMaxActual: [this.target.yMax],
|
||||
};
|
||||
}
|
||||
|
||||
load_settings() {
|
||||
let settings = require('Storage').readJSON('calibration.json', true) || {active: false};
|
||||
|
||||
// do nothing if the calibration is deactivated
|
||||
if (settings.active === true) {
|
||||
// cancel the calibration offset
|
||||
Bangle.on('touch', function(button, xy) {
|
||||
xy.x += settings.xoffset;
|
||||
xy.y += settings.yoffset;
|
||||
});
|
||||
}
|
||||
if (!settings.xoffset) settings.xoffset = 0;
|
||||
if (!settings.yoffset) settings.yoffset = 0;
|
||||
|
||||
console.log('loaded settings:');
|
||||
console.log(settings);
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
save_settings() {
|
||||
this.settings.active = true;
|
||||
this.settings.reload = false;
|
||||
require('Storage').writeJSON('calibration.json', this.settings);
|
||||
getMedian(array){
|
||||
array.sort();
|
||||
let i = Math.floor(array.length/2);
|
||||
if ( array.length % 2 && array.length > 1 ){
|
||||
return (array[i]+array[i+1])/2;
|
||||
} else {
|
||||
return array[i];
|
||||
}
|
||||
}
|
||||
|
||||
console.log('saved settings:');
|
||||
console.log(this.settings);
|
||||
getMedianSettings(){
|
||||
let medianSettings = {
|
||||
xoffset: this.getMedian(this.settings.xoffset),
|
||||
yoffset: this.getMedian(this.settings.yoffset)
|
||||
};
|
||||
|
||||
medianSettings.xscale = this.target.xMax / (medianSettings.xoffset + this.getMedian(this.settings.xMaxActual));
|
||||
medianSettings.yscale = this.target.yMax / (medianSettings.yoffset + this.getMedian(this.settings.yMaxActual));
|
||||
return medianSettings;
|
||||
}
|
||||
|
||||
save_settings() {
|
||||
let settingsToSave = this.getMedianSettings();
|
||||
settingsToSave.active = true;
|
||||
settingsToSave.reload = false;
|
||||
require('Storage').writeJSON('calibration.json', settingsToSave);
|
||||
|
||||
console.log('saved settings:', settingsToSave);
|
||||
}
|
||||
|
||||
explain() {
|
||||
|
@ -46,29 +66,78 @@ class BanglejsApp {
|
|||
}
|
||||
|
||||
drawTarget() {
|
||||
this.x = 16 + Math.floor(Math.random() * (g.getWidth() - 32));
|
||||
this.y = 40 + Math.floor(Math.random() * (g.getHeight() - 80));
|
||||
switch (this.step){
|
||||
case 0:
|
||||
this.x = this.target.xMin;
|
||||
this.y = this.target.yMin;
|
||||
break;
|
||||
case 1:
|
||||
this.x = this.target.xMax;
|
||||
this.y = this.target.yMin;
|
||||
break;
|
||||
case 2:
|
||||
this.x = this.target.xMin;
|
||||
this.y = this.target.yMax;
|
||||
break;
|
||||
case 3:
|
||||
this.x = this.target.xMax;
|
||||
this.y = this.target.yMax;
|
||||
break;
|
||||
}
|
||||
|
||||
g.clearRect(0, 24, g.getWidth(), g.getHeight() - 24);
|
||||
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawLine(this.x, this.y - 5, this.x, this.y + 5);
|
||||
g.drawLine(this.x - 5, this.y, this.x + 5, this.y);
|
||||
g.setFont('Vector', 10);
|
||||
g.drawString('current offset: ' + this.settings.xoffset + ', ' + this.settings.yoffset, 0, 24);
|
||||
let medianSettings = this.getMedianSettings();
|
||||
g.drawString('current offset: ' + medianSettings.xoffset.toFixed(3) + ', ' + medianSettings.yoffset.toFixed(3), 2, (g.getHeight()/2)-6);
|
||||
g.drawString('current scale: ' + medianSettings.xscale.toFixed(3) + ', ' + medianSettings.yscale.toFixed(3), 2, (g.getHeight()/2)+6);
|
||||
}
|
||||
|
||||
setOffset(xy) {
|
||||
this.settings.xoffset = Math.round((this.settings.xoffset + (this.x - Math.floor((this.x + xy.x)/2)))/2);
|
||||
this.settings.yoffset = Math.round((this.settings.yoffset + (this.y - Math.floor((this.y + xy.y)/2)))/2);
|
||||
switch (this.step){
|
||||
case 0:
|
||||
this.settings.xoffset.push(this.x - xy.x);
|
||||
this.settings.yoffset.push(this.y - xy.y);
|
||||
break;
|
||||
case 1:
|
||||
this.settings.xMaxActual.push(xy.x);
|
||||
this.settings.yoffset.push(this.y - xy.y);
|
||||
break;
|
||||
case 2:
|
||||
this.settings.xoffset.push(this.x - xy.x);
|
||||
this.settings.yMaxActual.push(xy.y);
|
||||
break;
|
||||
case 3:
|
||||
this.settings.xMaxActual.push(xy.x);
|
||||
this.settings.yMaxActual.push(xy.y);
|
||||
break;
|
||||
}
|
||||
|
||||
for (let c in this.settings){
|
||||
if (this.settings[c].length > this.maxSamples) this.settings[c] = this.settings[c].slice(1, this.maxSamples);
|
||||
}
|
||||
}
|
||||
|
||||
nextStep() {
|
||||
this.step++;
|
||||
if ( this.step == 4 ) this.step = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
E.srand(Date.now());
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
calibration = new BanglejsApp();
|
||||
calibration.load_settings();
|
||||
Bangle.disableCalibration = true;
|
||||
|
||||
function touchHandler (btn, xy){
|
||||
if (xy) calibration.setOffset(xy);
|
||||
calibration.nextStep();
|
||||
calibration.drawTarget();
|
||||
}
|
||||
|
||||
let modes = {
|
||||
mode : 'custom',
|
||||
|
@ -76,10 +145,7 @@ let modes = {
|
|||
calibration.save_settings(this.settings);
|
||||
load();
|
||||
},
|
||||
touch : function(btn, xy) {
|
||||
calibration.setOffset(xy);
|
||||
calibration.drawTarget();
|
||||
},
|
||||
touch : touchHandler,
|
||||
};
|
||||
Bangle.setUI(modes);
|
||||
calibration.drawTarget();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
let cal_settings = require('Storage').readJSON("calibration.json", true) || {active: false};
|
||||
Bangle.on('touch', function(button, xy) {
|
||||
// do nothing if the calibration is deactivated
|
||||
if (cal_settings.active === false) return;
|
||||
if (cal_settings.active === false || Bangle.disableCalibration) return;
|
||||
|
||||
// reload the calibration offset at each touch event /!\ bad for the flash memory
|
||||
if (cal_settings.reload === true) {
|
||||
|
@ -9,6 +9,6 @@ Bangle.on('touch', function(button, xy) {
|
|||
}
|
||||
|
||||
// apply the calibration offset
|
||||
xy.x += cal_settings.xoffset;
|
||||
xy.y += cal_settings.yoffset;
|
||||
xy.x = E.clip(Math.round((xy.x + (cal_settings.xoffset || 0)) * (cal_settings.xscale || 1)),0,g.getWidth());
|
||||
xy.y = E.clip(Math.round((xy.y + (cal_settings.yoffset || 0)) * (cal_settings.yscale || 1)),0,g.getHeight());
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "Touchscreen Calibration",
|
||||
"shortName":"Calibration",
|
||||
"icon": "calibration.png",
|
||||
"version":"1.00",
|
||||
"version":"1.01",
|
||||
"description": "A simple calibration app for the touchscreen",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
0.0: Main App.
|
||||
0.1: Performance Fixes.
|
||||
0.2: Correct Screen Clear and Draw.
|
||||
0.3: Minor Fixes.
|
||||
0.4: Clear Old Time on Screen Before Draw new Time
|
||||
0.5: Update Date and Time to use Native date Funcions
|
||||
0.6: Add Settings Page
|
||||
0.7: Update Rocket Sequences Scope to not use memory all time
|
||||
0.8: Update Some Variable Scopes to not use memory until need
|
||||
0.9: Remove ESLint spaces
|
|
@ -0,0 +1,10 @@
|
|||
# cassioWatch
|
||||
|
||||
 
|
||||
|
||||
Clock with Space Cassio Watch Style.
|
||||
|
||||
It displays current temperature,day,steps,battery.heartbeat and weather.
|
||||
|
||||
**To-do**:
|
||||
Integrate heartbeat and Weather, Align and change size of some elements.
|
|
@ -0,0 +1,133 @@
|
|||
require("Font6x12").add(Graphics);
|
||||
require("Font8x12").add(Graphics);
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
|
||||
let ClockInterval;
|
||||
let RocketInterval;
|
||||
let BatteryInterval;
|
||||
|
||||
function bigThenSmall(big, small, x, y) {
|
||||
g.setFont("7x11Numeric7Seg", 2);
|
||||
g.drawString(big, x, y);
|
||||
x += g.stringWidth(big);
|
||||
g.setFont("8x12");
|
||||
g.drawString(small, x, y);
|
||||
}
|
||||
|
||||
function ClearIntervals(inoreclock) {
|
||||
if (RocketInterval) clearInterval(RocketInterval);
|
||||
if (BatteryInterval) clearInterval(BatteryInterval);
|
||||
RocketInterval = undefined;
|
||||
BatteryInterval = undefined;
|
||||
if (inoreclock) return;
|
||||
if (ClockInterval) clearInterval(ClockInterval);
|
||||
ClockInterval = undefined;
|
||||
}
|
||||
|
||||
function getBackgroundImage() {
|
||||
return require("heatshrink").decompress(atob("2GwwkGIf4AfgMRkUiiIHCiMRiAMDAwYCCBAYVDAHMv/4ACkBIBAgPxBgM/BYXyAoICBCowA5gRADKQUDKAYMCmYCBiBXBCo4A5J4MxiMSKQUf+YBBBgSiBgc/kBXBBAMyCoK2CK/btCiUhfAJLCkBkDiMQgBXDCoUvNAJX+AAU/+MB/8wAQIAC+cQK5hoDgIEBBIQFEAYIPHBIgBBAQQIDBwZXSKIMxgJaBgEjmZYCmBXLgLBBkkAgUhiMxBIM0iMSCoMRkZECkQJEichBINDiETAgISBiQTDK6MvJAXzVIQrBBYMCK5E/K4kwGIJXFgdAMgQQBiYiCDgU0HQSlCgMikIEBEAMTDYJXQ+UikYDBj6nCAAMTWoJ6BK4oVEK4c0oQ+BK4MjAgMDJoJXHNYJXHBwa0BohcDY4QAKgJQE+LzBNwJVBkQMEkBXBCoyvFJAVAKISaBiMiHQRIDkVBoSyCK5CvBAgavNDAJAC+cQn5DCgSpBl4MDgBXBgCsBCoYoMLAKREgIKDBJIdKK5oA/AH4A/AH4A/ADUBIH4APiAFEi1mAGUADrkRKwUGK2ZXes1gK2xXfD8A3/K/4AWgxX/ACtga2AwIHLkAgCwvJw6RcDgIABK+w4cK/I4dsEGP5BXtSAQ6BV/5XSG4RX/K6Y3fK+42CK/5XTGwcGK/5XSVwY5cK+o1DAAayYsAhDsCv4K7BTBK4YeYK7CyFVzJXFFIpXtVwYiYK/rmZKYYDDELJXXG4YiaK/Y0aKgQAEK+gkdKt5XGKzqv5GTpX6ETlgK4xWrKTyxKVthXmAGRX/K/5X/AH5X/K/4gBAH4A/AFz/uAH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AHNggEGHfEAgAEHKyQXVK0qTCAggbUK+6SDAApzXK/5BRDYZX3KxBBSYqxXngyvaV25XEd4ZCSsAcBAoRZ2dQZXBLwgaQCIYeCAGirCS4YGCDSJXCC6ZaodYICBZzSw4S4I+XDgSv4K4rzCK/47RAQTMaWHI9YV3TscV3aVagByBK3SwCSqyt8AAQ+XK/4A/AH4A/AH4A3gAA/AH4AuZbdggwc3ADpX/K/5XxsEAgA+XK/o8BgBX/K64/WK/4/XK/5X/K/5XvgBX/K64cYHrw4CSTFggCuXK4oDCEQJXYDS6ScDgg4CPKyRCAAZX0HAgBDK+LlYK4oeBAwZ9aK+lgAoQGBgyvzDIIDBK66sCG4JXYCwIBDK7ADCK+xZCHwJXzGoQ8BK7DpBAAaSXSgRXZO4okCK+IaXV4oABEILSWSYjRCHSo3BDSxXEAAIcBAISvyKawcIAYIGCK/4cUH4YlaHS0AHgI1XOg5YBPrY6WHgRXfAGRXDHzBX8VoJX/K68ADjRX6sBX/K/5X/K8wdcK/UAG7B0iKzZYbK/BWDAH4A/hWpzWhIf4ASgOpzIAB0EAhhH/AB8ZzGJ1WazMA4pH/AB+pxOZxOpzVMqA2ugUzmcgD7cKVYOqzGqpnRFw8ykchK8kviEBmQFBgMiFocSCAcSkUQAgMikRsHhWqxOq0Ut4mqBw0DC4IxBD4wpBHAQMCA4cCGJIAFj8hDIQuBkMTCwU/AYQJBiUxFoPxiIVDK4kyxUz4cxl+KK5MfDQXyD4UCmMSmAEBAQQHDgMTmIxHAAqpBmaqCFwMDEYZRBgEjCQQBB+USK5E/ns/0Uzwc6K48ykYkCK4IfCc4I4CK4QHEBAYAMiICBmYuDmQEBh8iAgRXCLISvJO4MqwcklEiK5CADV4oaBV4oHEK6Eve4JNCbwRfCiMTFoMDkMRSAJXCD49azWp0UqzWayJXIQwcAO4cCkMCFIJOCA4XxK6KPBkR6DTwYyBAwYPEAggfFzORpWK1OZyAOHJ4QfERAUSEgQxIIIgAr1URWIOZzOgGtwAhgMZzWq1OaIv4ASKgOqzTkvAEmq1WgFtQA=="));
|
||||
}
|
||||
|
||||
function getRocketSequences() {
|
||||
return {
|
||||
1: require("heatshrink").decompress(atob("qFGwkCkQAiiEBEkUgKQhPhE8ogCE8YhCiQoEE7pKEPIgncTQ4neEwpQCPoh1eJYYwCJ7QmHKAh1hZIpOjPAUBJ0ZQCTzEhExZ1lPAZ1kKDQmOJ65O2E65OPOy5O2E64mPOyxO/J2wnPJyx2QJ35O/J2khE0p2POq52PEy4nOiQnlOrEhiSfMJrEggQnLJzB1CPBQmZkInMEzBQDPBImbPBR1ZEoRMCZYImhgQgEE0BzFKAgmaDwLDFKAbqdYQwHBOrcgDgLBFJrsiiRNGYbpLBY4Ymhd4omkkUhE0pQEEwUBJjrHBd4QmCdzoiBDwYrCPLyZHF4QnagQeCE8UgJwYniJwgnIOzwfFO0wJCJzMQE4gyFEzR2FBQombkInDQI4AakAnBTYS+ZE5BMDE0LEES7YnLE0R3FAEQA=")),
|
||||
2: require("heatshrink").decompress(atob("qFGwkCkQAikMAgIliKYon/AA0gEAQniEwIhCAgYndEIjqBE8CaGKogmgKAp1fKAgncExBQBBQR1gKAp7BJ0IndExR4CE0idaOpYnbExqeYJxxPYEx0BJ0x2XExx2XJ20QE6xONJi5OPGwJOlBwLFkLoLFlBwJOkOwJOlE4JOkTjBOOE/52Pdi5OPEy7FnE5wmXE5xOZT5gmYEoMiiB1lgR4KTLAkDPBJ1WIAYDDKA4mWJwchDwYEDTjQiDJQh4GYLAhHFosSJy6OCTIxaEEywbBKYwjEEzMgUQxQFBogAURwZOGOjTKJdTYnOEryfHE0JQEfIpQgYQMAgJLeAgrtfTI4ndgSaFE4h0bdQkSZQpOfEAgIBO0AnEdrh2FJAb1EdbInEBIpObOwhOEEzYnFXzZ2HE4QlhE4QlDFMKcDYooniO0QnDT0YnCE0ciA")),
|
||||
3: require("heatshrink").decompress(atob("qFGwkEogAjiMUEkVAKYgnhPYolgOQIniOYZ4FOcLqBE8CaGKojpgKAomhEYUQE7gmHKAIxCE0QkCPYR1gZIgnZExR4CJ0idmE7ZONYzImNgEUJ0p3YJRh2ZJJwnXOpQhBdkpaETsMEGQhOhE7jFLUYpOfTzgmKE4hOiE4hOigEUJ0rvCEywnPEqx2OTjBOOE7ImOTsqeZE5zFYoJOmT5kBJzEAih4LdK5mBAQInKOqoYDEgR4JEypHDEYbxJOq5ABdgZ7CEzZOEJQgnGihOYEIzJFTionCKYxWGEy9ADAYnGUIYmWog/EdBFAEy7KIKAwnjKwLqWE5pMeT48CVQpQfgMjKEtEiAnfEQJQCgJSCTcB6FJzkEdYcUE8FAdQghDOzonKTjh2EZAidcDoInHJzodBOwx/BE8JxcOwsAOwQmhJgSXDObwnFEwUUO0LFGE8aeiE4YmiokQE0tE")),
|
||||
4: require("heatshrink").decompress(atob("qFGwkCkQAjiMSEkRTFE/4AGkMAgQCBE8MgEIYEDE7whDdQIngTQxVEE0ChFTjxQFE7jnFKAgxCOsBQFZgJ1gE7wmKPAROkTrTEHGAwnYiBHJFAaeXOoyXBEQZPac5AsFgJOhAoh2XJwwnFKoROdE4J9GJzwnIiQmVkInPAC0QE5AJFE64mHY5DFdE4SBEYr5JDJ0hKDJ0jCZJxoACgInmKLAmOTq5OOEy5OPTsxOYE5wmXO5wlYkAnMOqshiRNCgR4LOC8CkJCCEzxHDAgYnJOqpAEDoZ4HEyodDEQpQHdCsQOwwFHEyzoCPYzJGEy0gEwaZGA4acVEQSjHKAomXkQYEYAwlZeRKYDE8gjCYa7zJEwcCkImfKAb4FAD0hdTh4LgRSBOcR0CJz0gYYrrgN4QnEYrxOEE4bEeiAnGF4J2idL6VDE8ohBE0gnFE0J0BE4QGBiROgdIQABgJ2hJoTtjYgZSEE8ScgE4omikUQTcQADA=")),
|
||||
5: require("heatshrink").decompress(atob("qFGwkCkQAikMAgIliKYonhiAnjkEATIIniEwIhCAgYndEIhQFYUZVEE0BQFOr5QEeQQmiKAL1DOr5QEE7ROCDgZVEAoInZDwchFQQoDPAJOdEQYrBdrZFDOYwncEJDsDVIpOXgJxEE4pObEAgGFgJOaE48BaIhOZJ5ZObY5ROcE441CE6xOGPAwtCJzpGCJ0hHDkI1DJzwoEJzInLFg52dUo5O/J35OzE54mWOx4mXJxx1XE54mXkUhExkSJzCfMOrAlBPBiZXgQDBAQQmgJgh4JOqoYEFYwmaDoZzEFgh1YDgkiiAFEKAroXJJAGFiQmVkCNDTIz5EJy57HKAomXkQYEJoqaYeRadEJrAnJEQUAgJPiAoYmeT4cCkAnBE0BKCJkT1EkDCeJYYiDOkLDFFL5wBE4guCPDhEBEwQiDY70CkInDiQnCJzkhOwhKDdzp2Idb4nEE0B0Bdo4niE0J0CeYhOhgESUYYnidsgnEE0KeCE0gnDE0ciA")),
|
||||
6: require("heatshrink").decompress(atob("qFGwkCkQA/ABEgKQZPhEwgABEsAoGJkBxBE8JKEAowAbJIhQEgLDiPooAdKA4ncTZAndSwhQEFoInaJQkSKAwlZdgwnfSgYADE4h1ZDwInlcggnIOzAdCE8i7EY5J3XDgYhGd4pOZEI52bSYwGCOAJ2bYIodEOzZOFFAjFcEwwAIE6xOHABBO/J34ndEyx2PJ00BJ00SJ0p1XE54mXOxxO/J5wmYgQnMOrB2BPBgkWiJ1CPBbBYAYR4KiTAXRwIrFTjgZDJYZ4IEyoiEIwrDcEJJQFOqwiBDARxFFwgmXkAYDEogsBF4QmXEQJ7GUYYkBEzDKJAgYmdEQbKFEzonEKYgngJwgmfZggmjKQghgiBRGkBzeTgUikJRgc47LDErTnDEAkQJzkCJwYnEJzonEJIaddOwhJEJzgdBE4hYEJzieJADgnEE0KUCXzoAGkJLEiB2hOgQDBT0TsDT0YmlE4YmjkQ=")),
|
||||
7: require("heatshrink").decompress(atob("qFGwkCkQAhkIpBiQlhkBSEJ8InlEIIoFE7whEE8pQFE7giBJQoneI4MCTYhQDE7YdCYYondEQYnEPwZ1bE5BQCJzonHkR2ZEAkBE4pNBE7zHFYrYhFUgonaXAQeEEwruZEYcgiROHJ7AfDAwxOeAAURiAmHE65HIOzwmOJ35OPE6xOPO35O/J35O/J1gnPEyx2PEy5OOOq5OnE5xOYO5omZgJQMJrQnLiQnagR4JOq5nCDgZ1fEYRLDE5DoZkUQNoZ4GOrJKGAoomXOw7lCAwYmYDgJSEAAUBA4QDBJzB6FOQrDXJwTJFdLjJKE9jDYZRAmkKAwmhKAgmiKAYmBkApdJIgjCKYIncOQYvJYTovGE84lagR2DE4xOakBOEgJXFOjYnEJAbtdOwggEkAmbDgInDE0B0BE4QgcE5AkiXYbpCOLonGYo4nhPMYnCUEgnBY0kiA==")),
|
||||
8: require("heatshrink").decompress(atob("qFGwkCkQA/ABBSEJ8MgE4kBEsBPFE7xMCOIJ3hOYgFEE7rCGE70gE4pQBiAndYQwjBUohOZD4ZQFE7YkBE5AICYbZ2GE7sggJRCAA8iYzZOITroALE7EhExh4CAC0QExpPXOponZExx2XJ24nWdh52XdhzF/Yu5O/J35O0E55OXOx5O/J2omXE5x1XO54mYgQnMJrR4LOrciiAmiJgR4KEzIjDPBAlYiAiEeI51YkEBE4J5CD4KceTQQcBJgRQFdTZDCJIjDcNIqhGdTQmCkByFTTInDKgoAEE7ZEEJwhPdE1R1FE0InEE0R3DEwTGcDwomEE7hKFPYqafE8ROCE5DJbE5B/IEqh2ED4gnCJrMCJwgnEiB2bE4qeFEzUggQmIBQLEaEQImHLIImaE4YfcOw4lEFMLECS7onJO8wmkE4QljAAIA==")),
|
||||
};
|
||||
}
|
||||
|
||||
let rocket_sequence = 1;
|
||||
|
||||
let settings = require('Storage').readJSON("cassioWatch.settings.json", true) || {};
|
||||
let rocketSpeed = settings.rocketSpeed || 700;
|
||||
delete settings;
|
||||
|
||||
g.clear();
|
||||
|
||||
function DrawClock() {
|
||||
g.setFont("7x11Numeric7Seg", 3);
|
||||
g.clearRect(80, 57, 170, 96);
|
||||
g.setColor(0, 255, 255);
|
||||
g.drawRect(80, 57, 170, 96);
|
||||
g.fillRect(80, 57, 170, 96);
|
||||
g.setColor(0, 0, 0);
|
||||
g.drawString(require("locale").time(new Date(), 1), 70, 60);
|
||||
g.setFont("8x12", 2);
|
||||
g.drawString(require("locale").dow(new Date(), 2).toUpperCase(), 18, 130);
|
||||
g.setFont("8x12");
|
||||
g.drawString(require("locale").month(new Date(), 2).toUpperCase(), 80, 126);
|
||||
g.setFont("8x12", 2);
|
||||
const time = new Date().getDate();
|
||||
g.drawString(time < 10 ? "0" + time : time, 78, 137);
|
||||
}
|
||||
|
||||
function DrawBattery() {
|
||||
bigThenSmall(E.getBattery(), "%", 135, 21);
|
||||
}
|
||||
|
||||
function DrawRocket() {
|
||||
let Rocket = getRocketSequences();
|
||||
g.clearRect(5, 62, 63, 115);
|
||||
g.setColor(0, 255, 255);
|
||||
g.drawRect(5, 62, 63, 115);
|
||||
g.fillRect(5, 62, 63, 115);
|
||||
g.drawImage(Rocket[rocket_sequence], 5, 65, { scale: 0.7 });
|
||||
g.setColor(0, 0, 0);
|
||||
rocket_sequence = rocket_sequence + 1;
|
||||
if (rocket_sequence > 8) rocket_sequence = 1;
|
||||
}
|
||||
|
||||
function DrawScene() {
|
||||
g.reset();
|
||||
g.clear();
|
||||
g.setColor(0, 255, 255);
|
||||
g.fillRect(0, 0, g.getWidth(), g.getHeight());
|
||||
let background = getBackgroundImage();
|
||||
g.drawImage(background, 0, 0, { scale: 1 });
|
||||
g.setColor(0, 0, 0);
|
||||
g.setFont("6x12");
|
||||
g.drawString("Launching Process", 30, 20);
|
||||
g.setFont("8x12");
|
||||
g.drawString("ACTIVATE", 40, 35);
|
||||
g.setFont("8x12", 2);
|
||||
g.drawString("30", 142, 132);
|
||||
g.drawString("55", 95, 98);
|
||||
g.setFont("8x12", 1);
|
||||
g.drawString(Bangle.getStepCount(), 143, 104);
|
||||
ClockInterval = setInterval(DrawClock, 30000);
|
||||
DrawClock();
|
||||
RocketInterval = setInterval(DrawRocket, rocketSpeed);
|
||||
DrawRocket();
|
||||
BatteryInterval = setInterval(DrawBattery, 5 * 60000);
|
||||
DrawBattery();
|
||||
}
|
||||
|
||||
Bangle.on("lcdPower", (on) => {
|
||||
if (!on) {
|
||||
g.clear();
|
||||
ClearIntervals(true);
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.on("lock", (locked) => {
|
||||
if (locked) {
|
||||
ClearIntervals(true);
|
||||
} else {
|
||||
ClearIntervals();
|
||||
DrawScene();
|
||||
}
|
||||
});
|
||||
|
||||
g.reset();
|
||||
g.clear();
|
||||
Bangle.setUI("clock");
|
||||
DrawScene();
|
||||
|
||||
if (Bangle.isLocked()) {
|
||||
ClearIntervals(true);
|
||||
}
|
After Width: | Height: | Size: 4.7 KiB |
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("lkswkGswAHtGIxGGBhATJAAYXNCYoWOFIwWNChQWKBYWYCxIqTxGJFgwnDnACBkUiCwuYFQo9ECYIAClAsJxIUElAUDwQWEyxAHBwITBmczmUiCwprHCgMjmUhiIYBA4JCGIAeCwQUBCYIXBiUyFggVCFQsziMjmdCkcxiZbBiJCEFQZUBmND93uolEochFgpWEIAUUCgIACp00iZVBzIVEAoJABmUeConu8cRiNEoMZNwIrCzEiiUxpwVF901IwNN6JuBtGJlAVBkchCg3umkSiMNCoIAEQIJWFkgCB8UYinUjIVFxEhKwszDAUU7tRCg2hilTH4xVB6kRzAUGBQIVECYVEorEDAAeBptBiUenw7BCYQACieCCosd6MYwUVBwNUAQYvBeYOJCYVoxVeoK5BAAq0C8MjiM4LALFCilNCAQpCN4lBmUoFQQVCxTlBiMieI3uqUoagIVDRAeCkclCgvlkciFYdmsxwDlESmTdE8lRmSCECosiFgMhqgUDkZABBwWYCoNpIIYXBmchiKLBkYqBNggVBNwIsECwMikQUBlEiBgWJCoRCEEQITDNIJrEIAQABZYOYnIWBFoQUGIAZCCTYYWCAAQUExIUDFggNDAA5AEFg4AIIAhvGEYU4ChgWHAAoUIWYpdFFRIWHChxEICZoWFFBIA=="))
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "cassioWatch",
|
||||
"name": "Cassio Watch",
|
||||
"description": "Animated Clock with Space Cassio Watch Style",
|
||||
"screenshots": [{ "url": "screens/screen_night.png" },{ "url": "screens/screen_day.png" }],
|
||||
"icon": "app.png",
|
||||
"version": "0.9",
|
||||
"type": "clock",
|
||||
"tags": "clock, weather, cassio, retro",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{ "name": "cassioWatch.app.js", "url": "app.js" },
|
||||
{"name":"cassioWatch.settings.js","url":"settings.js"},
|
||||
{ "name": "cassioWatch.img", "url": "icon.js", "evaluate": true }
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 60 KiB |
|
@ -0,0 +1,24 @@
|
|||
(function(back) {
|
||||
var FILE = "cassioWatch.settings.json";
|
||||
var settings = Object.assign({
|
||||
rocketSpeed: 700,
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
|
||||
E.showMenu({
|
||||
"" : { "title" : "Cassio Watch" },
|
||||
"< Back" : () => back(),
|
||||
'Rocket Speed': {
|
||||
value: 0|settings.rocketSpeed,
|
||||
min: 100, max: 60000,
|
||||
onchange: v => {
|
||||
settings.rocketSpeed = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
After Width: | Height: | Size: 6.1 KiB |
|
@ -0,0 +1,2 @@
|
|||
0.01: New clock
|
||||
0.02: Use ClockFace library, add settings
|
|
@ -0,0 +1,111 @@
|
|||
Graphics.prototype.setFont15x32N = function() {
|
||||
this.setFontCustom(atob(
|
||||
// 15x32.png, converted using http://ebfc.mattbrailsford.com/
|
||||
"/////oAAAAKAAAACgAAAAoAAAAKAAAACgf//AoEAAQKB//8CgAAAAoAAAAKAAAACgAAAAoAAAAL////+/wAB/oEAAQKBAAECgf//AoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAAC////AgAAAQIAAAH+/w///oEIAAKBCAACgQgAAoEIAAKBCAACgQg/AoEIIQKB+CECgAAhAoAAIQKAACECgAAhAoAAIQL//+H+/w/h/oEIIQKBCCECgQghAoEIIQKBCCECgQghAoEIIQKB+D8CgAAAAoAAAAKAAAACgAAAAoAAAAL////+///gAIAAIACAACAAgAAgAIAAIAD/+CAAAAggAAAIIAAACD/+//gAAoAAAAKAAAACgAAAAoAAAAL////+///h/oAAIQKAACECgAAhAoAAIQKAACECgfghAoEIIQKBCD8CgQgAAoEIAAKBCAACgQgAAoEIAAL/D//+/////oAAAAKAAAACgAAAAoAAAAKAAAACgfg/AoEIIQKBCD8CgQgAAoEIAAKBCAACgQgAAoEIAAL/D//+/wAAAIEAAACBAAAAgQAAAIEAAACBAAAAgQAAAIH///6AAAACgAAAAoAAAAKAAAACgAAAAoAAAAL////+/////oAAAAKAAAACgAAAAoAAAAKAAAACgfg/AoEIIQKB+D8CgAAAAoAAAAKAAAACgAAAAoAAAAL////+///h/oAAIQKAACECgAAhAoAAIQKAACECgfghAoEIIQKB+D8CgAAAAoAAAAKAAAACgAAAAoAAAAL////+"
|
||||
), "0".charCodeAt(0), 15, 32);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add coordinates for nth tooth to vertices
|
||||
* @param {array} poly Array to add points to
|
||||
* @param {number} n Tooth number
|
||||
*/
|
||||
function addTooth(poly, n) {
|
||||
const
|
||||
tau = Math.PI*2, arc = tau/clock.teeth,
|
||||
e = arc*clock.edge, p = arc*clock.point, s = (arc-(e+p))/2; // edge,point,slopes
|
||||
const sin = Math.sin, cos = Math.cos,
|
||||
x = clock.x, y = clock.y,
|
||||
r2 = clock.r2, r3 = clock.r3;
|
||||
let r = (n-1)*arc+e/2; // rads
|
||||
poly.push(x+r2*sin(r), y-r2*cos(r));
|
||||
r += s;
|
||||
poly.push(x+r3*sin(r), y-r3*cos(r));
|
||||
r += p;
|
||||
poly.push(x+r3*sin(r), y-r3*cos(r));
|
||||
r += s;
|
||||
poly.push(x+r2*sin(r), y-r2*cos(r));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n Tooth number to fill (1-based)
|
||||
* @param col Fill color
|
||||
*/
|
||||
function fillTooth(n, col) {
|
||||
if (!n) return; // easiest to check here
|
||||
let poly = [];
|
||||
addTooth(poly, n);
|
||||
g.setColor(col).fillPoly(poly)
|
||||
.setColor(g.theme.fg2).drawPoly(poly); // fillPoly colored over the outline
|
||||
}
|
||||
|
||||
const ClockFace = require("ClockFace");
|
||||
const clock = new ClockFace({
|
||||
precision: 1,
|
||||
settingsFile: "cogclock.settings.json",
|
||||
init: function() {
|
||||
this.r1 = 84; // inner radius
|
||||
this.r3 = Math.min(Bangle.appRect.w/2, Bangle.appRect.h/2); // outer radius
|
||||
this.r2 = (this.r1*3+this.r3*2)/5;
|
||||
this.teeth = 12;
|
||||
this.edge = 0.45;
|
||||
this.point = 0.35; // as fraction of arc
|
||||
this.x = Bangle.appRect.x+Bangle.appRect.w/2;
|
||||
this.y = Bangle.appRect.y+Bangle.appRect.h/2;
|
||||
},
|
||||
draw: function(d) {
|
||||
const x = this.x, y = this.y;
|
||||
g.setColor(g.theme.bg2).fillCircle(x, y, this.r2) // fill cog
|
||||
.setColor(g.theme.bg).fillCircle(x, y, this.r1) // clear center
|
||||
.setColor(g.theme.fg2).drawCircle(x, y, this.r1); // draw inner border
|
||||
let poly = []; // complete teeth outline
|
||||
for(let t = 1; t<=this.teeth; t++) {
|
||||
fillTooth(t, g.theme.bg2);
|
||||
addTooth(poly, t);
|
||||
}
|
||||
g.drawPoly(poly, true); // draw outer border
|
||||
if (!this.showDate) {
|
||||
// draw top/bottom rectangles (instead of year/date)
|
||||
g.reset()
|
||||
.fillRect(x-30, y-60, x+29, y-33).clearRect(x-28, y-58, x+27, y-33)
|
||||
.fillRect(x-30, y+60, x+29, y+30).clearRect(x-28, y+58, x+27, y+30);
|
||||
}
|
||||
this.tooth = 0;
|
||||
this.update(d, {s: 1, m: 1, h: 1, d: 1});
|
||||
},
|
||||
update: function(d, c) {
|
||||
g.reset();
|
||||
const pad2 = num => (num<10 ? "0" : "")+num,
|
||||
year = d.getFullYear(),
|
||||
date = pad2(d.getDate())+pad2(d.getMonth()),
|
||||
time = pad2(d.getHours())+pad2(d.getMinutes()),
|
||||
tooth = Math.round(d.getSeconds()/60*this.teeth);
|
||||
const x = this.x, y = this.y;
|
||||
if (c.m) {
|
||||
g.setFont("15x32N:2").setFontAlign(0, 0) // center middle
|
||||
.drawString(time, x, y, true);
|
||||
}
|
||||
if (this.showDate) {
|
||||
if (c.d) {
|
||||
g.setFont("15x32N").setFontAlign(0, -1) // center top
|
||||
.drawString(year, x, y+32, true)
|
||||
.setFont("15x32N").setFontAlign(0, 1) // center bottom
|
||||
.drawString(date, x, y-32, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (tooth!==this.tooth) {
|
||||
if (tooth>this.tooth) {
|
||||
for(let t = this.tooth; t<=tooth; t++) { // fill missing teeth
|
||||
fillTooth(t, g.theme.fg2);
|
||||
}
|
||||
} else {
|
||||
for(let t = this.tooth; t>tooth; t--) { // erase extraneous teeth
|
||||
fillTooth(t, g.theme.bg2);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.tooth = tooth;
|
||||
}
|
||||
});
|
||||
clock.start();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/ACcikQXpCQUCC4MgBAgqMCQIXEAgQXNBwIDCAggXOABAXLHwQAHMYQXmmczI5oiCBwUjCwIABmQXDEgJ0KCwMwCwMDDAgyGLYoWBgAXBgAYBMZIXEkYWBC4YYBGAh7FFwgHCC4YEBPRIwCFwYXFGAaqHC56oIIwgXFJAbUJLwpgHI4qPDIwpIFR4wWDLwa6BAAQHDVIYYCC/gYCC453MPIR3HU5gADd5bXHC4rvJMAYAECwJeCd5MjGAjVDC4ZHGNARIFGAgNDFw5IJUogwFC4gwBDAhGBBghIFBQhhBbYguEPAweCDAgACCwZACNg5LFXQYsIC5QAFdg4XcCxJHNBwYTEC6A+BJYQEEC5YYBMYhbCCxo0GCaIXbAHgA="))
|
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"id": "cogclock",
|
||||
"name": "Cog Clock",
|
||||
"version": "0.02",
|
||||
"description": "A cross-shaped clock inside a cog",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"cogclock.app.js","url":"app.js"},
|
||||
{"name":"cogclock.settings.js","url":"settings.js"},
|
||||
{"name":"cogclock.img","url":"icon.js","evaluate":true}
|
||||
],
|
||||
"data": [
|
||||
{"name": "cogclock.settings.json"}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 5.7 KiB |
|
@ -0,0 +1,19 @@
|
|||
(function(back) {
|
||||
let s = require('Storage').readJSON("cogclock.settings.json", true) || {};
|
||||
|
||||
function saver(key) {
|
||||
return value => {
|
||||
s[key] = value;
|
||||
require('Storage').writeJSON("cogclock.settings.json", s);
|
||||
}
|
||||
}
|
||||
|
||||
const menu = {
|
||||
"": {"title": /*LANG*/"Cog Clock"},
|
||||
/*LANG*/"< Back": back,
|
||||
/*LANG*/"Show date": require("ClockFace_menu").showDate(s.showDate, saver('showDate')),
|
||||
/*LANG*/"Load widgets": require("ClockFace_menu").loadWidgets(s.loadWidgets, saver('loadWidgets')),
|
||||
};
|
||||
|
||||
E.showMenu(menu);
|
||||
});
|
|
@ -4,3 +4,4 @@
|
|||
0.04: Fix for Bangle.js 2 and themes
|
||||
0.05: Fix bearing not clearing correctly (visible in single or double digit bearings)
|
||||
0.06: Add button for force compass calibration
|
||||
0.07: Use 360-heading to output the correct heading value (fix #1866)
|
||||
|
|
|
@ -49,7 +49,7 @@ Bangle.on('mag', function(m) {
|
|||
g.setFontAlign(0,0).setFont("6x8",3);
|
||||
var y = 36;
|
||||
g.clearRect(M-40,24,M+40,48);
|
||||
g.drawString(Math.round(m.heading),M,y,true);
|
||||
g.drawString(Math.round(360-m.heading),M,y,true);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "compass",
|
||||
"name": "Compass",
|
||||
"version": "0.06",
|
||||
"version": "0.07",
|
||||
"description": "Simple compass that points North",
|
||||
"icon": "compass.png",
|
||||
"screenshots": [{"url":"screenshot_compass.png"}],
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
1.00: Initial implementation
|
|
@ -0,0 +1,3 @@
|
|||
# Classic Football Chronometer Game
|
||||
|
||||
Context: https://www.anaitgames.com/analisis/analisis-casio-football-14
|
|
@ -0,0 +1 @@
|
|||
atob("MDABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAADwAAAAAADwAAAAAADwAAAAAADwAAAAAA//AAAAAA//AAAAAA//AAAAAA//AAAAAADw8AD/AADw8AD/AADw8AD/AADw8AD/AP/wAAD/AP/wAAD/AP/wAAD/AP/wAAD/AA8PAAAAAA8PAAAAAA8PAAAAAA8PAAAAAAAA8AAAAAAA8AAAAAAA8AAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
|
|
@ -0,0 +1,440 @@
|
|||
const digit = [];
|
||||
|
||||
const dash = {
|
||||
width: 75,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AH4A/AH4A/AH4A/AH4A/AB0D/4AB+AJEBAX/BAk/CQ8PCQ4kDCQoIDCQgkDCQgkECQgIE4ASHFxH8JRgSEEgYSEPJAkEAH4A/AH4A/AH4A/AH4A/ACg='))
|
||||
};
|
||||
function loadDigits () {
|
||||
digit[0] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AH4AGn//AAngBIMfBIvABIMPBIuABIMHBIoIBg0DBAn+gYSBgIJE/kHBIOABIn4h4JB4F/BIfwj4JB8BQEAoIJBBoJOEv4JBEIJOEIwMHGoIJDIIIJBJIJOEBIQOCJwYJDOIR9DBISFCSIYJCTISlDBIXwBIZoBBP4J/BP4J/BNX+gED//gBIc/BIMB//ABIcf/gDB/+ABIcP/AhCBAYuBFoU+BIkDFoUcBIkBFoUIBIkAFogA/AAZPJMZJ3JRZKfIWZLHJbZL5bBP4J/BP4J/BKPgBIc/BIfABIcfBIeA/4AB/EPBIcBBIX8AwIJB/0DBIQECBIIOCAAQYBBIIiCAAQsBBIPwGwIAC4F/BIPgJQIACAoIJBBoIJDDIIJBJwZQDBIJODKAcAgxODKAZxBJwgABPYROEKASFDAAiRCJwhQCTYYAkA'))
|
||||
};
|
||||
|
||||
digit[1] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AH4A2wAIHgIJIgYJIg4JIh4JIj4JIn4JIv4JI/4JHgIJIgYJIg4JIh4JIj4JIn4J/BP4J/BP4Jqj//BA0Ah/+BI8H/gJHgf4BI8B+AJHgHgBJFABJAA/J55jIO5KLJT5KzJY5L5nBP4J/BP4J/AAcfBJEPBJEHBJEDBJEBBJEABJN/BJE/BJEfBJEPBJEHBJEDBJEBBJEABJIA/AAwA='))
|
||||
};
|
||||
|
||||
digit[2] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AH4AGj//AAnwBIMPBIvgBIMHBIvABIMDBIuABIMBBIsBGQQIE/0DBIV/BIf8g4JCn4JD/EPKA/wj4JCKAngn4JCKAnAv4JCKAmA/4JCKAgEBQYZOEBIgADFgIJHIAKlJBI5oBBP4J/BP4J/BOcfBJEP/wJHg/8Aof/AAP+gf4BAUBBIX/gPwBIUDBIeA8AiDBIfAoA2DBIYSDJQQACEwZeCAAQ6DgF/BJATJE5I7IghPFBIUOMYomDO4g6EwCLDJwgiDAAhyFTohKEToheEBP4J/BP4J/BOHwBJHgBJHAv////8BImABAP//wJEAIIACBIf+BImABIX8g4JD4AJC/EPBIZACgfwj4JDKgUD8E/BIZoCgZODKAkDJwZQEgcBBIhQCgROEKAhOEKAhOEKAhOEKAgAm'))
|
||||
};
|
||||
|
||||
digit[3] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AH4AGj//AAnwBIMPBIvgBIMHBIvABIMDBIuABIMBBIsBGQQIE/0DBIV/BIf8g4JCn4JD/EPKA/wj4JCKAngn4JCKAnAv4JCKAmA/4JCKAgEBQYZOEBIgADFgIJHIAKlJBI5oBBI58BBP4J/BP4J/BL8/BJEf/wJHh/8BI8H/AFD/4AB/0D+AICgIJDgPgBIUDBIQ5B4AiDBIeAwA2DBIYSDJQIJDEwZeCAAQ6DOQQACJwgTJE5I7JJ5JjEgIUDO4kDFAgJC/kDIwipNj4JIn7HIbZL5TBP4J/BP4J/BJs/BJEfBJEPBIgjB//8g4JDgIIB//+gYJDAgIACBwIJCDAIACwAJDFgIAC4F/IAgAC8E/KggAC+EfIgoAB/EPBIQIDKAROFKAZOGKAROGKAQJI4BOGKAQJI+CfHAEAA=='))
|
||||
};
|
||||
digit[4] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AH4AswEBBJOABAoHBgPABIsDBIPgBIsHBIPwBIsPBIP4BIsfBIP8BIs/BIP+BIt/BIP/BIv/BIRQEAwQCBKAkDBIZQEg4JDKAkPBIZQEj4JIn4J/BP4J/BP4JjgAJFj//AYN/8AJDh/+C4QJEg/8C4XAv////+gYjCh+ABAIABgPwC4Q9BAAWAEYUCgYJD4FAFgYJDIAoJDEwRUDAARoGAAROCCZYnJHZJPJMZAABO46hCRYwAFT4YAFWYYAFY4YAFbYYJ/BP4J/BP4Jnj4JIh4JIg4JIgYJIgIJIgAJJv4JIn4JIj4JIh4JIg4JIgYJIgIJIgAJJAH4AGA='))
|
||||
};
|
||||
|
||||
digit[5] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AGUP/4AE/gJBg4JF/AJBgYJF+AJBgIJF8AJBwAJF4AJB4F/BImABIPgn4JEIoXwj4ID/wJC/BQEJwUA/hQEJwUA/xQEJwUA/5QEJwQJBKAhOCBIJQEJwQJBKAiVDFggAEIAgJFKgYJFNAYJ/BP4J/BP4Jmv/8BI8//AJHj/wBI8P8E//4ABBIcH4F/BIWABIUDwAIC//ABIUBgIJD8AeDgYJDGwkHBIZKEh4JDLwkfBIZyECZInJHZJPFkChEMYdwUIh3DFAiLDgKvIgbDIJQKvIUIgJFUIZ8FBP4J/BP4J/BL8PBJEHBJEDBJEBBIl//4ABwAJEBAQHBv4JCDAIAC8E/BIQsBAAXwj4JCIAIAC/EPBIRUBAAX8g4JCNAIAC/0DBI//gIJCJwZQCBIQIEKANAJwpQCJwxQCJwxQCJwxQCBJYAwA='))
|
||||
};
|
||||
|
||||
digit[6] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AGU//4AE8AJBj4JF4AJBh4JFwAJBg4JFBAMGgYIE/wSCgIJE/gJCwAJE/AJC4F/BIfwBIXgKAhOCg/wKAhOCg/4KAhOD/hQEOwUH/xQDJwRiCKAZOCBIRQDJwQJCGwQAEBIJKCBIxeCBP4J/BP4J/BOED//gBI0B//ABI0A/+ABI9/CoIAB/gJDnwpBAAP+BIccHoIACBIcIh4JDFgkfBIZAEBIhUEv4JDNAgJE/ATNn4nIHZBPGKARjFgIUCO4sDFASLFg48COQsPKARyGUILHGn6hBBIJyGco4J/BP4J/BP4Jm8AJDn4JD4AJDj4JDwAJDh4JDgP/AAP8AwIJB/0DBIQECBIIOCAAQYBBIP4EQIACwAJC+A2BAAXAv4JB8BKBAAQFBBIINBBIYZBBIIhBAAYtBBIJODKAcAgxODKAZnBJwhQCOIYAEPoROEKASZDAAilCJwhQCTYYAuA=='))
|
||||
};
|
||||
|
||||
digit[7] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AGUH/4AE/wJBgYJF/gJBgIJF+AeCBJN/BIngsAJBn4JE4HgBIMfBImBBIUPBIkDBIRQE/0HBIRQE/kPBIRQE/EfBIRQE+E/BIZQD8AJEKAfABYIJCKAYsBBIYADIAIJHKgIJHNAIJ/BP4J/BP4Jzg//4AJGgf/wAJGgP/BAwAB/wJIvgJInAJIiAJIAH5PPMZJ3JRZCfJWZLHJfM4J/BP4J/BP4JNg4JIgYJIgIJIgAJJv4JIn4JIj4JIh4JIeg4JIgYJIgIJIgAJJsAJIkAJIAH4AQA='))
|
||||
};
|
||||
|
||||
digit[8] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AGUf/4AE+AJBh4JF8AJBg4JF4AJBgYJFwAIBgIJFgOAgeABAn+A4MD4F/BIf8g4JB8E/BIf4h4JB+BQEAoIJBBoJOEn4JBEIJOEv4JBGoJOEAIIHBKAgEBBIRQDDAQJCKAYsCBISFCSIYJCTISlDBIX4BIZoBBP4J/BP4J/BNkB//wBIcf/4DB//gBIcP/wDBv/ABIcH/ghCwH/AAP+gYtCj4pBAAUBFoUOHoIACwAtCgkHBIfAoA2DBIZAEJQIACKgheBAARoGBKInJHZBPJMZJ3JRZKfJWZDHJfM4J/BP4J/BP4JP+AJDj4JD8AJDh4JD4AJDg4JDwH/AAP+AwQCBgIJCAgQJBBwQACDAIJB/giBAAXAv4JB/A2BAAXgn4JB+BKBAAQFBBIINBBIYZBBIIhBBIYtBBIJODKAYJBJwhQCwECJwhQCwBxCAAh9CJwhQCTIYAEUoROEKASbDAFwA='))
|
||||
};
|
||||
|
||||
digit[9] = {
|
||||
width: 80,
|
||||
height: 128,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('AGUP/4AE/gJBg4JF/AJBgYJF+AJBgIJF8AJBwAJF4FAgHAv4JEwHAgHgn4JEgIJB+EfBAf+gYJB/BQE/kHBIIDBJwkPBIIXBJwkfBIIrBJwk/BIRQEJYIJCKAgOBBIXgIwYiBBIR7CQ4YJCR4SbDBISjCV4YJC/wJDFYIJ/BP4J/BP4Jjv/8BIcP/+AgE//AJDg//C4XwBIcDEYUP8E//4ABgIjCg/Av4JCwAjCgeABAQ5BuAJBgMBBIfgkAsDBIY2EIAIACJQhUBAAReENAIACOQgTJE5I7JJ5KhBMYwABO44ABRY4AFT4YAFWYYAFY4YAFbYYJ/BP4J/BP4Jnh4JIg4JIgYJIgIJEv//AAOABIgICA4N/BIQYBAAXgn4JCFgIAC+EfBIRABAAX4h4JCKgIAC/kHBIRoBAAX+gYJCn4JD/8BBIRODKAQJCBAhQBoBOFKAROGKAROGKAROGKAQJLAGA='))
|
||||
};
|
||||
}
|
||||
|
||||
// sprites
|
||||
|
||||
const left0 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('ADAwEDgUcCgEAA==')
|
||||
};
|
||||
|
||||
const left1 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('ADAwEDh0ECAoAA==')
|
||||
};
|
||||
|
||||
const left2 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('ADAwEBg4EBg0AA==')
|
||||
};
|
||||
|
||||
const left4 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('ABgYVDgQEChEAA==')
|
||||
};
|
||||
|
||||
const right0 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('AAwMCBwoDhQgAA==')
|
||||
};
|
||||
|
||||
const right1 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('AAwMCBwuCAQUAA==')
|
||||
};
|
||||
|
||||
const right2 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('AAwMCBgcCBgsAA==')
|
||||
};
|
||||
|
||||
const right4 = left4;
|
||||
|
||||
const ball0 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('AAAAAAAwMAAAAA==')
|
||||
};
|
||||
|
||||
const ball1 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('AAAAAAAAGBgAAA==')
|
||||
};
|
||||
const gol01 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
transparent: 0,
|
||||
buffer: atob('ABhkhIS0sIAAAA==')
|
||||
};
|
||||
|
||||
const gol11 = {
|
||||
width: 8,
|
||||
height: 10,
|
||||
bpp: 1,
|
||||
buffer: require('heatshrink').decompress(atob('gEYk0hkMthsBBAI='))
|
||||
|
||||
};
|
||||
|
||||
loadDigits();
|
||||
|
||||
let goalFrame = 0;
|
||||
let score0 = 0;
|
||||
let score1 = 0;
|
||||
|
||||
function printNumber (n, x, y, options) {
|
||||
if (n > 9 || n < -1) {
|
||||
console.log(n);
|
||||
return; // error
|
||||
}
|
||||
if (digit.length === 0) {
|
||||
g.setColor(1, 1, 1);
|
||||
if (options.scale == 0.2) {
|
||||
g.setFont12x20(1);
|
||||
} else {
|
||||
g.setFont12x20(2.5);
|
||||
}
|
||||
g.drawString('' + n, x, y);
|
||||
return;
|
||||
}
|
||||
const img = (n == -1) ? dash : digit[n];
|
||||
if (img) {
|
||||
// g.setColor(0,0,0);
|
||||
// g.fillRect(x,y,x+32*options.scale,64*options.scale);
|
||||
g.setColor(1, 1, 1);
|
||||
g.drawImage(img, x, y, options);
|
||||
}
|
||||
}
|
||||
let inc = 0;
|
||||
let msinc = 0;
|
||||
let seq0 = 0;
|
||||
let seq1 = 0;
|
||||
let goaler = -1;
|
||||
const w = g.getWidth();
|
||||
let owner = -1;
|
||||
g.setBgColor(0, 0, 0);
|
||||
g.clear();
|
||||
g.setColor(1, 1, 1);
|
||||
function onStop () {
|
||||
if (goalFrame > 0) {
|
||||
return;
|
||||
}
|
||||
stopped = !stopped;
|
||||
if (stopped) {
|
||||
// Bangle.beep();
|
||||
if (msinc == 0) {
|
||||
// GOOL
|
||||
if (owner == 0) {
|
||||
score0++;
|
||||
goaler = 0;
|
||||
} else if (owner == 1) {
|
||||
score1++;
|
||||
goaler = 1;
|
||||
}
|
||||
goalFrame = 5;
|
||||
}
|
||||
let newOwner = 0;
|
||||
if (msinc % 2) {
|
||||
newOwner = 1;
|
||||
} else {
|
||||
newOwner = 0;
|
||||
}
|
||||
if (newOwner) {
|
||||
seq0--;
|
||||
seq1++;
|
||||
} else {
|
||||
seq0++;
|
||||
seq1--;
|
||||
}
|
||||
if (seq0 < 0) seq0 = 0;
|
||||
if (seq0 > 3) seq0 = 3;
|
||||
if (seq1 < 0) seq1 = 0;
|
||||
if (seq1 > 3) seq1 = 3;
|
||||
owner = newOwner;
|
||||
}
|
||||
refresh();
|
||||
refresh_ms();
|
||||
}
|
||||
var stopped = true;
|
||||
Bangle.on('tap', function (pos) {
|
||||
console.log('touch', pos);
|
||||
if (endGame) {
|
||||
Bangle.beep();
|
||||
score0 = 0;
|
||||
score1 = 0;
|
||||
seq0 = 0;
|
||||
seq1 = 0;
|
||||
inc = 0;
|
||||
msinc = 0;
|
||||
stopped = true;
|
||||
endGame = false;
|
||||
} else {
|
||||
if (inc == 0) {
|
||||
autogame();
|
||||
} else {
|
||||
onStop();
|
||||
}
|
||||
}
|
||||
});
|
||||
g.setFont12x20(3);
|
||||
let part = 0;
|
||||
let endInc = 0;
|
||||
var endGame = false;
|
||||
function refresh () {
|
||||
g.clear();
|
||||
if (inc > 59) {
|
||||
inc = 0;
|
||||
part++;
|
||||
}
|
||||
if (inc > 44) {
|
||||
if (part < 2) {
|
||||
part++;
|
||||
}
|
||||
if (part >= 2) {
|
||||
if (score0 != score1) {
|
||||
endGame = true;
|
||||
endInc = inc;
|
||||
inc = 0;
|
||||
}
|
||||
}
|
||||
// end of 1st or 2nd part of the game?
|
||||
}
|
||||
let two = (inc < 10) ? '0' + inc : '' + inc;
|
||||
if (endGame) {
|
||||
g.setColor(0, 0, 0);
|
||||
g.fillRect(0, 64, w, h);
|
||||
if (inc % 2) {
|
||||
two = (endInc < 10) ? '0' + endInc : '' + endInc;
|
||||
printNumber(-1, 2, 64 + 16, { scale: 0.4 });
|
||||
printNumber(part, 34, 64 + 16, { scale: 0.4 });
|
||||
printNumber(two[0], 74, 64 + 16, { scale: 0.4 });
|
||||
printNumber(two[1], 74 + 32, 64 + 16, { scale: 0.4 });
|
||||
}
|
||||
} else {
|
||||
// seconds
|
||||
printNumber(0, 2, 64 + 16, { scale: 0.4 });
|
||||
printNumber(part, 34, 64 + 16, { scale: 0.4 });
|
||||
printNumber(two[0], 74, 64 + 16, { scale: 0.4 });
|
||||
printNumber(two[1], 74 + 32, 64 + 16, { scale: 0.4 });
|
||||
}
|
||||
refresh_ms();
|
||||
refresh_score();
|
||||
refresh_pixels();
|
||||
}
|
||||
|
||||
function refresh_pixels () {
|
||||
let frame4 = inc % 2;
|
||||
if (goalFrame > 0) {
|
||||
frame4 = goalFrame % 2;
|
||||
if (goaler == 0) {
|
||||
g.drawImage(frame4 ? right4 : right0, 20, 10, { scale: 5 });
|
||||
g.setColor(1, 1, 1);
|
||||
g.drawImage(gol11, w - 50, 10, { scale: 5 });
|
||||
} else if (goaler == 1) {
|
||||
g.drawImage(frame4 ? left0 : left4, w - 50, 10, { scale: 5 });
|
||||
g.setColor(1, 1, 1);
|
||||
g.drawImage(gol01, 30, 10, { scale: 5 });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (endGame) {
|
||||
if (score0 > score1) {
|
||||
g.drawImage(frame4 ? right1 : right0, 5 + (seq0 * 10), 10, { scale: 5 });
|
||||
} else if (score0 < score1) {
|
||||
g.drawImage(frame4 ? left0 : left1, w - 30 - (seq1 * 10), 10, { scale: 5 });
|
||||
}
|
||||
return;
|
||||
}
|
||||
g.drawImage(frame4 ? right1 : right0, 5 + (seq0 * 10), 10, { scale: 5 });
|
||||
g.drawImage(frame4 ? left0 : left1, w - 30 - (seq1 * 10), 10, { scale: 5 });
|
||||
let bx = (owner == 0) ? w / 3 : w / 2;
|
||||
bx += 2;
|
||||
g.drawImage(frame4 ? ball0 : ball1, bx, 10, { scale: 5 });
|
||||
}
|
||||
let dots = 0;
|
||||
function refresh_dots () {
|
||||
if (endGame) {
|
||||
dots = 0;
|
||||
} else {
|
||||
dots++;
|
||||
}
|
||||
if (dots % 2) {
|
||||
g.setColor(1, 1, 1);
|
||||
} else {
|
||||
g.setColor(0, 0, 0);
|
||||
}
|
||||
const x = 67;
|
||||
let y = 100;
|
||||
g.fillRect(x, y, x + 4, y + 4);
|
||||
y += 16;
|
||||
g.fillRect(x, y, x + 4, y + 4);
|
||||
}
|
||||
|
||||
const h = g.getHeight();
|
||||
|
||||
function refresh_ms () {
|
||||
if (endGame) {
|
||||
if (inc % 2) {
|
||||
printNumber(-1, 80 + 64 - 4, 64 + 16 + 8 + 16, { scale: 0.2 });
|
||||
printNumber(-1, 80 + 64 + 16 - 4, 64 + 32 + 8, { scale: 0.2 });
|
||||
}
|
||||
return;
|
||||
}
|
||||
// nanoseconds
|
||||
if (msinc > 59) {
|
||||
msinc = 0;
|
||||
}
|
||||
g.setColor(0, 0, 0);
|
||||
g.fillRect(80 + 64, h / 2, 80 + 64 + 32, g.getHeight());
|
||||
const two = (msinc < 10) ? '0' + msinc : '' + msinc;
|
||||
printNumber(two[0], 80 + 64 - 4, 64 + 16 + 8 + 16, { scale: 0.2 });
|
||||
printNumber(two[1], 80 + 64 + 16 - 4, 64 + 32 + 8, { scale: 0.2 });
|
||||
}
|
||||
|
||||
function refresh_score () {
|
||||
g.setColor(0, 0, 0);
|
||||
g.fillRect(0, h - 32, w, h);
|
||||
let two = (score0 < 10) ? '0' + score0 : '' + score0;
|
||||
printNumber(two[0], 64 - 16, 32 + 64 + 16 + 8 + 16, { scale: 0.2 });
|
||||
printNumber(two[1], 64, 32 + 64 + 32 + 8, { scale: 0.2 });
|
||||
two = (score1 < 10) ? '0' + score1 : '' + score1;
|
||||
printNumber(two[0], 32 + 64, 32 + 64 + 16 + 8 + 16, { scale: 0.2 });
|
||||
printNumber(two[1], 32 + 64 + 16, 32 + 64 + 32 + 8, { scale: 0.2 });
|
||||
}
|
||||
refresh();
|
||||
|
||||
setInterval(function () {
|
||||
if (!stopped || endGame) {
|
||||
inc++;
|
||||
}
|
||||
if (goalFrame > 0) {
|
||||
goalFrame--;
|
||||
}
|
||||
refresh();
|
||||
}, 1000);
|
||||
|
||||
setInterval(function () {
|
||||
refresh_dots();
|
||||
}, 500);
|
||||
|
||||
setInterval(function () {
|
||||
if (!stopped) {
|
||||
msinc++;
|
||||
if (msinc > 59) {
|
||||
msinc = 0;
|
||||
}
|
||||
}
|
||||
}, 10);
|
||||
|
||||
setInterval(function () {
|
||||
if (!stopped) {
|
||||
refresh_ms();
|
||||
}
|
||||
}, 250);
|
||||
|
||||
function autogame () {
|
||||
if (endGame) {
|
||||
return;
|
||||
}
|
||||
onStop();
|
||||
if (stopped) {
|
||||
setTimeout(autogame, 500);
|
||||
} else {
|
||||
setTimeout(autogame, 315 + 10 * (Math.random() * 5));
|
||||
}
|
||||
}
|
||||
|
||||
Bangle.setOptions({ lockTimeout: 0, backlightTimeout: 0 });
|
||||
autogame();
|
After Width: | Height: | Size: 232 B |
After Width: | Height: | Size: 96 B |
After Width: | Height: | Size: 95 B |
After Width: | Height: | Size: 463 B |
After Width: | Height: | Size: 671 B |
After Width: | Height: | Size: 386 B |
After Width: | Height: | Size: 606 B |
After Width: | Height: | Size: 620 B |
After Width: | Height: | Size: 535 B |
After Width: | Height: | Size: 656 B |
After Width: | Height: | Size: 726 B |
After Width: | Height: | Size: 516 B |
After Width: | Height: | Size: 709 B |
After Width: | Height: | Size: 741 B |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 115 B |
After Width: | Height: | Size: 115 B |
After Width: | Height: | Size: 112 B |
After Width: | Height: | Size: 120 B |
After Width: | Height: | Size: 116 B |
After Width: | Height: | Size: 121 B |
After Width: | Height: | Size: 112 B |
After Width: | Height: | Size: 125 B |
After Width: | Height: | Size: 116 B |
After Width: | Height: | Size: 125 B |
After Width: | Height: | Size: 113 B |
After Width: | Height: | Size: 125 B |
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"id": "football",
|
||||
"name": "football",
|
||||
"shortName": "football",
|
||||
"version": "1.00",
|
||||
"type": "app",
|
||||
"description": "Classic football game of the CASIO chronometer",
|
||||
"icon": "app.png",
|
||||
"allow_emulator": true,
|
||||
"tags": "games",
|
||||
"supports": [
|
||||
"BANGLEJS2"
|
||||
],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{
|
||||
"name": "football.app.js",
|
||||
"url": "app.js"
|
||||
},
|
||||
{
|
||||
"name": "football.img",
|
||||
"url": "app-icon.js",
|
||||
"evaluate": true
|
||||
}
|
||||
],
|
||||
"screenshots": [
|
||||
{
|
||||
"url": "screenshot.png"
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -81,6 +81,7 @@ function onInit(device) {
|
|||
if (crc==3435933210) version = "2v11.52";
|
||||
if (crc==46757280) version = "2v11.58";
|
||||
if (crc==3508163280 || crc==1418074094) version = "2v12";
|
||||
if (crc==4056371285) version = "2v13";
|
||||
if (!ok) {
|
||||
version += `(⚠ update required)`;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ var nhwmn = { // New homework Menu
|
|||
|
||||
function newHomeworkMenu() {
|
||||
E.showMessage("Getting subjects...");
|
||||
var rawsubjects = require("Storage").read("subjects.txt"); // This code reads out the subjects list and removes the newline character at the end
|
||||
var rawsubjects = require("Storage").read("homework.subjects.txt"); // This code reads out the subjects list and removes the newline character at the end
|
||||
var splitsubjects = rawsubjects.split(",");
|
||||
var lastItem = splitsubjects[splitsubjects.length - 1];
|
||||
var thiscurrentsubject;
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"custom": "subjects.html",
|
||||
"data": [
|
||||
{"name":"homework.txt" },
|
||||
{"name":"homework.subjects.txt" }
|
||||
],
|
||||
"storage": [
|
||||
{"name":"homework.app.js","url":"app.js"},
|
||||
{"name":"homework.img","url":"app-icon.js","evaluate":true}
|
||||
|
|
|
@ -20,9 +20,10 @@
|
|||
// send finished app (in addition to contents of app.json)
|
||||
sendCustomizedApp({
|
||||
storage:[
|
||||
{name:"subjects.txt"},
|
||||
{name:"homework.subjects.txt", url:"subjects.txt", content:app},
|
||||
]
|
||||
});
|
||||
console.log("Sent homework.subjects.txt!");
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
@ -158,6 +158,8 @@ const itemsN = Math.ceil(apps.length / appsN);
|
|||
Bangle.setUI({
|
||||
mode: "custom",
|
||||
drag: (e) => {
|
||||
g.setColor(g.theme.fg);
|
||||
g.setBgColor(g.theme.bg);
|
||||
let dy = e.dy;
|
||||
if (scroll + R.h - dy > itemsN * itemSize) {
|
||||
dy = scroll + R.h - itemsN * itemSize;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
0.01: New App
|
||||
0.02: Allow drawing polys
|
||||
0.03: Allow partly importing Amazfit decompiler formatted watchfaces
|
||||
0.04: Allow writing all image data to separate file to save some RAM
|
||||
Allow hiding elements on lock
|
||||
0.05: Add precompilation to js for performance
|
||||
0.06: Watchfaces can be refreshed partly
|
||||
0.07: Allow wrapping drawing in timeouts to get faster reactions
|
||||
Show/Hide widgets with swipe up or down
|
|
@ -0,0 +1,297 @@
|
|||
# Imageclock
|
||||
|
||||
This app is a highly customizable watchface. To use it, you need to select
|
||||
a watchface from another source. There is a native format as described here. You can also load decompiled watchfaces for Amazfit BIP fitness trackers.
|
||||
|
||||
# Usage
|
||||
|
||||
## Install a watchface
|
||||
|
||||
Choose the folder which contains the watchface, then clock "Upload to watch".
|
||||
|
||||
## Usage on the watch
|
||||
|
||||
Slide up/down to hide/show widgets.
|
||||
Press button to start launcher.
|
||||
|
||||
# Design watch faces
|
||||
|
||||
## Folder structure
|
||||
|
||||
* watchfacename
|
||||
* resources/
|
||||
* face.json
|
||||
* info.json
|
||||
|
||||
|
||||
### resources
|
||||
|
||||
This folder contains image files. It can have subfolders. These files will
|
||||
be read and converted into a resource bundle used by the clock
|
||||
|
||||
Folder types:
|
||||
|
||||
* Number
|
||||
* Contains files named 0.ext to 9.ext and minus.ext
|
||||
* CodedImage
|
||||
* Contains files named with only digits for codes, i.e. 721.ext
|
||||
* Contains a file named fallback.ext in case no code matches
|
||||
* Codes are evaluated as follows: 721 -> if not found check 720 -> if not found check 700 -> if found use
|
||||
* MultiState
|
||||
* Notifications: sound.ext, silent.ext, vibrate.ext
|
||||
* other status icons: on.ext, off.ext
|
||||
* Scale
|
||||
* Contains the components of the scale, named 0.ext to y.ext, y beeing the last element of the scale
|
||||
|
||||
### face.json
|
||||
|
||||
This file contains the description of the watch face elements.
|
||||
|
||||
#### Object types:
|
||||
|
||||
##### Properties
|
||||
```
|
||||
Properties: {
|
||||
"Redraw": {
|
||||
"Unlocked": 5000,
|
||||
"Locked": 60000,
|
||||
"Default": "Always",
|
||||
"Events": ["HRM"],
|
||||
"Clear": true
|
||||
},
|
||||
"Events": ["lock","HRM"]
|
||||
}
|
||||
```
|
||||
|
||||
Possible values for `Default` are `Always`, `Change`.
|
||||
|
||||
##### Images
|
||||
|
||||
```
|
||||
"Image": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Scale": 1,
|
||||
"RotationValue": "Seconds",
|
||||
"MinRotationValue": 0,
|
||||
"MaxRotationValue": 60,
|
||||
"ImagePath": [ "path", "in", "resources", "file" ]
|
||||
}
|
||||
```
|
||||
|
||||
`RotationValue` references one of the implemented numerical values.
|
||||
Mandatory:
|
||||
* `ImagePath`
|
||||
|
||||
```
|
||||
"Image": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Value": "BatteryPercentage",
|
||||
"Steps": 7,
|
||||
"ImagePath": [ "path", "in", "resources", "file" ]
|
||||
}
|
||||
```
|
||||
If `Value` and `Steps`are given, the value is scaled and `Steps` number of images starting at 0 are used to display the value.
|
||||
|
||||
##### Coded Images
|
||||
```
|
||||
"CodedImage": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Value": "WeatherCode",
|
||||
"ImagePath": [ "path", "in", "resources", "file" ]
|
||||
}
|
||||
```
|
||||
The `Value` field is one of the implemented numerical values.
|
||||
|
||||
##### Number
|
||||
|
||||
Can be aligned to bottom left, top left, bottom right, top right and center. Will currently force all numbers to
|
||||
be integer.
|
||||
|
||||
```
|
||||
"Number": {
|
||||
"X": 123,
|
||||
"Y": 123,
|
||||
"Alignment": "BottomRight",
|
||||
"Value": "Temperature",
|
||||
"Spacing": 1,
|
||||
"ImagePath": [ "path", "to", "numbers", "folder" ]
|
||||
}
|
||||
```
|
||||
The `Value` field is one of the implemented numerical values.
|
||||
`Alignment` is either `BottomRight` or `TopLeft`
|
||||
|
||||
Mandatory:
|
||||
* `ImagePath`
|
||||
* `Value`
|
||||
|
||||
##### Scale
|
||||
|
||||
```
|
||||
"Scale": {
|
||||
"X": 123,
|
||||
"Y": 123,
|
||||
"Value": "Temperature",
|
||||
"MinValue": "-20",
|
||||
"MaxValue": "50",
|
||||
"ImagePath": [ "path", "to", "scale", "folder" ],
|
||||
"Segments": [
|
||||
{ "X": 5, "Y": 5},
|
||||
{ "X": 10, "Y": 10 }
|
||||
]
|
||||
}
|
||||
```
|
||||
The `Value` field is one of the implemented numerical values.
|
||||
`MaxValue` and `MinValue` set the start and endpoints of the scale.
|
||||
|
||||
Mandatory:
|
||||
* `ImagePath`
|
||||
* `Value`
|
||||
|
||||
##### MultiState
|
||||
|
||||
```
|
||||
"MultiState": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Value": "Lock",
|
||||
"ImagePath": ["icons", "status", "lock"]
|
||||
}
|
||||
```
|
||||
The `Value` field is one of the implemented multi state values.
|
||||
|
||||
Mandatory:
|
||||
* `ImagePath`
|
||||
* `Value`
|
||||
|
||||
##### Poly
|
||||
|
||||
```
|
||||
"Poly":{
|
||||
"Filled": true,
|
||||
"RotationValue": "Second",
|
||||
"MinRotationValue": "0",
|
||||
"MaxRotationValue": "60",
|
||||
"ForegroundColor": "#00f",
|
||||
"BackgroundColor": "#008",
|
||||
"Vertices":[
|
||||
{"X":-1, "Y":13},
|
||||
{"X":0, "Y":15},
|
||||
{"X":1, "Y":13},
|
||||
{"X":2, "Y":0},
|
||||
{"X":1, "Y":-75},
|
||||
{"X":0, "Y":-80},
|
||||
{"X":-1, "Y":-75},
|
||||
{"X":-2, "Y":0}
|
||||
]
|
||||
}
|
||||
```
|
||||
The `RotationValue` field is one of the implemented numeric values.
|
||||
|
||||
##### Rect
|
||||
|
||||
```
|
||||
"Rect":{
|
||||
"X": 10,
|
||||
"Y": 20,
|
||||
"Width": 30,
|
||||
"Height": 40,
|
||||
"Filled": true,
|
||||
"ForegroundColor": "#00f",
|
||||
"BackgroundColor": "#008"
|
||||
}
|
||||
```
|
||||
The `RotationValue` field is one of the implemented numeric values.
|
||||
|
||||
##### Nesting
|
||||
```
|
||||
"Container": {
|
||||
"X": 10,
|
||||
"Y": 10,
|
||||
"OtherContainer": {
|
||||
"X": 5,
|
||||
"Y": 5,
|
||||
"SomeElement": {
|
||||
"X": 2,
|
||||
"Y": 2,
|
||||
<Content>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
`SomeElement` will be drawn at X- and Y-position 2+5+10=17, because positions are relative to parent element.
|
||||
Container names can be everything but other object names.
|
||||
|
||||
#### Implemented data sources
|
||||
|
||||
##### Numerical
|
||||
|
||||
* Hour
|
||||
* Hour12Analog
|
||||
* Hour12
|
||||
* HourTens
|
||||
* HourOnes
|
||||
* Minute
|
||||
* MinuteAnalog
|
||||
* MinuteTens
|
||||
* MinuteOnes
|
||||
* Second
|
||||
* SecondAnalog
|
||||
* SecondTens
|
||||
* SecondOnes
|
||||
* WeekDay
|
||||
* WeekDayMondayFirst
|
||||
* Day
|
||||
* DayTens
|
||||
* DayOnes
|
||||
* Month
|
||||
* MonthTens
|
||||
* MonthOnes
|
||||
* Pulse
|
||||
* Steps
|
||||
* Temperature
|
||||
* Pressure
|
||||
* Altitude
|
||||
* BatteryPercentage
|
||||
* BatteryVoltage
|
||||
* StepsGoal
|
||||
* WeatherCode
|
||||
* WeatherTemperature
|
||||
|
||||
##### Multistate
|
||||
|
||||
* on/off
|
||||
* Lock
|
||||
* Charge
|
||||
* Alarm
|
||||
* Bluetooth
|
||||
* BluetoothPeripheral
|
||||
* HRM
|
||||
* Barometer
|
||||
* Compass
|
||||
* GPS
|
||||
* StepsGoal
|
||||
* WeatherTemperatureNegative
|
||||
* on/off/vibrate
|
||||
* Notifications
|
||||
* celsius/fahrenheit/unknown
|
||||
* WeatherTemperatureUnit
|
||||
|
||||
### info.json
|
||||
|
||||
This file contains information for the conversion process, it will not be
|
||||
stored on the watch
|
||||
|
||||
# TODO
|
||||
|
||||
* Handle events and redraws better
|
||||
* Performance improvements
|
||||
* Mark elements with how often they need to be redrawn
|
||||
* Finalize the file format
|
||||
* Localization
|
||||
|
||||
# Creator
|
||||
|
||||
[halemmerich](https://github.com/halemmerich)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwIdah/wAof//4ECgYFB4AFBg4FB8AFBj/wh/4AoM/wEB/gFBvwCEBAU/AQP4gfAj8AgPwAoMPwED8AFBg/AAYIBDA4ngg4TB4EBApkPKgJSBJQIFTMgIFCJIIFDKoIFEvgFBGoMAnw7DP4IFEh+BAoItBg+DNIQwBMIaeCKoKxCPoIzCEgKVHUIqtFXIrFFaIrdFdIwAV"))
|
|
@ -0,0 +1,774 @@
|
|||
var watchface = require("Storage").readJSON("imageclock.face.json");
|
||||
var watchfaceResources = require("Storage").readJSON("imageclock.resources.json");
|
||||
var precompiledJs = eval(require("Storage").read("imageclock.draw.js"));
|
||||
var settings = require('Storage').readJSON("imageclock.json", true) || {};
|
||||
|
||||
var performanceLog = {};
|
||||
|
||||
var startPerfLog = () => {};
|
||||
var endPerfLog = () => {};
|
||||
var printPerfLog = () => print("Deactivated");
|
||||
var resetPerfLog = () => {performanceLog = {};};
|
||||
|
||||
var colormap={
|
||||
"#000":0,
|
||||
"#00f":1,
|
||||
"#0f0":2,
|
||||
"#0ff":3,
|
||||
"#f00":4,
|
||||
"#f0f":5,
|
||||
"#ff0":6,
|
||||
"#fff":7
|
||||
};
|
||||
|
||||
var palette = new Uint16Array([
|
||||
0x0000, //black #000
|
||||
0x001f, //blue #00f
|
||||
0x07e0, //green #0f0
|
||||
0x07ff, //cyan #0ff
|
||||
0xf800, //red #f00
|
||||
0xf81f, //magenta #f0f
|
||||
0xffe0, //yellow #ff0
|
||||
0xffff, //white #fff
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
])
|
||||
|
||||
var p0 = g;
|
||||
var p1;
|
||||
|
||||
if (settings.perflog){
|
||||
startPerfLog = function(name){
|
||||
var time = getTime();
|
||||
if (!performanceLog.start) performanceLog.start={};
|
||||
performanceLog.start[name] = time;
|
||||
};
|
||||
endPerfLog = function (name){
|
||||
var time = getTime();
|
||||
if (!performanceLog.last) performanceLog.last={};
|
||||
var duration = time - performanceLog.start[name];
|
||||
performanceLog.last[name] = duration;
|
||||
if (!performanceLog.cum) performanceLog.cum={};
|
||||
if (!performanceLog.cum[name]) performanceLog.cum[name] = 0;
|
||||
performanceLog.cum[name] += duration;
|
||||
if (!performanceLog.count) performanceLog.count={};
|
||||
if (!performanceLog.count[name]) performanceLog.count[name] = 0;
|
||||
performanceLog.count[name]++;
|
||||
};
|
||||
|
||||
printPerfLog = function(){
|
||||
var result = "";
|
||||
var keys = [];
|
||||
for (var c in performanceLog.cum){
|
||||
keys.push(c);
|
||||
}
|
||||
keys.sort();
|
||||
for (var k of keys){
|
||||
print(k, "last:", (performanceLog.last[k] * 1000).toFixed(0), "average:", (performanceLog.cum[k]/performanceLog.count[k]*1000).toFixed(0), "count:", performanceLog.count[k], "total:", (performanceLog.cum[k] * 1000).toFixed(0));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function delay(t) {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(resolve, t);
|
||||
});
|
||||
}
|
||||
|
||||
function prepareImg(resource){
|
||||
startPerfLog("prepareImg");
|
||||
//print("prepareImg: ", resource);
|
||||
|
||||
if (resource.dataOffset !== undefined){
|
||||
resource.buffer = E.toArrayBuffer(require("Storage").read("imageclock.resources.data", resource.dataOffset, resource.dataLength));
|
||||
delete resource.dataOffset;
|
||||
delete resource.dataLength;
|
||||
if (resource.paletteData){
|
||||
result.palette = new Uint16Array(resource.paletteData);
|
||||
delete resource.paletteData;
|
||||
}
|
||||
}
|
||||
endPerfLog("prepareImg");
|
||||
return resource;
|
||||
}
|
||||
|
||||
function getByPath(object, path, lastElem){
|
||||
startPerfLog("getByPath");
|
||||
//print("getByPath", path,lastElem);
|
||||
var current = object;
|
||||
if (path.length) {
|
||||
for (var c of path){
|
||||
if (!current[c]) return undefined;
|
||||
current = current[c];
|
||||
}
|
||||
}
|
||||
if (lastElem!==undefined){
|
||||
if (!current["" + lastElem]) return undefined;
|
||||
//print("Found by lastElem", lastElem);
|
||||
current = current["" + lastElem];
|
||||
}
|
||||
endPerfLog("getByPath");
|
||||
if (typeof current == "function"){
|
||||
//print("current was function");
|
||||
return undefined;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function splitNumberToDigits(num){
|
||||
return String(num).split('').map(item => Number(item));
|
||||
}
|
||||
|
||||
function isChangedNumber(element){
|
||||
return element.lastDrawnValue != getValue(element.Value);
|
||||
}
|
||||
|
||||
function isChangedMultistate(element){
|
||||
return element.lastDrawnValue != getMultistate(element.Value);
|
||||
}
|
||||
|
||||
function drawNumber(graphics, resources, element){
|
||||
startPerfLog("drawNumber");
|
||||
var number = getValue(element.Value);
|
||||
var spacing = element.Spacing ? element.Spacing : 0;
|
||||
var unit = element.Unit;
|
||||
|
||||
var imageIndexMinus = element.ImageIndexMinus;
|
||||
var imageIndexUnit = element.ImageIndexUnit;
|
||||
var numberOfDigits = element.Digits;
|
||||
|
||||
|
||||
//print("drawNumber: ", number, element);
|
||||
if (number) number = number.toFixed(0);
|
||||
|
||||
var isNegative;
|
||||
var digits;
|
||||
if (number == undefined){
|
||||
isNegative = true;
|
||||
digits = [];
|
||||
numberOfDigits = 0;
|
||||
} else {
|
||||
isNegative = number < 0;
|
||||
if (isNegative) number *= -1;
|
||||
digits = splitNumberToDigits(number);
|
||||
}
|
||||
|
||||
//print("digits: ", digits);
|
||||
if (!numberOfDigits) numberOfDigits = digits.length;
|
||||
var firstDigitX = element.X;
|
||||
var firstDigitY = element.Y;
|
||||
var imageIndex = element.ImageIndex ? element.ImageIndex : 0;
|
||||
|
||||
var firstImage;
|
||||
if (imageIndex){
|
||||
firstImage = getByPath(resources, [], "" + (0 + imageIndex));
|
||||
} else {
|
||||
firstImage = getByPath(resources, element.ImagePath, 0);
|
||||
}
|
||||
|
||||
var minusImage;
|
||||
if (imageIndexMinus){
|
||||
minusImage = getByPath(resources, [], "" + (0 + imageIndexMinus));
|
||||
} else {
|
||||
minusImage = getByPath(resources, element.ImagePath, "minus");
|
||||
}
|
||||
|
||||
var unitImage;
|
||||
//print("Get image for unit", imageIndexUnit);
|
||||
if (imageIndexUnit !== undefined){
|
||||
unitImage = getByPath(resources, [], "" + (0 + imageIndexUnit));
|
||||
//print("Unit image is", unitImage);
|
||||
} else if (element.Unit){
|
||||
unitImage = getByPath(resources, element.ImagePath, getMultistate(element.Unit, "unknown"));
|
||||
}
|
||||
|
||||
var numberWidth = (numberOfDigits * firstImage.width) + (Math.max((numberOfDigits - 1),0) * spacing);
|
||||
if (isNegative && minusImage){
|
||||
//print("Adding to width", minusImage);
|
||||
numberWidth += minusImage.width + spacing;
|
||||
}
|
||||
if (unitImage){
|
||||
//print("Adding to width", unitImage);
|
||||
numberWidth += unitImage.width + spacing;
|
||||
}
|
||||
//print("numberWidth:", numberWidth);
|
||||
|
||||
if (element.Alignment == "Center") {
|
||||
firstDigitX = Math.round(element.X - (numberWidth/2)) + 1;
|
||||
firstDigitY = Math.round(element.Y - (firstImage.height/2)) + 1;
|
||||
} else if (element.Alignment == "BottomRight"){
|
||||
firstDigitX = element.X - numberWidth + 1;
|
||||
firstDigitY = element.Y - firstImage.height + 1;
|
||||
} else if (element.Alignment == "TopRight") {
|
||||
firstDigitX = element.X - numberWidth + 1;
|
||||
firstDigitY = element.Y;
|
||||
} else if (element.Alignment == "BottomLeft") {
|
||||
firstDigitX = element.X;
|
||||
firstDigitY = element.Y - firstImage.height + 1;
|
||||
}
|
||||
|
||||
var currentX = firstDigitX;
|
||||
if (isNegative && minusImage){
|
||||
//print("Draw minus at", currentX);
|
||||
if (imageIndexMinus){
|
||||
drawElement(graphics, resources, {X:currentX,Y:firstDigitY}, element, "" + (0 + imageIndexMinus));
|
||||
} else {
|
||||
drawElement(graphics, resources, {X:currentX,Y:firstDigitY}, element, "minus");
|
||||
}
|
||||
currentX += minusImage.width + spacing;
|
||||
}
|
||||
for (var d = 0; d < numberOfDigits; d++){
|
||||
var currentDigit;
|
||||
var difference = numberOfDigits - digits.length;
|
||||
if (d >= difference){
|
||||
currentDigit = digits[d-difference];
|
||||
} else {
|
||||
currentDigit = 0;
|
||||
}
|
||||
//print("Digit " + currentDigit + " " + currentX);
|
||||
drawElement(graphics, resources, {X:currentX,Y:firstDigitY}, element, currentDigit + imageIndex);
|
||||
currentX += firstImage.width + spacing;
|
||||
}
|
||||
if (imageIndexUnit){
|
||||
//print("Draw unit at", currentX);
|
||||
drawElement(graphics, resources, {X:currentX,Y:firstDigitY}, element, "" + (0 + imageIndexUnit));
|
||||
} else if (element.Unit){
|
||||
drawElement(graphics, resources, {X:currentX,Y:firstDigitY}, element, getMultistate(element.Unit,"unknown"));
|
||||
}
|
||||
element.lastDrawnValue = number;
|
||||
|
||||
endPerfLog("drawNumber");
|
||||
}
|
||||
|
||||
function drawElement(graphics, resources, pos, element, lastElem){
|
||||
startPerfLog("drawElement");
|
||||
var cacheKey = "_"+(lastElem?lastElem:"nole");
|
||||
if (!element.cachedImage) element.cachedImage={};
|
||||
if (!element.cachedImage[cacheKey]){
|
||||
var resource = getByPath(resources, element.ImagePath, lastElem);
|
||||
if (resource){
|
||||
prepareImg(resource);
|
||||
//print("lastElem", typeof resource)
|
||||
if (resource) {
|
||||
element.cachedImage[cacheKey] = resource;
|
||||
//print("cache res ",typeof element.cachedImage[cacheKey]);
|
||||
} else {
|
||||
element.cachedImage[cacheKey] = null;
|
||||
//print("cache null",typeof element.cachedImage[cacheKey]);
|
||||
//print("Could not create image from", resource);
|
||||
}
|
||||
} else {
|
||||
//print("Could not get resource from", element, lastElem);
|
||||
}
|
||||
}
|
||||
|
||||
//print("cache ",typeof element.cachedImage[cacheKey], element.ImagePath, lastElem);
|
||||
if(element.cachedImage[cacheKey]){
|
||||
//print("drawElement ",pos, path, lastElem);
|
||||
//print("resource ", resource,pos, path, lastElem);
|
||||
//print("drawImage from drawElement", image, pos);
|
||||
var options={};
|
||||
if (element.RotationValue){
|
||||
options.rotate = radians(element);
|
||||
}
|
||||
if (element.Scale){
|
||||
options.scale = element.ScaleValue;
|
||||
}
|
||||
//print("options", options);
|
||||
//print("Memory before drawing", process.memory(false));
|
||||
startPerfLog("drawElement_g.drawImage");
|
||||
try{
|
||||
graphics.drawImage(element.cachedImage[cacheKey] ,(pos.X ? pos.X : 0), (pos.Y ? pos.Y : 0), options);} catch (e) {
|
||||
//print("Error", e, element.cachedImage[cacheKey]);
|
||||
}
|
||||
endPerfLog("drawElement_g.drawImage");
|
||||
}
|
||||
endPerfLog("drawElement");
|
||||
}
|
||||
|
||||
function getValue(value, defaultValue){
|
||||
if (typeof value == "string"){
|
||||
return numbers[value]();
|
||||
}
|
||||
if (value == undefined) return defaultValue;
|
||||
return value;
|
||||
}
|
||||
|
||||
function getMultistate(name, defaultValue){
|
||||
if (typeof name == "string"){
|
||||
return multistates[name]();
|
||||
} else {
|
||||
if (name == undefined) return defaultValue;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function drawScale(graphics, resources, scale){
|
||||
startPerfLog("drawScale");
|
||||
//print("drawScale", scale);
|
||||
var segments = scale.Segments;
|
||||
var imageIndex = scale.ImageIndex !== undefined ? scale.ImageIndex : 0;
|
||||
|
||||
var value = scaledown(scale.Value, scale.MinValue, scale.MaxValue);
|
||||
|
||||
//print("Value is ", value, "(", maxValue, ",", minValue, ")");
|
||||
|
||||
var segmentsToDraw = Math.ceil(value * segments.length);
|
||||
|
||||
for (var i = 0; i < segmentsToDraw; i++){
|
||||
drawElement(graphics, resources, segments[i], scale, imageIndex + i);
|
||||
}
|
||||
scale.lastDrawnValue = segmentsToDraw;
|
||||
|
||||
endPerfLog("drawScale");
|
||||
}
|
||||
|
||||
function drawImage(graphics, resources, image, name){
|
||||
startPerfLog("drawImage");
|
||||
//print("drawImage", image.X, image.Y, name);
|
||||
if (image.Value && image.Steps){
|
||||
var steps = Math.floor(scaledown(image.Value, image.MinValue, image.MaxValue) * (image.Steps - 1));
|
||||
//print("Step", steps, "of", image.Steps);
|
||||
drawElement(graphics, resources, image, image, "" + steps);
|
||||
} else if (image.ImageIndex !== undefined) {
|
||||
drawElement(graphics, resources, image, image, image.ImageIndex);
|
||||
} else {
|
||||
drawElement(graphics, resources, image, image, name ? "" + name: undefined);
|
||||
}
|
||||
|
||||
endPerfLog("drawImage");
|
||||
}
|
||||
|
||||
function drawCodedImage(graphics, resources, image){
|
||||
startPerfLog("drawCodedImage");
|
||||
var code = getValue(image.Value);
|
||||
//print("drawCodedImage", image, code);
|
||||
|
||||
if (image.ImagePath) {
|
||||
var factor = 1;
|
||||
var currentCode = code;
|
||||
while (code / factor > 1){
|
||||
currentCode = Math.floor(currentCode/factor)*factor;
|
||||
//print("currentCode", currentCode);
|
||||
if (getByPath(resources, image.ImagePath, currentCode)){
|
||||
break;
|
||||
}
|
||||
factor *= 10;
|
||||
}
|
||||
if (code / factor > 1){
|
||||
//print("found match");
|
||||
drawImage(graphics, resources, image, currentCode);
|
||||
} else {
|
||||
//print("fallback");
|
||||
drawImage(graphics, resources, image, "fallback");
|
||||
}
|
||||
}
|
||||
image.lastDrawnValue = code;
|
||||
|
||||
startPerfLog("drawCodedImage");
|
||||
}
|
||||
|
||||
function getWeatherCode(){
|
||||
var jsonWeather = require("Storage").readJSON('weather.json');
|
||||
var weather = (jsonWeather && jsonWeather.weather) ? jsonWeather.weather : undefined;
|
||||
|
||||
if (weather && weather.code){
|
||||
return weather.code;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getWeatherTemperature(){
|
||||
var jsonWeather = require("Storage").readJSON('weather.json');
|
||||
var weather = (jsonWeather && jsonWeather.weather) ? jsonWeather.weather : undefined;
|
||||
|
||||
var result = { unit: "unknown"};
|
||||
if (weather && weather.temp){
|
||||
//print("Weather is", weather);
|
||||
var temp = require('locale').temp(weather.temp-273.15);
|
||||
result.value = Number(temp.match(/[\d\-]*/)[0]);
|
||||
var unit;
|
||||
if (temp.includes("C")){
|
||||
result.unit = "celsius";
|
||||
} else if (temp.includes("F")){
|
||||
result.unit = "fahrenheit";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function scaledown(value, min, max){
|
||||
//print("scaledown", value, min, max);
|
||||
var scaled = E.clip(getValue(value),getValue(min,0),getValue(max,1));
|
||||
scaled -= getValue(min,0);
|
||||
scaled /= getValue(max,1);
|
||||
return scaled;
|
||||
}
|
||||
|
||||
function radians(rotation){
|
||||
var value = scaledown(rotation.RotationValue, rotation.MinRotationValue, rotation.MaxRotationValue);
|
||||
value -= rotation.RotationOffset ? rotation.RotationOffset : 0;
|
||||
value *= 360;
|
||||
value *= Math.PI / 180;
|
||||
return value;
|
||||
}
|
||||
|
||||
function drawPoly(graphics, resources, element){
|
||||
startPerfLog("drawPoly");
|
||||
var vertices = [];
|
||||
|
||||
startPerfLog("drawPoly_transform");
|
||||
for (var c of element.Vertices){
|
||||
vertices.push(c.X);
|
||||
vertices.push(c.Y);
|
||||
}
|
||||
var transform = { x: element.X ? element.X : 0,
|
||||
y: element.Y ? element.Y : 0
|
||||
};
|
||||
if (element.RotationValue){
|
||||
transform.rotate = radians(element);
|
||||
}
|
||||
vertices = graphics.transformVertices(vertices, transform);
|
||||
|
||||
endPerfLog("drawPoly_transform");
|
||||
|
||||
if (element.Filled){
|
||||
startPerfLog("drawPoly_g.fillPoly");
|
||||
graphics.fillPoly(vertices,true);
|
||||
endPerfLog("drawPoly_g.fillPoly");
|
||||
} else {
|
||||
startPerfLog("drawPoly_g.drawPoly");
|
||||
graphics.drawPoly(vertices,true);
|
||||
endPerfLog("drawPoly_g.drawPoly");
|
||||
}
|
||||
|
||||
endPerfLog("drawPoly");
|
||||
}
|
||||
|
||||
function drawRect(graphics, resources, element){
|
||||
startPerfLog("drawRect");
|
||||
var vertices = [];
|
||||
|
||||
if (element.Filled){
|
||||
startPerfLog("drawRect_g.fillRect");
|
||||
graphics.fillRect(element.X, element.Y, element.X + element.Width, element.Y + element.Height);
|
||||
endPerfLog("drawRect_g.fillRect");
|
||||
} else {
|
||||
startPerfLog("drawRect_g.fillRect");
|
||||
graphics.drawRect(element.X, element.Y, element.X + element.Width, element.Y + element.Height);
|
||||
endPerfLog("drawRect_g.fillRect");
|
||||
}
|
||||
endPerfLog("drawRect");
|
||||
}
|
||||
|
||||
function drawCircle(graphics, resources, element){
|
||||
startPerfLog("drawCircle");
|
||||
|
||||
if (element.Filled){
|
||||
startPerfLog("drawCircle_g.fillCircle");
|
||||
graphics.fillCircle(element.X, element.Y, element.Radius);
|
||||
endPerfLog("drawCircle_g.fillCircle");
|
||||
} else {
|
||||
startPerfLog("drawCircle_g.drawCircle");
|
||||
graphics.drawCircle(element.X, element.Y, element.Radius);
|
||||
endPerfLog("drawCircle_g.drawCircle");
|
||||
}
|
||||
endPerfLog("drawCircle");
|
||||
}
|
||||
|
||||
var numbers = {};
|
||||
numbers.Hour = () => { return new Date().getHours(); };
|
||||
numbers.HourTens = () => { return Math.floor(new Date().getHours()/10); };
|
||||
numbers.HourOnes = () => { return Math.floor(new Date().getHours()%10); };
|
||||
numbers.Hour12 = () => { return new Date().getHours()%12; };
|
||||
numbers.Hour12Analog = () => { var date = new Date(); return date.getHours()%12 + (date.getMinutes()/59); };
|
||||
numbers.Hour12Tens = () => { return Math.floor((new Date().getHours()%12)/10); };
|
||||
numbers.Hour12Ones = () => { return Math.floor((new Date().getHours()%12)%10); };
|
||||
numbers.Minute = () => { return new Date().getMinutes(); };
|
||||
numbers.MinuteAnalog = () => { var date = new Date(); return date.getMinutes() + (date.getSeconds()/59); };
|
||||
numbers.MinuteTens = () => { return Math.floor(new Date().getMinutes()/10); };
|
||||
numbers.MinuteOnes = () => { return Math.floor(new Date().getMinutes()%10); };
|
||||
numbers.Second = () => { return new Date().getSeconds(); };
|
||||
numbers.SecondAnalog = () => { var date = new Date(); return date.getSeconds() + (date.getMilliseconds()/999); };
|
||||
numbers.SecondTens = () => { return Math.floor(new Date().getSeconds()/10); };
|
||||
numbers.SecondOnes = () => { return Math.floor(new Date().getSeconds()%10); };
|
||||
numbers.WeekDay = () => { return new Date().getDay(); };
|
||||
numbers.WeekDayMondayFirst = () => { var day = (new Date().getDay() - 1); if (day < 0) day = 7 + day; return day; };
|
||||
numbers.Day = () => { return new Date().getDate(); };
|
||||
numbers.DayTens = () => { return Math.floor(new Date().getDate()/10); };
|
||||
numbers.DayOnes = () => { return Math.floor(new Date().getDate()%10); };
|
||||
numbers.Month = () => { return new Date().getMonth() + 1; };
|
||||
numbers.MonthTens = () => { return Math.floor((new Date().getMonth() + 1)/10); };
|
||||
numbers.MonthOnes = () => { return Math.floor((new Date().getMonth() + 1)%10); };
|
||||
numbers.Pulse = () => { return pulse; };
|
||||
numbers.Steps = () => { return Bangle.getHealthStatus ? Bangle.getHealthStatus("day").steps : undefined; };
|
||||
numbers.StepsGoal = () => { return settings.stepsgoal || 10000; };
|
||||
numbers.Temperature = () => { return temp; };
|
||||
numbers.Pressure = () => { return press; };
|
||||
numbers.Altitude = () => { return alt; };
|
||||
numbers.BatteryPercentage = E.getBattery;
|
||||
numbers.BatteryVoltage = NRF.getBattery;
|
||||
numbers.WeatherCode = getWeatherCode;
|
||||
numbers.WeatherTemperature = () => { return getWeatherTemperature().value; };
|
||||
|
||||
var multistates = {};
|
||||
multistates.Lock = () => { return Bangle.isLocked() ? "on" : "off"; };
|
||||
multistates.Charge = () => { return Bangle.isCharging() ? "on" : "off"; };
|
||||
multistates.Notifications = () => { return ((require("Storage").readJSON("setting.json", 1) || {}).quiet|0) ? "off" : "vibrate"; };
|
||||
multistates.Alarm = () => { return (require('Storage').readJSON('alarm.json',1)||[]).some(alarm=>alarm.on) ? "on" : "off"; };
|
||||
multistates.Bluetooth = () => { return NRF.getSecurityStatus().connected ? "on" : "off"; };
|
||||
//TODO: Implement peripheral connection status
|
||||
multistates.BluetoothPeripheral = () => { return NRF.getSecurityStatus().connected ? "on" : "off"; };
|
||||
multistates.HRM = () => { return Bangle.isHRMOn ? "on" : "off"; };
|
||||
multistates.Barometer = () => { return Bangle.isBarometerOn() ? "on" : "off"; };
|
||||
multistates.Compass = () => { return Bangle.isCompassOn() ? "on" : "off"; };
|
||||
multistates.GPS = () => { return Bangle.isGPSOn() ? "on" : "off"; };
|
||||
multistates.WeatherTemperatureNegative = () => { return getWeatherTemperature().value ? getWeatherTemperature().value : 0 < 0; };
|
||||
multistates.WeatherTemperatureUnit = () => { return getWeatherTemperature().unit; };
|
||||
multistates.StepsGoal = () => { return (numbers.Steps() >= (settings.stepsgoal || 10000)) ? "on": "off"; };
|
||||
|
||||
function drawMultiState(graphics, resources, element){
|
||||
startPerfLog("drawMultiState");
|
||||
//print("drawMultiState", element);
|
||||
var value = multistates[element.Value]();
|
||||
//print("drawImage from drawMultiState", element, value);
|
||||
drawImage(graphics, resources, element, value);
|
||||
element.lastDrawnValue = value;
|
||||
endPerfLog("drawMultiState");
|
||||
}
|
||||
|
||||
var pulse,alt,temp,press;
|
||||
|
||||
|
||||
var requestedDraws = 0;
|
||||
var isDrawing = false;
|
||||
|
||||
var drawingTime;
|
||||
|
||||
var start;
|
||||
|
||||
function initialDraw(resources, face){
|
||||
//print("Free memory", process.memory(false).free);
|
||||
requestedDraws++;
|
||||
if (!isDrawing){
|
||||
//print(new Date().toISOString(), "Can draw,", requestedDraws, "draws requested so far");
|
||||
isDrawing = true;
|
||||
resetPerfLog();
|
||||
requestedDraws = 0;
|
||||
//print(new Date().toISOString(), "Drawing start");
|
||||
startPerfLog("initialDraw");
|
||||
//start = Date.now();
|
||||
drawingTime = 0;
|
||||
//print("Precompiled");
|
||||
var promise = precompiledJs(watchfaceResources, watchface);
|
||||
|
||||
promise.then(()=>{
|
||||
var currentDrawingTime = Date.now();
|
||||
if (showWidgets){
|
||||
//print("Draw widgets");
|
||||
Bangle.drawWidgets();
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawLine(0,24,g.getWidth(),24);
|
||||
}
|
||||
lastDrawTime = Date.now() - start;
|
||||
drawingTime += Date.now() - currentDrawingTime;
|
||||
//print(new Date().toISOString(), "Drawing done in", lastDrawTime.toFixed(0), "active:", drawingTime.toFixed(0));
|
||||
isDrawing=false;
|
||||
firstDraw=false;
|
||||
requestRefresh = false;
|
||||
endPerfLog("initialDraw");
|
||||
}).catch((e)=>{
|
||||
print("Error during drawing", e);
|
||||
});
|
||||
|
||||
if (requestedDraws > 0){
|
||||
//print(new Date().toISOString(), "Had deferred drawing left, drawing again");
|
||||
requestedDraws = 0;
|
||||
setTimeout(()=>{initialDraw(resources, face);}, 10);
|
||||
}
|
||||
} //else {
|
||||
//print("queued draw");
|
||||
//}
|
||||
}
|
||||
|
||||
function handleHrm(e){
|
||||
if (e.confidence > 70){
|
||||
pulse = e.bpm;
|
||||
if (!redrawEvents || redrawEvents.includes("HRM") && !Bangle.isLocked()){
|
||||
//print("Redrawing on HRM");
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handlePressure(e){
|
||||
alt = e.altitude;
|
||||
temp = e.temperature;
|
||||
press = e.pressure;
|
||||
if (!redrawEvents || redrawEvents.includes("pressure") && !Bangle.isLocked()){
|
||||
//print("Redrawing on pressure");
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCharging(e){
|
||||
if (!redrawEvents || redrawEvents.includes("charging") && !Bangle.isLocked()){
|
||||
//print("Redrawing on charging");
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getMatchedWaitingTime(time){
|
||||
var result = time - (Date.now() % time);
|
||||
//print("Matched timeout", time, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setMatchedInterval(callable, time, intervalHandler, delay){
|
||||
//print("Setting matched interval for", time);
|
||||
var matchedTime = getMatchedWaitingTime(time + delay);
|
||||
setTimeout(()=>{
|
||||
var interval = setInterval(callable, time);
|
||||
if (intervalHandler) intervalHandler(interval);
|
||||
callable();
|
||||
}, matchedTime);
|
||||
}
|
||||
|
||||
var unlockedDrawInterval;
|
||||
var lockedDrawInterval;
|
||||
|
||||
var lastDrawTime = 0;
|
||||
var firstDraw = true;
|
||||
|
||||
var lockedRedraw = getByPath(watchface, ["Properties","Redraw","Locked"]) || 60000;
|
||||
var unlockedRedraw = getByPath(watchface, ["Properties","Redraw","Unlocked"]) || 1000;
|
||||
var defaultRedraw = getByPath(watchface, ["Properties","Redraw","Default"]) || "Always";
|
||||
var redrawEvents = getByPath(watchface, ["Properties","Redraw","Events"]);
|
||||
var clearOnRedraw = getByPath(watchface, ["Properties","Redraw","Clear"]);
|
||||
var events = getByPath(watchface, ["Properties","Events"]);
|
||||
|
||||
//print("events", events);
|
||||
//print("redrawEvents", redrawEvents);
|
||||
|
||||
function handleLock(isLocked, forceRedraw){
|
||||
//print("isLocked", Bangle.isLocked());
|
||||
if (lockedDrawInterval) clearInterval(lockedDrawInterval);
|
||||
if (unlockedDrawInterval) clearInterval(unlockedDrawInterval);
|
||||
if (!isLocked){
|
||||
if (forceRedraw || !redrawEvents || (redrawEvents.includes("unlock"))){
|
||||
//print("Redrawing on unlock", isLocked);
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
setMatchedInterval(()=>{
|
||||
//print("Redrawing on unlocked interval");
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
},unlockedRedraw, (v)=>{
|
||||
unlockedDrawInterval = v;
|
||||
}, lastDrawTime);
|
||||
if (!events || events.includes("HRM")) Bangle.setHRMPower(1, "imageclock");
|
||||
if (!events || events.includes("pressure")) Bangle.setBarometerPower(1, 'imageclock');
|
||||
} else {
|
||||
if (forceRedraw || !redrawEvents || (redrawEvents.includes("lock"))){
|
||||
//print("Redrawing on lock", isLocked);
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
setMatchedInterval(()=>{
|
||||
//print("Redrawing on locked interval");
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
},lockedRedraw, (v)=>{
|
||||
lockedDrawInterval = v;
|
||||
}, lastDrawTime);
|
||||
Bangle.setHRMPower(0, "imageclock");
|
||||
Bangle.setBarometerPower(0, 'imageclock');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var showWidgets = false;
|
||||
var showWidgetsChanged = false;
|
||||
var currentDragDistance = 0;
|
||||
|
||||
Bangle.setUI("clock");
|
||||
Bangle.on('drag', (e)=>{
|
||||
currentDragDistance += e.dy;
|
||||
if (Math.abs(currentDragDistance) < 10) return;
|
||||
dragDown = currentDragDistance > 0;
|
||||
currentDragDistance = 0;
|
||||
if (!showWidgets && dragDown){
|
||||
//print("Enable widgets");
|
||||
if (WIDGETS && typeof WIDGETS === "object") {
|
||||
for (let w in WIDGETS) {
|
||||
var wd = WIDGETS[w];
|
||||
wd.draw = originalWidgetDraw[w];
|
||||
wd.area = originalWidgetArea[w];
|
||||
}
|
||||
}
|
||||
showWidgetsChanged = true;
|
||||
}
|
||||
if (showWidgets && !dragDown){
|
||||
//print("Disable widgets");
|
||||
clearWidgetsDraw();
|
||||
firstDraw = true;
|
||||
showWidgetsChanged = true;
|
||||
}
|
||||
if (showWidgetsChanged){
|
||||
showWidgetsChanged = false;
|
||||
//print("Draw after widget change");
|
||||
showWidgets = dragDown;
|
||||
initialDraw();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!events || events.includes("pressure")){
|
||||
Bangle.on('pressure', handlePressure);
|
||||
try{
|
||||
Bangle.setBarometerPower(1, 'imageclock');
|
||||
} catch (e){
|
||||
print("Error during barometer power up", e);
|
||||
}
|
||||
}
|
||||
if (!events || events.includes("HRM")) {
|
||||
Bangle.on('HRM', handleHrm);
|
||||
Bangle.setHRMPower(1, "imageclock");
|
||||
}
|
||||
if (!events || events.includes("lock")) {
|
||||
Bangle.on('lock', handleLock);
|
||||
}
|
||||
if (!events || events.includes("charging")) {
|
||||
Bangle.on('charging', handleCharging);
|
||||
}
|
||||
|
||||
var originalWidgetDraw = {};
|
||||
var originalWidgetArea = {};
|
||||
|
||||
function clearWidgetsDraw(){
|
||||
//print("Clear widget draw calls");
|
||||
if (WIDGETS && typeof WIDGETS === "object") {
|
||||
originalWidgetDraw = {};
|
||||
originalWidgetArea = {};
|
||||
for (let w in WIDGETS) {
|
||||
var wd = WIDGETS[w];
|
||||
originalWidgetDraw[w] = wd.draw;
|
||||
originalWidgetArea[w] = wd.area;
|
||||
wd.draw = () => {};
|
||||
wd.area = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(()=>{
|
||||
Bangle.loadWidgets();
|
||||
clearWidgetsDraw();
|
||||
}, 0);
|
||||
|
||||
handleLock(Bangle.isLocked());
|