Merge branch 'espruino:master' into master

pull/2116/head
eleanor 2022-09-09 08:45:26 -05:00 committed by GitHub
commit f11f1a2538
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 214 additions and 21 deletions

View File

@ -2,3 +2,4 @@
0.02: Added settings page to force calendar sync
0.03: Disable past events display from settings
0.04: Added awareness of allDay field
0.05: Displaying calendar colour and name

View File

@ -1,3 +1,20 @@
# Agenda
Basic agenda reading the events synchronised from GadgetBridge
Basic agenda reading the events synchronised from GadgetBridge.
### Functionalities
* List all events in the next week (or whatever is synchronized)
* Optionally view past events (until GB removes them)
* Show start time and location of the events in the list
* Show the colour of the calendar in the list
* Display description, location and calendar name after tapping on events
### Report a bug
You can easily open an issue in the espruino repo, but I won't be notified and it might take time.
If you want a (hopefully) quicker response, just report [on my fork](https://github.com/glemco/BangleApps).
### Known Problems
Any all-day event lasts just one day: that is a GB limitation that we will likely fix in the future.

View File

@ -6,6 +6,8 @@
title,
description,
location,
color:int,
calName,
allDay: bool,
}
*/
@ -73,6 +75,8 @@ function showEvent(ev) {
lines = lines.concat(/*LANG*/"Location"+": ", g.wrapString(ev.location, g.getWidth()-10));
if(ev.description)
lines = lines.concat("",g.wrapString(ev.description, g.getWidth()-10));
if(ev.calName)
lines = lines.concat(/*LANG*/"Calendar"+": ", g.wrapString(ev.calName, g.getWidth()-10));
lines = lines.concat(["",/*LANG*/"< Back"]);
E.showScroller({
h : g.getFontHeight(), // height of each menu item in pixels
@ -116,17 +120,16 @@ function showList() {
var body = formatDateShort(getDate(ev.timestamp),ev.allDay)+"\n"+(ev.location?ev.location:/*LANG*/"No location");
if(settings.pastEvents) isPast = ev.timestamp + ev.durationInSeconds < (new Date())/1000;
if (title) g.setFontAlign(-1,-1).setFont(fontBig)
.setColor(isPast ? "#888" : g.theme.fg).drawString(title, x,r.y+2);
.setColor(isPast ? "#888" : g.theme.fg).drawString(title, x+4,r.y+2);
if (body) {
g.setFontAlign(-1,-1).setFont(fontMedium).setColor(isPast ? "#888" : g.theme.fg);
var l = g.wrapString(body, r.w-(x+14));
if (l.length>3) {
l = l.slice(0,3);
l[l.length-1]+="...";
}
g.drawString(l.join("\n"), x+10,r.y+20);
g.drawString(body, x+10,r.y+20);
}
g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items
if(ev.color) {
g.setColor("#"+(0x1000000+Number(ev.color)).toString(16).padStart(6,"0"));
g.fillRect(r.x,r.y+4,r.x+3, r.y+r.h-4);
}
},
select : idx => showEvent(CALENDAR[idx]),
back : () => load()

View File

@ -1,7 +1,7 @@
{
"id": "agenda",
"name": "Agenda",
"version": "0.04",
"version": "0.05",
"description": "Simple agenda",
"icon": "agenda.png",
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],

View File

@ -17,4 +17,5 @@
0.17: Fix - Step count was no more shown in the menu.
0.18: Set timer for an agenda entry by simply clicking in the middle of the screen. Only one timer can be set.
0.19: Fix - Compatibility with "Digital clock widget"
0.20: Better handling of async data such as getPressure.
0.20: Better handling of async data such as getPressure.
0.21: On the default menu the week of year can be shown.

View File

@ -198,6 +198,7 @@ function imgMountain() {
var menu = [
[
function(){ return [ null, null ] },
function(){ return [ "Week " + weekOfYear(), null ] },
],
[
function(){ return [ "Bangle", imgWatch() ] },
@ -423,6 +424,19 @@ function getWeather(){
};
}
// From https://weeknumber.com/how-to/javascript
function weekOfYear() {
var date = new Date();
date.setHours(0, 0, 0, 0);
// Thursday in current week decides the year.
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
// January 4 is always in week 1.
var week1 = new Date(date.getFullYear(), 0, 4);
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
- 3 + (week1.getDay() + 6) % 7) / 7);
}
function isAlarmEnabled(idx){
try{
@ -600,7 +614,7 @@ function drawTime(){
}
var imgWidth = 0;
if(menuImg !== undefined){
if(menuImg){
imgWidth = 24.0;
var strWidth = g.stringWidth(menuText);
var scale = imgWidth / menuImg.width;

View File

@ -1,7 +1,7 @@
{
"id": "bwclk",
"name": "BW Clock",
"version": "0.20",
"version": "0.21",
"description": "A very minimalistic clock to mainly show date and time.",
"readme": "README.md",
"icon": "app.png",

View File

@ -70,11 +70,21 @@ let alarm = require("sched").newDefaultAlarm();
// Get a new timer with default values
let timer = require("sched").newDefaultTimer();
// Add/update an existing alarm
require("sched").setAlarm("mytimer", {
// Add/update an existing alarm (using fields from the object shown above)
require("sched").setAlarm("mytimer", { // as a timer
msg : "Wake up",
timer : 10 * 60 * 1000 // 10 minutes
});
require("sched").setAlarm("myalarm", { // as an alarm
msg : "Wake up",
t : 9 * 3600000 // 9 o'clock (in ms)
});
require("sched").setAlarm("mydayalarm", { // as an alarm on a date
msg : "Wake up",
date : "2022-04-04",
t : 9 * 3600000 // 9 o'clock (in ms)
});
// Ensure the widget and alarm timer updates to schedule the new alarm properly
require("sched").reload();

View File

@ -1,4 +1,4 @@
#!/usr/bin/env nodejs
#!/usr/bin/env node
/* Simple Command-line app loader for Node.js
===============================================
@ -8,14 +8,13 @@ as a normal dependency) because we want `sanitycheck.js`
to be able to run *quickly* in travis for every commit,
and we don't want NPM pulling in (and compiling native modules)
for Noble.
*/
var SETTINGS = {
pretokenise : true
};
var APPSDIR = __dirname+"/../apps/";
var Utils = require("../core/js/utils.js");
var AppInfo = require("../core/js/appinfo.js");
var noble;
try {
noble = require('@abandonware/noble');
@ -28,15 +27,22 @@ if (!noble) {
console.log(" npm install @abandonware/noble")
console.log("or:")
console.log(" npm install noble")
process.exit(1);
}
var apps = [];
function ERROR(msg) {
console.error(msg);
process.exit(1);
}
//eval(require("fs").readFileSync(__dirname+"../core/js/utils.js"));
var AppInfo = require("../core/js/appinfo.js");
global.Const = {
/* Are we only putting a single app on a device? If so
apps should all be saved as .bootcde and we write info
about the current app into app.info */
SINGLE_APP_ONLY : false,
};
var deviceId = "BANGLEJS2";
var apps = [];
var dirs = require("fs").readdirSync(APPSDIR, {withFileTypes: true});
dirs.forEach(dir => {
@ -69,6 +75,10 @@ apploader.js list
apploader.js devices
- list available device addresses
apploader.js install appname [de:vi:ce:ad:dr:es]
NOTE: Currently this App Loader expects the device it uploads to
(deviceId) to be BANGLEJS2, so it won't work for Bangle.js 1 without
modification.
`);
process.exit(0);
}
@ -104,7 +114,10 @@ function cmdInstallApp(appId, deviceAddress) {
fileGetter:function(url) {
console.log(__dirname+"/"+url);
return Promise.resolve(require("fs").readFileSync(__dirname+"/../"+url).toString("binary"));
}, settings : SETTINGS}).then(files => {
},
settings : SETTINGS,
device : { id : deviceId }
}).then(files => {
//console.log(files);
var command = files.map(f=>f.cmd).join("\n")+"\n";
bangleSend(command, deviceAddress).then(() => process.exit(0));

114
modules/clock_info.js Normal file
View File

@ -0,0 +1,114 @@
var exports = {};
/* Module that allows for loading of clock 'info' displays
that can be scrolled through on the clock face.
`load()` returns an array of menu items:
* 'item.name' : friendly name
* 'item.get' : function that resolves with:
{
'text' : the text to display for this item
'img' : a 24x24px image to display for this item
}
* 'item.show' : called when item should be shown. Enables updates. Call BEFORE 'get'
* 'item.hide' : called when item should be hidden. Disables updates.
* .on('redraw', ...) : event that is called when 'get' should be called again (only after 'item.show')
* 'item.run' : (optional) called if the info screen is tapped - can perform some action
See the bottom of this file for example usage...
example.clkinfo.js :
(function() {
return [
{ name : "Example",
get : () => ({ text : "Bangle.js",
img : atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA==") }),
show : () => {},
hide : () => {}
}
];
}) // must not have a semi-colon!
*/
exports.load = function() {
// info used for drawing...
var hrm = "--";
var alt = "--";
// callbacks (needed for easy removal of listeners)
function batteryUpdateHandler() { items[0].emit("redraw"); }
function stepUpdateHandler() { items[1].emit("redraw"); }
function hrmUpdateHandler() { items[2].emit("redraw"); }
function altUpdateHandler() {
Bangle.getPressure().then(data=>{
if (!data) return;
alt = Math.round(data.altitude) + "m";
items[3].emit("redraw");
});
}
// actual items
var items = [
{ name : "Battery",
get : () => ({
text : E.getBattery() + "%",
img : atob(Bangle.isCharging() ? "GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA==" : "GBiBAAAAAAAAAAAAAAAAAAAAAD//+P///IAAAr//Ar//Ar//A7//A7//A7//A7//Ar//AoAAAv///D//+AAAAAAAAAAAAAAAAAAAAA==") }),
show : function() {
this.interval = setInterval(()=>this.emit('redraw'), 60000);
Bangle.on("charging", batteryUpdateHandler);
},
hide : function() {
clearInterval(this.interval);
delete this.interval;
Bangle.removeListener("charging", batteryUpdateHandler);
},
},
{ name : "Steps", get : () => ({
text : Bangle.getHealthStatus("day").steps,
img : atob("GBiBAAcAAA+AAA/AAA/AAB/AAB/gAA/g4A/h8A/j8A/D8A/D+AfH+AAH8AHn8APj8APj8AHj4AHg4AADAAAHwAAHwAAHgAAHgAADAA==") }),
show : function() { Bangle.on("step", stepUpdateHandler); },
hide : function() { Bangle.removeListener("step", stepUpdateHandler); },
},
{ name : "HRM", get : () => ({
text : Math.round(Bangle.getHealthStatus("last").bpm) + " bpm",
img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAADAAADAAAHAAAHjAAHjgAPngH9n/n82/gA+AAA8AAA8AAAcAAAYAAAYAAAAAAAAAAAAAAAAAAAAAA==") }),
show : function() { Bangle.setHRMPower(1,"clkinfo"); Bangle.on("HRM", hrmUpdateHandler); hrm = Math.round(Bangle.getHealthStatus("last").bpm); },
hide : function() { Bangle.setHRMPower(0,"clkinfo"); Bangle.removeListener("HRM", hrmUpdateHandler); hrm = "--"; },
}
];
if (Bangle.getPressure) // Altimeter may not exist
items.push({ name : "Altitude", get : () => ({
text : alt,
img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAAACAAAGAAAPAAEZgAOwwAPwQAZgYAwAMBgAGBAACDAADGAABv///////wAAAAAAAAAAAAAAAAAAAA==") }),
show : function() { this.interval = setInterval(altUpdateHandler, 60000); alt = "--"; altUpdateHandler(); },
hide : function() { clearInterval(this.interval); delete this.interval; },
});
// now load extra data from a third party files
require("Storage").list(/clkinfo.js$/).forEach(fn => {
items = items.concat(eval(require("Storage").read(fn))());
});
// return it all!
return items;
};
// Code for testing
/*
g.clear();
var items = exports.load(); // or require("clock_info").load()
items.forEach((itm,i) => {
var y = i*24;
console.log("Starting", itm.name);
function draw() {
var info = itm.get();
g.reset().setFont("6x8:2").setFontAlign(-1,0);
g.clearRect(0,y,g.getWidth(),y+23);
g.drawImage(info.img, 0,y);
g.drawString(info.text, 48,y+12);
}
itm.on('redraw', draw); // ensures we redraw when we need to
itm.show();
draw();
});
*/

20
modules/wear_detect.js Normal file
View File

@ -0,0 +1,20 @@
/** Returns a promise that resolves with whether the Bangle
is worn or not.
Usage:
require("wear_detect").isWorn().then(worn => {
console.log(worn ? "is worn" : "not worn");
});
*/
exports.isWorn = function() {
return new Promise(resolve => {
if (Bangle.isCharging())
return resolve(false);
if (E.getTemperature() > 24.625)
return resolve(true);
if (Bangle.getAccel().mag > 1.045)
return resolve(true);
return resolve(false);
});
};