Merge branch 'master' into themefiles
|
@ -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 @@
|
|||
0.01: Initial version
|
|
@ -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,104 @@
|
|||
// <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)
|
||||
);
|
||||
};
|
||||
|
||||
// the following 2 sections are used from waveclk to schedule minutely updates
|
||||
// timeout used to update every minute
|
||||
var drawTimeout;
|
||||
|
||||
// schedule a draw for the next minute
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function () {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60300 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
function drawBackground() {
|
||||
g.setBgColor(0, 0, 0);
|
||||
g.setColor(1, 1, 1);
|
||||
g.clear();
|
||||
}
|
||||
|
||||
function digit(num) {
|
||||
return String.fromCharCode(num + 48);
|
||||
}
|
||||
|
||||
function timeString(h, m) {
|
||||
return digit(h / 10) + digit(h % 10) + ":" + digit(m / 10) + digit(m % 10);
|
||||
}
|
||||
|
||||
function dayString(w) {
|
||||
return digit(w / 10) + digit(w % 10);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
g.reset();
|
||||
drawBackground();
|
||||
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);
|
||||
|
||||
g.setBgColor(0, 0, 0);
|
||||
g.setColor(1, 1, 1);
|
||||
|
||||
g.setFontOpenSans();
|
||||
g.setFontAlign(0, -1);
|
||||
g.drawString(timeString(h, m), g.getWidth() / 2, 30);
|
||||
g.drawString(dayString(d), g.getWidth() * 3 / 4, 98);
|
||||
g.setFont('Vector', 52);
|
||||
g.setFontAlign(-1, -1);
|
||||
g.drawString("SUMOTUWETHFRSA".slice(2*w,2*w+2), 6, 103);
|
||||
g.setColor(0, 1, 0);
|
||||
g.fillRect(0, 90, g.getWidth(), 94);
|
||||
g.reset();
|
||||
|
||||
g.setColor(1,1,1);
|
||||
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(0,0,0);
|
||||
g.fillRect(12+width+1,162,162,168);
|
||||
}
|
||||
// widget redraw
|
||||
Bangle.drawWidgets();
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
Bangle.loadWidgets();
|
||||
draw();
|
||||
|
||||
//the following section is also from waveclk
|
||||
Bangle.on('lcdPower', on => {
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.setUI("clock");
|
||||
|
||||
Bangle.drawWidgets();
|
|
@ -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 clock",
|
||||
"version":"0.01",
|
||||
"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: 16 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);
|
||||
});
|
|
@ -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());
|
After Width: | Height: | Size: 929 B |
|
@ -0,0 +1,29 @@
|
|||
var demostate = 0;
|
||||
function demoMode(){
|
||||
lockedRedraw = 2000;
|
||||
unlockedRedraw = 2000;
|
||||
for (var c in multistates){
|
||||
multistates[c] = ()=>{return ["on","off"][demostate%2];};
|
||||
if (c == "WeatherTemperatureUnit") multistates[c] = ()=>{return ["celsius","fahrenheit"][demostate%2];};
|
||||
if (c == "Notifications") multistates[c] = ()=>{return ["on","off","vibrate"][demostate%3];};
|
||||
}
|
||||
for (var c in numbers){
|
||||
if (c.contains("Minute")) numbers[c] = ()=>{return Math.floor((Math.random() * 9) + 1);};
|
||||
if (c.contains("Second")) numbers[c] = ()=>{return Math.floor((Math.random() * 9) + 1);};
|
||||
if (c.contains("Hour")) numbers[c] = ()=>{return Math.floor((Math.random() * 9) + 1);};
|
||||
}
|
||||
for (var c in numbers){
|
||||
if (c.contains("Ones")) numbers[c] = ()=>{return Math.floor((Math.random() * 9) + 1);};
|
||||
if (c.contains("Tens")) numbers[c] = ()=>{return Math.floor((Math.random() * 9) + 1);};
|
||||
}
|
||||
numbers.Pulse = ()=>{return Math.floor((Math.random() * 60) + 40);};
|
||||
numbers.Steps = ()=>{return Math.floor((Math.random() * 10000) + 10);};
|
||||
numbers.Temperature = ()=>{return Math.floor((Math.random() * 15) + 10);};
|
||||
numbers.Pressure = ()=>{return Math.floor((Math.random() * 1000) + 10);};
|
||||
numbers.Altitude = ()=>{return Math.floor((Math.random() * 1000) + 10);};
|
||||
numbers.WeatherCode = ()=>{return Math.floor((Math.random() * 800) + 100);};
|
||||
numbers.WeatherTemperature = ()=>{return Math.floor((Math.random() * 10) + 0);};
|
||||
}
|
||||
demoMode();
|
||||
handleLock(false);
|
||||
setInterval(()=>{demostate++;},1000);
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": "imageclock",
|
||||
"name": "Imageclock",
|
||||
"shortName": "Imageclock",
|
||||
"version": "0.07",
|
||||
"type": "clock",
|
||||
"description": "BETA!!! File formats still subject to change --- This app is a highly customizable watchface. To use it, you need to select a watchface. You can build the watchfaces yourself without programming anything. All you need to do is write some json and create image files.",
|
||||
"icon": "app.png",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"custom": "custom.html",
|
||||
"customConnect": false,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"imageclock.app.js","url":"app.js"},
|
||||
{"name":"imageclock.settings.js","url":"settings.js"},
|
||||
{"name":"imageclock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
(function(back) {
|
||||
var FILE = "imageclock.json";
|
||||
|
||||
var settings = Object.assign({
|
||||
stepsgoal: 10000,
|
||||
perflog: false
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'Imageclock' },
|
||||
'< Back': back,
|
||||
'Steps goal': {
|
||||
value: settings.stepsgoal,
|
||||
min: 0,
|
||||
step: 500,
|
||||
max: 50000,
|
||||
onchange: v => {
|
||||
settings.stepsgoal = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Performance log': {
|
||||
value: !!settings.perflog,
|
||||
format: v => settings.perflog ? "On" : "Off",
|
||||
onchange: v => {
|
||||
settings.perflog = v;
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,23 @@
|
|||
# App Name
|
||||
|
||||
Invader
|
||||
|
||||
## Usage
|
||||
|
||||
For fun! - I'm creating this demo to learn JavaScript with the Bangle.js 2
|
||||
|
||||
## Features
|
||||
|
||||
Shoot the Alien, you have three lives
|
||||
|
||||
## Controls
|
||||
|
||||
Touch the lower Left or Right hand sides of the screen to move turret left or right, tap upper Right hand part of screen to fire, tap upper Left hand part of screen to restart
|
||||
|
||||
## Requests
|
||||
|
||||
bkumanchik on Espruino Forums
|
||||
|
||||
## Creator
|
||||
|
||||
Brian Kumanchik 2022
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwhHXAH4A/AH4A/AH4AYgAACC/4X/C/YbTFbYv/F/4rTAC4v/F/4vfC5YnPGaYv/F/4vLc7b3TF/4v/F/4v/F/4vTA5YAPE64v/F/4fTa55DXF/4v/AH4A/AH4A/AH4A/AHIA=="))
|
|
@ -0,0 +1,452 @@
|
|||
// Brian Kumanchik
|
||||
// Started 05-25-22
|
||||
// My Invader Demo, for Bangle.js 2, written JavaScript - using Espruino Web IDE
|
||||
|
||||
|
||||
// note: resolution is 176x176
|
||||
|
||||
|
||||
// to do:
|
||||
// upload to official app page
|
||||
// make invader clock
|
||||
|
||||
|
||||
|
||||
// - variables -----------------------------------------
|
||||
// invader variables
|
||||
var inv_x = 77;
|
||||
var inv_y = 20;
|
||||
var i_anim_delay = 10; // invader animation (and move) delay
|
||||
var inv_frame = 1; // invader start animation frame
|
||||
var ix_speed = 6; // march speed
|
||||
var i_dir = 1; // 1 = right, 0 = left
|
||||
var been_hit = false; // invader hit state
|
||||
// - shoot variables
|
||||
var inv_shot_x = -32;
|
||||
var inv_shot_y = -32;
|
||||
var inv_fire_pause = 30;
|
||||
var inv_fired = false; // invader fired state
|
||||
// - explode variables
|
||||
var been_hit = false; // invader hit state
|
||||
var bx = -32; // blast x
|
||||
var by = -32; // blast y
|
||||
var blast_delay = 15; // invader blast delay - pause after explosion
|
||||
var boom_play = false;
|
||||
|
||||
// turret variables
|
||||
var tur_x = 77;
|
||||
var tur_y = 148;
|
||||
var shot_fired = false; // turret fired state
|
||||
var sx = -20; // turret shot starting x - off screen
|
||||
var sy = -20; // turret shot starting y - off screen
|
||||
var turret_been_hit = false;
|
||||
var turret_blast_delay = 25; // keep blast active on screen for 60 frames
|
||||
var turret_exp_frame = 1; // turret explode start animation frame
|
||||
var turret_anim_delay = 3; // turret explode animation delay
|
||||
var explosion_play = false;
|
||||
|
||||
// misc variables
|
||||
var score = 0; // starting score
|
||||
var lives = 3; // starting lives
|
||||
var game_state = 0; // game state - 0 = game not started, 1 = game running, 3 = game over
|
||||
var ang = 0.1;
|
||||
var start_been_pressed = false; // stops double press on restart
|
||||
var fire_been_pressed = false; // stops auto fire
|
||||
|
||||
// input(screen controller) variables
|
||||
var BTNL, BTNR, BTNF, BTNS; // button - left, right, fire, start
|
||||
var tap = {};
|
||||
// use tapping on screen for left, right, fire, start
|
||||
Bangle.on('drag',e=>tap=e);
|
||||
BTNL = { read : _=>tap.b && tap.x < 88 && tap.y > 88};
|
||||
BTNR = { read : _=>tap.b && tap.x > 88 && tap.y > 88};
|
||||
BTNF = { read : _=>tap.b && tap.x > 88 && tap.y < 88};
|
||||
BTNS = { read : _=>tap.b && tap.x < 88 && tap.y < 88};
|
||||
|
||||
|
||||
// - sprites -------------------------------------------
|
||||
// invader sprites
|
||||
var invader_a =
|
||||
require("heatshrink").decompress(atob("hcIwkBiIBBAQoECCQQFBgEQAIMBEhUBDoYWDAYI="));
|
||||
var invader_b =
|
||||
require("heatshrink").decompress(atob("hcIwkBiIBBAQMQAoQEBgISCAYUQAIQAEB4YEBEAgEDAYIA=="));
|
||||
var boom =
|
||||
require("heatshrink").decompress(atob("hcJwkBiMQAIURgMQAgIKBAIICFAIMAAwIWBBAYSIEAgrDiA="));
|
||||
var inv_shot =
|
||||
require("heatshrink").decompress(atob("gcFwkBiERiAABAYQ"));
|
||||
|
||||
// turret sprites
|
||||
var turret =
|
||||
require("heatshrink").decompress(atob("h8IwkBiIABAYYACgAHFiEABggADCAInFgITBAAgOPA=="));
|
||||
var tur_exp_a =
|
||||
require("heatshrink").decompress(atob("h8IwkBiMRiACBAAwJEiAABBQgZCAAkAiAJBBoIUBgIABBgQACDIQ9ECQIA=="));
|
||||
var tur_exp_b =
|
||||
require("heatshrink").decompress(atob("h8IwkBiIBBAAUBiADCiMQAwQFDCIYXEB4IABgMAEYQXBiEAAQIQBAoIABDAQUCAAIVBA"));
|
||||
var shot =
|
||||
require("heatshrink").decompress(atob("gMDwkBAoIA=="));
|
||||
|
||||
|
||||
// function to move and animate invader
|
||||
function move_anim_inv() {
|
||||
// invader anim code
|
||||
i_anim_delay -= 1;
|
||||
if ((i_anim_delay < 0) && !(been_hit)) {
|
||||
i_anim_delay = 10;
|
||||
|
||||
inv_frame += 1;
|
||||
if (inv_frame > 2) {
|
||||
inv_frame = 1;
|
||||
}
|
||||
|
||||
// move right
|
||||
if (i_dir == 1){
|
||||
inv_x += ix_speed;
|
||||
if (inv_x >= 142) {
|
||||
inv_y += 8; // step down
|
||||
i_dir = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// move left
|
||||
if (i_dir < 1){
|
||||
inv_x -= ix_speed;
|
||||
if (inv_x <= 10) {
|
||||
inv_y += 8; // step down
|
||||
i_dir = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// function to make invader fire
|
||||
function invader_fire() {
|
||||
inv_fire_pause -= 1;
|
||||
|
||||
if (!(inv_fired)) { // so once invader shot is fired it doesn't follow the invader still
|
||||
inv_shot_x = inv_x + 8;
|
||||
inv_shot_y = inv_y + 18;
|
||||
}
|
||||
|
||||
if (inv_fire_pause < 0) {
|
||||
inv_fired = true;
|
||||
inv_shot_y += 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// function to make turret explode (when hit) then start back in center
|
||||
function turret_hit() {
|
||||
if (turret_been_hit) {
|
||||
if (!(explosion_play)) {
|
||||
//Bangle.buzz();
|
||||
//Bangle.beep();
|
||||
}
|
||||
|
||||
explosion_play = true;
|
||||
turret_anim_delay -= 1;
|
||||
turret_blast_delay -= 1;
|
||||
|
||||
if (turret_anim_delay < 0) {
|
||||
turret_exp_frame += 1;
|
||||
if (turret_exp_frame > 2) {
|
||||
Bangle.buzz();
|
||||
turret_exp_frame = 1;
|
||||
}
|
||||
turret_anim_delay = 3;
|
||||
}
|
||||
|
||||
if (turret_blast_delay < 0) {
|
||||
turret_blast_delay = 21;
|
||||
turret_been_hit = false;
|
||||
explosion_play = false;
|
||||
tur_x = 77; // reset turret x
|
||||
tur_y = 148; // reset turret y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// function to make invader explode (when hit) then randomly start somewhere else
|
||||
function invader_hit() {
|
||||
if (been_hit) {
|
||||
if (!(boom_play)) {
|
||||
Bangle.buzz();
|
||||
//Bangle.beep();
|
||||
}
|
||||
|
||||
inv_shot_x = -32; // hide shot
|
||||
inv_shot_y = -32; // hide shot
|
||||
inv_fire_pause = 30; // and reset pause
|
||||
|
||||
boom_play = true;
|
||||
blast_delay -= 1;
|
||||
|
||||
if (blast_delay < 0) {
|
||||
blast_delay = 15;
|
||||
boom_play = false;
|
||||
been_hit = false;
|
||||
bx = -32; // move boom off screen (following invader)
|
||||
by = -32;
|
||||
// generate a random rounded number between 10 and 142;
|
||||
inv_x = Math.floor(Math.random() * 142) + 10;
|
||||
inv_y = 20; // move invader back up after being hit
|
||||
i_dir = 1; // reset invader direction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// - setup stuff ---------------------------------------
|
||||
function gameStart() {
|
||||
setInterval(onFrame, 50);
|
||||
}
|
||||
|
||||
|
||||
// - main loop -------------------------------------------------------------
|
||||
function onFrame() {
|
||||
|
||||
// game not started state (title screen) ***************************
|
||||
if(game_state == 0) {
|
||||
g.clear();
|
||||
|
||||
|
||||
if (!(BTNS.read())) {
|
||||
start_been_pressed = false; // stops double press on restart
|
||||
}
|
||||
|
||||
|
||||
// draw text during game over state
|
||||
g.setFont("4x6", 4); // set font and size x 2
|
||||
g.setColor(0,1,0); // set color (black)
|
||||
g.drawString("INVADER", 33, 55);
|
||||
|
||||
|
||||
// just animate invader
|
||||
// invader anim code
|
||||
i_anim_delay -= 1;
|
||||
if(i_anim_delay < 0) {
|
||||
i_anim_delay = 15;
|
||||
|
||||
inv_frame += 1;
|
||||
if (inv_frame > 2) {
|
||||
inv_frame = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// draw sprites during game over state
|
||||
// next 2 line for a rotating invader on the title screen
|
||||
//ang += 0.1;
|
||||
//g.drawImage(invader_a, 88, 98, {scale:4, rotate:ang});
|
||||
if(inv_frame == 1) {
|
||||
g.drawImage(invader_a, 88-22, 85, {scale:4});
|
||||
}
|
||||
else if(inv_frame == 2) {
|
||||
g.drawImage(invader_b, 88-22, 85, {scale:4});
|
||||
}
|
||||
|
||||
// reset stuff
|
||||
if(BTNS.read() && !(start_been_pressed)) {
|
||||
turret_been_hit = false;
|
||||
tur_x = 77; // reset turret to center of screen
|
||||
tur_y = 148; // reset turret y
|
||||
inv_x = 77; // reset invader to center of screen
|
||||
inv_y = 20; // reset invader back to top
|
||||
i_dir = 1; // reset invader direction
|
||||
lives = 3; // reset lives
|
||||
score = 0; // reset score
|
||||
explosion_play = false;
|
||||
game_state = 1;
|
||||
turret_blast_delay = 25;
|
||||
}
|
||||
|
||||
|
||||
g.flip();
|
||||
}
|
||||
|
||||
|
||||
// game over state *************************************************
|
||||
if(game_state == 3) {
|
||||
g.clear();
|
||||
|
||||
// draw text during game over state
|
||||
g.setFont("4x6", 2); // set font and size x 2
|
||||
g.setColor(0,0,0); // set color (black)
|
||||
g.drawString("SCORE:" + score ,5, 5);
|
||||
g.drawString("LIVES:" + lives ,117, 5);
|
||||
g.drawString("GAME OVER", 52, 80);
|
||||
|
||||
|
||||
// draw sprites during game over state
|
||||
// - invader frame 2
|
||||
g.drawImage(invader_b, inv_x, inv_y, {scale:2});
|
||||
g.drawImage(tur_exp_b, tur_x, tur_y, {scale:2});
|
||||
g.drawImage(inv_shot, inv_shot_x, inv_shot_y, {scale:2});
|
||||
|
||||
|
||||
// reset stuff
|
||||
if(BTNS.read()) {
|
||||
//turret_been_hit = false;
|
||||
//tur_x = 77; // reset turret to center of screen
|
||||
//tur_y = 148; // reset turret y
|
||||
//inv_x = 77; // reset invader to center of screen
|
||||
//inv_y = 20; // reset invader back to top
|
||||
//i_dir = 1; // reset invader direction
|
||||
//lives = 3; // reset lives
|
||||
//score = 0; // reset score
|
||||
//explosion_play = false;
|
||||
game_state = 0;
|
||||
start_been_pressed = true;
|
||||
//turret_blast_delay = 25;
|
||||
}
|
||||
|
||||
|
||||
g.flip();
|
||||
}
|
||||
|
||||
|
||||
// not game over state (game running) ******************************
|
||||
if(game_state == 1) {
|
||||
Bangle.setLCDPower(1); // optional - this keeps the watch LCD lit up
|
||||
g.clear();
|
||||
|
||||
|
||||
if (!(BTNF.read())) {
|
||||
fire_been_pressed = false; // stops auto fire
|
||||
}
|
||||
|
||||
|
||||
// call function to move and animate invader
|
||||
move_anim_inv();
|
||||
|
||||
// call function to make invader fire
|
||||
invader_fire();
|
||||
|
||||
|
||||
// check input (screen presses)
|
||||
if(BTNL.read() && tur_x >= 12 && !(turret_been_hit)) {
|
||||
tur_x -= 6;
|
||||
}
|
||||
else if(BTNR.read() && tur_x <= 140 && !(turret_been_hit)) {
|
||||
tur_x += 6;
|
||||
}
|
||||
else if(BTNF.read() && !(turret_been_hit) && !(fire_been_pressed) && !(shot_fired)) {
|
||||
shot_fired = true;
|
||||
fire_been_pressed = true; // stops auto fire
|
||||
sx=tur_x + 12;
|
||||
sy=tur_y - 7;
|
||||
}
|
||||
|
||||
|
||||
// check for turret shot going off screen before allowing to fire again
|
||||
if (shot_fired) {
|
||||
sy -= 8;
|
||||
if (sy < 22) {
|
||||
shot_fired = false;
|
||||
sx = -32;
|
||||
sy = -32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check for invader shot going off screen before allowing to fire again
|
||||
if (inv_shot_y > 150
|
||||
) {
|
||||
inv_fired = false;
|
||||
inv_shot_x = inv_x - 1;
|
||||
inv_shot_y = inv_y + 7;
|
||||
inv_fire_pause = 30;
|
||||
}
|
||||
|
||||
|
||||
// check for turret shot and invader collision
|
||||
if ((sx >= inv_x) && (sx <= inv_x + 20) && (sy <= inv_y + 14)) {
|
||||
sx = -32;
|
||||
sy = -32;
|
||||
been_hit = true;
|
||||
score += 10;
|
||||
}
|
||||
|
||||
|
||||
// check for invader shot and turret collision
|
||||
if ((inv_shot_x + 4) >= (tur_x) && (inv_shot_x) <= (tur_x + 24) && (inv_shot_y + 8) >= (tur_y + 6)) {
|
||||
if (!(turret_been_hit)) {
|
||||
lives -= 1;
|
||||
|
||||
if (lives == 0) {
|
||||
game_state = 3;
|
||||
Bangle.buzz();
|
||||
}
|
||||
turret_been_hit = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// - draw sprites ----------------------------------
|
||||
// invader sprites
|
||||
if(!(been_hit)) {
|
||||
if(inv_frame == 1) {
|
||||
// - invader frame 1
|
||||
g.drawImage(invader_a, inv_x, inv_y, {scale:2});
|
||||
}
|
||||
else if(inv_frame == 2) {
|
||||
// - invader frame 2
|
||||
g.drawImage(invader_b, inv_x, inv_y, {scale:2});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// - invader explosion
|
||||
g.drawImage(boom, inv_x, inv_y, {scale:2});
|
||||
}
|
||||
// - invader shot
|
||||
if (inv_fired) {
|
||||
g.drawImage(inv_shot, inv_shot_x, inv_shot_y, {scale:2});
|
||||
}
|
||||
else {
|
||||
g.drawImage(inv_shot, -32, -32, {scale:2});
|
||||
}
|
||||
|
||||
// turret sprites
|
||||
if(!(turret_been_hit)) {
|
||||
// - undamaged turret
|
||||
g.drawImage(turret, tur_x, tur_y, {scale:2});
|
||||
}
|
||||
else {
|
||||
if(turret_exp_frame == 1) {
|
||||
// - turret explosion frame 1
|
||||
g.drawImage(tur_exp_a, tur_x, tur_y, {scale:2});
|
||||
}
|
||||
else if(turret_exp_frame == 2) {
|
||||
// - turret explosion frame 2
|
||||
g.drawImage(tur_exp_b, tur_x, tur_y, {scale:2});
|
||||
}
|
||||
}
|
||||
// - turret shot
|
||||
g.drawImage(shot, sx, sy, {scale:2});
|
||||
|
||||
|
||||
// call function to make invader explode then randomly start somewhere else
|
||||
invader_hit();
|
||||
|
||||
|
||||
// call function to make turret explode (when hit) then start back in center
|
||||
turret_hit();
|
||||
|
||||
|
||||
// - draw text -------------------------------------
|
||||
g.setFont("4x6", 2); // set font and size x 2
|
||||
g.setColor(0,0,0); // set color (black)
|
||||
g.drawString("SCORE:" + score ,5,5);
|
||||
g.drawString("LIVES:" + lives ,117,5);
|
||||
|
||||
|
||||
g.flip();
|
||||
}
|
||||
|
||||
} // end main loop ---------------------------------------------------------
|
||||
|
||||
|
||||
gameStart();
|
||||
|
||||
|
After Width: | Height: | Size: 665 B |
|
@ -0,0 +1,15 @@
|
|||
{ "id": "invader",
|
||||
"name": "Invader",
|
||||
"shortName":"Invader",
|
||||
"version":"0.11",
|
||||
"description": "A Space Invader game-like demo - work in progress",
|
||||
"icon": "app.png",
|
||||
"screenshots" : [ { "url":"screenshot_0.png" }, { "url":"screenshot_1.png" }, { "url":"screenshot_2.png" } ],
|
||||
"tags": "game",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"invader.app.js","url":"app.js"},
|
||||
{"name":"invader.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
|
@ -19,4 +19,5 @@
|
|||
0.19: Alarms can not go bigger than 100.
|
||||
0.20: Use alarm for alarm functionality instead of own implementation.
|
||||
0.21: Add custom theming.
|
||||
0.22: Fix alarm and add build in function for step counting.
|
||||
0.22: Fix alarm and add build in function for step counting.
|
||||
0.23: Add warning for low flash memory
|
||||
|
|
|
@ -147,8 +147,7 @@ var iconCharging = {
|
|||
buffer : require("heatshrink").decompress(atob("23btugAwUBtoICARG0h048eODQYCJ6P/AAUCCJfbo4SDxYRLtEcuPHjlwgoRJ7RnIloUHoYjDAQfAExEAwUIkACEkSAIEYwCBhZKH6EIJI0CJRFHEY0BJRWBSgf//0AJRYSE4BKLj4SE8BKLv4RD/hK/JS2AXY0gXwRKG4cMmACCJQMAg8csEFJQsBAwfasEAm379u0gFbcBfHzgFBz1xMQZKBjY/D0E2+BOChu26yVEEYdww+cgAFCg+cgIfB6RKF4HbgEIkGChEAthfCJQ0eEAIjBBAMxk6GCJQtgtyVBwRKBAQMbHAJKGXIIFCgACBhl54qVG2E+EAJKBJoWAm0WJQ6SCXgdxFgMLJQvYjeAEAUwFIUitEtJQ14NwUHgEwKYZKGwOwNYX7XgWCg3CJQ5rB4MevPnAoPDJRJrCgEG/ECAoNsJRUwoEesIIBiJKI3CVDti/CJRKVDiJHBSo0YsOGjED8AjBcAcIgdhcAXAPIUAcAYIBcA4dBAQUG8BrBgBuCgOwcBEeXIK2BBAIFBgRqBGoYAChq8CcYUE4FbUYOACQsHzgjDgwFBCIImBAQsDtwYD7cAloRI22B86YBw5QBgoRJ7dAgYEDCJaeBJoMcsARMAQNoJIIRE6A"))
|
||||
};
|
||||
|
||||
var iconNoBattery = {
|
||||
text: "NO BAT",
|
||||
var iconWarning = {
|
||||
width : 50, height : 50, bpp : 3,
|
||||
transparent : 1,
|
||||
buffer : require("heatshrink").decompress(atob("kmSpIC/AWMyoQIFsmECJFJhMmA4QXByVICIwODAQ4RRFIQGD5JVLkIGDzJqMyAGDph8MiRKGyApEAoZKFyYIDQwMkSQNkQZABBhIIOOJRuEL5gRIAUKACVQMhmUSNYNDQYJTBBwYFByGTkOE5FJWYNMknCAQKYCiaSCpmGochDoSYBhMwTAZrChILBhmEzKPBF4ImBTAREBDoMmEwJVDoYjBycJFgWEJQRuLJQ1kmQCCjJlCBYbjCagaDBwyDBmBuBF4TjJAUQKINBChCDQxZBcZIIQF4NIgEAgKSDiQmEVQKMBoARBAAMCSQLLBVoxqKL4gaCChVCNwoRKOIo4CJIgABBoSMHpIRFgDdJOIJUBCAUJRgJuEAQb+DIIgRIAX4C/ASOQA"))
|
||||
|
@ -321,19 +320,21 @@ function drawState(){
|
|||
|
||||
if(!isAlarmEnabled()){
|
||||
var bat = E.getBattery();
|
||||
var flash = storage.getFree() / process.env.STORAGE;
|
||||
var current = new Date();
|
||||
var hours = current.getHours();
|
||||
var iconImg =
|
||||
Bangle.isCharging() ? iconCharging :
|
||||
bat < 30 ? iconNoBattery :
|
||||
Bangle.isGPSOn() ? iconSatellite :
|
||||
hours % 4 == 0 ? iconSaturn :
|
||||
hours % 4 == 1 ? iconMars :
|
||||
hours % 4 == 2 ? iconMoon :
|
||||
iconEarth;
|
||||
g.drawImage(iconImg, 23, 118);
|
||||
var iconMsg =
|
||||
Bangle.isCharging() ? { icon: iconCharging, text: "STATUS" } :
|
||||
bat < 30 ? { icon: iconWarning, text: "BAT" } :
|
||||
flash < 0.1 ? { icon: iconWarning, text: "DISK" } :
|
||||
Bangle.isGPSOn() ? { icon: iconSatellite, text: "STATUS" } :
|
||||
hours % 4 == 0 ? { icon: iconSaturn, text: "STATUS" } :
|
||||
hours % 4 == 1 ? { icon: iconMars, text: "STATUS" } :
|
||||
hours % 4 == 2 ? { icon: iconMoon, text: "STATUS" } :
|
||||
{ icon: iconEarth, text: "STATUS" };
|
||||
g.drawImage(iconMsg.icon, 23, 118);
|
||||
g.setColor(cWhite);
|
||||
g.drawString("STATUS", 23+26, 108);
|
||||
g.drawString(iconMsg.text, 23+26, 108);
|
||||
} else {
|
||||
// Alarm within symbol
|
||||
g.setColor(color2);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "LCARS Clock",
|
||||
"shortName":"LCARS",
|
||||
"icon": "lcars.png",
|
||||
"version":"0.22",
|
||||
"version":"0.23",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"description": "Library Computer Access Retrieval System (LCARS) clock.",
|
||||
|
|
|
@ -51,3 +51,5 @@
|
|||
0.36: Ensure a new message plus an almost immediate deletion of that message doesn't load the messages app (fix #1362)
|
||||
0.37: Now use the setUI 'back' icon in the top left rather than specific buttons/menu items
|
||||
0.38: Add telegram foss handling
|
||||
0.39: Set default color for message icons according to theme
|
||||
Don't turn on the screen after unread timeout expires (#1873)
|
||||
|
|
|
@ -317,7 +317,7 @@ function showMessage(msgid) {
|
|||
{type:"txt", font:fontSmall, label:msg.src||/*LANG*/"Message", bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:2, halign:1 },
|
||||
title?{type:"txt", font:titleFont, label:title, bgCol:g.theme.bg2, col: g.theme.fg2, fillx:1, pad:2 }:{},
|
||||
]},
|
||||
{ type:"btn", src:require("messages").getMessageImage(msg), col:require("messages").getMessageImageCol(msg), pad: 3, cb:()=>{
|
||||
{ type:"btn", src:require("messages").getMessageImage(msg), col:require("messages").getMessageImageCol(msg, g.theme.fg2), pad: 3, cb:()=>{
|
||||
cancelReloadTimeout(); // don't auto-reload to clock now
|
||||
showMessageSettings(msg);
|
||||
}},
|
||||
|
@ -411,19 +411,17 @@ function cancelReloadTimeout() {
|
|||
unreadTimeout = undefined;
|
||||
}
|
||||
|
||||
|
||||
g.clear();
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
setTimeout(() => {
|
||||
var unreadTimeoutSecs = settings.unreadTimeout;
|
||||
if (unreadTimeoutSecs===undefined) unreadTimeoutSecs=60;
|
||||
if (unreadTimeoutSecs)
|
||||
unreadTimeout = setTimeout(function() {
|
||||
print("Message not seen - reloading");
|
||||
load();
|
||||
}, unreadTimeoutSecs*1000);
|
||||
var unreadTimeoutMillis = (settings.unreadTimeout || 60) * 1000;
|
||||
if (unreadTimeoutMillis) {
|
||||
unreadTimeout = setTimeout(load, unreadTimeoutMillis);
|
||||
}
|
||||
// only openMusic on launch if music is new
|
||||
var newMusic = MESSAGES.some(m=>m.id==="music"&&m.new);
|
||||
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:1,openMusic:newMusic&&settings.openMusic});
|
||||
},10); // if checkMessages wants to 'load', do that
|
||||
var newMusic = MESSAGES.some(m => m.id === "music" && m.new);
|
||||
checkMessages({ clockIfNoMsg: 0, clockIfAllRead: 0, showMsgIfUnread: 1, openMusic: newMusic && settings.openMusic });
|
||||
}, 10); // if checkMessages wants to 'load', do that
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "messages",
|
||||
"name": "Messages",
|
||||
"version": "0.38",
|
||||
"version": "0.39",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge/Android",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
|
@ -27,7 +27,6 @@ draw:function(recall) {
|
|||
if (quiet) WIDGETS["messages"].t -= 500000; // if quiet, set last time in the past so there is no buzzing
|
||||
WIDGETS["messages"].width=this.iconwidth;
|
||||
Bangle.drawWidgets();
|
||||
Bangle.setLCDPower(1);// turns screen on
|
||||
},hide:function() {
|
||||
delete WIDGETS["messages"].t;
|
||||
delete WIDGETS["messages"].l;
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New App!
|
||||
0.02: Redraw only when seconds change
|
||||
0.03: Fix typo in redraw check
|
||||
|
|
|
@ -48,7 +48,7 @@ function setLineWidth(x1, y1, x2, y2, lw) {
|
|||
|
||||
function drawMixedClock(force) {
|
||||
var date = new Date();
|
||||
if ((force || Bangle.isLCDOn()) && buf.buffer && date.getSeconds() === lastDate.getSeconds()) {
|
||||
if ((force || Bangle.isLCDOn()) && buf.buffer && date.getSeconds() !== lastDate.getSeconds()) {
|
||||
lastDate = date;
|
||||
var dateArray = date.toString().split(" ");
|
||||
var isEn = locale.name.startsWith("en");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "miclock2",
|
||||
"name": "Mixed Clock 2",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "White color variant of the Mixed Clock with thicker clock hands for better readability in the bright sunlight, extra space under the clock for widgets and seconds in the digital clock.",
|
||||
"icon": "clock-mixed.png",
|
||||
"type": "clock",
|
||||
|
|
|
@ -10,3 +10,6 @@
|
|||
0.09: Move some functions to new time_utils module
|
||||
0.10: Default to sched.js if custom js not found
|
||||
0.11: Fix default dow
|
||||
0.12: Update default buzz patterns to new values
|
||||
Improve timer message using formatDuration
|
||||
Fix wrong fallback for buzz pattern
|
||||
|
|
|
@ -106,8 +106,8 @@ exports.getSettings = function () {
|
|||
defaultRepeat: false,
|
||||
buzzCount: 10,
|
||||
buzzIntervalMillis: 3000, // 3 seconds
|
||||
defaultAlarmPattern: "..",
|
||||
defaultTimerPattern: ".."
|
||||
defaultAlarmPattern: "::",
|
||||
defaultTimerPattern: "::"
|
||||
},
|
||||
require("Storage").readJSON("sched.settings.json", true) || {}
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "sched",
|
||||
"name": "Scheduler",
|
||||
"version": "0.11",
|
||||
"version": "0.12",
|
||||
"description": "Scheduling library for alarms and timers",
|
||||
"icon": "app.png",
|
||||
"type": "scheduler",
|
||||
|
|
|
@ -9,7 +9,7 @@ function showAlarm(alarm) {
|
|||
const settings = require("sched").getSettings();
|
||||
|
||||
let msg = "";
|
||||
msg += require("time_utils").formatTime(alarm.timer ? alarm.timer : alarm.t);
|
||||
msg += alarm.timer ? require("time_utils").formatDuration(alarm.timer) : require("time_utils").formatTime(alarm.t);
|
||||
if (alarm.msg) {
|
||||
msg += "\n"+alarm.msg;
|
||||
} else {
|
||||
|
@ -50,7 +50,8 @@ function showAlarm(alarm) {
|
|||
Bangle.setLocked(false);
|
||||
}
|
||||
|
||||
require("buzz").pattern(alarm.vibrate === undefined ? ".." : alarm.vibrate).then(() => {
|
||||
const pattern = alarm.vibrate || (alarm.timer ? settings.defaultTimerPattern : settings.defaultAlarmPattern);
|
||||
require("buzz").pattern(pattern).then(() => {
|
||||
if (buzzCount--) {
|
||||
setTimeout(buzz, settings.buzzIntervalMillis);
|
||||
} else if (alarm.as) { // auto-snooze
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
min: 5,
|
||||
max: 30,
|
||||
step: 5,
|
||||
format: v => v + /*LANG*/" min",
|
||||
format: v => v + /*LANG*/"m",
|
||||
onchange: v => {
|
||||
settings.defaultSnoozeMillis = v * 60000;
|
||||
require("sched").setSettings(settings);
|
||||
|
|
|
@ -50,4 +50,6 @@
|
|||
UI improvements to Locale and Date & Time menu
|
||||
0.45: Add calibrate battery option
|
||||
0.46: Fix regression after making 'calibrate battery' only for Bangle.js 2
|
||||
0.47: Allow colors to be translated
|
||||
Improve "Turn Off" user experience
|
||||
0.48: Allow reading custom themes from files
|
||||
|
|
|
@ -270,22 +270,28 @@ function showThemeMenu() {
|
|||
}
|
||||
upd(th);
|
||||
}
|
||||
let rgb = {
|
||||
black: "#000", white: "#fff",
|
||||
red: "#f00", green: "#0f0", blue: "#00f",
|
||||
cyan: "#0ff", magenta: "#f0f", yellow: "#ff0",
|
||||
};
|
||||
if (!BANGLEJS2) Object.assign(rgb, {
|
||||
let rgb = {};
|
||||
rgb[/*LANG*/'black'] = "#000";
|
||||
rgb[/*LANG*/'white'] = "#fff";
|
||||
rgb[/*LANG*/'red'] = "#f00";
|
||||
rgb[/*LANG*/'green'] = "#0f0";
|
||||
rgb[/*LANG*/'blue'] = "#00f";
|
||||
rgb[/*LANG*/'cyan'] = "#0ff";
|
||||
rgb[/*LANG*/'magenta'] = "#f0f";
|
||||
rgb[/*LANG*/'yellow'] = "#ff0";
|
||||
if (!BANGLEJS2) {
|
||||
// these would cause dithering, which is not great for e.g. text
|
||||
orange: "#ff7f00", purple: "#7f00ff", grey: "#7f7f7f",
|
||||
});
|
||||
rgb[/*LANG*/'orange'] = "#ff7f00";
|
||||
rgb[/*LANG*/'purple'] = "#7f00ff";
|
||||
rgb[/*LANG*/'grey'] = "#7f7f7f";
|
||||
}
|
||||
let colors = [], names = [];
|
||||
for(const c in rgb) {
|
||||
names.push(c);
|
||||
colors.push(cl(rgb[c]));
|
||||
}
|
||||
let menu = {
|
||||
'':{title:'Custom Theme'},
|
||||
'':{title:/*LANG*/'Custom Theme'},
|
||||
"< Back": () => showThemeMenu()
|
||||
};
|
||||
const labels = {
|
||||
|
@ -587,7 +593,25 @@ function showUtilMenu() {
|
|||
} else showUtilMenu();
|
||||
});
|
||||
};
|
||||
menu[/*LANG*/'Turn Off'] = ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() };
|
||||
menu[/*LANG*/"Turn Off"] = () => {
|
||||
E.showPrompt(/*LANG*/"Are you sure? Alarms and timers won't fire", {
|
||||
title:/*LANG*/"Turn Off"
|
||||
}).then((confirmed) => {
|
||||
if (confirmed) {
|
||||
E.showMessage(/*LANG*/"See you\nlater!", /*LANG*/"Goodbye");
|
||||
setTimeout(() => {
|
||||
// clear the screen so when the user will turn on the watch they'll see
|
||||
// an empty screen instead of the latest displayed screen
|
||||
E.showMessage();
|
||||
g.clear(true);
|
||||
|
||||
Bangle.softOff ? Bangle.softOff() : Bangle.off();
|
||||
}, 2500);
|
||||
} else {
|
||||
showUtilMenu();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (Bangle.factoryReset) {
|
||||
menu[/*LANG*/'Factory Reset'] = ()=>{
|
||||
|
|
|
@ -37,7 +37,7 @@ When the GPS obtains a fix the number of satellites is displayed as 'Sats:nn'. W
|
|||
|
||||
## Power Saving
|
||||
|
||||
The The GPS Adv Sport app obeys the watch screen off timeouts as a power saving measure. Restore the screen as per any of the colck/watch apps. Use BTN2 to lock the screen on but doing this will use more battery.
|
||||
The The GPS Adv Sport app obeys the watch screen off timeouts as a power saving measure. Restore the screen as per any of the clock/watch apps. Use BTN2 to lock the screen on but doing this will use more battery.
|
||||
|
||||
This app will work quite happily on its own but will use the [GPS Setup App](https://banglejs.com/apps/#gps%20setup) if it is installed. You may choose to use the GPS Setup App to gain significantly longer battery life while the GPS is on. Please read the Low Power GPS Setup App Readme to understand what this does.
|
||||
|
||||
|
@ -65,7 +65,7 @@ The Droidscript script file is called : **GPS Adv Sports II.js**
|
|||
|
||||
Start/Stop buttons tell the Bangle.js to start or stop sending BLE data packets to the Android device. While stopped the Bangle.js reverts to full power saving mode when the screen is asleep.
|
||||
|
||||
When runnig a blue 'led' will flash each time a data packet is recieved to refresh the android display.
|
||||
When running a blue 'led' will flash each time a data packet is recieved to refresh the android display.
|
||||
|
||||
An orange 'led' will flash for each reconnection attempt if no data is received for 30 seconds. It will keep trying to reconnect so you can restart the Bangle, run another Bangle app or temprarily turn off bluetooth. The android mirror display will automatically reconnect when the GPS Adv Sports II app is running on the Bangle again. ( Designed to leave the Android device running as the display mirror in a sealed case all day while retaining the ability to do other functions on the Bangle.js and returning to the GPS Speed Alt II app. )
|
||||
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
0.02: Fix position and overdraw bugs
|
||||
0.03: Better memory usage, theme support
|
||||
0.04: Replace the 8 phases by a more exact drawing, see forum.espruino.com/conversations/371985
|
||||
0.05: Fixed the algorithm for calculating the moon's phase
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "widmp",
|
||||
"name": "Moon Phase Widget",
|
||||
"version": "0.04",
|
||||
"version": "0.05",
|
||||
"description": "Display the current moon phase in blueish for both hemispheres. In the southern hemisphere the 'My Location' app is needed.",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
WIDGETS["widmoon"] = { area: "tr", width: 24, draw: function() {
|
||||
const CenterX = this.x + 12, CenterY = this.y + 12, Radius = 11;
|
||||
var southernHemisphere = false; // when in southern hemisphere, use the "My Location" App
|
||||
var lastCalculated = 0; // When we last calculated the phase
|
||||
var phase = 0; // The last phase we calculated
|
||||
|
||||
const simulate = false; // simulate one month in one minute
|
||||
const updateR = 1000; // update every x ms in simulation
|
||||
|
||||
function moonPhase() {
|
||||
const d = Date();
|
||||
var month = d.getMonth(), year = d.getFullYear(), day = d.getDate();
|
||||
if (simulate) day = d.getSeconds() / 2 +1;
|
||||
if (month < 3) {year--; month += 12;}
|
||||
mproz = ((365.25 * year + 30.6 * ++month + day - 694039.09) / 29.5305882);
|
||||
mproz = mproz - (mproz | 0); // strip integral digits, result is between 0 and <1
|
||||
if (simulate) console.log(mproz + " " + day);
|
||||
return (mproz);
|
||||
// https://deirdreobyrne.github.io/calculating_moon_phases/
|
||||
function moonPhase(millis) {
|
||||
k = (millis - 946728000000) / 3155760000000;
|
||||
mp = (8328.69142475915 * k) + 2.35555563685;
|
||||
m = (628.30195516723 * k) + 6.24006012726;
|
||||
d = (7771.37714483372 * k) + 5.19846652984;
|
||||
t = d + (0.109764 * Math.sin (mp)) - (0.036652 * Math.sin(m)) + (0.022235 * Math.sin(d+d-mp)) + (0.011484 * Math.sin(d+d)) + (0.003735 * Math.sin(mp+mp)) + (0.00192 * Math.sin(d));
|
||||
k = (1 - Math.cos(t))/2;
|
||||
if (Math.sin(t) < 0) {
|
||||
k = -k;
|
||||
}
|
||||
return (k); // Goes 0 -> 1 for waxing, and from -1 -> 0 for waning
|
||||
}
|
||||
|
||||
function loadLocation() {
|
||||
|
@ -44,11 +49,19 @@ WIDGETS["widmoon"] = { area: "tr", width: 24, draw: function() {
|
|||
g.fillRect(CenterX - Radius, CenterY - Radius, CenterX + Radius, CenterY + Radius);
|
||||
g.setColor(0x41f);
|
||||
|
||||
mproz = moonPhase(); // mproz = 0..<1
|
||||
millis = (new Date()).getTime();
|
||||
if ((millis - lastCalculated) >= 7200000) {
|
||||
phase = moonPhase(millis);
|
||||
lastCalculated = millis;
|
||||
}
|
||||
|
||||
leftFactor = mproz * 4 - 1;
|
||||
rightFactor = (1 - mproz) * 4 - 1;
|
||||
if (mproz >= 0.5) leftFactor = 1; else rightFactor = 1;
|
||||
if (phase < 0) { // waning - phase goes from -1 to 0
|
||||
leftFactor = 1;
|
||||
rightFactor = -1 - 2*phase;
|
||||
} else { // waxing - phase goes from 0 to 1
|
||||
rightFactor = 1;
|
||||
leftFactor = -1 + 2*phase;
|
||||
}
|
||||
if (true == southernHemisphere) {
|
||||
var tmp=leftFactor; leftFactor=rightFactor; rightFactor=tmp;
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
"On": "Ein",
|
||||
"Off": "Aus",
|
||||
"Ok": "OK",
|
||||
"New Timer": "Neue Zeitschaltuhr",
|
||||
"New Timer": "Neuer Kurzzeitwecker",
|
||||
"(repeat)": "(Wiederholung)",
|
||||
"music": "Musik",
|
||||
"Keep Msgs": "Msgs behalten",
|
||||
"circle count": "Kreiszahl",
|
||||
"Keep Msgs": "Nachrichten behalten",
|
||||
"circle count": "Anzahl Kreise",
|
||||
"Auto snooze": "Automatisches Schlummern",
|
||||
"week": "Woche",
|
||||
"Heartrate": "Herzfrequenz",
|
||||
|
@ -43,7 +43,7 @@
|
|||
"colorize icon": "Symbol einfärben",
|
||||
"min. confidence": "Mindestvertrauen",
|
||||
"maximum": "maximal",
|
||||
"distance goal": "Fernziel",
|
||||
"distance goal": "Entfernungsziel",
|
||||
"Circle": "Kreis",
|
||||
"Yes\ndefinitely": "Ja\ndefinitiv",
|
||||
"TAP right top/bottom": "TAP rechts oben/unten",
|
||||
|
@ -52,28 +52,29 @@
|
|||
"Mark Unread": "Ungelesen markieren",
|
||||
"Delete all messages": "Alle Nachrichten löschen",
|
||||
"Unread timer": "Ungelesener Timer",
|
||||
"Quiet Mode": "Leiser Modus",
|
||||
"Quiet Mode": "Stiller Modus",
|
||||
"Utils": "Werkzeuge",
|
||||
"Piezo": "Piezo",
|
||||
"LCD": "LCD",
|
||||
"Record Run": "Rekordlauf",
|
||||
"Record Run": "Lauf aufzeichnen",
|
||||
"Apps": "Apps",
|
||||
"Delete All Messages": "Alle Nachrichten löschen",
|
||||
"start&lap/reset, BTN1: EXIT": "Start&Runde/Zurücksetzen, BTN1: EXIT",
|
||||
"No Messages": "Keine Nachrichten",
|
||||
"Bluetooth": "Bluetooth",
|
||||
"BTNs 1:startlap 2:exit 3:reset": "BTNs 1:startlap 2:exit 3:reset",
|
||||
"BTNs 1:startlap 2:exit 3:reset": "BTNs 1:Rundenstart 2:Exit 3:Reset",
|
||||
"View Message": "Nachricht anzeigen",
|
||||
"Vector font size": "Vektor-Schriftgröße",
|
||||
"Light BW": "Licht BW",
|
||||
"Light BW": "Hell S/W",
|
||||
"BLE": "BLE",
|
||||
"Make Connectable": "Verbindbar machen",
|
||||
"Vibration": "Vibration",
|
||||
"Foreground": "Vorderseite",
|
||||
"Customize": "Anpassen",
|
||||
"Foreground": "Vordergrund",
|
||||
"Customize": "Individualisieren",
|
||||
"Custom Theme": "Individueller Stil",
|
||||
"HID": "HID",
|
||||
"Dark BW": "Dunkel BW",
|
||||
"Passkey BETA": "Hauptschlüssel BETA",
|
||||
"Dark BW": "Dunkel S/W",
|
||||
"Passkey BETA": "Passwort BETA",
|
||||
"Show clocks": "Uhren anzeigen",
|
||||
"Font": "Schriftart",
|
||||
"Launcher Settings": "Launcher-Einstellungen",
|
||||
|
@ -82,30 +83,30 @@
|
|||
"Background 2": "Hintergrund 2",
|
||||
"Foreground 2": "Vordergrund 2",
|
||||
"Add Device": "Gerät hinzufügen",
|
||||
"Highlight BG": "Hervorhebung BG",
|
||||
"Highlight BG": "Hervorhebung HG",
|
||||
"Background": "Hintergrund",
|
||||
"Highlight FG": "Highlight FG",
|
||||
"Highlight FG": "Hervorhebung VG",
|
||||
"Wake on Touch": "Wecken bei Berührung",
|
||||
"Twist Timeout": "Twist Timeout",
|
||||
"Twist Max Y": "Twist Max Y",
|
||||
"LCD Timeout": "LCD-Zeitüberschreitung",
|
||||
"LCD Timeout": "LCD-Leuchtdauer",
|
||||
"LCD Brightness": "LCD-Helligkeit",
|
||||
"Utilities": "Versorgungsunternehmen",
|
||||
"Utilities": "Werkzeuge",
|
||||
"Log": "Protokoll",
|
||||
"Compact Storage": "Kompakte Lagerung",
|
||||
"Wake on BTN3": "Wake auf BTN3",
|
||||
"Twist Threshold": "Schwellenwert verdrehen",
|
||||
"Compact Storage": "Speicherwartung",
|
||||
"Wake on BTN3": "Aufwachen auf BTN3",
|
||||
"Twist Threshold": "Twist Schwellenwert",
|
||||
"Remove": "entfernen",
|
||||
"Connect device\nto add to\nwhitelist": "Gerät verbinden\nzum Hinzufügen zur\nWhitelist",
|
||||
"Debug Info": "Debug-Informationen",
|
||||
"Time Zone": "Zeitzone",
|
||||
"Clock Style": "Uhr Stil",
|
||||
"Wake on BTN2": "Wake auf BTN2",
|
||||
"Wake on FaceUp": "Wake on FaceUp",
|
||||
"Wake on BTN1": "Wake auf BTN1",
|
||||
"Wake on Twist": "Wake on Twist",
|
||||
"Connectable": "Anschließbar",
|
||||
"Second": "Zweite",
|
||||
"Clock Style": "Uhrenstil",
|
||||
"Wake on BTN2": "Aufwecken mit BTN2",
|
||||
"Wake on FaceUp": "Aufwecken mit Display oben",
|
||||
"Wake on BTN1": "Aufwecken mit BTN1",
|
||||
"Wake on Twist": "Aufwecken mit Twist",
|
||||
"Connectable": "Erreichbar",
|
||||
"Second": "Sekunde",
|
||||
"Minute": "Minute",
|
||||
"Turn Off": "Ausschalten",
|
||||
"No Clocks Found": "Keine Uhren gefunden",
|
||||
|
@ -114,10 +115,10 @@
|
|||
"Reset to Defaults": "Auf Standardwerte zurücksetzen",
|
||||
"Flattening battery - this can take hours.\nLong-press button to cancel": "Entladen der Batterie - dies kann Stunden dauern.\nLanger Tastendruck zum Abbrechen",
|
||||
"Reset Settings": "Einstellungen zurücksetzen",
|
||||
"Rewrite Settings": "Einstellungen umschreiben",
|
||||
"Compacting...\nTakes approx\n1 minute": "Verdichten...\nDauert ca.\n1 Minute",
|
||||
"Stay Connectable": "Anschlussfähig bleiben",
|
||||
"Storage": "Lagerung",
|
||||
"Rewrite Settings": "Einstellungen neu schreiben",
|
||||
"Compacting...\nTakes approx\n1 minute": "Speicherwartung...\nDauert ca.\n1 Minute",
|
||||
"Stay Connectable": "Erreichbar bleiben",
|
||||
"Storage": "Speicher",
|
||||
"This will remove everything": "Dadurch wird alles entfernt",
|
||||
"on": "auf",
|
||||
"TIMER": "TIMER",
|
||||
|
@ -126,9 +127,10 @@
|
|||
"Beep": "Piep",
|
||||
"Reset": "Zurücksetzen",
|
||||
"No app has settings": "Keine App hat Einstellungen",
|
||||
"Day": "Tag",
|
||||
"Month": "Monat",
|
||||
"Reset All": "Alle zurücksetzen",
|
||||
"Flatten Battery": "Batterie abflachen",
|
||||
"Flatten Battery": "Batterie entladen",
|
||||
"Right": "Rechts",
|
||||
"Side": "Seite",
|
||||
"Left": "Links",
|
||||
|
@ -141,12 +143,12 @@
|
|||
"Vibrate": "Vibrieren",
|
||||
"Reset all widgets": "Alle Widgets zurücksetzen",
|
||||
"System": "System",
|
||||
"Alerts": "Warnungen",
|
||||
"Locale": "Schauplatz",
|
||||
"Alerts": "Alarme",
|
||||
"Locale": "Ort",
|
||||
"Whitelist": "Whitelist",
|
||||
"Select Clock": "Uhr auswählen",
|
||||
"Disable": "Deaktivieren Sie",
|
||||
"Timer": "Zeitschaltuhr",
|
||||
"Disable": "Deaktivieren",
|
||||
"Timer": "Kurzzeitwecker",
|
||||
"Error in settings": "Fehler in den Einstellungen",
|
||||
"Set Time": "Zeit einstellen",
|
||||
"ALARM": "ALARM",
|
||||
|
@ -164,7 +166,7 @@
|
|||
"Music": "Musik",
|
||||
"color": "Farbe",
|
||||
"off": "aus",
|
||||
"Theme": "Thema",
|
||||
"Theme": "Stil",
|
||||
"one": "eins",
|
||||
"two": "zwei",
|
||||
"three": "drei",
|
||||
|
@ -176,7 +178,22 @@
|
|||
"nine": "neun",
|
||||
"ten": "zehn",
|
||||
"eleven": "elf",
|
||||
"twelve": "zwölf"
|
||||
"twelve": "zwölf",
|
||||
"Time Format": "Zeitformat",
|
||||
"Start Week On": "Wochenbeginn",
|
||||
"Date & Time": "Datum und Zeit",
|
||||
"Calibrate Battery": "Akku kalibrieren",
|
||||
"black": "Schwarz",
|
||||
"white": "Weiß",
|
||||
"red": "Rot",
|
||||
"green": "Grün",
|
||||
"blue": "Blau",
|
||||
"yellow": "Gelb",
|
||||
"magenta": "Magenta",
|
||||
"cyan": "Cyan",
|
||||
"orange": "Orange",
|
||||
"purple": "Violett",
|
||||
"grey": "Grau"
|
||||
},
|
||||
"alarm": {
|
||||
"//": "App-specific overrides",
|
||||
|
@ -189,7 +206,7 @@
|
|||
"ten past *$1": "zehn nach *$1",
|
||||
"quarter past *$1": "viertel nach *$1",
|
||||
"twenty past *$1": "zwanzig nach *$1",
|
||||
"twenty five past *$1": "fünf for halb *$2",
|
||||
"twenty five past *$1": "fünf vor halb *$2",
|
||||
"half past *$1": "halb *$2",
|
||||
"twenty five to *$2": "fünf nach halb *$2",
|
||||
"twenty to *$2": "zwanzig vor *$2",
|
||||
|
|