Merge branch 'master' into notificationColor

pull/1919/head
Gordon Williams 2022-06-06 12:03:38 +01:00 committed by GitHub
commit 78734a1670
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
84 changed files with 1922 additions and 196 deletions

View File

@ -3,3 +3,4 @@
0.03: Do not alarm while charging 0.03: Do not alarm while charging
0.04: Obey system quiet mode 0.04: Obey system quiet mode
0.05: Battery optimisation, add the pause option, bug fixes 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

View File

@ -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 - 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 - 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 - Min steps: Minimal amount of steps to count as an activity
- Temp Threshold: Temperature threshold to determine if the watch is worn

View File

@ -2,17 +2,17 @@ function run() {
if (isNotWorn()) return; if (isNotWorn()) return;
let now = new Date(); let now = new Date();
let h = now.getHours(); 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 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 || health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch
activityreminder_data.stepsOnDate = health.steps; activityreminder_data.stepsOnDate = health.steps;
activityreminder_data.stepsDate = now; activityreminder_data.stepsDate = now;
activityreminder.saveData(activityreminder_data); activityreminder.saveData(activityreminder_data);
/* todo in a futur release /* todo in a futur release
add settimer to trigger like 10 secs after the stepsDate + minSteps Add settimer to trigger like 30 secs after going in this part cause the person have been walking
cancel all other timers of this app (pass some argument to run() to handle long walks and not triggering so often)
*/ */
} }
@ -24,22 +24,42 @@ function run() {
} }
function isNotWorn() { function isNotWorn() {
// todo in a futur release check temperature and mouvement in a futur release return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature());
return Bangle.isCharging();
} }
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 = require("activityreminder");
const activityreminder_settings = activityreminder.loadSettings(); const activityreminder_settings = activityreminder.loadSettings();
if (activityreminder_settings.enabled) { if (activityreminder_settings.enabled) {
const activityreminder_data = activityreminder.loadData(); const activityreminder_data = activityreminder.loadData();
if(activityreminder_data.firstLoad){ if(activityreminder_data.firstLoad){
activityreminder_data.firstLoad =false; activityreminder_data.firstLoad = false;
activityreminder.saveData(activityreminder_data); activityreminder.saveData(activityreminder_data);
} }
setInterval(run, 60000); setInterval(run, 60000);
/* todo in a futur release /* todo in a futur release
increase setInterval time to something that is still sensible (5 mins ?) increase setInterval time to something that is still sensible (5 mins ?)
add settimer to trigger like 10 secs after the stepsDate + minSteps when we added a settimer
cancel all other timers of this app
*/ */
} }

View File

@ -8,7 +8,8 @@ exports.loadSettings = function () {
maxInnactivityMin: 30, maxInnactivityMin: 30,
dismissDelayMin: 15, dismissDelayMin: 15,
pauseDelayMin: 120, pauseDelayMin: 120,
minSteps: 50 minSteps: 50,
tempThreshold: 27
}, storage.readJSON("activityreminder.s.json", true) || {}); }, storage.readJSON("activityreminder.s.json", true) || {});
}; };

View File

@ -3,7 +3,7 @@
"name": "Activity Reminder", "name": "Activity Reminder",
"shortName":"Activity Reminder", "shortName":"Activity Reminder",
"description": "A reminder to take short walks for the ones with a sedentary lifestyle", "description": "A reminder to take short walks for the ones with a sedentary lifestyle",
"version":"0.05", "version":"0.06",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",
"tags": "tool,activity", "tags": "tool,activity",

View File

@ -55,7 +55,7 @@
}, },
'Pause delay': { 'Pause delay': {
value: settings.pauseDelayMin, value: settings.pauseDelayMin,
min: 30, max: 240, min: 30, max: 240, step: 5,
onchange: v => { onchange: v => {
settings.pauseDelayMin = v; settings.pauseDelayMin = v;
activityreminder.writeSettings(settings); activityreminder.writeSettings(settings);
@ -66,11 +66,20 @@
}, },
'Min steps': { 'Min steps': {
value: settings.minSteps, value: settings.minSteps,
min: 10, max: 500, min: 10, max: 500, step: 10,
onchange: v => { onchange: v => {
settings.minSteps = v; settings.minSteps = v;
activityreminder.writeSettings(settings); 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);
}
} }
}); });
}) })

View File

@ -29,3 +29,4 @@
0.27: New UI! 0.27: New UI!
0.28: Fix bug with alarms not firing when configured to fire only once 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.29: Fix wrong 'dow' handling in new timer if first day of week is Monday
0.30: Fix "Enable All"

View File

@ -86,7 +86,8 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
const menu = { const menu = {
"": { "title": isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm" }, "": { "title": isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm" },
"< Back": () => { "< Back": () => {
saveAlarm(alarm, alarmIndex, time); prepareAlarmForSave(alarm, alarmIndex, time);
saveAndReload();
showMainMenu(); showMainMenu();
}, },
/*LANG*/"Hour": { /*LANG*/"Hour": {
@ -144,7 +145,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
E.showMenu(menu); E.showMenu(menu);
} }
function saveAlarm(alarm, alarmIndex, time) { function prepareAlarmForSave(alarm, alarmIndex, time) {
alarm.t = require("time_utils").encodeTime(time); alarm.t = require("time_utils").encodeTime(time);
alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0; alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0;
@ -153,8 +154,6 @@ function saveAlarm(alarm, alarmIndex, time) {
} else { } else {
alarms[alarmIndex] = alarm; alarms[alarmIndex] = alarm;
} }
saveAndReload();
} }
function saveAndReload() { function saveAndReload() {
@ -251,7 +250,8 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
const menu = { const menu = {
"": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" }, "": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" },
"< Back": () => { "< Back": () => {
saveTimer(timer, timerIndex, time); prepareTimerForSave(timer, timerIndex, time);
saveAndReload();
showMainMenu(); showMainMenu();
}, },
/*LANG*/"Hours": { /*LANG*/"Hours": {
@ -293,7 +293,7 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
E.showMenu(menu); E.showMenu(menu);
} }
function saveTimer(timer, timerIndex, time) { function prepareTimerForSave(timer, timerIndex, time) {
timer.timer = require("time_utils").encodeTime(time); timer.timer = require("time_utils").encodeTime(time);
timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer; timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer;
timer.last = 0; timer.last = 0;
@ -303,8 +303,6 @@ function saveTimer(timer, timerIndex, time) {
} else { } else {
alarms[timerIndex] = timer; alarms[timerIndex] = timer;
} }
saveAndReload();
} }
function showAdvancedMenu() { function showAdvancedMenu() {
@ -327,7 +325,16 @@ function enableAll(on) {
} else { } else {
E.showPrompt(/*LANG*/"Are you sure?", { title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All" }).then((confirm) => { E.showPrompt(/*LANG*/"Are you sure?", { title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All" }).then((confirm) => {
if (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(); saveAndReload();
showMainMenu(); showMainMenu();
} else { } else {

View File

@ -2,7 +2,7 @@
"id": "alarm", "id": "alarm",
"name": "Alarms & Timers", "name": "Alarms & Timers",
"shortName": "Alarms", "shortName": "Alarms",
"version": "0.29", "version": "0.30",
"description": "Set alarms and timers on your Bangle", "description": "Set alarms and timers on your Bangle",
"icon": "app.png", "icon": "app.png",
"tags": "tool,alarm,widget", "tags": "tool,alarm,widget",

View File

@ -9,3 +9,5 @@
0.09: Fix time/date disappearing after fullscreen notification 0.09: Fix time/date disappearing after fullscreen notification
0.10: Use ClockFace library 0.10: Use ClockFace library
0.11: Use ClockFace.is12Hour 0.11: Use ClockFace.is12Hour
0.12: Add settings to hide date,widgets
0.13: Add font setting

View File

@ -4,3 +4,7 @@ A simple digital clock showing seconds as a horizontal bar.
| 24hr style | 12hr style | | 24hr style | 12hr style |
| --- | --- | | --- | --- |
| ![24-hour bar clock](screenshot.png) | ![12-hour bar clock with meridian](screenshot_pm.png) | | ![24-hour bar clock](screenshot.png) | ![12-hour bar clock with meridian](screenshot_pm.png) |
## Settings
* `Show date`: display date at the bottom of screen
* `Font`: choose between bitmap or vector fonts

View File

@ -51,24 +51,25 @@ function dateText(date) {
const ClockFace = require("ClockFace"), const ClockFace = require("ClockFace"),
clock = new ClockFace({ clock = new ClockFace({
precision:1, precision:1,
settingsFile:'barclock.settings.json',
init: function() { init: function() {
const Layout = require("Layout"); const Layout = require("Layout");
this.layout = new Layout({ this.layout = new Layout({
type: "v", c: [ type: "v", c: [
{ {
type: "h", 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: "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}, {id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
{height: 40}, this.showDate ? {height: 40} : {},
{id: "date", type: "txt", font: "10%", valign: 1}, this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {},
], ],
}, {lazy: true}); }, {lazy: true});
// adjustments based on screen size and whether we display am/pm // adjustments based on screen size and whether we display am/pm
let thickness; // bar thickness, same as time font "pixel block" size 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) // Maximum font size = (<screen width> - <ampm: 2chars * (2*6)px>) / (5chars * 6px)
thickness = Math.floor((Bangle.appRect.w-24)/(5*6)); thickness = Math.floor((Bangle.appRect.w-24)/(5*6));
} else { } else {
@ -76,13 +77,23 @@ const ClockFace = require("ClockFace"),
thickness = Math.floor(Bangle.appRect.w/(5*6)); thickness = Math.floor(Bangle.appRect.w/(5*6));
} }
this.layout.bar.height = thickness+1; 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(); this.layout.update();
}, },
update: function(date, c) { update: function(date, c) {
if (c.m) this.layout.time.label = timeText(date); if (c.m) this.layout.time.label = timeText(date);
if (c.h) this.layout.ampm.label = ampmText(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; const SECONDS_PER_MINUTE = 60;
if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE; if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
this.layout.render(); this.layout.render();

View File

@ -1,7 +1,7 @@
{ {
"id": "barclock", "id": "barclock",
"name": "Bar Clock", "name": "Bar Clock",
"version": "0.11", "version": "0.13",
"description": "A simple digital clock showing seconds as a bar", "description": "A simple digital clock showing seconds as a bar",
"icon": "clock-bar.png", "icon": "clock-bar.png",
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}], "screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
@ -12,6 +12,10 @@
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
{"name":"barclock.app.js","url":"clock-bar.js"}, {"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} {"name":"barclock.img","url":"clock-bar-icon.js","evaluate":true}
],
"data": [
{"name":"barclock.settings.json"}
] ]
} }

26
apps/barclock/settings.js Normal file
View File

@ -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);
});

9
apps/barcode/ChangeLog Normal file
View File

@ -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...)

23
apps/barcode/README.md Normal file
View File

@ -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.

428
apps/barcode/barcode.app.js Normal file
View File

@ -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();
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -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//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -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"}]
}

1
apps/bigdclock/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: Initial version

14
apps/bigdclock/README.md Normal file
View File

@ -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.
![screenshot](./screenshot.png)
## Creator
Created by [Deirdre O'Byrne](https://github.com/deirdreobyrne)

View File

@ -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();

View File

@ -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"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -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}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -7,3 +7,4 @@
0.07: Fix off-by-one-error on previous month 0.07: Fix off-by-one-error on previous month
0.08: Do not register as watch, manually start clock on button 0.08: Do not register as watch, manually start clock on button
read start of week from system settings read start of week from system settings
0.09: Fix scope of let variables

View File

@ -16,6 +16,12 @@ const white = "#ffffff";
const red = "#d41706"; const red = "#d41706";
const blue = "#0000ff"; const blue = "#0000ff";
const yellow = "#ffff00"; 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 settings = require('Storage').readJSON("calendar.json", true) || {};
let startOnSun = ((require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0) === 0; let startOnSun = ((require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0) === 0;
@ -27,19 +33,12 @@ if (settings.ndColors === undefined)
} }
if (settings.ndColors === true) { if (settings.ndColors === true) {
let bgColor = white; bgColor = white;
let bgColorMonth = blue; bgColorMonth = blue;
let bgColorDow = black; bgColorDow = black;
let bgColorWeekend = yellow; bgColorWeekend = yellow;
let fgOtherMonth = blue; fgOtherMonth = blue;
let fgSameMonth = black; fgSameMonth = black;
} else {
let bgColor = color4;
let bgColorMonth = color1;
let bgColorDow = color2;
let bgColorWeekend = color3;
let fgOtherMonth = gray1;
let fgSameMonth = white;
} }
function getDowLbls(locale) { function getDowLbls(locale) {

View File

@ -1,7 +1,7 @@
{ {
"id": "calendar", "id": "calendar",
"name": "Calendar", "name": "Calendar",
"version": "0.08", "version": "0.09",
"description": "Simple calendar", "description": "Simple calendar",
"icon": "calendar.png", "icon": "calendar.png",
"screenshots": [{"url":"screenshot_calendar.png"}], "screenshots": [{"url":"screenshot_calendar.png"}],

View File

@ -0,0 +1,2 @@
1.00: New App!
1.01: Use fractional numbers and scale the points to keep working consistently on whole screen

View File

@ -6,6 +6,7 @@ A simple calibration app for the touchscreen
Once lauched touch the cross that appear on the screen to make Once lauched touch the cross that appear on the screen to make
another spawn elsewhere. another spawn elsewhere.
each new touch on the screen will help to calibrate the offset Each new touch on the screen will help to calibrate the offset
of your finger on the screen. After five or more input, press of your finger on the screen. After four or more inputs, press
the button to save the calibration and close the application. the button to save the calibration and close the application. Quality
of the calibration gets better with every touch on a cross.

View File

@ -1,40 +1,60 @@
class BanglejsApp { class BanglejsApp {
constructor() { 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.x = 0;
this.y = 0; this.y = 0;
this.step = 0;
this.settings = { this.settings = {
xoffset: 0, xoffset: [0],
yoffset: 0, yoffset: [0],
xMaxActual: [this.target.xMax],
yMaxActual: [this.target.yMax],
}; };
} }
load_settings() { load_settings() {
let settings = require('Storage').readJSON('calibration.json', true) || {active: false}; 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('loaded settings:');
console.log(settings); console.log(settings);
return settings; return settings;
} }
save_settings() { getMedian(array){
this.settings.active = true; array.sort();
this.settings.reload = false; let i = Math.floor(array.length/2);
require('Storage').writeJSON('calibration.json', this.settings); if ( array.length % 2 && array.length > 1 ){
return (array[i]+array[i+1])/2;
} else {
return array[i];
}
}
console.log('saved settings:'); getMedianSettings(){
console.log(this.settings); 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() { explain() {
@ -46,29 +66,78 @@ class BanglejsApp {
} }
drawTarget() { drawTarget() {
this.x = 16 + Math.floor(Math.random() * (g.getWidth() - 32)); switch (this.step){
this.y = 40 + Math.floor(Math.random() * (g.getHeight() - 80)); 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, this.y - 5, this.x, this.y + 5);
g.drawLine(this.x - 5, this.y, this.x + 5, this.y); g.drawLine(this.x - 5, this.y, this.x + 5, this.y);
g.setFont('Vector', 10); 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) { setOffset(xy) {
this.settings.xoffset = Math.round((this.settings.xoffset + (this.x - Math.floor((this.x + xy.x)/2)))/2); switch (this.step){
this.settings.yoffset = Math.round((this.settings.yoffset + (this.y - Math.floor((this.y + xy.y)/2)))/2); 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()); E.srand(Date.now());
Bangle.loadWidgets();
Bangle.drawWidgets();
calibration = new BanglejsApp(); calibration = new BanglejsApp();
calibration.load_settings(); calibration.load_settings();
Bangle.disableCalibration = true;
function touchHandler (btn, xy){
if (xy) calibration.setOffset(xy);
calibration.nextStep();
calibration.drawTarget();
}
let modes = { let modes = {
mode : 'custom', mode : 'custom',
@ -76,10 +145,7 @@ let modes = {
calibration.save_settings(this.settings); calibration.save_settings(this.settings);
load(); load();
}, },
touch : function(btn, xy) { touch : touchHandler,
calibration.setOffset(xy);
calibration.drawTarget();
},
}; };
Bangle.setUI(modes); Bangle.setUI(modes);
calibration.drawTarget(); calibration.drawTarget();

View File

@ -1,7 +1,7 @@
let cal_settings = require('Storage').readJSON("calibration.json", true) || {active: false}; let cal_settings = require('Storage').readJSON("calibration.json", true) || {active: false};
Bangle.on('touch', function(button, xy) { Bangle.on('touch', function(button, xy) {
// do nothing if the calibration is deactivated // 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 // reload the calibration offset at each touch event /!\ bad for the flash memory
if (cal_settings.reload === true) { if (cal_settings.reload === true) {
@ -9,6 +9,6 @@ Bangle.on('touch', function(button, xy) {
} }
// apply the calibration offset // apply the calibration offset
xy.x += cal_settings.xoffset; xy.x = E.clip(Math.round((xy.x + (cal_settings.xoffset || 0)) * (cal_settings.xscale || 1)),0,g.getWidth());
xy.y += cal_settings.yoffset; xy.y = E.clip(Math.round((xy.y + (cal_settings.yoffset || 0)) * (cal_settings.yscale || 1)),0,g.getHeight());
}); });

View File

@ -2,7 +2,7 @@
"name": "Touchscreen Calibration", "name": "Touchscreen Calibration",
"shortName":"Calibration", "shortName":"Calibration",
"icon": "calibration.png", "icon": "calibration.png",
"version":"1.00", "version":"1.01",
"description": "A simple calibration app for the touchscreen", "description": "A simple calibration app for the touchscreen",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",

BIN
apps/cogclock/15x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

2
apps/cogclock/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New clock
0.02: Use ClockFace library, add settings

111
apps/cogclock/app.js Normal file
View File

@ -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();

1
apps/cogclock/icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwhC/ACcikQXpCQUCC4MgBAgqMCQIXEAgQXNBwIDCAggXOABAXLHwQAHMYQXmmczI5oiCBwUjCwIABmQXDEgJ0KCwMwCwMDDAgyGLYoWBgAXBgAYBMZIXEkYWBC4YYBGAh7FFwgHCC4YEBPRIwCFwYXFGAaqHC56oIIwgXFJAbUJLwpgHI4qPDIwpIFR4wWDLwa6BAAQHDVIYYCC/gYCC453MPIR3HU5gADd5bXHC4rvJMAYAECwJeCd5MjGAjVDC4ZHGNARIFGAgNDFw5IJUogwFC4gwBDAhGBBghIFBQhhBbYguEPAweCDAgACCwZACNg5LFXQYsIC5QAFdg4XcCxJHNBwYTEC6A+BJYQEEC5YYBMYhbCCxo0GCaIXbAHgA="))

BIN
apps/cogclock/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -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"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

19
apps/cogclock/settings.js Normal file
View File

@ -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);
});

View File

@ -14,7 +14,7 @@ var nhwmn = { // New homework Menu
function newHomeworkMenu() { function newHomeworkMenu() {
E.showMessage("Getting subjects..."); 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 splitsubjects = rawsubjects.split(",");
var lastItem = splitsubjects[splitsubjects.length - 1]; var lastItem = splitsubjects[splitsubjects.length - 1];
var thiscurrentsubject; var thiscurrentsubject;

View File

@ -9,6 +9,10 @@
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"custom": "subjects.html", "custom": "subjects.html",
"data": [
{"name":"homework.txt" },
{"name":"homework.subjects.txt" }
],
"storage": [ "storage": [
{"name":"homework.app.js","url":"app.js"}, {"name":"homework.app.js","url":"app.js"},
{"name":"homework.img","url":"app-icon.js","evaluate":true} {"name":"homework.img","url":"app-icon.js","evaluate":true}

View File

@ -20,9 +20,10 @@
// send finished app (in addition to contents of app.json) // send finished app (in addition to contents of app.json)
sendCustomizedApp({ sendCustomizedApp({
storage:[ storage:[
{name:"subjects.txt"}, {name:"homework.subjects.txt", url:"subjects.txt", content:app},
] ]
}); });
console.log("Sent homework.subjects.txt!");
}); });
</script> </script>

1
apps/invader/ChangeLog Normal file
View File

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

23
apps/invader/README.md Normal file
View File

@ -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

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

@ -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=="))

452
apps/invader/app.js Normal file
View File

@ -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();

BIN
apps/invader/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

View File

@ -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}
]
}

BIN
apps/invader/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -20,3 +20,4 @@
0.20: Use alarm for alarm functionality instead of own implementation. 0.20: Use alarm for alarm functionality instead of own implementation.
0.21: Add custom theming. 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

View File

@ -147,8 +147,7 @@ var iconCharging = {
buffer : require("heatshrink").decompress(atob("23btugAwUBtoICARG0h048eODQYCJ6P/AAUCCJfbo4SDxYRLtEcuPHjlwgoRJ7RnIloUHoYjDAQfAExEAwUIkACEkSAIEYwCBhZKH6EIJI0CJRFHEY0BJRWBSgf//0AJRYSE4BKLj4SE8BKLv4RD/hK/JS2AXY0gXwRKG4cMmACCJQMAg8csEFJQsBAwfasEAm379u0gFbcBfHzgFBz1xMQZKBjY/D0E2+BOChu26yVEEYdww+cgAFCg+cgIfB6RKF4HbgEIkGChEAthfCJQ0eEAIjBBAMxk6GCJQtgtyVBwRKBAQMbHAJKGXIIFCgACBhl54qVG2E+EAJKBJoWAm0WJQ6SCXgdxFgMLJQvYjeAEAUwFIUitEtJQ14NwUHgEwKYZKGwOwNYX7XgWCg3CJQ5rB4MevPnAoPDJRJrCgEG/ECAoNsJRUwoEesIIBiJKI3CVDti/CJRKVDiJHBSo0YsOGjED8AjBcAcIgdhcAXAPIUAcAYIBcA4dBAQUG8BrBgBuCgOwcBEeXIK2BBAIFBgRqBGoYAChq8CcYUE4FbUYOACQsHzgjDgwFBCIImBAQsDtwYD7cAloRI22B86YBw5QBgoRJ7dAgYEDCJaeBJoMcsARMAQNoJIIRE6A")) buffer : require("heatshrink").decompress(atob("23btugAwUBtoICARG0h048eODQYCJ6P/AAUCCJfbo4SDxYRLtEcuPHjlwgoRJ7RnIloUHoYjDAQfAExEAwUIkACEkSAIEYwCBhZKH6EIJI0CJRFHEY0BJRWBSgf//0AJRYSE4BKLj4SE8BKLv4RD/hK/JS2AXY0gXwRKG4cMmACCJQMAg8csEFJQsBAwfasEAm379u0gFbcBfHzgFBz1xMQZKBjY/D0E2+BOChu26yVEEYdww+cgAFCg+cgIfB6RKF4HbgEIkGChEAthfCJQ0eEAIjBBAMxk6GCJQtgtyVBwRKBAQMbHAJKGXIIFCgACBhl54qVG2E+EAJKBJoWAm0WJQ6SCXgdxFgMLJQvYjeAEAUwFIUitEtJQ14NwUHgEwKYZKGwOwNYX7XgWCg3CJQ5rB4MevPnAoPDJRJrCgEG/ECAoNsJRUwoEesIIBiJKI3CVDti/CJRKVDiJHBSo0YsOGjED8AjBcAcIgdhcAXAPIUAcAYIBcA4dBAQUG8BrBgBuCgOwcBEeXIK2BBAIFBgRqBGoYAChq8CcYUE4FbUYOACQsHzgjDgwFBCIImBAQsDtwYD7cAloRI22B86YBw5QBgoRJ7dAgYEDCJaeBJoMcsARMAQNoJIIRE6A"))
}; };
var iconNoBattery = { var iconWarning = {
text: "NO BAT",
width : 50, height : 50, bpp : 3, width : 50, height : 50, bpp : 3,
transparent : 1, transparent : 1,
buffer : require("heatshrink").decompress(atob("kmSpIC/AWMyoQIFsmECJFJhMmA4QXByVICIwODAQ4RRFIQGD5JVLkIGDzJqMyAGDph8MiRKGyApEAoZKFyYIDQwMkSQNkQZABBhIIOOJRuEL5gRIAUKACVQMhmUSNYNDQYJTBBwYFByGTkOE5FJWYNMknCAQKYCiaSCpmGochDoSYBhMwTAZrChILBhmEzKPBF4ImBTAREBDoMmEwJVDoYjBycJFgWEJQRuLJQ1kmQCCjJlCBYbjCagaDBwyDBmBuBF4TjJAUQKINBChCDQxZBcZIIQF4NIgEAgKSDiQmEVQKMBoARBAAMCSQLLBVoxqKL4gaCChVCNwoRKOIo4CJIgABBoSMHpIRFgDdJOIJUBCAUJRgJuEAQb+DIIgRIAX4C/ASOQA")) buffer : require("heatshrink").decompress(atob("kmSpIC/AWMyoQIFsmECJFJhMmA4QXByVICIwODAQ4RRFIQGD5JVLkIGDzJqMyAGDph8MiRKGyApEAoZKFyYIDQwMkSQNkQZABBhIIOOJRuEL5gRIAUKACVQMhmUSNYNDQYJTBBwYFByGTkOE5FJWYNMknCAQKYCiaSCpmGochDoSYBhMwTAZrChILBhmEzKPBF4ImBTAREBDoMmEwJVDoYjBycJFgWEJQRuLJQ1kmQCCjJlCBYbjCagaDBwyDBmBuBF4TjJAUQKINBChCDQxZBcZIIQF4NIgEAgKSDiQmEVQKMBoARBAAMCSQLLBVoxqKL4gaCChVCNwoRKOIo4CJIgABBoSMHpIRFgDdJOIJUBCAUJRgJuEAQb+DIIgRIAX4C/ASOQA"))
@ -321,19 +320,21 @@ function drawState(){
if(!isAlarmEnabled()){ if(!isAlarmEnabled()){
var bat = E.getBattery(); var bat = E.getBattery();
var flash = storage.getFree() / process.env.STORAGE;
var current = new Date(); var current = new Date();
var hours = current.getHours(); var hours = current.getHours();
var iconImg = var iconMsg =
Bangle.isCharging() ? iconCharging : Bangle.isCharging() ? { icon: iconCharging, text: "STATUS" } :
bat < 30 ? iconNoBattery : bat < 30 ? { icon: iconWarning, text: "BAT" } :
Bangle.isGPSOn() ? iconSatellite : flash < 0.1 ? { icon: iconWarning, text: "DISK" } :
hours % 4 == 0 ? iconSaturn : Bangle.isGPSOn() ? { icon: iconSatellite, text: "STATUS" } :
hours % 4 == 1 ? iconMars : hours % 4 == 0 ? { icon: iconSaturn, text: "STATUS" } :
hours % 4 == 2 ? iconMoon : hours % 4 == 1 ? { icon: iconMars, text: "STATUS" } :
iconEarth; hours % 4 == 2 ? { icon: iconMoon, text: "STATUS" } :
g.drawImage(iconImg, 23, 118); { icon: iconEarth, text: "STATUS" };
g.drawImage(iconMsg.icon, 23, 118);
g.setColor(cWhite); g.setColor(cWhite);
g.drawString("STATUS", 23+26, 108); g.drawString(iconMsg.text, 23+26, 108);
} else { } else {
// Alarm within symbol // Alarm within symbol
g.setColor(color2); g.setColor(color2);

View File

@ -3,7 +3,7 @@
"name": "LCARS Clock", "name": "LCARS Clock",
"shortName":"LCARS", "shortName":"LCARS",
"icon": "lcars.png", "icon": "lcars.png",
"version":"0.22", "version":"0.23",
"readme": "README.md", "readme": "README.md",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"description": "Library Computer Access Retrieval System (LCARS) clock.", "description": "Library Computer Access Retrieval System (LCARS) clock.",

View File

@ -52,3 +52,4 @@
0.37: Now use the setUI 'back' icon in the top left rather than specific buttons/menu items 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.38: Add telegram foss handling
0.39: Set default color for message icons according to theme 0.39: Set default color for message icons according to theme
Don't turn on the screen after unread timeout expires (#1873)

View File

@ -411,19 +411,17 @@ function cancelReloadTimeout() {
unreadTimeout = undefined; unreadTimeout = undefined;
} }
g.clear(); g.clear();
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
setTimeout(() => { setTimeout(() => {
var unreadTimeoutSecs = settings.unreadTimeout; var unreadTimeoutMillis = (settings.unreadTimeout || 60) * 1000;
if (unreadTimeoutSecs===undefined) unreadTimeoutSecs=60; if (unreadTimeoutMillis) {
if (unreadTimeoutSecs) unreadTimeout = setTimeout(load, unreadTimeoutMillis);
unreadTimeout = setTimeout(function() { }
print("Message not seen - reloading");
load();
}, unreadTimeoutSecs*1000);
// only openMusic on launch if music is new // only openMusic on launch if music is new
var newMusic = MESSAGES.some(m=>m.id==="music"&&m.new); var newMusic = MESSAGES.some(m => m.id === "music" && m.new);
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:1,openMusic:newMusic&&settings.openMusic}); checkMessages({ clockIfNoMsg: 0, clockIfAllRead: 0, showMsgIfUnread: 1, openMusic: newMusic && settings.openMusic });
},10); // if checkMessages wants to 'load', do that }, 10); // if checkMessages wants to 'load', do that

View File

@ -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 if (quiet) WIDGETS["messages"].t -= 500000; // if quiet, set last time in the past so there is no buzzing
WIDGETS["messages"].width=this.iconwidth; WIDGETS["messages"].width=this.iconwidth;
Bangle.drawWidgets(); Bangle.drawWidgets();
Bangle.setLCDPower(1);// turns screen on
},hide:function() { },hide:function() {
delete WIDGETS["messages"].t; delete WIDGETS["messages"].t;
delete WIDGETS["messages"].l; delete WIDGETS["messages"].l;

View File

@ -1,2 +1,3 @@
0.01: New App! 0.01: New App!
0.02: Redraw only when seconds change 0.02: Redraw only when seconds change
0.03: Fix typo in redraw check

View File

@ -48,7 +48,7 @@ function setLineWidth(x1, y1, x2, y2, lw) {
function drawMixedClock(force) { function drawMixedClock(force) {
var date = new Date(); 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; lastDate = date;
var dateArray = date.toString().split(" "); var dateArray = date.toString().split(" ");
var isEn = locale.name.startsWith("en"); var isEn = locale.name.startsWith("en");

View File

@ -1,7 +1,7 @@
{ {
"id": "miclock2", "id": "miclock2",
"name": "Mixed Clock 2", "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.", "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", "icon": "clock-mixed.png",
"type": "clock", "type": "clock",

View File

@ -10,3 +10,6 @@
0.09: Move some functions to new time_utils module 0.09: Move some functions to new time_utils module
0.10: Default to sched.js if custom js not found 0.10: Default to sched.js if custom js not found
0.11: Fix default dow 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

View File

@ -106,8 +106,8 @@ exports.getSettings = function () {
defaultRepeat: false, defaultRepeat: false,
buzzCount: 10, buzzCount: 10,
buzzIntervalMillis: 3000, // 3 seconds buzzIntervalMillis: 3000, // 3 seconds
defaultAlarmPattern: "..", defaultAlarmPattern: "::",
defaultTimerPattern: ".." defaultTimerPattern: "::"
}, },
require("Storage").readJSON("sched.settings.json", true) || {} require("Storage").readJSON("sched.settings.json", true) || {}
); );

View File

@ -1,7 +1,7 @@
{ {
"id": "sched", "id": "sched",
"name": "Scheduler", "name": "Scheduler",
"version": "0.11", "version": "0.12",
"description": "Scheduling library for alarms and timers", "description": "Scheduling library for alarms and timers",
"icon": "app.png", "icon": "app.png",
"type": "scheduler", "type": "scheduler",

View File

@ -9,7 +9,7 @@ function showAlarm(alarm) {
const settings = require("sched").getSettings(); const settings = require("sched").getSettings();
let msg = ""; 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) { if (alarm.msg) {
msg += "\n"+alarm.msg; msg += "\n"+alarm.msg;
} else { } else {
@ -50,7 +50,8 @@ function showAlarm(alarm) {
Bangle.setLocked(false); 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--) { if (buzzCount--) {
setTimeout(buzz, settings.buzzIntervalMillis); setTimeout(buzz, settings.buzzIntervalMillis);
} else if (alarm.as) { // auto-snooze } else if (alarm.as) { // auto-snooze

View File

@ -29,7 +29,7 @@
min: 5, min: 5,
max: 30, max: 30,
step: 5, step: 5,
format: v => v + /*LANG*/" min", format: v => v + /*LANG*/"m",
onchange: v => { onchange: v => {
settings.defaultSnoozeMillis = v * 60000; settings.defaultSnoozeMillis = v * 60000;
require("sched").setSettings(settings); require("sched").setSettings(settings);

View File

@ -50,3 +50,5 @@
UI improvements to Locale and Date & Time menu UI improvements to Locale and Date & Time menu
0.45: Add calibrate battery option 0.45: Add calibrate battery option
0.46: Fix regression after making 'calibrate battery' only for Bangle.js 2 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

View File

@ -1,7 +1,7 @@
{ {
"id": "setting", "id": "setting",
"name": "Settings", "name": "Settings",
"version": "0.46", "version": "0.47",
"description": "A menu for setting up Bangle.js", "description": "A menu for setting up Bangle.js",
"icon": "settings.png", "icon": "settings.png",
"tags": "tool,system", "tags": "tool,system",

View File

@ -252,22 +252,28 @@ function showThemeMenu() {
} }
upd(th); upd(th);
} }
let rgb = { let rgb = {};
black: "#000", white: "#fff", rgb[/*LANG*/'black'] = "#000";
red: "#f00", green: "#0f0", blue: "#00f", rgb[/*LANG*/'white'] = "#fff";
cyan: "#0ff", magenta: "#f0f", yellow: "#ff0", rgb[/*LANG*/'red'] = "#f00";
}; rgb[/*LANG*/'green'] = "#0f0";
if (!BANGLEJS2) Object.assign(rgb, { 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 // 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 = []; let colors = [], names = [];
for(const c in rgb) { for(const c in rgb) {
names.push(c); names.push(c);
colors.push(cl(rgb[c])); colors.push(cl(rgb[c]));
} }
let menu = { let menu = {
'':{title:'Custom Theme'}, '':{title:/*LANG*/'Custom Theme'},
"< Back": () => showThemeMenu() "< Back": () => showThemeMenu()
}; };
const labels = { const labels = {
@ -569,7 +575,25 @@ function showUtilMenu() {
} else 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) { if (Bangle.factoryReset) {
menu[/*LANG*/'Factory Reset'] = ()=>{ menu[/*LANG*/'Factory Reset'] = ()=>{

View File

@ -37,7 +37,7 @@ When the GPS obtains a fix the number of satellites is displayed as 'Sats:nn'. W
## Power Saving ## 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. 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. 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. ) 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. )

View File

@ -20,11 +20,11 @@
"On": "Ein", "On": "Ein",
"Off": "Aus", "Off": "Aus",
"Ok": "OK", "Ok": "OK",
"New Timer": "Neue Zeitschaltuhr", "New Timer": "Neuer Kurzzeitwecker",
"(repeat)": "(Wiederholung)", "(repeat)": "(Wiederholung)",
"music": "Musik", "music": "Musik",
"Keep Msgs": "Msgs behalten", "Keep Msgs": "Nachrichten behalten",
"circle count": "Kreiszahl", "circle count": "Anzahl Kreise",
"Auto snooze": "Automatisches Schlummern", "Auto snooze": "Automatisches Schlummern",
"week": "Woche", "week": "Woche",
"Heartrate": "Herzfrequenz", "Heartrate": "Herzfrequenz",
@ -43,7 +43,7 @@
"colorize icon": "Symbol einfärben", "colorize icon": "Symbol einfärben",
"min. confidence": "Mindestvertrauen", "min. confidence": "Mindestvertrauen",
"maximum": "maximal", "maximum": "maximal",
"distance goal": "Fernziel", "distance goal": "Entfernungsziel",
"Circle": "Kreis", "Circle": "Kreis",
"Yes\ndefinitely": "Ja\ndefinitiv", "Yes\ndefinitely": "Ja\ndefinitiv",
"TAP right top/bottom": "TAP rechts oben/unten", "TAP right top/bottom": "TAP rechts oben/unten",
@ -52,28 +52,29 @@
"Mark Unread": "Ungelesen markieren", "Mark Unread": "Ungelesen markieren",
"Delete all messages": "Alle Nachrichten löschen", "Delete all messages": "Alle Nachrichten löschen",
"Unread timer": "Ungelesener Timer", "Unread timer": "Ungelesener Timer",
"Quiet Mode": "Leiser Modus", "Quiet Mode": "Stiller Modus",
"Utils": "Werkzeuge", "Utils": "Werkzeuge",
"Piezo": "Piezo", "Piezo": "Piezo",
"LCD": "LCD", "LCD": "LCD",
"Record Run": "Rekordlauf", "Record Run": "Lauf aufzeichnen",
"Apps": "Apps", "Apps": "Apps",
"Delete All Messages": "Alle Nachrichten löschen", "Delete All Messages": "Alle Nachrichten löschen",
"start&lap/reset, BTN1: EXIT": "Start&Runde/Zurücksetzen, BTN1: EXIT", "start&lap/reset, BTN1: EXIT": "Start&Runde/Zurücksetzen, BTN1: EXIT",
"No Messages": "Keine Nachrichten", "No Messages": "Keine Nachrichten",
"Bluetooth": "Bluetooth", "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", "View Message": "Nachricht anzeigen",
"Vector font size": "Vektor-Schriftgröße", "Vector font size": "Vektor-Schriftgröße",
"Light BW": "Licht BW", "Light BW": "Hell S/W",
"BLE": "BLE", "BLE": "BLE",
"Make Connectable": "Verbindbar machen", "Make Connectable": "Verbindbar machen",
"Vibration": "Vibration", "Vibration": "Vibration",
"Foreground": "Vorderseite", "Foreground": "Vordergrund",
"Customize": "Anpassen", "Customize": "Individualisieren",
"Custom Theme": "Individueller Stil",
"HID": "HID", "HID": "HID",
"Dark BW": "Dunkel BW", "Dark BW": "Dunkel S/W",
"Passkey BETA": "Hauptschlüssel BETA", "Passkey BETA": "Passwort BETA",
"Show clocks": "Uhren anzeigen", "Show clocks": "Uhren anzeigen",
"Font": "Schriftart", "Font": "Schriftart",
"Launcher Settings": "Launcher-Einstellungen", "Launcher Settings": "Launcher-Einstellungen",
@ -82,30 +83,30 @@
"Background 2": "Hintergrund 2", "Background 2": "Hintergrund 2",
"Foreground 2": "Vordergrund 2", "Foreground 2": "Vordergrund 2",
"Add Device": "Gerät hinzufügen", "Add Device": "Gerät hinzufügen",
"Highlight BG": "Hervorhebung BG", "Highlight BG": "Hervorhebung HG",
"Background": "Hintergrund", "Background": "Hintergrund",
"Highlight FG": "Highlight FG", "Highlight FG": "Hervorhebung VG",
"Wake on Touch": "Wecken bei Berührung", "Wake on Touch": "Wecken bei Berührung",
"Twist Timeout": "Twist Timeout", "Twist Timeout": "Twist Timeout",
"Twist Max Y": "Twist Max Y", "Twist Max Y": "Twist Max Y",
"LCD Timeout": "LCD-Zeitüberschreitung", "LCD Timeout": "LCD-Leuchtdauer",
"LCD Brightness": "LCD-Helligkeit", "LCD Brightness": "LCD-Helligkeit",
"Utilities": "Versorgungsunternehmen", "Utilities": "Werkzeuge",
"Log": "Protokoll", "Log": "Protokoll",
"Compact Storage": "Kompakte Lagerung", "Compact Storage": "Speicherwartung",
"Wake on BTN3": "Wake auf BTN3", "Wake on BTN3": "Aufwachen auf BTN3",
"Twist Threshold": "Schwellenwert verdrehen", "Twist Threshold": "Twist Schwellenwert",
"Remove": "entfernen", "Remove": "entfernen",
"Connect device\nto add to\nwhitelist": "Gerät verbinden\nzum Hinzufügen zur\nWhitelist", "Connect device\nto add to\nwhitelist": "Gerät verbinden\nzum Hinzufügen zur\nWhitelist",
"Debug Info": "Debug-Informationen", "Debug Info": "Debug-Informationen",
"Time Zone": "Zeitzone", "Time Zone": "Zeitzone",
"Clock Style": "Uhr Stil", "Clock Style": "Uhrenstil",
"Wake on BTN2": "Wake auf BTN2", "Wake on BTN2": "Aufwecken mit BTN2",
"Wake on FaceUp": "Wake on FaceUp", "Wake on FaceUp": "Aufwecken mit Display oben",
"Wake on BTN1": "Wake auf BTN1", "Wake on BTN1": "Aufwecken mit BTN1",
"Wake on Twist": "Wake on Twist", "Wake on Twist": "Aufwecken mit Twist",
"Connectable": "Anschließbar", "Connectable": "Erreichbar",
"Second": "Zweite", "Second": "Sekunde",
"Minute": "Minute", "Minute": "Minute",
"Turn Off": "Ausschalten", "Turn Off": "Ausschalten",
"No Clocks Found": "Keine Uhren gefunden", "No Clocks Found": "Keine Uhren gefunden",
@ -114,10 +115,10 @@
"Reset to Defaults": "Auf Standardwerte zurücksetzen", "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", "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", "Reset Settings": "Einstellungen zurücksetzen",
"Rewrite Settings": "Einstellungen umschreiben", "Rewrite Settings": "Einstellungen neu schreiben",
"Compacting...\nTakes approx\n1 minute": "Verdichten...\nDauert ca.\n1 Minute", "Compacting...\nTakes approx\n1 minute": "Speicherwartung...\nDauert ca.\n1 Minute",
"Stay Connectable": "Anschlussfähig bleiben", "Stay Connectable": "Erreichbar bleiben",
"Storage": "Lagerung", "Storage": "Speicher",
"This will remove everything": "Dadurch wird alles entfernt", "This will remove everything": "Dadurch wird alles entfernt",
"on": "auf", "on": "auf",
"TIMER": "TIMER", "TIMER": "TIMER",
@ -126,9 +127,10 @@
"Beep": "Piep", "Beep": "Piep",
"Reset": "Zurücksetzen", "Reset": "Zurücksetzen",
"No app has settings": "Keine App hat Einstellungen", "No app has settings": "Keine App hat Einstellungen",
"Day": "Tag",
"Month": "Monat", "Month": "Monat",
"Reset All": "Alle zurücksetzen", "Reset All": "Alle zurücksetzen",
"Flatten Battery": "Batterie abflachen", "Flatten Battery": "Batterie entladen",
"Right": "Rechts", "Right": "Rechts",
"Side": "Seite", "Side": "Seite",
"Left": "Links", "Left": "Links",
@ -141,12 +143,12 @@
"Vibrate": "Vibrieren", "Vibrate": "Vibrieren",
"Reset all widgets": "Alle Widgets zurücksetzen", "Reset all widgets": "Alle Widgets zurücksetzen",
"System": "System", "System": "System",
"Alerts": "Warnungen", "Alerts": "Alarme",
"Locale": "Schauplatz", "Locale": "Ort",
"Whitelist": "Whitelist", "Whitelist": "Whitelist",
"Select Clock": "Uhr auswählen", "Select Clock": "Uhr auswählen",
"Disable": "Deaktivieren Sie", "Disable": "Deaktivieren",
"Timer": "Zeitschaltuhr", "Timer": "Kurzzeitwecker",
"Error in settings": "Fehler in den Einstellungen", "Error in settings": "Fehler in den Einstellungen",
"Set Time": "Zeit einstellen", "Set Time": "Zeit einstellen",
"ALARM": "ALARM", "ALARM": "ALARM",
@ -164,7 +166,7 @@
"Music": "Musik", "Music": "Musik",
"color": "Farbe", "color": "Farbe",
"off": "aus", "off": "aus",
"Theme": "Thema", "Theme": "Stil",
"one": "eins", "one": "eins",
"two": "zwei", "two": "zwei",
"three": "drei", "three": "drei",
@ -176,7 +178,22 @@
"nine": "neun", "nine": "neun",
"ten": "zehn", "ten": "zehn",
"eleven": "elf", "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": { "alarm": {
"//": "App-specific overrides", "//": "App-specific overrides",
@ -189,7 +206,7 @@
"ten past *$1": "zehn nach *$1", "ten past *$1": "zehn nach *$1",
"quarter past *$1": "viertel nach *$1", "quarter past *$1": "viertel nach *$1",
"twenty past *$1": "zwanzig 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", "half past *$1": "halb *$2",
"twenty five to *$2": "fünf nach halb *$2", "twenty five to *$2": "fünf nach halb *$2",
"twenty to *$2": "zwanzig vor *$2", "twenty to *$2": "zwanzig vor *$2",

View File

@ -12,6 +12,7 @@
{"code":"tr_TR","name":"Turkish","url":"tr_TR.json"}, {"code":"tr_TR","name":"Turkish","url":"tr_TR.json"},
{"code":"ru_RU","name":"Russian","url":"ru_RU.json", "disabled":"Characters not in ISO Latin codepage"}, {"code":"ru_RU","name":"Russian","url":"ru_RU.json", "disabled":"Characters not in ISO Latin codepage"},
{"code":"pt_PT","name":"Portuguese","url":"pt_PT.json"}, {"code":"pt_PT","name":"Portuguese","url":"pt_PT.json"},
{"code":"pt_BR","name":"Portuguese Brasil","url":"pt_BR.json"},
{"code":"bg_BG","name":"Bulgarian","url":"bg_BG.json", "disabled":"Characters not in ISO Latin codepage"}, {"code":"bg_BG","name":"Bulgarian","url":"bg_BG.json", "disabled":"Characters not in ISO Latin codepage"},
{"code":"da_DA","name":"Danish","url":"da_DA.json"}, {"code":"da_DA","name":"Danish","url":"da_DA.json"},
{"code":"el_EL","name":"Greek","url":"el_EL.json", "disabled":"Characters not in ISO Latin codepage"}, {"code":"el_EL","name":"Greek","url":"el_EL.json", "disabled":"Characters not in ISO Latin codepage"},

167
lang/pt_BR.json Normal file
View File

@ -0,0 +1,167 @@
{
"//": "Portuguese Brasil language translations",
"GLOBAL": {
"//": "Translations that apply for all apps",
"New Timer": "Novo Temporizador",
"New Alarm": "Novo Alarme",
"Auto snooze": "Soneca automática",
"week": "semana",
"circle 3": "círculo 3",
"(repeat)": "(repetir)",
"Save": "Salvar",
"Keep Msgs": "Manter Msgs",
"music": "música",
"circle 4": "círculo 4",
"circle 2": "círculo 2",
"circle count": "contagem em círculo",
"circle 1": "círculo 1",
"battery warn": "aviso de bateria",
"show widgets": "exibir widgets",
"data": "dados",
"heartrate": "frequência cardíaca",
"distance goal": "distancia de chegada",
"Circle": "Círculo",
"colorize icon": "colorir ícone",
"min. confidence": "min. confiança",
"minimum": "mínimo",
"maximum": "máximo",
"Heartrate": "Frequência Cardíaca",
"weather circle": "círculo meteorológico",
"step length": "comprimento do passo",
"valid period": "período válido",
"TAP right top/bottom": "TAP superior/inferior direita",
"Vector font size": "Tamanho de letra vectorial",
"Yes\ndefinitely": "Sim\ndefinitivamente",
"BTNs 1:startlap 2:exit 3:reset": "BTNs 1:startlap 2:exit 3:reset",
"STEPS": "ETAPAS",
"Font": "Fonte",
"Show clocks": "Mostrar relógios",
"App Source\nNot found": "Fonte do aplicativo\nNão encontrado",
"Mark Unread": "Marcar como não lido",
"View Message": "Ver Mensagem",
"start&lap/reset, BTN1: EXIT": "start&lap/reset, BTN1: SAÍDA",
"Launcher Settings": "Configurações do Launcher",
"Delete All Messages": "Apagar todas as mensagens",
"Delete all messages": "Apagar todas as mensagens",
"Utils": "Utils",
"LCD": "LCD",
"Apps": "Apps",
"Record Run": "Gravar Corrida",
"No Messages": "Sem Mensagens",
"Unread timer": "Temporizador não visto",
"Are you sure": "Tem certeza",
"Make Connectable": "Habilitar Conexão",
"Piezo": "Piezo",
"Bluetooth": "Bluetooth",
"BLE": "BLE",
"Programmable": "Programável",
"Vibration": "Vibração",
"Quiet Mode": "Modo Silencioso",
"Foreground": "Primeiro plano",
"Passkey BETA": "Senha BETA",
"HID": "HID",
"Light BW": "BW Leve",
"Foreground 2": "Primeiro plano 2",
"Dark BW": "BW Escuro",
"Background": "Plano de Fundo",
"Highlight FG": "Destaque FG",
"Customize": "Personalizar",
"Background 2": "Plano de Fundo 2",
"Wake on BTN3": "Acordar no BTN3",
"Wake on BTN2": "Acordar no BTN2",
"Highlight BG": "Destaque BG",
"LCD Timeout": "Tempo limite do LCD",
"Wake on FaceUp": "Acordar no FaceUp",
"Wake on BTN1": "Acordar no BTN1",
"Wake on Twist": "Acordar ao Balançar",
"Wake on Touch": "Acordar ao Tocar",
"Connect device\nto add to\nwhitelist": "Ligar dispositivo\npara adicionar a\nWhitelist",
"Remove": "Remover",
"Add Device": "Adicionar dispositivo",
"LCD Brightness": "Luminosidade do LCD",
"Twist Max Y": "Max Y do Balanço",
"Utilities": "Utilidades",
"Twist Threshold": "Limiar do Balanço",
"Time Zone": "Fuso horário",
"Twist Timeout": "Timeout do Balanço",
"Clock Style": "Estilo do Relógio",
"Debug Info": "Debug Infos",
"Log": "Logs",
"Storage": "Armazenamento",
"Rewrite Settings": "Re-escrever Configurações",
"Compacting...\nTakes approx\n1 minute": "Compactando...\nLeva aproximadamente\n1 minuto",
"Flatten Battery": "Drenar Bateria",
"Reset Settings": "Resetar Configurações",
"Compact Storage": "Armazenamento compacto",
"Stay Connectable": "Manter Conectavel",
"Turn Off": "Desativar",
"Connectable": "Conectável",
"This will remove everything": "Esta ação irá apagar tudo",
"Date": "Data",
"Month": "Mês",
"Second": "Segundo",
"Minute": "Minuto",
"Flattening battery - this can take hours.\nLong-press button to cancel": "Drenando Bateria - isto pode demorar horas.\nSegure o Botão para Cancelar",
"Reset to Defaults": "Resetar Dispositivo",
"Hour": "Hora",
"No Clocks Found": "Não encontramos nenhum Relogio",
"Right": "Confirmar",
"No app has settings": "Nenhum aplicativo possui configurações",
"App Settings": "Configurações do aplicativo",
"OFF": "DESLIGADO",
"Side": "Lado",
"Left": "Esquerda",
"Sort Order": "Ordem de classificação",
"Widgets": "Widgets",
"Invalid settings": "Configurações inválidas",
"Sleep Phase Alarm": "Alarme da Fase do Sono",
"Alarm": "Alarme",
"Minutes": "Minutos",
"TIMER": "TIMER",
"Hours": "Horário",
"on": "em",
"Reset All": "Resetar tudo",
"Repeat": "Repetir",
"Delete": "Deletar",
"Enabled": "Habilitado",
"Reset all widgets": "Redefinir todos os widgets",
"Reset": "Resetar",
"goal": "meta",
"Message": "Mensagem",
"Beep": "Bip",
"Vibrate": "Vibrar",
"System": "Sistema",
"Alerts": "Alertas",
"Locale": "Localização",
"Set Time": "Tempo Definido",
"Whitelist": "Whitelist",
"Select Clock": "Selecionar Relógio",
"BACK": "VOLTAR",
"Timer": "Temporizador",
"Error in settings": "Erro nas Configurações",
"Disable": "Desativar",
"Factory Reset": "Reset de Fábrica",
"Connected": "Conectado",
"ALARM": "ALARME",
"Sleep": "Dormir",
"Messages": "Mensagens",
"Hide": "Esconder",
"Show": "Mostrar",
"On": "Ativo",
"Ok": "Ok",
"No": "Não",
"Settings": "Configurações",
"steps": "passos",
"back": "voltar",
"Steps": "Passos",
"Year": "Ano",
"Yes": "Sim",
"Loading": "Carregando",
"Music": "Música",
"color": "cor",
"off": "desativado",
"Off": "Desativado",
"Theme": "Tema",
"Back": "Voltar"
}
}

View File

@ -10,7 +10,8 @@ function ClockFace(options) {
"precision", "precision",
"init", "draw", "update", "init", "draw", "update",
"pause", "resume", "pause", "resume",
"up", "down", "upDown" "up", "down", "upDown",
"settingsFile",
].includes(k)) throw `Invalid ClockFace option: ${k}`; ].includes(k)) throw `Invalid ClockFace option: ${k}`;
}); });
if (!options.draw && !options.update) throw "ClockFace needs at least one of draw() or update() functions"; if (!options.draw && !options.update) throw "ClockFace needs at least one of draw() or update() functions";
@ -33,7 +34,18 @@ function ClockFace(options) {
}; };
if (options.upDown) this._upDown = options.upDown; if (options.upDown) this._upDown = options.upDown;
this.is12Hour = !!(require("Storage").readJSON("setting.json", 1) || {})["12hour"]; if (options.settingsFile) {
const settings = (require("Storage").readJSON(options.settingsFile, true) || {});
Object.keys(settings).forEach(k => {
this[k] = settings[k];
});
}
// these default to true
["showDate", "loadWidgets"].forEach(k => {
if (this[k]===undefined) this[k] = true;
});
// use global 24/12-hour setting if not set by clock-settings
if (!('is12Hour' in this)) this.is12Hour = !!(require("Storage").readJSON("setting.json", true) || {})["12hour"];
} }
ClockFace.prototype.tick = function() { ClockFace.prototype.tick = function() {
@ -46,7 +58,7 @@ ClockFace.prototype.tick = function() {
}; };
if (!this._last) { if (!this._last) {
g.clear(true); g.clear(true);
Bangle.drawWidgets(); if (global.WIDGETS) Bangle.drawWidgets();
g.reset(); g.reset();
this.draw.apply(this, [time, {d: true, h: true, m: true, s: true}]); this.draw.apply(this, [time, {d: true, h: true, m: true, s: true}]);
} else { } else {
@ -70,7 +82,7 @@ ClockFace.prototype.start = function() {
.CLOCK is set by Bangle.setUI('clock') but we want to load widgets so we can check appRect and *then* .CLOCK is set by Bangle.setUI('clock') but we want to load widgets so we can check appRect and *then*
call setUI. see #1864 */ call setUI. see #1864 */
Bangle.CLOCK = 1; Bangle.CLOCK = 1;
Bangle.loadWidgets(); if (this.loadWidgets) Bangle.loadWidgets();
if (this.init) this.init.apply(this); if (this.init) this.init.apply(this);
if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d])); if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d]));
else Bangle.setUI("clock"); else Bangle.setUI("clock");

View File

@ -85,6 +85,7 @@ var clock = new ClockFace({
if (dir === -1) // Up if (dir === -1) // Up
else // (dir === 1): Down else // (dir === 1): Down
}, },
settingsFile: 'appid.settings.json', // optional, values from file will be applied to `this`
}); });
clock.start(); clock.start();
@ -110,11 +111,51 @@ clock.start();
``` ```
SettingsFile
------------
If you use the `settingsFile` option, values from that file are loaded and set
directly on the clock.
For example:
```json
// example.settings.json:
{
"showDate": false,
"foo": 123
}
```
```js
var ClockFace = require("ClockFace");
var clock = new ClockFace({
draw: function(){/*...*/},
settingsFile: "example.settings.json",
});
// now
clock.showDate === false;
clock.foo === 123;
clock.loadWidgets === true; // default when not in settings file
clock.is12Hour === ??; // not in settings file: uses global setting
clock.start();
```
Properties Properties
---------- ----------
The following properties are automatically set on the clock: The following properties are automatically set on the clock:
* `is12Hour`: `true` if the "Time Format" setting is set to "12h", `false` for "24h". * `is12Hour`: `true` if the "Time Format" setting is set to "12h", `false` for "24h".
* `paused`: `true` while the clock is paused. (You don't need to check this inside your `draw()` code) * `paused`: `true` while the clock is paused. (You don't need to check this inside your `draw()` code)
* `showDate`: `true` (if not overridden through the settings file.)
* `loadWidgets`: `true` (if not overridden through the settings file.)
If set to `false` before calling `start()`, the clock won't call `Bangle.loadWidgets();` for you.
Best is to add a setting for this, but if you never want to load widgets, you could do this:
```js
var ClockFace = require("ClockFace");
var clock = new ClockFace({draw: function(){/*...*/}});
clock.loadWidgets = false; // prevent loading of widgets
clock.start();
```
Inside the `draw()`/`update()` function you can access these using `this`: Inside the `draw()`/`update()` function you can access these using `this`:

10
modules/ClockFace_menu.js Normal file
View File

@ -0,0 +1,10 @@
// boolean options, which default to true
exports.showDate =
exports.loadWidgets =
function(value, callback) {
if (value === undefined) value = true;
return {
value: !!value,
onchange: v=>callback(v),
};
};

View File

@ -1,14 +1,33 @@
/* Call this with a pattern like '.-.', '.. .' or '..' to buzz that pattern /**
out on the internal vibration motor. use buzz_menu to display a menu * Buzz the passed `pattern` out on the internal vibration motor.
where the patterns can be chosen. */ *
* A pattern is a sequence of `.`, `,`, `-`, `:`, `;` and `=` where
* - `.` is one short and weak vibration
* - `,` is one medium and weak vibration
* - `-` is one long and weak vibration
* - `:` is one short and strong vibration
* - `;` is one medium and strong vibration
* - `=` is one long and strong vibration
*
* You can use the `buzz_menu` module to display a menu where some common patterns can be chosen.
*
* @param {string} pattern A string like `.-.`, `..=`, `:.:`, `..`, etc.
* @returns a Promise
*/
exports.pattern = pattern => new Promise(resolve => { exports.pattern = pattern => new Promise(resolve => {
function b() { function doBuzz() {
if (pattern=="") resolve(); if (pattern == "") resolve();
var c = pattern[0]; var c = pattern[0];
pattern = pattern.substr(1); pattern = pattern.substr(1);
if (c==".") Bangle.buzz().then(()=>setTimeout(b,100)); const BUZZ_WEAK = 0.25, BUZZ_STRONG = 1;
else if (c=="-") Bangle.buzz(500).then(()=>setTimeout(b,100)); const SHORT_MS = 100, MEDIUM_MS = 200, LONG_MS = 500;
else setTimeout(b,100); if (c == ".") Bangle.buzz(SHORT_MS, BUZZ_WEAK).then(() => setTimeout(doBuzz, 100));
else if (c == ",") Bangle.buzz(MEDIUM_MS, BUZZ_WEAK).then(() => setTimeout(doBuzz, 100));
else if (c == "-") Bangle.buzz(LONG_MS, BUZZ_WEAK).then(() => setTimeout(doBuzz, 100));
else if (c == ":") Bangle.buzz(SHORT_MS, BUZZ_STRONG).then(() => setTimeout(doBuzz, 100));
else if (c == ";") Bangle.buzz(MEDIUM_MS, BUZZ_STRONG).then(() => setTimeout(doBuzz, 100));
else if (c == "=") Bangle.buzz(LONG_MS, BUZZ_STRONG).then(() => setTimeout(doBuzz, 100));
else setTimeout(doBuzz, 100);
} }
b(); doBuzz();
}); });

View File

@ -1,14 +1,19 @@
/* Display a menu to select from various vibration patterns for use with buzz.js */ /**
* Display a menu to select from various common vibration patterns for use with buzz.js.
exports.pattern = function(value, callback) { *
var vibPatterns = ["", ".", "..", "-", "--", "-.-", "---"]; * @param {string} value The pre-selected pattern
* @param {*} callback A function called with the user selected pattern
*/
exports.pattern = function (value, callback) {
var patterns = ["", ".", ":", "..", "::", ",", ";", ",,", ";;", "-", "=", "--", "==", "...", ":::", "---", ";;;", "==="];
return { return {
value: Math.max(0,vibPatterns.indexOf(value)), value: Math.max(0, patterns.indexOf(value)),
min: 0, max: vibPatterns.length-1, min: 0,
format: v => vibPatterns[v]||/*LANG*/"Off", max: patterns.length - 1,
format: v => patterns[v] || /*LANG*/"Off",
onchange: v => { onchange: v => {
require("buzz").pattern(vibPatterns[v]); require("buzz").pattern(patterns[v]);
callback(vibPatterns[v]); callback(patterns[v]);
} }
}; };
} }

View File

@ -55,7 +55,7 @@ exports.decodeTime = (millis) => {
*/ */
exports.formatTime = (value) => { exports.formatTime = (value) => {
var time = safeTime(typeof value === "object" ? value : exports.decodeTime(value)); var time = safeTime(typeof value === "object" ? value : exports.decodeTime(value));
if (time.d != 0) throw "(d)ays not supported here"; if (time.d != 0) throw "days not supported here";
if (time.h < 0 || time.h > 23) throw "Invalid value: must be 0 <= h <= 23"; if (time.h < 0 || time.h > 23) throw "Invalid value: must be 0 <= h <= 23";
if (time.m < 0 || time.m > 59) throw "Invalid value: must be 0 <= m <= 59"; if (time.m < 0 || time.m > 59) throw "Invalid value: must be 0 <= m <= 59";
return time.h + ":" + ("0" + time.m).substr(-2); return time.h + ":" + ("0" + time.m).substr(-2);
@ -63,16 +63,19 @@ exports.formatTime = (value) => {
/** /**
* @param {object|int} value {d, h, m, s} object or milliseconds * @param {object|int} value {d, h, m, s} object or milliseconds
* @returns an human-readable duration string like "3d 1h 10m 45s" * @param {boolean} compact `true` to remove all whitespaces between the values
* @returns an human-readable duration string like "3d 1h 10m 45s" (or "3d1h10m45s" if `compact` is `true`)
*/ */
exports.formatDuration = (value) => { exports.formatDuration = (value, compact) => {
compact = compact || false;
var duration = ""; var duration = "";
var time = safeTime(typeof value === "object" ? value : exports.decodeTime(value)); var time = safeTime(typeof value === "object" ? value : exports.decodeTime(value));
if (time.d > 0) duration += time.d + "d "; if (time.d > 0) duration += time.d + "d ";
if (time.h > 0) duration += time.h + "h "; if (time.h > 0) duration += time.h + "h ";
if (time.m > 0) duration += time.m + "m "; if (time.m > 0) duration += time.m + "m ";
if (time.s > 0) duration += time.s + "s" if (time.s > 0) duration += time.s + "s"
return duration.trim(); duration = duration.trim()
return compact ? duration.replace(" ", "") : duration;
} }
exports.getCurrentTimeMillis = () => { exports.getCurrentTimeMillis = () => {