Merge branch 'master' into master

pull/184/head
Gordon Williams 2020-04-02 14:30:24 +01:00 committed by GitHub
commit 0c2708d46f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 258 additions and 52 deletions

6
CHANGELOG.md Normal file
View File

@ -0,0 +1,6 @@
App Loader ChangeLog
====================
Changed for individual apps are listed in `apps/appname/ChangeLog`
* `Remove All Apps` now doesn't perform a reset before erase - fixes inability to update firmware if settings are wrong

View File

@ -27,7 +27,7 @@
{ "id": "daysl",
"name": "Days left",
"icon": "app.png",
"version":"0.01",
"version":"0.02",
"description": "Shows you the days left until a certain date. Date can be set with a settings app and is written to a file.",
"tags": "",
"allow_emulator":false,
@ -65,7 +65,7 @@
{ "id": "locale",
"name": "Languages",
"icon": "locale.png",
"version":"0.04",
"version":"0.05",
"description": "Translations for different countries",
"tags": "tool,system,locale,translate",
"type": "locale",
@ -334,6 +334,17 @@
{"name":"widbat.wid.js","url":"widget.js"}
]
},
{ "id": "widbatpc",
"name": "Battery Level Widget (with percentage)",
"icon": "widget.png",
"version":"0.06",
"description": "Show the current battery level and charging status in the top right of the clock, with charge percentage",
"tags": "widget,battery",
"type":"widget",
"storage": [
{"name":"widbatpc.wid.js","url":"widget.js"}
]
},
{ "id": "widbt",
"name": "Bluetooth Widget",
"icon": "widget.png",
@ -881,7 +892,7 @@
{ "id": "marioclock",
"name": "Mario Clock",
"icon": "marioclock.png",
"version":"0.03",
"version":"0.04",
"description": "Animated Mario clock, jumps to change the time!",
"tags": "clock,mario,retro",
"type": "clock",
@ -972,5 +983,16 @@
{"name":"waxing-gibbous.img","url":"waxing-gibbous-icon.js","evaluate":true},
{"name":"waxing-crescent.img","url":"waxing-crescent-icon.js","evaluate":true}
]
},
{ "id": "widhwt",
"name": "Hand Wash Timer",
"icon": "widget.png",
"version":"0.01",
"description": "Swipe your wrist over the watch face to start your personal Bangle.js hand wash timer for 35 sec. Start washing after the short buzz and stop after the long buzz.",
"tags": "widget,tool",
"type":"widget",
"storage": [
{"name":"widhwt.wid.js","url":"widget.js"}
]
}
]

1
apps/cliock/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.07: Submitted to App Loader

View File

@ -1 +1,2 @@
0.01: New Widget!
0.02: Improved calculation, new image for app

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("mEwwMB//D/4CgwHDFYPDgICBAoQCB/4CKADmAAgcBIARCCAqQAXF/4v24CtDgYFR"))
require("heatshrink").decompress(atob("mEwgmIAH4A/AH4A/AEEAAAgGOC/4XLAgoGIDgYXTwEIBY4JEAw8YCIOAEY4+EAwwTCL44XNO5IX/C6i6LC8YABa5AXOF67vIwA5DAw5GDMhg7HjAXWIwQLFZIoGNC/4XKAH4A/AH4A/ADoA="))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 468 B

View File

@ -4,12 +4,12 @@ let settings;
function updateSettings() {
storage.write('daysleft.json', settings);
}
function resetSettings() {
settings = {
day : 17,
month : 6,
year: 1981
year: 2020
};
updateSettings();
}
@ -17,17 +17,23 @@ function updateSettings() {
settings = storage.readJSON('daysleft.json',1);
if (!settings) resetSettings();
var dd = settings.day+1,
mm = settings.month-1,
var dd = settings.day,
mm = settings.month-1, //month is zero-based
yy = settings.year;
const oneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
const targetDate = new Date(yy, mm, dd);
const today = new Date();
const diffDays = Math.round(Math.abs((targetDate - today) / oneDay));
//create date object with today, but 00:00:00
const currentYear = today.getFullYear();
const currentMonth = today.getMonth();
const currentDay = today.getDate();
const todayMorning = new Date (currentYear, currentMonth, currentDay, 0, 0, 0);
const diffDays = (targetDate - todayMorning) / oneDay;
WIDGETS["daysl"]={area:"tl",width:40,draw:function(){
g.setFont("6x8", 1);
g.drawString(diffDays,this.x+12,this.y+12);
}};

View File

@ -2,3 +2,5 @@
0.02: Fix locale.currencySym
0.03: Fix global 'locale' variable
0.04: Add function meridian
0.05: Inline locale details - faster, less memory overhead
Add correct scaling for speed/distance/temperature

View File

@ -15,6 +15,45 @@
<script src="locales.js"></script>
<script>
/*
eg. the built-in en_GB is:
exports = { name : "en_GB", currencySym:"£",
translate : str=>str, // as-is
date : (d,short) => short?("0"+d.getDate()).substr(-2)+"/"+("0"+(d.getMonth()+1)).substr(-2)+"/"+d.getFullYear():d.toString().substr(4,11), // Date to "Feb 28 2020" or "28/02/2020"(short)
time : (d,short) => { // Date to "4:15.28 pm" or "15:42"(short)
if (short)
return d.toString().substr(16,5);
else {
var h = d.getHours(), m = d.getMinutes(), r = "am";
if (h==0) { h=12; }
else if (h>=12) {
if (h>12) h-=12;
r = "pm";
}
return (" "+h).substr(-2)+":"+("0"+m).substr(-2)+"."+("0"+d.getSeconds()).substr(-2)+" "+r;
}
},
dow : (d,short) => short?d.toString().substr(0,3):"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(",")[d.getDay()], // Date to "Monday" or "Mon"(short)
month : (d,short) => short?d.toString().substr(4,3):"January,February,March,April,May,June,July,August,September,October,November,December".split(",")[d.getMonth()], // Date to "February" or "Feb"(short)
number : n => n.toString(), // more fancy?
currency : n => "£"+n.toFixed(2), // number to "£1.00"
distance : m => (m<1000)?Math.round(m)+"m":Math.round(m/160.934)/10+"mi", // meters to "123m" or "1.2mi" depending on size
speed : s => Math.round(s*0.621371)+"mph",// kph to "123mph"
temp : t => Math.round(t)+"'C" // degrees C to degrees C
meridian: d => (d.getHours() <= 12) ? "am":"pm",
};
*/
// do some sanity checks
Object.keys(locales).forEach(function(localeName) {
var locale = locales[localeName];
if (distanceUnits[locale.distance[0]]===undefined) console.error(localeName+": Unknown distance unit "+locale.distance[0]);
if (distanceUnits[locale.distance[1]]===undefined) console.error(localeName+": Unknown distance unit "+locale.distance[1]);
if (speedUnits[locale.speed]===undefined) console.error(localeName+": Unknown speed unit "+locale.speed);
});
var languageSelector = document.getElementById("languages");
languageSelector.innerHTML = Object.keys(locales).map(l=>`<option value="${l}">${l}</option>`).join("\n");
@ -30,22 +69,25 @@
return;
}
function js(x) { return JSON.stringify(x); }
var replaceList = {
"%Y": "${d.getFullYear()}",
"%y": "${(d.getFullYear().toString()).substr(-2)}",
"%m": "${('0'+(d.getMonth()+1).toString()).substr(-2)}",
"%-m": "${d.getMonth()+1}",
"%d": "${('0'+d.getDate()).substr(-2)}",
"%-d": "${d.getDate()}",
"%HH": "${('0'+d.getHours()).substr(-2)}",
"%MM": "${('0'+d.getMinutes()).substr(-2)}",
"%SS": "${('0'+d.getSeconds()).substr(-2)}",
"%A": "${locale.day.split(',')[d.getDay()]}",
"%a": "${locale.abday.split(',')[d.getDay()]}",
"%B": "${locale.month.split(',')[d.getMonth()]}",
"%b": "${locale.abmonth.split(',')[d.getMonth()]}",
"%p": "${(d.getHours()<12)?locale.ampm[0].toUpperCase():locale.ampm[1].toUpperCase()}",
"%P": "${(d.getHours()<12)?locale.ampm[0].toLowerCase():locale.ampm[1].toLowerCase()}"
"%Y": "d.getFullYear()",
"%y": "(d.getFullYear().toString()).substr(-2)",
"%m": "('0'+(d.getMonth()+1).toString()).substr(-2)",
"%-m": "d.getMonth()+1",
"%d": "('0'+d.getDate()).substr(-2)",
"%-d": "d.getDate()",
"%HH": "('0'+d.getHours()).substr(-2)",
"%MM": "('0'+d.getMinutes()).substr(-2)",
"%SS": "('0'+d.getSeconds()).substr(-2)",
"%A": "l.day.split(',')[d.getDay()]",
"%a": "l.abday.split(',')[d.getDay()]",
"%B": "l.month.split(',')[d.getMonth()]",
"%b": "l.abmonth.split(',')[d.getMonth()]",
"%p": `(d.getHours()<12)?${js(locale.ampm[0].toUpperCase())}:${js(locale.ampm[1].toUpperCase())}`,
"%P": `(d.getHours()<12)?${js(locale.ampm[0].toLowerCase())}:${js(locale.ampm[1].toLowerCase())}`
};
var timeN = locales[lang].timePattern[0];
@ -53,33 +95,51 @@
var dateN = locales[lang].datePattern[0];
var dateS = locales[lang].datePattern[1];
Object.keys(replaceList).forEach(e => {
timeN = timeN.replace(e,replaceList[e]);
timeS = timeS.replace(e,replaceList[e]);
dateN = dateN.replace(e,replaceList[e]);
dateS = dateS.replace(e,replaceList[e]);
timeN = timeN.replace(e,"${"+replaceList[e]+"}");
timeS = timeS.replace(e,"${"+replaceList[e]+"}");
dateN = dateN.replace(e,"${"+replaceList[e]+"}");
dateS = dateS.replace(e,"${"+replaceList[e]+"}");
});
var currency = locale.currency_first ?
`${js(locale.currency_symbol)} + n.toFixed(2)`:
`n.toFixed(2) + ${js(locale.currency_symbol)}`;
var temperature;
if (locale.temperature=='°C') temperature="t";
else if (locale.temperature=='°F') temperature="(t*9/5)+32";
else throw new Error("Unknown temperature unit "+locale.temperature);
var app = `var locale = ${JSON.stringify(locales[lang])};
var localeModule = `var l = ${JSON.stringify({
abday : locale.abday,
day : locale.day,
abmonth : locale.abmonth,
month : locale.month,
})};
exports = {
lang: locale.lang,
currencySym: locale.currency_symbol,
dow: (d,short) => {day = d.getDay();return (short) ? locale.abday.split(",")[day] : locale.day.split(",")[day];},
month: (d,short) => { month = d.getMonth(); return (short) ? locale.abmonth.split(",")[month] : locale.month.split(",")[month];},
number: n => n.toString().replace(locale.thousands_sep, locale.decimal_point),
currency: n => n.toFixed(2).replace(locale.thousands_sep, locale.decimal_point) + locale.currency_symbol,
distance: n => (n < 1000) ? Math.round(n) + locale.distance[0] : Math.round(n/1000) + locale.distance[1],
speed: s => Math.round(s) +locale.speed,
temp: t => Math.round(t) + locale.temperature,
translate: s => {s=""+s;return locale.trans[s]||locale.trans[s.toLowerCase()]||s;},
name: ${js(locale.lang)},
currencySym: ${js(locale.currency_symbol)},
dow: (d,short) => {day = d.getDay();return (short) ? l.abday.split(",")[day] : l.day.split(",")[day];},
month: (d,short) => { month = d.getMonth(); return (short) ? l.abmonth.split(",")[month] : l.month.split(",")[month];},
number: n => n.toString(),
currency: n => ${currency},
distance: n => (n < ${distanceUnits[locale.distance[1]]}) ? Math.round(n/${distanceUnits[locale.distance[0]]}) + ${js(locale.distance[0])} : Math.round(n/${distanceUnits[locale.distance[1]]}) + ${js(locale.distance[1])},
speed: s => Math.round(s/${speedUnits[locale.speed]}) + ${js(locale.speed)},
temp: t => Math.round(${temperature}) + ${js(locale.temperature)},
translate: s => {var t=${js(locale.trans)};s=""+s;return t[s]||t[s.toLowerCase()]||s;},
date: (d,short) => (short) ? \`${dateS}\`: \`${dateN}\`,
time: (d,short) => (short) ? \`${timeS}\`: \`${timeN}\`,
meridian: d => (d.getHours() <= 12) ? locale.ampm[0]:locale.ampm[1],
meridian: d => (d.getHours() <= 12) ? ${js(locale.ampm[0])}:${js(locale.ampm[1])},
};`;
console.log("Locale Module is:",localeModule);
/*
FIXME:
* Number/Currency need to add thousands separators: .replace(${js(locale.thousands_sep)}, ${js(locale.decimal_point)}) won't cut it as toString doesn't add separators itself
* distance (and speed) should probably use 1 decimal point for numbers less than 10
*/
sendCustomizedApp({
storage:[
{name:"locale", content:app}
{name:"locale", content:localeModule}
]
});
});

View File

@ -1,4 +1,20 @@
const distanceUnits = { // how many meters per X?
"m" : 1,
"yd" : 0.9144,
"mi" : 1609.34,
"km" : 1000,
"kmi" : 1000
};
const speedUnits = { // how many kph per X?
"kmh" : 1,
"kph" : 1,
"mph" : 1.60934
};
/*
timePattern / datePattern:
%Y year four digits
%y last two digits of year (00..99)
%m month (01..12)
@ -21,10 +37,10 @@ var locales = {
lang: "en_GB",
decimal_point: ".",
thousands_sep: ",",
currency_symbol: "£",
currency_symbol: "£", currency_first:true,
int_curr_symbol: "GBP",
speed: 'mph',
distance: { "0": "mi", "1": "kmi" },
distance: { "0": "yd", "1": "mi" },
temperature: '°C',
ampm: {0:"am",1:"pm"},
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
@ -55,10 +71,10 @@ var locales = {
lang: "en_US",
decimal_point: ".",
thousands_sep: ",",
currency_symbol: "$",
currency_symbol: "$", currency_first:true,
int_curr_symbol: "USD",
speed: "mph",
distance: { 0: "mi", 1: "kmi" },
distance: { 0: "yd", 1: "mi" },
temperature: "°F",
ampm: {0:"am",1:"pm"},
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
@ -353,5 +369,5 @@ var locales = {
month: "Január,Február,Március,Április,Május,Június,Július,Augusztus,Szeptember,Október,November,December",
abday: "Vas,Hét,Ke,Szer,Csüt,Pén,Szom",
day: "Vasárnap,Hétfő,Kedd,Szerda,Csütörtök,Péntek,Szombat",
trans: { yes: "igen", Yes: "Igen", no: "nem", No: "Nem", ok: "ok", on: "be", off: "ki" }},
trans: { yes: "igen", Yes: "Igen", no: "nem", No: "Nem", ok: "ok", on: "be", off: "ki" }},
};

View File

@ -1,3 +1,4 @@
0.01: Create mario app
0.02: Fix day of the week and add padding
0.03: use short date format from locale, take timeout from settings
0.04: modify date to display to be more at the original idea but still localized

View File

@ -285,7 +285,10 @@ function drawTime() {
function drawDate() {
g.setFont("6x8");
g.setColor(LIGHTEST);
const dateStr = locale.date(new Date(), true);
let d = new Date();
let dateStr = locale.date(d, true);
dateStr = dateStr.replace(d.getFullYear(), "").trim().replace(/\/$/i,"");
dateStr = locale.dow(d, true) + " " + dateStr;
g.drawString(dateStr, (W - g.stringWidth(dateStr))/2, 0, true);
}

5
apps/widbatpc/ChangeLog Normal file
View File

@ -0,0 +1,5 @@
0.02: Now refresh battery monitor every minute if LCD on
0.03: Tweaks for variable size widget system
0.04: Ensure redrawing works with variable size widget system
0.05: Change color depending on battery level, cloned from widbat
0.06: Show battery percentage as text

59
apps/widbatpc/widget.js Normal file
View File

@ -0,0 +1,59 @@
(function(){
const levelColor = (l) => {
if (Bangle.isCharging()) return 0x07E0; // "Green"
if (l >= 50) return 0x05E0; // slightly darker green
if (l >= 15) return 0xFD20; // "Orange"
return 0xF800; // "Red"
}
function setWidth() {
WIDGETS["bat"].width = 40 + (Bangle.isCharging()?16:0);
}
function draw() {
var s = 39;
var x = this.x, y = this.y;
const l = E.getBattery(), c = levelColor(l);
if (Bangle.isCharging()) {
g.setColor(c).drawImage(atob(
"DhgBHOBzgc4HOP////////////////////3/4HgB4AeAHgB4AeAHgB4AeAHg"),x,y);
x+=16;
}
g.setColor(-1);
g.fillRect(x,y+2,x+s-4,y+21);
g.clearRect(x+2,y+4,x+s-6,y+19);
g.fillRect(x+s-3,y+10,x+s,y+14);
g.setColor(c).fillRect(x+4,y+6,x+4+l*(s-12)/100,y+17);
g.setColor(-1);
g.setFontAlign(-1,-1);
if (l >= 100) {
g.setFont('4x6', 2);
g.drawString(l, x + 6, y + 7);
} else {
if (l < 10) x+=6;
g.setFont('6x8', 2);
g.drawString(l, x + 6, y + 4);
}
}
Bangle.on('charging',function(charging) {
if(charging) Bangle.buzz();
setWidth();
Bangle.drawWidgets(); // relayout widgets
g.flip();
});
var batteryInterval;
Bangle.on('lcdPower', function(on) {
if (on) {
WIDGETS["bat"].draw();
// refresh once a minute if LCD on
if (!batteryInterval)
batteryInterval = setInterval(draw, 60000);
} else {
if (batteryInterval) {
clearInterval(batteryInterval);
batteryInterval = undefined;
}
}
});
WIDGETS["bat"]={area:"tr",width:40,draw:draw};
setWidth();
})()

BIN
apps/widbatpc/widget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

1
apps/widhwt/ChangeLog Normal file
View File

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

23
apps/widhwt/widget.js Normal file
View File

@ -0,0 +1,23 @@
/* jshint esversion: 6 */
(() => {
var icon = require("heatshrink").decompress(atob("jEYwIKHgwCBhwCBh4CEggPCkACBmAXDBwVZ+EB+F4gEsjl8EgMP+EChk/gEMh+ehkA+YIBxwxBnF/4HggH/wEAj0AA=="));
var color = 0x4A69;
function draw() {
g.reset().setColor(color).drawImage(icon, this.x + 1, 0);
}
WIDGETS["widhwt"] = { area: "tr", width: 26, draw: draw };
Bangle.on('swipe', function() {
color = 0x41f;
Bangle.buzz();
Bangle.drawWidgets();
setTimeout(() => {
color = 0x4A69;
Bangle.buzz(1E3, 1);
Bangle.drawWidgets();
}, 35E3);
});
})();

BIN
apps/widhwt/widget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -73,13 +73,13 @@ removeApp : app => { // expects an app structure
}));
},
removeAllApps : () => {
return Comms.reset("wipe").then(() => new Promise((resolve,reject) => {
return new Promise((resolve,reject) => {
// Use write with newline here so we wait for it to finish
Puck.write('\x10E.showMessage("Erasing...");require("Storage").eraseAll();Bluetooth.println("OK")\n', (result,err) => {
Puck.write('\x10E.showMessage("Erasing...");require("Storage").eraseAll();Bluetooth.println("OK");reset()\n', (result,err) => {
if (!result || result.trim()!="OK") return reject(err || "");
resolve();
}, true /* wait for newline */);
}));
});
},
setTime : () => {
return new Promise((resolve,reject) => {