Merge branch 'espruino:master' into master

pull/925/head
Ronin0000 2021-09-21 07:24:54 -07:00 committed by GitHub
commit 9b344eb1ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 199 additions and 206 deletions

View File

@ -94,7 +94,7 @@
"name": "Notifications (default)",
"shortName":"Notifications",
"icon": "notify.png",
"version":"0.09",
"version":"0.10",
"description": "A handler for displaying notifications that displays them in a bar at the top of the screen",
"tags": "widget",
"type": "notify",
@ -107,9 +107,9 @@
"name": "Fullscreen Notifications",
"shortName":"Notifications",
"icon": "notify.png",
"version":"0.10",
"version":"0.11",
"description": "A handler for displaying notifications that displays them fullscreen. This may not fully restore the screen after on some apps. See `Notifications (default)` for more information about the notifications library.",
"tags": "widget",
"tags": "widget,b2",
"type": "notify",
"storage": [
{"name":"notify","url":"notify.js"}
@ -155,7 +155,7 @@
"icon": "app.png",
"version":"0.24",
"description": "The default notification handler for Gadgetbridge notifications from Android",
"tags": "tool,system,android,widget",
"tags": "tool,system,android,widget,b2",
"readme": "README.md",
"type":"widget",
"dependencies": { "notify":"type" },
@ -202,7 +202,7 @@
"name": "Default Alarm",
"shortName":"Alarms",
"icon": "app.png",
"version":"0.11",
"version":"0.12",
"description": "Set and respond to alarms",
"tags": "tool,alarm,widget,b2",
"storage": [
@ -571,7 +571,7 @@
{ "id": "weather",
"name": "Weather",
"icon": "icon.png",
"version":"0.07",
"version":"0.08",
"description": "Show Gadgetbridge weather report",
"readme": "readme.md",
"tags": "widget,outdoors",
@ -1184,7 +1184,7 @@
{ "id": "widpedom",
"name": "Pedometer widget",
"icon": "widget.png",
"version":"0.17",
"version":"0.19",
"description": "Daily pedometer widget",
"tags": "widget,b2",
"type":"widget",

View File

@ -97,7 +97,7 @@ function startRecord(force) {
{type:"txt", id:"samples", font:"6x8:2", label:" - ", pad:5},
{type:"txt", font:"6x8", label:"Time", pad:2},
{type:"txt", id:"time", font:"6x8:2", label:" - ", pad:5},
{type:"txt", font:"6x8:2", label:"RECORDING", bgCol:"#f00", pad:5, fillx:true},
{type:"txt", font:"6x8:2", label:"RECORDING", bgCol:"#f00", pad:5, fillx:1},
]
},[ // Buttons...
{label:"STOP", cb:()=>{

View File

@ -9,3 +9,4 @@
0.09: Add per alarm auto-snooze option
0.10: Fix auto-snooze option (this stopped new alarms being added) (fix #506)
0.11: Respect Quiet Mode
0.12: Fix widget for bangle 2, now uses theme

View File

@ -5,7 +5,7 @@
delete alarms;
// add the widget
WIDGETS["alarm"]={area:"tl",width:24,draw:function() {
g.setColor(-1);
g.setColor(g.theme.fg);
g.drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
}};
})()

View File

@ -6,3 +6,4 @@
0.07: Auto-calculate height, and pad text down even when there's no title (so it stays on-screen)
0.08: Don't turn on screen during Quiet Mode
0.09: Add onHide callback
0.10: Improvements to help notifications work with themes

View File

@ -5,6 +5,10 @@ A handler for displaying notifications that displays them in a bar at the top of
This is not an app, but instead it is a library that can be used by
other applications or widgets to display messages.
**Note:** There are other implementations of this library available such
as `notifyfs` (Fullscreen Notifications). These can be used in the exact
same way from code, but they look different to the user.
## Usage
```JS

View File

@ -96,15 +96,17 @@ exports.show = function(options) {
b = y+h-1, r = x+w-1; // bottom,right
g.setClipRect(x,y, r,b);
// clear area
g.setColor(options.bgColor||0).fillRect(x,y, r,b);
g.reset();
if (options.bgColor!==undefined) g.setColor(options.bgColor);
g.clearRect(x,y, r,b);
// bottom border
g.setColor(0x39C7).fillRect(0,b-1, r,b);
g.setColor("#333").fillRect(0,b-1, r,b);
b -= 2;h -= 2;
// title bar
if (options.title || options.src) {
g.setColor(options.titleBgColor||0x39C7).fillRect(x,y, r,y+20);
const title = options.title||options.src;
g.setColor(-1).setFontAlign(-1, -1, 0).setFont("6x8", 2);
g.setColor(g.theme.fg).setFontAlign(-1, -1, 0).setFont("6x8", 2);
g.drawString(title.trim().substring(0, 13), x+25,y+3);
if (options.title && options.src) {
g.setFont("6x8", 1).setFontAlign(1, 1, 0);
@ -122,7 +124,7 @@ exports.show = function(options) {
}
// body text
if (options.body) {
g.setColor(-1).setFont("6x8", 1).setFontAlign(-1, -1, 0).drawString(text, x+6,y+4);
g.setColor(g.theme.fg).setFont("6x8", 1).setFontAlign(-1, -1, 0).drawString(text, x+6,y+4);
}
if (options.render) {

View File

@ -8,3 +8,4 @@
0.08: Don't turn on screen during Quiet Mode
0.09: Add onHide callback
0.10: Ensure dismissing a notification dismissal doesn't enter launcher if in clock mode
0.11: Improvements to help notifications work with themes, Bangle.js 2 support

View File

@ -50,22 +50,24 @@ exports.show = function(options) {
if (options.on===undefined) options.on=true;
id = ("id" in options)?options.id:null;
let size = options.size||120;
if (size>120) {size=120}
Bangle.setLCDMode("direct");
if (size>120) size=120;
try { Bangle.setLCDMode("direct"); } catch(e) {} // not supported/needed on Bangle.js 2
let x = 0,
y = 40,
w = 240,
w = g.getWidth(),
h = size;
// clear screen
g.setColor(options.bgColor||0).fillRect(0,0,g.getWidth(),g.getHeight());
g.reset();
if (options.bgColor!==undefined) g.setColor(options.bgColor);
g.clearRect(0,0,g.getWidth(),g.getHeight());
// top bar
if (options.title||options.src) {
const title = options.title || options.src
g.setColor(options.titleBgColor||0x39C7).fillRect(x, y, x+w-1, y+30);
g.setColor(-1).setFontAlign(-1, -1, 0).setFont("6x8", 3);
const title = options.title || options.src;
g.setColor(options.titleBgColor||"#333").fillRect(x, y, x+w-1, y+30);
g.setColor(g.theme.fg).setFontAlign(-1, -1, 0).setFont("6x8", 3);
g.drawString(title.trim().substring(0, 13), x+5, y+3);
if (options.title && options.src) {
g.setColor(-1).setFontAlign(1, 1, 0).setFont("6x8", 2);
g.setColor(g.theme.fg).setFontAlign(1, 1, 0).setFont("6x8", 2);
// above drawing area, but we are fullscreen
g.drawString(options.src.substring(0, 10), w-16, y-4);
}
@ -73,8 +75,8 @@ exports.show = function(options) {
}
if (options.icon) {
let i = options.icon, iw,ih;
if ("string"==typeof i) {iw=i.charCodeAt(0); ih=i.charCodeAt(1)}
else {iw=i[0]; ih=i[1]}
if ("string"==typeof i) {iw=i.charCodeAt(0); ih=i.charCodeAt(1);}
else {iw=i[0]; ih=i[1];}
const iy=y ? (y+4) : (h-ih)/2; // show below title bar if present, otherwise center vertically
g.drawImage(i, x+4,iy);
x += iw+4;w -= iw+4;
@ -84,16 +86,13 @@ exports.show = function(options) {
const maxRows = Math.floor((h-4)/16), // font=2*(6x8)
maxChars = Math.floor((w-4)/12),
text=fitWords(options.body, maxRows, maxChars);
g.setColor(-1).setFont("6x8", 2).setFontAlign(-1, -1, 0).drawString(text, x+4, y+4);
g.setColor(g.theme.fg).setFont("6x8", 2).setFontAlign(-1, -1, 0).drawString(text, x+4, y+4);
}
if (options.render) {
const area={x:x, y:y, w:w, h:h}
options.render(area);
}
if (options.on && !(require('Storage').readJSON('setting.json',1)||{}).quiet) {
if (options.render)
options.render({x:x, y:y, w:w, h:h});
if (options.on && !(require('Storage').readJSON('setting.json',1)||{}).quiet)
Bangle.setLCDPower(1); // light up
}
Bangle.on("touch", exports.hide);
if (options.onHide)
hideCallback = options.onHide;

View File

@ -3,4 +3,5 @@
0.04: Adjust "weather unknown" message according to Bluetooth connection.
0.05: Add wind direction.
0.06: Use setUI for launcher.
0.07: Add theme support and unknown icon.
0.07: Add theme support and unknown icon.
0.08: Refactor and reduce widget ram usage.

View File

@ -1,5 +1,6 @@
(() => {
const weather = require('weather');
let current = weather.get();
function formatDuration(millis) {
let pluralize = (n, w) => n + " " + w + (n == 1 ? "" : "s");
@ -10,16 +11,15 @@
}
function draw() {
let w = weather.current;
g.reset();
g.clearRect(0, 24, 239, 239);
weather.drawIcon(w.txt, 65, 90, 55);
weather.drawIcon(current.txt, 65, 90, 55);
const locale = require("locale");
g.reset();
const temp = locale.temp(w.temp-273.15).match(/^(\D*\d*)(.*)$/);
const temp = locale.temp(current.temp-273.15).match(/^(\D*\d*)(.*)$/);
let width = g.setFont("Vector", 40).stringWidth(temp[1]);
width += g.setFont("Vector", 20).stringWidth(temp[2]);
g.setFont("Vector", 40).setFontAlign(-1, -1, 0);
@ -31,19 +31,19 @@
g.setFontAlign(-1, 0, 0);
g.drawString("Humidity", 135, 130);
g.setFontAlign(1, 0, 0);
g.drawString(w.hum+"%", 225, 130);
if ('wind' in w) {
g.drawString(current.hum+"%", 225, 130);
if ('wind' in current) {
g.setFontAlign(-1, 0, 0);
g.drawString("Wind", 135, 142);
g.setFontAlign(1, 0, 0);
g.drawString(locale.speed(w.wind)+' '+w.wrose.toUpperCase(), 225, 142);
g.drawString(locale.speed(current.wind)+' '+current.wrose.toUpperCase(), 225, 142);
}
g.setFont("6x8", 2).setFontAlign(0, 0, 0);
g.drawString(w.loc, 120, 170);
g.drawString(current.loc, 120, 170);
g.setFont("6x8", 1).setFontAlign(0, 0, 0);
g.drawString(w.txt.charAt(0).toUpperCase()+w.txt.slice(1), 120, 190);
g.drawString(current.txt.charAt(0).toUpperCase()+current.txt.slice(1), 120, 190);
drawUpdateTime();
@ -51,8 +51,8 @@
}
function drawUpdateTime() {
if (!weather.current || !weather.current.time) return;
let text = `Last update received ${formatDuration(Date.now() - weather.current.time)} ago`;
if (!current || !current.time) return;
let text = `Last update received ${formatDuration(Date.now() - current.time)} ago`;
g.reset();
g.clearRect(0, 202, 239, 210);
g.setFont("6x8", 1).setFontAlign(0, 0, 0);
@ -60,8 +60,9 @@
}
function update() {
current = weather.get();
NRF.removeListener("connect", update);
if (weather.current) {
if (current) {
draw();
} else if (NRF.getSecurityStatus().connected) {
E.showMessage("Weather unknown\n\nIs Gadgetbridge\nweather reporting\nset up on your\nphone?");

View File

@ -1,6 +1,6 @@
const storage = require('Storage');
let expiryTimeout = undefined;
let expiryTimeout;
function scheduleExpiry(json) {
if (expiryTimeout) {
clearTimeout(expiryTimeout);
@ -9,53 +9,35 @@ function scheduleExpiry(json) {
let expiry = "expiry" in json ? json.expiry : 2*3600000;
if (json.weather && json.weather.time && expiry) {
let t = json.weather.time + expiry - Date.now();
expiryTimeout = setTimeout(() => {
expiryTimeout = undefined;
let json = storage.readJSON('weather.json')||{};
delete json.weather;
storage.write('weather.json', json);
exports.current = undefined;
exports.emit("update");
}, t);
expiryTimeout = setTimeout(update, t);
}
}
/**
* Convert numeric direction into human-readable label
*
* @param {number} deg - Direction in degrees
* @return {string|null} - Nearest compass point
*/
function compassRose(deg) {
if (typeof deg === 'undefined') return null;
while (deg<0 || deg>360) {
deg = (deg+360)%360;
}
return ['n','ne','e','se','s','sw','w','nw','n'][Math.floor((deg+22.5)/45)];
}
function setCurrentWeather(json) {
scheduleExpiry(json);
exports.current = json.weather;
}
function update(weatherEvent) {
let weather = Object.assign({}, weatherEvent);
weather.time = Date.now();
if ('wdir' in weather) {
weather.wrose = compassRose(weather.wdir);
}
delete weather.t;
let json = storage.readJSON('weather.json')||{};
json.weather = weather;
if (weatherEvent) {
let weather = weatherEvent.clone();
delete weather.t;
weather.time = Date.now();
if (weather.wdir != null) {
// Convert numeric direction into human-readable label
let deg = weather.wdir;
while (deg<0 || deg>360) {
deg = (deg+360)%360;
}
weather.wrose = ['n','ne','e','se','s','sw','w','nw','n'][Math.floor((deg+22.5)/45)];
}
json.weather = weather;
}
else {
delete json.weather;
}
storage.write('weather.json', json);
setCurrentWeather(json);
exports.emit("update");
scheduleExpiry(json);
exports.emit("update", json.weather);
}
const _GB = global.GB;
@ -64,7 +46,11 @@ global.GB = (event) => {
if (_GB) setTimeout(_GB, 0, event);
};
setCurrentWeather(storage.readJSON('weather.json')||{});
exports.get = function() {
return storage.readJSON('weather.json').weather;
}
scheduleExpiry(storage.readJSON('weather.json')||{});
exports.drawIcon = function(cond, x, y, r) {
function drawSun(x, y, r) {

View File

@ -1,45 +1,23 @@
(() => {
const weather = require('weather');
function draw() {
const w = weather.current;
if (!w) return;
g.reset();
g.clearRect(this.x, this.y, this.x+this.width-1, this.y+23);
if (w.txt) {
weather.drawIcon(w.txt, this.x+10, this.y+8, 7.5);
}
if (w.temp) {
let t = require('locale').temp(w.temp-273.15); // applies conversion
t = t.match(/[\d\-]*/)[0]; // but we have no room for units
g.reset();
g.setFontAlign(0, 1); // center horizontally at bottom of widget
g.setFont('6x8', 1);
g.drawString(t, this.x+10, this.y+24);
}
}
var dirty = false;
function update() {
if (!WIDGETS["weather"].width) {
WIDGETS["weather"].width = 20;
Bangle.drawWidgets();
} else if (Bangle.isLCDOn()) {
WIDGETS["weather"].draw();
} else {
dirty = true;
weather.on("update", w => {
if (w) {
if (!WIDGETS["weather"].width) {
WIDGETS["weather"].width = 20;
Bangle.drawWidgets();
} else if (Bangle.isLCDOn()) {
WIDGETS["weather"].draw();
} else {
dirty = true;
}
}
else {
WIDGETS["weather"].width = 0;
Bangle.drawWidgets();
}
}
function hide() {
WIDGETS["weather"].width = 0;
Bangle.drawWidgets();
}
weather.on("update", () => {
if (weather.current) update();
else hide();
});
Bangle.on('lcdPower', on => {
@ -51,7 +29,23 @@
WIDGETS["weather"] = {
area: "tl",
width: weather.current ? 20 : 0,
draw: draw,
width: weather.get() ? 20 : 0,
draw: function() {
const w = weather.get();
if (!w) return;
g.reset();
g.clearRect(this.x, this.y, this.x+this.width-1, this.y+23);
if (w.txt) {
weather.drawIcon(w.txt, this.x+10, this.y+8, 7.5);
}
if (w.temp) {
let t = require('locale').temp(w.temp-273.15); // applies conversion
t = t.match(/[\d\-]*/)[0]; // but we have no room for units
g.reset();
g.setFontAlign(0, 1); // center horizontally at bottom of widget
g.setFont('6x8', 1);
g.drawString(t, this.x+10, this.y+24);
}
},
};
})();

View File

@ -14,3 +14,8 @@
0.15: Settings option to hide the widget icon
0.16: Settings option to show large digits in widget area
0.17: Cope with 2v10+ firmware sometimes reporting >1 step
0.18: Adjust widget width when displaying large text
0.19: Allow goal in large font mode
Stop goal drawing outside widget area
Fix issue with widget overwrite in large font mode
Memory usage enhancements

View File

@ -32,7 +32,7 @@
onchange: (g) => {
s.goal = g
s.progress = !!g
save()
save();
},
},
'Show Progress': {
@ -40,7 +40,7 @@
format: () => (s.progress ? 'Yes' : 'No'),
onchange: () => {
s.progress = !s.progress
save()
save();
},
},
'Large Digits': {
@ -48,7 +48,7 @@
format: () => (s.large ? 'Yes' : 'No'),
onchange: () => {
s.large = !s.large
save()
save();
},
},
'Hide Widget': {
@ -56,7 +56,7 @@
format: () => (s.hide ? 'Yes' : 'No'),
onchange: () => {
s.hide = !s.hide
save()
save();
},
},
'< Back': back,

View File

@ -23,78 +23,6 @@
return (key in settings) ? settings[key] : DEFAULTS[key];
}
function drawProgress(stps) {
if (setting('hide')) return;
const width = 24, half = width/2;
const goal = setting('goal'), left = Math.max(goal-stps,0);
const c = left ? "#00f" : "#090"; // blue or dark green
g.setColor(c).fillCircle(this.x + half, this.y + half, half);
const TAU = Math.PI*2;
if (left) {
const f = left/goal; // fraction to blank out
let p = [];
p.push(half,half);
p.push(half,0);
if(f>1/8) p.push(0,0);
if(f>2/8) p.push(0,half);
if(f>3/8) p.push(0,width);
if(f>4/8) p.push(half,width);
if(f>5/8) p.push(width,width);
if(f>6/8) p.push(width,half);
if(f>7/8) p.push(width,0);
p.push(half - Math.sin(f * TAU) * half);
p.push(half - Math.cos(f * TAU) * half);
for (let i = p.length; i; i -= 2) {
p[i - 2] += this.x;
p[i - 1] += this.y;
}
g.setColor(g.theme.bg).fillPoly(p);
}
}
// show the step count in the widget area in a readable sized font
function draw_large(st) {
var width = 12 * st.length;
g.reset();
g.clearRect(this.x, this.y, this.x + width, this.y + 16); // erase background
g.setColor(g.theme.fg);
g.setFont("6x8",2);
g.setFontAlign(-1, -1);
g.drawString(st, this.x + 4, this.y + 2);
}
// draw your widget
function draw() {
if (setting('hide')) return;
var width = 24;
if (stp_today > 99999){
stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters
}
let stps = stp_today.toString();
if (setting('large')) {
draw_large.call(this, stps);
return;
}
g.reset().clearRect(this.x, this.y, this.x + width, this.y + 23); // erase background
if (setting('progress')){ drawProgress.call(this, stps); }
g.setColor(g.theme.fg);
if (stps.length > 3){
stps = stps.slice(0,-3) + "," + stps.slice(-3);
g.setFont("4x6", 1); // if big, shrink text to fix
} else {
g.setFont("6x8", 1);
}
g.setFontAlign(0, 0); // align to x: center, y: center
g.drawString(stps, this.x+width/2, this.y+19);
// on low bpp screens, draw 1 bit. Currently there is no getBPP so we just do it based on resolution
g.drawImage(atob("CgoCLguH9f2/7+v6/79f56CtAAAD9fw/n8Hx9A=="),this.x+(width-10)/2,this.y+2);
}
function reload() {
loadSettings()
draw()
}
Bangle.on('step', stepCount => {
var steps = stepCount-lastStepCount;
if (lastStepCount===undefined || steps<0) steps=1;
@ -115,11 +43,11 @@
}
lastUpdate = date
//console.log("up: " + up + " stp: " + stp_today + " " + date.toString());
if (Bangle.isLCDOn()) WIDGETS["wpedom"].draw();
WIDGETS["wpedom"].redraw();
});
// redraw when the LCD turns on
Bangle.on('lcdPower', function(on) {
if (on) WIDGETS["wpedom"].draw();
if (on) WIDGETS["wpedom"].redraw();
});
// When unloading, save state
E.on('kill', () => {
@ -134,10 +62,80 @@
// add your widget
WIDGETS["wpedom"]={area:"tl",width:26,
draw:draw,
reload:reload,
getSteps:()=>stp_today
};
redraw:function() { // work out the width, and queue a full redraw if needed
let stps = stp_today.toString();
let newWidth = 24;
if (setting('hide'))
newWidth = 0;
else {
if (setting('large')) {
newWidth = 12 * stps.length + 3;
if (setting('progress'))
newWidth += 24;
}
}
if (newWidth!=this.width) {
// width has changed, re-layout all widgets
this.width = newWidth;
Bangle.drawWidgets();
} else {
// width not changed - just redraw
WIDGETS["wpedom"].draw();
}
},
draw:function() {
if (setting('hide')) return;
if (stp_today > 99999)
stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters
let stps = stp_today.toString();
g.reset().clearRect(this.x, this.y, this.x + this.width, this.y + 23); // erase background
if (setting('progress')) {
const width = 23, half = 11;
const goal = setting('goal'), left = Math.max(goal-stps,0);
// blue or dark green
g.setColor(left ? "#08f" : "#080").fillCircle(this.x + half, this.y + half, half);
if (left) {
const TAU = Math.PI*2;
const f = left/goal; // fraction to blank out
let p = [];
p.push(half,half);
p.push(half,0);
if(f>1/8) p.push(0,0);
if(f>2/8) p.push(0,half);
if(f>3/8) p.push(0,width);
if(f>4/8) p.push(half,width);
if(f>5/8) p.push(width,width);
if(f>6/8) p.push(width,half);
if(f>7/8) p.push(width,0);
p.push(half - Math.sin(f * TAU) * half);
p.push(half - Math.cos(f * TAU) * half);
g.setColor(g.theme.bg).fillPoly(g.transformVertices(p,{x:this.x,y:this.y}));
}
g.reset();
}
if (setting('large')) {
g.setFont("6x8",2);
g.setFontAlign(-1, 0);
g.drawString(stps, this.x + (setting('progress')?28:4), this.y + 12);
} else {
let w = 24;
if (stps.length > 3){
stps = stps.slice(0,-3) + "," + stps.slice(-3);
g.setFont("4x6", 1); // if big, shrink text to fix
} else {
g.setFont("6x8", 1);
}
g.setFontAlign(0, 0); // align to x: center, y: center
g.drawString(stps, this.x+w/2, this.y+19);
g.drawImage(atob("CgoCLguH9f2/7+v6/79f56CtAAAD9fw/n8Hx9A=="),this.x+(w-10)/2,this.y+2);
}
},
reload:function() {
loadSettings();
WIDGETS["wpedom"].redraw();
},
getSteps:()=>stp_today
};
// Load data at startup
let pedomData = require("Storage").readJSON(PEDOMFILE,1);
if (pedomData) {

2
core

@ -1 +1 @@
Subproject commit 2aac601e38d659876eb7db5aebc7a12dd3c39da7
Subproject commit 0fd608f085deff9b39f2db3559ecc88edb232aba