Merge branch 'upstream/master' into fix/improve-formatter-check

master
Rob Pilling 2024-02-05 22:30:31 +00:00
commit b2ff9dbb78
24 changed files with 461 additions and 281 deletions

View File

@ -1 +1 @@
0.01: New Widget! 0.01: New Clock Info!

View File

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

View File

@ -0,0 +1,25 @@
# Clock Name
More info on making Clock Faces: https://www.espruino.com/Bangle.js+Clock
Describe the Clock...
## Usage
Describe how to use it
## Features
Name the function
## Controls
Name the buttons and what they are used for
## Requests
Name who should be contacted for support/update requests
## Creator
Your name

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="))

View File

@ -0,0 +1,44 @@
// 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();
}, 60000 - (Date.now() % 60000));
}
function draw() {
// queue next draw in one minute
queueDraw();
// Work out where to draw...
var x = g.getWidth()/2;
var y = g.getHeight()/2;
g.reset();
// work out locale-friendly date/time
var date = new Date();
var timeStr = require("locale").time(date,1);
var dateStr = require("locale").date(date);
// draw time
g.setFontAlign(0,0).setFont("Vector",48);
g.clearRect(0,y-15,g.getWidth(),y+25); // clear the background
g.drawString(timeStr,x,y);
// draw date
y += 35;
g.setFontAlign(0,0).setFont("6x8");
g.clearRect(0,y-4,g.getWidth(),y+4); // clear the background
g.drawString(dateStr,x,y);
}
// Clear the screen once, at startup
g.clear();
// draw immediately at first, queue update
draw();
// Show launcher when middle button pressed
Bangle.setUI("clock");
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,15 @@
{ "id": "7chname",
"name": "My clock human readable name",
"shortName":"Short Name",
"version":"0.01",
"description": "A detailed description of my clock",
"icon": "icon.png",
"type": "clock",
"tags": "clock",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"7chname.app.js","url":"app.js"},
{"name":"7chname.img","url":"app-icon.js","evaluate":true}
]
}

View File

@ -32,3 +32,4 @@
Allow alarm enable/disable Allow alarm enable/disable
0.31: Implement API for activity fetching 0.31: Implement API for activity fetching
0.32: Added support for loyalty cards from gadgetbridge 0.32: Added support for loyalty cards from gadgetbridge
0.33: Fix alarms created in Gadgetbridge not repeating

View File

@ -81,7 +81,12 @@
for (var j = 0; j < event.d.length; j++) { for (var j = 0; j < event.d.length; j++) {
// prevents all alarms from going off at once?? // prevents all alarms from going off at once??
var dow = event.d[j].rep; var dow = event.d[j].rep;
if (!dow) dow = 127; //if no DOW selected, set alarm to all DOW var rp = false;
if (!dow) {
dow = 127; //if no DOW selected, set alarm to all DOW
} else {
rp = true;
}
var last = (event.d[j].h * 3600000 + event.d[j].m * 60000 < currentTime) ? (new Date()).getDate() : 0; var last = (event.d[j].h * 3600000 + event.d[j].m * 60000 < currentTime) ? (new Date()).getDate() : 0;
var a = require("sched").newDefaultAlarm(); var a = require("sched").newDefaultAlarm();
a.id = "gb"+j; a.id = "gb"+j;
@ -89,6 +94,7 @@
a.on = event.d[j].on !== undefined ? event.d[j].on : true; a.on = event.d[j].on !== undefined ? event.d[j].on : true;
a.t = event.d[j].h * 3600000 + event.d[j].m * 60000; a.t = event.d[j].h * 3600000 + event.d[j].m * 60000;
a.dow = ((dow&63)<<1) | (dow>>6); // Gadgetbridge sends DOW in a different format a.dow = ((dow&63)<<1) | (dow>>6); // Gadgetbridge sends DOW in a different format
a.rp = rp;
a.last = last; a.last = last;
alarms.push(a); alarms.push(a);
} }

View File

@ -2,7 +2,7 @@
"id": "android", "id": "android",
"name": "Android Integration", "name": "Android Integration",
"shortName": "Android", "shortName": "Android",
"version": "0.32", "version": "0.33",
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system,messages,notifications,gadgetbridge", "tags": "tool,system,messages,notifications,gadgetbridge",

View File

@ -2,7 +2,7 @@
"name": "BTHome Temperature and Pressure", "name": "BTHome Temperature and Pressure",
"shortName":"BTHome T", "shortName":"BTHome T",
"version":"0.02", "version":"0.02",
"description": "Displays temperature and pressure, and advertises them over bluetooth using BTHome.io standard", "description": "Displays temperature and pressure, and advertises them over bluetooth for Home Assistant using BTHome.io standard",
"icon": "app.png", "icon": "app.png",
"tags": "bthome,bluetooth,temperature", "tags": "bthome,bluetooth,temperature",
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],

View File

@ -2,7 +2,7 @@
"id": "ha", "id": "ha",
"name": "Home Assistant", "name": "Home Assistant",
"version": "0.10", "version": "0.10",
"description": "Integrates your Bangle.js into Home Assistant.", "description": "Integrates your Bangle.js into Home Assistant using Android Integration/Gadgetbridge",
"icon": "ha.png", "icon": "ha.png",
"type": "app", "type": "app",
"tags": "tool,clkinfo,bluetooth", "tags": "tool,clkinfo,bluetooth",

View File

@ -3,7 +3,7 @@
"name": "Home Assistant Sensors", "name": "Home Assistant Sensors",
"shortName": "HA sensors", "shortName": "HA sensors",
"version": "0.02", "version": "0.02",
"description": "Send sensor values to Home Assistant using the Android Integration.", "description": "Send sensor values to Home Assistant using Android Integration/Gadgetbridge",
"icon": "ha.png", "icon": "ha.png",
"type": "bootloader", "type": "bootloader",
"tags": "tool,sensors", "tags": "tool,sensors",

View File

@ -1 +1,2 @@
0.01: New App! 0.01: New App!
0.02: Add more control styles

View File

@ -10,8 +10,21 @@ in the future this app will be able to support other types of remote (see below)
## Usage ## Usage
Run the app, and ensure you're not connected to your watch via Bluetooth Run the app, then choose the type of controls you want and ensure you're not connected
(a warning will pop up if so). to your watch via Bluetooth (a warning will pop up if so).
Linear mode controls A/B axes individually, and allows you to vary the speed of the
motors based on the distance you drag from the centre. Other modes just use on/off
buttons.
| Mode | up | down | left | right |
|------------|------|------|------|-------|
| **Linear** | +A | -A | -B | +B |
| **Normal** | +A | -A | -B | +B |
| **Tank** | -A+B | +A-B | +A+B | -A-B |
| **Merged** | -A-B | +A+B | +A-B | -A+B |
In all cases pressing the C/D buttons will turn on C/D outputs
Now press the arrow keys on the screen to control the robot. Now press the arrow keys on the screen to control the robot.

View File

@ -1,5 +1,4 @@
var lego = require("mouldking"); var lego = require("mouldking");
lego.start();
E.on('kill', () => { E.on('kill', () => {
// return to normal Bluetooth advertising // return to normal Bluetooth advertising
NRF.setAdvertising({},{showName:true}); NRF.setAdvertising({},{showName:true});
@ -12,15 +11,17 @@ var controlState = "";
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
var R = Bangle.appRect; var R = Bangle.appRect;
// we'll divide up into 3x3
function getBoxCoords(x,y) { function startLegoButtons(controls) {
// we'll divide up into 3x3
function getBoxCoords(x,y) {
return { return {
x : R.x + R.w*x/3, x : R.x + R.w*x/3,
y : R.y + R.h*y/3 y : R.y + R.h*y/3
}; };
} }
function draw() { function draw() {
g.reset().clearRect(R); g.reset().clearRect(R);
var c, ninety = Math.PI/2; var c, ninety = Math.PI/2;
var colOn = "#f00", colOff = g.theme.fg; var colOn = "#f00", colOff = g.theme.fg;
@ -36,24 +37,24 @@ function draw() {
c = getBoxCoords(1.5, 2.5); c = getBoxCoords(1.5, 2.5);
g.setFontAlign(0,0).setFont("6x8").drawString("WARNING:\nBluetooth Connected\nYou must disconnect\nbefore LEGO will work",c.x,c.y); g.setFontAlign(0,0).setFont("6x8").drawString("WARNING:\nBluetooth Connected\nYou must disconnect\nbefore LEGO will work",c.x,c.y);
} }
} g.setFont("6x8:3").setFontAlign(0,0);
draw(); c = getBoxCoords(0.5, 0.5);
NRF.on('connect', draw); g.setColor(controlState=="c"?colOn:colOff).drawString("C",c.x,c.y);
NRF.on('disconnect', draw); c = getBoxCoords(2.5, 0.5);
g.setColor(controlState=="d"?colOn:colOff).drawString("D",c.x,c.y);
}
function setControlState(s) { function setControlState(s) {
controlState = s; controlState = s;
var c = {}; var c = {};
var speed = 3; if (s in controls)
if (s=="up") c={a:-speed,b:-speed}; c = controls[s];
if (s=="down") c={a:speed,b:speed};
if (s=="left") c={a:speed,b:-speed};
if (s=="right") c={a:-speed,b:speed};
draw(); draw();
lego.set(c); lego.set(c);
} }
Bangle.on('drag',e => { lego.start();
Bangle.setUI({mode:"custom", drag : e => {
var x = Math.floor(E.clip((e.x - R.x) * 3 / R.w,0,2.99)); var x = Math.floor(E.clip((e.x - R.x) * 3 / R.w,0,2.99));
var y = Math.floor(E.clip((e.y - R.y) * 3 / R.h,0,2.99)); var y = Math.floor(E.clip((e.y - R.y) * 3 / R.h,0,2.99));
if (!e.b) { if (!e.b) {
@ -61,10 +62,82 @@ Bangle.on('drag',e => {
return; return;
} }
if (y==0) { // top row if (y==0) { // top row
if (x==0) setControlState("c");
if (x==1) setControlState("up"); if (x==1) setControlState("up");
if (x==2) setControlState("d");
} else if (y==1) { } else if (y==1) {
if (x==0) setControlState("left"); if (x==0) setControlState("left");
if (x==1) setControlState("down"); if (x==1) setControlState("down");
if (x==2) setControlState("right"); if (x==2) setControlState("right");
} }
}});
draw();
NRF.on('connect', draw);
NRF.on('disconnect', draw);
}
function startLegoLinear() {
var mx = R.x+R.w/2;
var my = R.y+R.h/2;
var x=0,y=0;
var scale = 10;
function draw() {
g.reset().clearRect(R);
for (var i=3;i<60;i+=10)
g.drawCircle(mx,my,i);
g.setColor("#F00");
var px = E.clip(mx + x*scale, R.x+20, R.x2-20);
var py = E.clip(my + y*scale, R.y+20, R.y2-20);
g.fillCircle(px, py, 20);
}
lego.start();
Bangle.setUI({mode:"custom", drag : e => {
x = Math.round((e.x - mx) / scale);
y = Math.round((e.y - my) / scale);
if (!e.b) {
x=0; y=0;
}
lego.set({a:x, b:y});
draw();
}});
draw();
NRF.on('connect', draw);
NRF.on('disconnect', draw);
}
// Mappings of button to output
const CONTROLS = {
normal : {
up : {a: 7,b: 0},
down : {a:-7,b: 0},
left : {a: 0,b:-7},
right: {a: 0,b: 7},
c : {c:7},
d : {d:7}
}, tank : {
up : {a:-7,b:7},
down : {a: 7,b:-7},
left : {a: 7,b:7},
right: {a:-7,b:-7},
c : {c:7},
d : {d:7}
}, merged : {
up : {a: 7,b: 7},
down : {a:-7,b:-7},
left : {a: 7,b:-7},
right: {a:-7,b: 7},
c : {c:7},
d : {d:7}
}
};
E.showMenu({ "" : {title:"LEGO Remote", back:()=>load()},
"Linear" : () => startLegoLinear(),
"Normal" : () => startLego(CONTROLS.normal),
"Tank" : () => startLego(CONTROLS.tank),
"Marged" : () => startLego(CONTROLS.merged),
}); });

View File

@ -1,7 +1,7 @@
{ "id": "legoremote", { "id": "legoremote",
"name": "LEGO Remote control", "name": "LEGO Remote control",
"shortName":"LEGO Remote", "shortName":"LEGO Remote",
"version":"0.01", "version":"0.02",
"description": "Use your Bangle.js to control LEGO models. See the README for compatibility", "description": "Use your Bangle.js to control LEGO models. See the README for compatibility",
"icon": "app.png", "icon": "app.png",
"tags": "toy,lego,bluetooth", "tags": "toy,lego,bluetooth",

View File

@ -33,3 +33,5 @@
0.26: Ensure that when redrawing, we always cancel any in-progress track draw 0.26: Ensure that when redrawing, we always cancel any in-progress track draw
0.27: Display message if no map is installed 0.27: Display message if no map is installed
0.28: Fix rounding errors 0.28: Fix rounding errors
0.29: Keep exit at bottom of menu
Speed up latLonToXY for track rendering

View File

@ -172,7 +172,6 @@ function showMenu() {
var menu = { var menu = {
"":{title:/*LANG*/"Map"}, "":{title:/*LANG*/"Map"},
"< Back": ()=> showMap(), "< Back": ()=> showMap(),
/*LANG*/"Exit": () => load(),
}; };
// If we have a GPS fix, add a menu item to center it // If we have a GPS fix, add a menu item to center it
if (fix.fix) menu[/*LANG*/"Center GPS"]=() =>{ if (fix.fix) menu[/*LANG*/"Center GPS"]=() =>{
@ -180,7 +179,6 @@ function showMenu() {
m.lon = fix.lon; m.lon = fix.lon;
showMap(); showMap();
}; };
menu = Object.assign(menu, { menu = Object.assign(menu, {
/*LANG*/"Zoom In": () =>{ /*LANG*/"Zoom In": () =>{
m.scale /= 2; m.scale /= 2;
@ -234,6 +232,7 @@ function showMenu() {
} }
}; };
} }
menu[/*LANG*/"Exit"] = () => load();
E.showMenu(menu); E.showMenu(menu);
} }

View File

@ -2,7 +2,7 @@
"id": "openstmap", "id": "openstmap",
"name": "OpenStreetMap", "name": "OpenStreetMap",
"shortName": "OpenStMap", "shortName": "OpenStMap",
"version": "0.28", "version": "0.29",
"description": "Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are. Once installed this also adds map functionality to `GPS Recorder` and `Recorder` apps", "description": "Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are. Once installed this also adds map functionality to `GPS Recorder` and `Recorder` apps",
"readme": "README.md", "readme": "README.md",
"icon": "app.png", "icon": "app.png",

View File

@ -38,17 +38,17 @@ if (m.map) {
m.lat = m.map.lat; // position of middle of screen m.lat = m.map.lat; // position of middle of screen
m.lon = m.map.lon; // position of middle of screen m.lon = m.map.lon; // position of middle of screen
} }
var CX = g.getWidth()/2;
var CY = g.getHeight()/2;
// return number of tiles drawn // return number of tiles drawn
exports.draw = function() { exports.draw = function() {
var cx = g.getWidth()/2;
var cy = g.getHeight()/2;
var p = Bangle.project({lat:m.lat,lon:m.lon}); var p = Bangle.project({lat:m.lat,lon:m.lon});
let count = 0; let count = 0;
m.maps.forEach((map,idx) => { m.maps.forEach((map,idx) => {
var d = map.scale/m.scale; var d = map.scale/m.scale;
var ix = (p.x-map.center.x)/m.scale + (map.imgx*d/2) - cx; var ix = (p.x-map.center.x)/m.scale + (map.imgx*d/2) - CX;
var iy = (map.center.y-p.y)/m.scale + (map.imgy*d/2) - cy; var iy = (map.center.y-p.y)/m.scale + (map.imgy*d/2) - CY;
var o = {}; var o = {};
var s = map.tilesize; var s = map.tilesize;
if (d!=1) { // if the two are different, add scaling if (d!=1) { // if the two are different, add scaling
@ -85,14 +85,12 @@ exports.draw = function() {
}; };
/// Convert lat/lon to pixels on the screen /// Convert lat/lon to pixels on the screen
exports.latLonToXY = function(lat, lon) { exports.latLonToXY = function(lat, lon) { "ram"
var p = Bangle.project({lat:m.lat,lon:m.lon}); var p = Bangle.project({lat:m.lat,lon:m.lon}),
var q = Bangle.project({lat:lat, lon:lon}); q = Bangle.project({lat:lat, lon:lon});
var cx = g.getWidth()/2;
var cy = g.getHeight()/2;
return { return {
x : Math.round((q.x-p.x)/m.scale + cx), x : Math.round((q.x-p.x)/m.scale + CX),
y : Math.round(cy - (q.y-p.y)/m.scale) y : Math.round(CY - (q.y-p.y)/m.scale)
}; };
}; };

View File

@ -45,3 +45,4 @@
0.36: When recording with 1 second periods, log time with one decimal. 0.36: When recording with 1 second periods, log time with one decimal.
0.37: 1 second periods + gps log => log when gps event is received, not with 0.37: 1 second periods + gps log => log when gps event is received, not with
setInterval. setInterval.
0.38: Tweaks to speed up track rendering

View File

@ -213,8 +213,10 @@ function viewTrack(filename, info) {
}); });
}; };
menu['< Back'] = () => { viewTracks(); }; menu['< Back'] = () => { viewTracks(); };
return E.showMenu(menu);
}
function plotTrack(info) { "ram" function plotTrack(info) { "ram"
function distance(lat1,long1,lat2,long2) { "ram" function distance(lat1,long1,lat2,long2) { "ram"
var x = (long1-long2) * Math.cos((lat1+lat2)*Math.PI/360); var x = (long1-long2) * Math.cos((lat1+lat2)*Math.PI/360);
var y = lat2 - lat1; var y = lat2 - lat1;
@ -222,7 +224,7 @@ function viewTrack(filename, info) {
} }
// Function to convert lat/lon to XY // Function to convert lat/lon to XY
var getMapXY; var XY;
if (info.qOSTM) { if (info.qOSTM) {
// scale map to view full track // scale map to view full track
const max = Bangle.project({lat: info.maxLat, lon: info.maxLong}); const max = Bangle.project({lat: info.maxLat, lon: info.maxLong});
@ -230,9 +232,9 @@ function viewTrack(filename, info) {
const scaleX = (max.x-min.x)/Bangle.appRect.w; const scaleX = (max.x-min.x)/Bangle.appRect.w;
const scaleY = (max.y-min.y)/Bangle.appRect.h; const scaleY = (max.y-min.y)/Bangle.appRect.h;
osm.scale = Math.ceil((scaleX > scaleY ? scaleX : scaleY)*1.1); // add 10% margin osm.scale = Math.ceil((scaleX > scaleY ? scaleX : scaleY)*1.1); // add 10% margin
getMapXY = osm.latLonToXY.bind(osm); XY = osm.latLonToXY.bind(osm);
} else { } else {
getMapXY = function(lat, lon) { "ram" XY = function(lat, lon) { "ram"
return {x:cx + Math.round((long - info.lon)*info.lfactor*info.scale), return {x:cx + Math.round((long - info.lon)*info.lfactor*info.scale),
y:cy + Math.round((info.lat - lat)*info.scale)}; y:cy + Math.round((info.lat - lat)*info.scale)};
}; };
@ -243,6 +245,7 @@ function viewTrack(filename, info) {
g.flip(); // on buffered screens, draw a not saying we're busy g.flip(); // on buffered screens, draw a not saying we're busy
g.clear(1); g.clear(1);
var s = require("Storage"); var s = require("Storage");
var G = g;
var W = g.getWidth(); var W = g.getWidth();
var H = g.getHeight(); var H = g.getHeight();
var cx = W/2; var cx = W/2;
@ -275,7 +278,7 @@ function viewTrack(filename, info) {
// now start plotting // now start plotting
var lat = +c[latIdx]; var lat = +c[latIdx];
var long = +c[lonIdx]; var long = +c[lonIdx];
var mp = getMapXY(lat, long); var mp = XY(lat, long);
g.moveTo(mp.x,mp.y); g.moveTo(mp.x,mp.y);
g.setColor("#0f0"); g.setColor("#0f0");
g.fillCircle(mp.x,mp.y,5); g.fillCircle(mp.x,mp.y,5);
@ -288,16 +291,16 @@ function viewTrack(filename, info) {
if (c[latIdx]=="")continue; if (c[latIdx]=="")continue;
lat = +c[latIdx]; lat = +c[latIdx];
long = +c[lonIdx]; long = +c[lonIdx];
mp = getMapXY(lat, long); mp = XY(lat, long);
g.lineTo(mp.x,mp.y); G.lineTo(mp.x,mp.y);
if (info.qOSTM) g.fillCircle(mp.x,mp.y,2); // make the track more visible if (info.qOSTM) G.fillCircle(mp.x,mp.y,2); // make the track more visible
var d = distance(olat,olong,lat,long); var d = distance(olat,olong,lat,long);
if (!isNaN(d)) dist+=d; if (!isNaN(d)) dist+=d;
olat = lat; olat = lat;
olong = long; olong = long;
ox = mp.x; ox = mp.x;
oy = mp.y; oy = mp.y;
if (++i > 100) { g.flip();i=0; } if (++i > 100) { G.flip();i=0; }
} }
g.setColor("#f00"); g.setColor("#f00");
g.fillCircle(ox,oy,5); g.fillCircle(ox,oy,5);
@ -313,9 +316,9 @@ function viewTrack(filename, info) {
}, isBTN3?BTN3:BTN1); }, isBTN3?BTN3:BTN1);
Bangle.drawWidgets(); Bangle.drawWidgets();
g.flip(); g.flip();
} }
function plotGraph(info, style) { "ram" function plotGraph(info, style) { "ram"
E.showMenu(); // remove menu E.showMenu(); // remove menu
E.showMessage(/*LANG*/"Calculating...",/*LANG*/"Track "+info.fn); E.showMessage(/*LANG*/"Calculating...",/*LANG*/"Track "+info.fn);
var filename = info.filename; var filename = info.filename;
@ -433,9 +436,6 @@ function viewTrack(filename, info) {
viewTrack(info.filename, info); viewTrack(info.filename, info);
}, isBTN3?BTN3:BTN1); }, isBTN3?BTN3:BTN1);
g.flip(); g.flip();
}
return E.showMenu(menu);
} }

View File

@ -2,7 +2,7 @@
"id": "recorder", "id": "recorder",
"name": "Recorder", "name": "Recorder",
"shortName": "Recorder", "shortName": "Recorder",
"version": "0.37", "version": "0.38",
"description": "Record GPS position, heart rate and more in the background, then download to your PC.", "description": "Record GPS position, heart rate and more in the background, then download to your PC.",
"icon": "app.png", "icon": "app.png",
"tags": "tool,outdoors,gps,widget,clkinfo", "tags": "tool,outdoors,gps,widget,clkinfo",