Merge branch 'Eveeeon-master'
|
@ -1,3 +1,4 @@
|
|||
0.01: Initial version
|
||||
0.02: More compact rendering & app icon
|
||||
0.03: Tell clock widgets to hide.
|
||||
0.04: Improve current time readability in light theme.
|
||||
|
|
|
@ -68,7 +68,7 @@ function drawEvent(event, y) {
|
|||
var curEventHeight = 0;
|
||||
|
||||
function drawCurrentEvents(y) {
|
||||
g.setColor("#0ff");
|
||||
g.setColor(g.theme.dark ? "#0ff" : "#0000ff");
|
||||
g.clearRect(5, y, g.getWidth() - 5, y + curEventHeight);
|
||||
curEventHeight = y;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "calclock",
|
||||
"name": "Calendar Clock",
|
||||
"shortName": "CalClock",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "Show the current and upcoming events synchronized from Gadgetbridge",
|
||||
"icon": "calclock.png",
|
||||
"type": "clock",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,43 @@
|
|||
# GPS Trekking
|
||||
|
||||
Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!
|
||||
|
||||
This app is inspired by and uses code from "GPS Navigation" and "Navigation compass".
|
||||
|
||||
## Usage
|
||||
|
||||
Tapping or button to switch to the next information display, swipe right for the menu.
|
||||
|
||||
Choose either a route or a waypoint as basis for the display.
|
||||
|
||||
After this selection and availability of a GPS fix the compass will show a blue dot for your destination and a green one for possibly available waypoints on the way.
|
||||
Waypoints are shown with name if available and distance to waypoint.
|
||||
|
||||
### Route
|
||||
|
||||
Routes can be created from .gpx files containing "trkpt" elements with this script: [createRoute.sh](createRoute.sh)
|
||||
|
||||
The resulting file needs to be uploaded to the watch and will be shown in the file selection menu.
|
||||
|
||||
The route can be mirrored to switch start and destination.
|
||||
|
||||
If the GPS position is closer than 30m to the next waypoint, the route is automatically advanced to the next waypoint.
|
||||
|
||||
### Waypoints
|
||||
|
||||
You can select a waypoint from the "Waypoints" app as destination.
|
||||
|
||||
## Calibration
|
||||
|
||||
### Altitude
|
||||
|
||||
You can correct the barometric altitude display either by manually setting a known correct value or using the GPS fix elevation as reference. This will only affect the display of altitude values.
|
||||
|
||||
### Compass
|
||||
|
||||
If the compass fallback starts to show unreliable values, you can reset the calibration in the menu. It starts to show values again after turning 360°.
|
||||
|
||||
## Widget
|
||||
|
||||
The widget keeps the sensors alive and records some very basic statics when the app is not started.
|
||||
This uses a lot of power so ensure to stop the app if you are not actively using it.
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwIjggOAApMD4AFJg4FF8AFJh/wApMf/AFJn/8ApN//wFDvfeAof774FD+fPLwYFBMAUB8fHAoUDAoJaCgfD4YFIg+D4JgCAosPAoJgCh6DBAoUfAoJgCjwFBvAFBnwFBvgFBngFBngFBvh3BnwFBvH//8eMgQFBMwX//k//5eB//wh//wAFBAQcDRoU/4EDJQfAbYbfFACYA="))
|
|
@ -0,0 +1,828 @@
|
|||
const STORAGE = require("Storage");
|
||||
const showWidgets = true;
|
||||
let numberOfSlices=4;
|
||||
|
||||
if (showWidgets){
|
||||
Bangle.loadWidgets();
|
||||
}
|
||||
|
||||
let state = WIDGETS["gpstrek"].getState();
|
||||
WIDGETS["gpstrek"].start();
|
||||
|
||||
function parseNumber(toParse){
|
||||
if (toParse.includes(".")) return parseFloat(toParse);
|
||||
return parseFloat("" + toParse + ".0");
|
||||
}
|
||||
|
||||
function parseWaypoint(filename, offset, result){
|
||||
result.lat = parseNumber(STORAGE.read(filename, offset, 11));
|
||||
result.lon = parseNumber(STORAGE.read(filename, offset += 11, 12));
|
||||
return offset + 12;
|
||||
}
|
||||
|
||||
function parseWaypointWithElevation(filename, offset, result){
|
||||
offset = parseWaypoint(filename, offset, result);
|
||||
result.alt = parseNumber(STORAGE.read(filename, offset, 6));
|
||||
return offset + 6;
|
||||
}
|
||||
|
||||
function parseWaypointWithName(filename, offset, result){
|
||||
offset = parseWaypoint(filename, offset, result);
|
||||
return parseName(filename, offset, result);
|
||||
}
|
||||
|
||||
function parseName(filename, offset, result){
|
||||
let nameLength = STORAGE.read(filename, offset, 2) - 0;
|
||||
result.name = STORAGE.read(filename, offset += 2, nameLength);
|
||||
return offset + nameLength;
|
||||
}
|
||||
|
||||
function parseWaypointWithElevationAndName(filename, offset, result){
|
||||
offset = parseWaypointWithElevation(filename, offset, result);
|
||||
return parseName(filename, offset, result);
|
||||
}
|
||||
|
||||
function getEntry(filename, offset, result){
|
||||
result.fileOffset = offset;
|
||||
let type = STORAGE.read(filename, offset++, 1);
|
||||
if (type == "") return -1;
|
||||
switch (type){
|
||||
case "A":
|
||||
offset = parseWaypoint(filename, offset, result);
|
||||
break;
|
||||
case "B":
|
||||
offset = parseWaypointWithName(filename, offset, result);
|
||||
break;
|
||||
case "C":
|
||||
offset = parseWaypointWithElevation(filename, offset, result);
|
||||
break;
|
||||
case "D":
|
||||
offset = parseWaypointWithElevationAndName(filename, offset, result);
|
||||
break;
|
||||
default:
|
||||
print("Unknown entry type", type);
|
||||
return -1;
|
||||
}
|
||||
offset++;
|
||||
|
||||
result.fileLength = offset - result.fileOffset;
|
||||
//print(result);
|
||||
return offset;
|
||||
}
|
||||
|
||||
const labels = ["N","NE","E","SE","S","SW","W","NW"];
|
||||
const loc = require("locale");
|
||||
|
||||
function matchFontSize(graphics, text, height, width){
|
||||
graphics.setFontVector(height);
|
||||
let metrics;
|
||||
let size = 1;
|
||||
while (graphics.stringMetrics(text).width > 0.90 * width){
|
||||
size -= 0.05;
|
||||
graphics.setFont("Vector",Math.floor(height*size));
|
||||
}
|
||||
}
|
||||
|
||||
function getDoubleLineSlice(title1,title2,provider1,provider2,refreshTime){
|
||||
let lastDrawn = Date.now() - Math.random()*refreshTime;
|
||||
return {
|
||||
refresh: function (){
|
||||
return Date.now() - lastDrawn > (Bangle.isLocked()?(refreshTime?refreshTime:5000):(refreshTime?refreshTime*2:10000));
|
||||
},
|
||||
draw: function (graphics, x, y, height, width){
|
||||
lastDrawn = Date.now();
|
||||
if (typeof title1 == "function") title1 = title1();
|
||||
if (typeof title2 == "function") title2 = title2();
|
||||
graphics.clearRect(x,y,x+width,y+height);
|
||||
|
||||
let value = provider1();
|
||||
matchFontSize(graphics, title1 + value, Math.floor(height*0.5), width);
|
||||
graphics.setFontAlign(-1,-1);
|
||||
graphics.drawString(title1, x+2, y);
|
||||
graphics.setFontAlign(1,-1);
|
||||
graphics.drawString(value, x+width, y);
|
||||
|
||||
value = provider2();
|
||||
matchFontSize(graphics, title2 + value, Math.floor(height*0.5), width);
|
||||
graphics.setFontAlign(-1,-1);
|
||||
graphics.drawString(title2, x+2, y+(height*0.5));
|
||||
graphics.setFontAlign(1,-1);
|
||||
graphics.drawString(value, x+width, y+(height*0.5));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getTargetSlice(targetDataSource){
|
||||
let nameIndex = 0;
|
||||
let lastDrawn = Date.now() - Math.random()*3000;
|
||||
return {
|
||||
refresh: function (){
|
||||
return Date.now() - lastDrawn > (Bangle.isLocked()?10000:3000);
|
||||
},
|
||||
draw: function (graphics, x, y, height, width){
|
||||
lastDrawn = Date.now();
|
||||
graphics.clearRect(x,y,x+width,y+height);
|
||||
if (targetDataSource.icon){
|
||||
graphics.drawImage(targetDataSource.icon,x,y + (height - 16)/2);
|
||||
x += 16;
|
||||
width -= 16;
|
||||
}
|
||||
|
||||
if (!targetDataSource.getTarget() || !targetDataSource.getStart()) return;
|
||||
|
||||
let dist = distance(targetDataSource.getStart(),targetDataSource.getTarget());
|
||||
if (isNaN(dist)) dist = Infinity;
|
||||
let bearingString = bearing(targetDataSource.getStart(),targetDataSource.getTarget()) + "°";
|
||||
if (targetDataSource.getTarget().name) {
|
||||
graphics.setFont("Vector",Math.floor(height*0.5));
|
||||
let scrolledName = (targetDataSource.getTarget().name || "").substring(nameIndex);
|
||||
if (graphics.stringMetrics(scrolledName).width > width){
|
||||
nameIndex++;
|
||||
} else {
|
||||
nameIndex = 0;
|
||||
}
|
||||
graphics.drawString(scrolledName, x+2, y);
|
||||
|
||||
let distanceString = loc.distance(dist,2);
|
||||
matchFontSize(graphics, distanceString + bearingString, height*0.5, width);
|
||||
graphics.drawString(bearingString, x+2, y+(height*0.5));
|
||||
graphics.setFontAlign(1,-1);
|
||||
graphics.drawString(distanceString, x + width, y+(height*0.5));
|
||||
} else {
|
||||
graphics.setFont("Vector",Math.floor(height*1));
|
||||
let bearingString = bearing(targetDataSource.getStart(),targetDataSource.getTarget()) + "°";
|
||||
let formattedDist = loc.distance(dist,2);
|
||||
let distNum = (formattedDist.match(/[0-9\.]+/) || [Infinity])[0];
|
||||
let size = 0.8;
|
||||
let distNumMetrics;
|
||||
while (graphics.stringMetrics(bearingString).width + (distNumMetrics = graphics.stringMetrics(distNum)).width > 0.90 * width){
|
||||
size -= 0.05;
|
||||
graphics.setFont("Vector",Math.floor(height*size));
|
||||
}
|
||||
graphics.drawString(bearingString, x+2, y + (height - distNumMetrics.height)/2);
|
||||
graphics.setFontAlign(1,-1);
|
||||
graphics.drawString(distNum, x + width, y + (height - distNumMetrics.height)/2);
|
||||
graphics.setFont("Vector",Math.floor(height*0.25));
|
||||
|
||||
graphics.setFontAlign(-1,1);
|
||||
if (targetDataSource.getProgress){
|
||||
graphics.drawString(targetDataSource.getProgress(), x + 2, y + height);
|
||||
}
|
||||
graphics.setFontAlign(1,1);
|
||||
if (!isNaN(distNum) && distNum != Infinity)
|
||||
graphics.drawString(formattedDist.match(/[a-zA-Z]+/), x + width, y + height);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function drawCompass(graphics, x, y, height, width, increment, start){
|
||||
graphics.setFont12x20();
|
||||
graphics.setFontAlign(0,-1);
|
||||
graphics.setColor(graphics.theme.fg);
|
||||
let frag = 0 - start%15;
|
||||
if (frag>0) frag = 0;
|
||||
let xpos = 0 + frag*increment;
|
||||
for (let i=start;i<=720;i+=15){
|
||||
var res = i + frag;
|
||||
if (res%90==0) {
|
||||
graphics.drawString(labels[Math.floor(res/45)%8],xpos,y+2);
|
||||
graphics.fillRect(xpos-2,Math.floor(y+height*0.6),xpos+2,Math.floor(y+height));
|
||||
} else if (res%45==0) {
|
||||
graphics.drawString(labels[Math.floor(res/45)%8],xpos,y+2);
|
||||
graphics.fillRect(xpos-2,Math.floor(y+height*0.75),xpos+2,Math.floor(y+height));
|
||||
} else if (res%15==0) {
|
||||
graphics.fillRect(xpos,Math.floor(y+height*0.9),xpos+1,Math.floor(y+height));
|
||||
}
|
||||
xpos+=increment*15;
|
||||
if (xpos > width + 20) break;
|
||||
}
|
||||
}
|
||||
|
||||
function getCompassSlice(compassDataSource){
|
||||
let lastDrawn = Date.now() - Math.random()*2000;
|
||||
const buffers = 4;
|
||||
let buf = [];
|
||||
return {
|
||||
refresh : function (){return Bangle.isLocked()?(Date.now() - lastDrawn > 2000):true;},
|
||||
draw: function (graphics, x,y,height,width){
|
||||
lastDrawn = Date.now();
|
||||
const max = 180;
|
||||
const increment=width/max;
|
||||
|
||||
graphics.clearRect(x,y,x+width,y+height);
|
||||
|
||||
var start = compassDataSource.getCourse() - 90;
|
||||
if (isNaN(compassDataSource.getCourse())) start = -90;
|
||||
if (start<0) start+=360;
|
||||
start = start % 360;
|
||||
|
||||
if (state.acc && compassDataSource.getCourseType() == "MAG"){
|
||||
drawCompass(graphics,0,y+width*0.05,height-width*0.05,width,increment,start);
|
||||
} else {
|
||||
drawCompass(graphics,0,y,height,width,increment,start);
|
||||
}
|
||||
|
||||
|
||||
if (compassDataSource.getPoints){
|
||||
for (let p of compassDataSource.getPoints()){
|
||||
var bpos = p.bearing - compassDataSource.getCourse();
|
||||
if (bpos>180) bpos -=360;
|
||||
if (bpos<-180) bpos +=360;
|
||||
bpos+=120;
|
||||
let min = 0;
|
||||
let max = 180;
|
||||
if (bpos<=min){
|
||||
bpos = Math.floor(width*0.05);
|
||||
} else if (bpos>=max) {
|
||||
bpos = Math.ceil(width*0.95);
|
||||
} else {
|
||||
bpos=Math.round(bpos*increment);
|
||||
}
|
||||
graphics.setColor(p.color);
|
||||
graphics.fillCircle(bpos,y+height-12,Math.floor(width*0.03));
|
||||
}
|
||||
}
|
||||
if (compassDataSource.getMarkers){
|
||||
for (let m of compassDataSource.getMarkers()){
|
||||
g.setColor(m.fillcolor);
|
||||
let mpos = m.xpos * width;
|
||||
if (m.xpos < 0.05) mpos = Math.floor(width*0.05);
|
||||
if (m.xpos > 0.95) mpos = Math.ceil(width*0.95);
|
||||
g.fillPoly(triangle(mpos,y+height-m.height, m.height, m.width));
|
||||
g.setColor(m.linecolor);
|
||||
g.drawPoly(triangle(mpos,y+height-m.height, m.height, m.width),true);
|
||||
}
|
||||
}
|
||||
graphics.setColor(g.theme.fg);
|
||||
graphics.fillRect(x,y,Math.floor(width*0.05),y+height);
|
||||
graphics.fillRect(Math.ceil(width*0.95),y,width,y+height);
|
||||
if (state.acc && compassDataSource.getCourseType() == "MAG") {
|
||||
let xh = E.clip(width*0.5-height/2+(((state.acc.x+1)/2)*height),width*0.5 - height/2, width*0.5 + height/2);
|
||||
let yh = E.clip(y+(((state.acc.y+1)/2)*height),y,y+height);
|
||||
|
||||
graphics.fillRect(width*0.5 - height/2, y, width*0.5 + height/2, y + Math.floor(width*0.05));
|
||||
|
||||
graphics.setColor(g.theme.bg);
|
||||
graphics.drawLine(width*0.5 - 5, y, width*0.5 - 5, y + Math.floor(width*0.05));
|
||||
graphics.drawLine(width*0.5 + 5, y, width*0.5 + 5, y + Math.floor(width*0.05));
|
||||
graphics.fillRect(xh-1,y,xh+1,y+Math.floor(width*0.05));
|
||||
|
||||
let left = Math.floor(width*0.05);
|
||||
let right = Math.ceil(width*0.95);
|
||||
graphics.drawLine(0,y+height/2-5,left,y+height/2-5);
|
||||
graphics.drawLine(right,y+height/2-5,x+width,y+height/2-5);
|
||||
graphics.drawLine(0,y+height/2+5,left,y+height/2+5);
|
||||
graphics.drawLine(right,y+height/2+5,x+width,y+height/2+5);
|
||||
graphics.fillRect(0,yh-1,left,yh+1);
|
||||
graphics.fillRect(right,yh-1,x+width,yh+1);
|
||||
}
|
||||
graphics.setColor(g.theme.fg);
|
||||
graphics.drawRect(Math.floor(width*0.05),y,Math.ceil(width*0.95),y+height);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function radians(a) {
|
||||
return a*Math.PI/180;
|
||||
}
|
||||
|
||||
function degrees(a) {
|
||||
var d = a*180/Math.PI;
|
||||
return (d+360)%360;
|
||||
}
|
||||
|
||||
function bearing(a,b){
|
||||
if (!a || !b || !a.lon || !a.lat || !b.lon || !b.lat) return Infinity;
|
||||
var delta = radians(b.lon-a.lon);
|
||||
var alat = radians(a.lat);
|
||||
var blat = radians(b.lat);
|
||||
var y = Math.sin(delta) * Math.cos(blat);
|
||||
var x = Math.cos(alat)*Math.sin(blat) -
|
||||
Math.sin(alat)*Math.cos(blat)*Math.cos(delta);
|
||||
return Math.round(degrees(Math.atan2(y, x)));
|
||||
}
|
||||
|
||||
function distance(a,b){
|
||||
if (!a || !b || !a.lon || !a.lat || !b.lon || !b.lat) return Infinity;
|
||||
var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2));
|
||||
var y = radians(b.lat-a.lat);
|
||||
return Math.round(Math.sqrt(x*x + y*y) * 6371000);
|
||||
}
|
||||
|
||||
function triangle (x, y, width, height){
|
||||
return [
|
||||
Math.round(x),Math.round(y),
|
||||
Math.round(x+width * 0.5), Math.round(y+height),
|
||||
Math.round(x-width * 0.5), Math.round(y+height)
|
||||
];
|
||||
}
|
||||
|
||||
function setButtons(){
|
||||
Bangle.setUI("leftright", (dir)=>{
|
||||
if (dir < 0) {
|
||||
nextScreen();
|
||||
} else if (dir > 0) {
|
||||
switchMenu();
|
||||
} else {
|
||||
nextScreen();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getApproxFileSize(name){
|
||||
let currentStart = STORAGE.getStats().totalBytes;
|
||||
let currentSize = 0;
|
||||
for (let i = currentStart; i > 500; i/=2){
|
||||
let currentDiff = i;
|
||||
//print("Searching", currentDiff);
|
||||
while (STORAGE.read(name, currentSize+currentDiff, 1) == ""){
|
||||
//print("Loop", currentDiff);
|
||||
currentDiff = Math.ceil(currentDiff/2);
|
||||
}
|
||||
i = currentDiff*2;
|
||||
currentSize += currentDiff;
|
||||
}
|
||||
return currentSize;
|
||||
}
|
||||
|
||||
function parseRouteData(filename, progressMonitor){
|
||||
let routeInfo = {};
|
||||
|
||||
routeInfo.filename = filename;
|
||||
routeInfo.refs = [];
|
||||
|
||||
let c = {};
|
||||
let scanOffset = 0;
|
||||
routeInfo.length = 0;
|
||||
routeInfo.count = 0;
|
||||
routeInfo.mirror = false;
|
||||
let lastSeenWaypoint;
|
||||
let lastSeenAlt;
|
||||
let waypoint = {};
|
||||
|
||||
routeInfo.up = 0;
|
||||
routeInfo.down = 0;
|
||||
|
||||
let size = getApproxFileSize(filename);
|
||||
|
||||
while ((scanOffset = getEntry(filename, scanOffset, waypoint)) > 0) {
|
||||
if (routeInfo.count % 5 == 0) progressMonitor(scanOffset, "Loading", size);
|
||||
if (lastSeenWaypoint){
|
||||
routeInfo.length += distance(lastSeenWaypoint, waypoint);
|
||||
|
||||
let diff = waypoint.alt - lastSeenAlt;
|
||||
//print("Distance", routeInfo.length, "alt", lastSeenAlt, waypoint.alt, diff);
|
||||
if (waypoint.alt && lastSeenAlt && diff > 3){
|
||||
if (lastSeenAlt < waypoint.alt){
|
||||
//print("Up", diff);
|
||||
routeInfo.up += diff;
|
||||
} else {
|
||||
//print("Down", diff);
|
||||
routeInfo.down += diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
routeInfo.count++;
|
||||
routeInfo.refs.push(waypoint.fileOffset);
|
||||
lastSeenWaypoint = waypoint;
|
||||
if (!isNaN(waypoint.alt)) lastSeenAlt = waypoint.alt;
|
||||
waypoint = {};
|
||||
}
|
||||
|
||||
set(routeInfo, 0);
|
||||
return routeInfo;
|
||||
}
|
||||
|
||||
function hasPrev(route){
|
||||
if (route.mirror) return route.index < (route.count - 1);
|
||||
return route.index > 0;
|
||||
}
|
||||
|
||||
function hasNext(route){
|
||||
if (route.mirror) return route.index > 0;
|
||||
return route.index < (route.count - 1);
|
||||
}
|
||||
|
||||
function next(route){
|
||||
if (!hasNext(route)) return;
|
||||
if (route.mirror) set(route, --route.index);
|
||||
if (!route.mirror) set(route, ++route.index);
|
||||
}
|
||||
|
||||
function set(route, index){
|
||||
route.currentWaypoint = {};
|
||||
route.index = index;
|
||||
getEntry(route.filename, route.refs[index], route.currentWaypoint);
|
||||
}
|
||||
|
||||
function prev(route){
|
||||
if (!hasPrev(route)) return;
|
||||
if (route.mirror) set(route, ++route.index);
|
||||
if (!route.mirror) set(route, --route.index);
|
||||
}
|
||||
|
||||
let lastMirror;
|
||||
let cachedLast;
|
||||
|
||||
function getLast(route){
|
||||
let wp = {};
|
||||
if (lastMirror != route.mirror){
|
||||
if (route.mirror) getEntry(route.filename, route.refs[0], wp);
|
||||
if (!route.mirror) getEntry(route.filename, route.refs[route.count - 1], wp);
|
||||
lastMirror = route.mirror;
|
||||
cachedLast = wp;
|
||||
}
|
||||
return cachedLast;
|
||||
}
|
||||
|
||||
function removeMenu(){
|
||||
E.showMenu();
|
||||
switchNav();
|
||||
}
|
||||
|
||||
function showProgress(progress, title, max){
|
||||
//print("Progress",progress,max)
|
||||
let message = title? title: "Loading";
|
||||
if (max){
|
||||
message += " " + E.clip((progress/max*100),0,100).toFixed(0) +"%";
|
||||
} else {
|
||||
let dots = progress % 4;
|
||||
for (let i = 0; i < dots; i++) message += ".";
|
||||
for (let i = dots; i < 4; i++) message += " ";
|
||||
}
|
||||
E.showMessage(message);
|
||||
}
|
||||
|
||||
function handleLoading(c){
|
||||
E.showMenu();
|
||||
state.route = parseRouteData(c, showProgress);
|
||||
state.waypoint = null;
|
||||
removeMenu();
|
||||
state.route.mirror = false;
|
||||
}
|
||||
|
||||
function showRouteSelector (){
|
||||
var menu = {
|
||||
"" : {
|
||||
back : showRouteMenu,
|
||||
}
|
||||
};
|
||||
|
||||
for (let c of STORAGE.list((/\.trf$/))){
|
||||
let file = c;
|
||||
menu[file] = ()=>{handleLoading(file);};
|
||||
}
|
||||
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function showRouteMenu(){
|
||||
var menu = {
|
||||
"" : {
|
||||
"title" : "Route",
|
||||
back : showMenu,
|
||||
},
|
||||
"Select file" : showRouteSelector
|
||||
};
|
||||
|
||||
if (state.route){
|
||||
menu.Mirror = {
|
||||
value: state && state.route && !!state.route.mirror || false,
|
||||
onchange: v=>{
|
||||
state.route.mirror = v;
|
||||
}
|
||||
};
|
||||
menu['Select closest waypoint'] = function () {
|
||||
if (state.currentPos && state.currentPos.lat){
|
||||
setClosestWaypoint(state.route, null, showProgress); removeMenu();
|
||||
} else {
|
||||
E.showAlert("No position").then(()=>{E.showMenu(menu);});
|
||||
}
|
||||
};
|
||||
menu['Select closest waypoint (not visited)'] = function () {
|
||||
if (state.currentPos && state.currentPos.lat){
|
||||
setClosestWaypoint(state.route, state.route.index, showProgress); removeMenu();
|
||||
} else {
|
||||
E.showAlert("No position").then(()=>{E.showMenu(menu);});
|
||||
}
|
||||
};
|
||||
menu['Select waypoint'] = {
|
||||
value : state.route.index,
|
||||
min:1,max:state.route.count,step:1,
|
||||
onchange : v => { set(state.route, v-1); }
|
||||
};
|
||||
menu['Select waypoint as current position'] = function (){
|
||||
state.currentPos.lat = state.route.currentWaypoint.lat;
|
||||
state.currentPos.lon = state.route.currentWaypoint.lon;
|
||||
state.currentPos.alt = state.route.currentWaypoint.alt;
|
||||
removeMenu();
|
||||
};
|
||||
}
|
||||
|
||||
if (state.route && hasPrev(state.route))
|
||||
menu['Previous waypoint'] = function() { prev(state.route); removeMenu(); };
|
||||
if (state.route && hasNext(state.route))
|
||||
menu['Next waypoint'] = function() { next(state.route); removeMenu(); };
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function showWaypointSelector(){
|
||||
let waypoints = require("waypoints").load();
|
||||
var menu = {
|
||||
"" : {
|
||||
back : showWaypointMenu,
|
||||
}
|
||||
};
|
||||
|
||||
for (let c in waypoints){
|
||||
menu[waypoints[c].name] = function (){
|
||||
state.waypoint = waypoints[c];
|
||||
state.waypointIndex = c;
|
||||
state.route = null;
|
||||
removeMenu();
|
||||
};
|
||||
}
|
||||
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function showCalibrationMenu(){
|
||||
let menu = {
|
||||
"" : {
|
||||
"title" : "Calibration",
|
||||
back : showMenu,
|
||||
},
|
||||
"Barometer (GPS)" : ()=>{
|
||||
if (!state.currentPos || isNaN(state.currentPos.alt)){
|
||||
E.showAlert("No GPS altitude").then(()=>{E.showMenu(menu);});
|
||||
} else {
|
||||
state.calibAltDiff = state.altitude - state.currentPos.alt;
|
||||
E.showAlert("Calibrated Altitude Difference: " + state.calibAltDiff.toFixed(0)).then(()=>{removeMenu();});
|
||||
}
|
||||
},
|
||||
"Barometer (Manual)" : {
|
||||
value : Math.round(state.currentPos && (state.currentPos.alt != undefined && !isNaN(state.currentPos.alt)) ? state.currentPos.alt: state.altitude),
|
||||
min:-2000,max: 10000,step:1,
|
||||
onchange : v => { state.calibAltDiff = state.altitude - v; }
|
||||
},
|
||||
"Reset Compass" : ()=>{ Bangle.resetCompass(); removeMenu();},
|
||||
};
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function showWaypointMenu(){
|
||||
let menu = {
|
||||
"" : {
|
||||
"title" : "Waypoint",
|
||||
back : showMenu,
|
||||
},
|
||||
"Select waypoint" : showWaypointSelector,
|
||||
};
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function showMenu(){
|
||||
var mainmenu = {
|
||||
"" : {
|
||||
"title" : "Main",
|
||||
back : removeMenu,
|
||||
},
|
||||
"Route" : showRouteMenu,
|
||||
"Waypoint" : showWaypointMenu,
|
||||
"Calibration": showCalibrationMenu,
|
||||
"Start" : ()=>{ E.showPrompt("Start?").then((v)=>{ if (v) {state.active = true; removeMenu();} else {E.showMenu(mainmenu);}});},
|
||||
"Stop" : ()=>{ E.showPrompt("Stop?").then((v)=>{ if (v) {WIDGETS["gpstrek"].stop(); removeMenu();} else {E.showMenu(mainmenu);}});},
|
||||
"Reset" : ()=>{ E.showPrompt("Do Reset?").then((v)=>{ if (v) {WIDGETS["gpstrek"].resetState(); removeMenu();} else {E.showMenu(mainmenu);}});},
|
||||
"Slices" : {
|
||||
value : numberOfSlices,
|
||||
min:1,max:6,step:1,
|
||||
onchange : v => { setNumberOfSlices(v); }
|
||||
},
|
||||
};
|
||||
|
||||
E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
let scheduleDraw = true;
|
||||
|
||||
function switchMenu(){
|
||||
screen = 0;
|
||||
scheduleDraw = false;
|
||||
showMenu();
|
||||
}
|
||||
|
||||
function drawInTimeout(){
|
||||
setTimeout(()=>{
|
||||
draw();
|
||||
if (scheduleDraw)
|
||||
setTimeout(drawInTimeout, 0);
|
||||
},0);
|
||||
}
|
||||
|
||||
function switchNav(){
|
||||
if (!screen) screen = 1;
|
||||
setButtons();
|
||||
scheduleDraw = true;
|
||||
drawInTimeout();
|
||||
}
|
||||
|
||||
function nextScreen(){
|
||||
screen++;
|
||||
if (screen > maxScreens){
|
||||
screen = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function setClosestWaypoint(route, startindex, progress){
|
||||
if (startindex >= state.route.count) startindex = state.route.count - 1;
|
||||
if (!state.currentPos.lat){
|
||||
set(route, startindex);
|
||||
return;
|
||||
}
|
||||
let minDist = 100000000000000;
|
||||
let minIndex = 0;
|
||||
for (let i = startindex?startindex:0; i < route.count - 1; i++){
|
||||
if (progress && (i % 5 == 0)) progress(i-(startindex?startindex:0), "Searching", route.count);
|
||||
let wp = {};
|
||||
getEntry(route.filename, route.refs[i], wp);
|
||||
let curDist = distance(state.currentPos, wp);
|
||||
if (curDist < minDist){
|
||||
minDist = curDist;
|
||||
minIndex = i;
|
||||
} else {
|
||||
if (startindex) break;
|
||||
}
|
||||
}
|
||||
set(route, minIndex);
|
||||
}
|
||||
|
||||
let screen = 1;
|
||||
|
||||
const compassSliceData = {
|
||||
getCourseType: function(){
|
||||
return (state.currentPos && state.currentPos.course) ? "GPS" : "MAG";
|
||||
},
|
||||
getCourse: function (){
|
||||
if(compassSliceData.getCourseType() == "GPS") return state.currentPos.course;
|
||||
return state.compassHeading?state.compassHeading:undefined;
|
||||
},
|
||||
getPoints: function (){
|
||||
let points = [];
|
||||
if (state.currentPos && state.currentPos.lon && state.route && state.route.currentWaypoint){
|
||||
points.push({bearing:bearing(state.currentPos, state.route.currentWaypoint), color:"#0f0"});
|
||||
}
|
||||
if (state.currentPos && state.currentPos.lon && state.route){
|
||||
points.push({bearing:bearing(state.currentPos, getLast(state.route)), color:"#00f"});
|
||||
}
|
||||
return points;
|
||||
},
|
||||
getMarkers: function (){
|
||||
return [{xpos:0.5, width:10, height:10, linecolor:g.theme.fg, fillcolor:"#f00"}];
|
||||
}
|
||||
};
|
||||
|
||||
const waypointData = {
|
||||
icon: atob("EBCBAAAAAAAAAAAAcIB+zg/uAe4AwACAAAAAAAAAAAAAAAAA"),
|
||||
getProgress: function() {
|
||||
return (state.route.index + 1) + "/" + state.route.count;
|
||||
},
|
||||
getTarget: function (){
|
||||
if (distance(state.currentPos,state.route.currentWaypoint) < 30 && hasNext(state.route)){
|
||||
next(state.route);
|
||||
Bangle.buzz(1000);
|
||||
}
|
||||
return state.route.currentWaypoint;
|
||||
},
|
||||
getStart: function (){
|
||||
return state.currentPos;
|
||||
}
|
||||
};
|
||||
|
||||
const finishData = {
|
||||
icon: atob("EBABAAA/4DmgJmAmYDmgOaAmYD/gMAAwADAAMAAwAAAAAAA="),
|
||||
getTarget: function (){
|
||||
if (state.route) return getLast(state.route);
|
||||
if (state.waypoint) return state.waypoint;
|
||||
},
|
||||
getStart: function (){
|
||||
return state.currentPos;
|
||||
}
|
||||
};
|
||||
|
||||
let sliceHeight;
|
||||
function setNumberOfSlices(number){
|
||||
numberOfSlices = number;
|
||||
sliceHeight = Math.floor((g.getHeight()-(showWidgets?24:0))/numberOfSlices);
|
||||
}
|
||||
|
||||
let slices = [];
|
||||
let maxScreens = 1;
|
||||
setNumberOfSlices(3);
|
||||
|
||||
let compassSlice = getCompassSlice(compassSliceData);
|
||||
let waypointSlice = getTargetSlice(waypointData);
|
||||
let finishSlice = getTargetSlice(finishData);
|
||||
let eleSlice = getDoubleLineSlice("Up","Down",()=>{
|
||||
return loc.distance(state.up,3) + "/" + (state.route ? loc.distance(state.route.up,3):"---");
|
||||
},()=>{
|
||||
return loc.distance(state.down,3) + "/" + (state.route ? loc.distance(state.route.down,3): "---");
|
||||
});
|
||||
|
||||
let statusSlice = getDoubleLineSlice("Speed","Alt",()=>{
|
||||
let speed = 0;
|
||||
if (state.currentPos && state.currentPos.speed) speed = state.currentPos.speed;
|
||||
return loc.speed(speed,2);
|
||||
},()=>{
|
||||
let alt = Infinity;
|
||||
if (!isNaN(state.altitude)){
|
||||
alt = isNaN(state.calibAltDiff) ? state.altitude : (state.altitude - state.calibAltDiff);
|
||||
}
|
||||
if (state.currentPos && state.currentPos.alt) alt = state.currentPos.alt;
|
||||
return loc.distance(alt,3);
|
||||
});
|
||||
|
||||
let status2Slice = getDoubleLineSlice("Compass","GPS",()=>{
|
||||
return (state.compassHeading?Math.round(state.compassHeading):"---") + "°";
|
||||
},()=>{
|
||||
let course = "---°";
|
||||
if (state.currentPos && state.currentPos.course) course = state.currentPos.course + "°";
|
||||
return course;
|
||||
},200);
|
||||
|
||||
let healthSlice = getDoubleLineSlice("Heart","Steps",()=>{
|
||||
return state.bpm;
|
||||
},()=>{
|
||||
return state.steps;
|
||||
});
|
||||
|
||||
let system2Slice = getDoubleLineSlice("Bat","",()=>{
|
||||
return (Bangle.isCharging()?"+":"") + E.getBattery().toFixed(0)+"% " + NRF.getBattery().toFixed(2) + "V";
|
||||
},()=>{
|
||||
return "";
|
||||
});
|
||||
|
||||
let systemSlice = getDoubleLineSlice("RAM","Storage",()=>{
|
||||
let ram = process.memory(false);
|
||||
return ((ram.blocksize * ram.free)/1024).toFixed(0)+"kB";
|
||||
},()=>{
|
||||
return (STORAGE.getFree()/1024).toFixed(0)+"kB";
|
||||
});
|
||||
|
||||
function updateSlices(){
|
||||
slices = [];
|
||||
slices.push(compassSlice);
|
||||
|
||||
if (state.currentPos && state.currentPos.lat && state.route && state.route.currentWaypoint && state.route.index < state.route.count - 1) {
|
||||
slices.push(waypointSlice);
|
||||
}
|
||||
if (state.currentPos && state.currentPos.lat && (state.route || state.waypoint)) {
|
||||
slices.push(finishSlice);
|
||||
}
|
||||
if ((state.route && state.route.down !== undefined) || state.down != undefined) {
|
||||
slices.push(eleSlice);
|
||||
}
|
||||
slices.push(statusSlice);
|
||||
slices.push(status2Slice);
|
||||
slices.push(healthSlice);
|
||||
slices.push(systemSlice);
|
||||
slices.push(system2Slice);
|
||||
maxScreens = Math.ceil(slices.length/numberOfSlices);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
g.clearRect(0,(showWidgets ? 24 : 0), g.getWidth(),g.getHeight());
|
||||
}
|
||||
let lastDrawnScreen;
|
||||
let firstDraw = true;
|
||||
|
||||
function draw(){
|
||||
if (!screen) return;
|
||||
let ypos = showWidgets ? 24 : 0;
|
||||
|
||||
let firstSlice = (screen-1)*numberOfSlices;
|
||||
|
||||
updateSlices();
|
||||
|
||||
let force = lastDrawnScreen != screen || firstDraw;
|
||||
if (force){
|
||||
clear();
|
||||
if (showWidgets){
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
}
|
||||
lastDrawnScreen = screen;
|
||||
|
||||
for (let slice of slices.slice(firstSlice,firstSlice + numberOfSlices)) {
|
||||
g.reset();
|
||||
if (!slice.refresh || slice.refresh() || force) slice.draw(g,0,ypos,sliceHeight,g.getWidth());
|
||||
ypos += sliceHeight+1;
|
||||
g.drawLine(0,ypos-1,g.getWidth(),ypos-1);
|
||||
}
|
||||
firstDraw = false;
|
||||
}
|
||||
|
||||
|
||||
switchNav();
|
||||
|
||||
g.clear();
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
[ -z "$1" ] && echo Give gpx file name
|
||||
|
||||
|
||||
xmlstarlet select -t -m '//_:trkpt' \
|
||||
--if '_:name and _:ele' -o D \
|
||||
--elif '_:ele and not(_:name)' -o C \
|
||||
--elif 'not(_:ele) and _:name' -o B \
|
||||
--else -o A -b \
|
||||
-v 'format-number(@lat,"+00.0000000;-00.0000000")' \
|
||||
-v 'format-number(@lon,"+000.0000000;-000.0000000")' \
|
||||
--if '_:ele' -v 'format-number(_:ele,"+00000;-00000")' -b \
|
||||
--if _:name -v 'format-number(string-length(_:name),"00")' -v '_:name' -b \
|
||||
-n "$1" | iconv -f utf8 -t iso8859-1 > "$(basename "$1" | sed -e "s|.gpx||").trf"
|
After Width: | Height: | Size: 851 B |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "gpstrek",
|
||||
"name": "GPS Trekking",
|
||||
"version": "0.01",
|
||||
"description": "Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screen1.png"},{"url":"screen2.png"},{"url":"screen3.png"},{"url":"screen4.png"}],
|
||||
"tags": "tool,outdoors,gps",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"dependencies" : { "waypoints":"type" },
|
||||
"storage": [
|
||||
{"name":"gpstrek.app.js","url":"app.js"},
|
||||
{"name":"gpstrek.wid.js","url":"widget.js"},
|
||||
{"name":"gpstrek.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 3.0 KiB |
|
@ -0,0 +1,129 @@
|
|||
(() => {
|
||||
const STORAGE=require('Storage');
|
||||
let state = STORAGE.readJSON("gpstrek.state.json")||{};
|
||||
|
||||
function saveState(){
|
||||
state.saved = Date.now();
|
||||
STORAGE.writeJSON("gpstrek.state.json", state);
|
||||
}
|
||||
|
||||
E.on("kill",()=>{
|
||||
if (state.active){
|
||||
saveState();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function onPulse(e){
|
||||
state.bpm = e.bpm;
|
||||
}
|
||||
|
||||
function onGPS(fix) {
|
||||
if(fix.fix) state.currentPos = fix;
|
||||
}
|
||||
|
||||
Bangle.on('accel', function(e) {
|
||||
state.acc = e;
|
||||
});
|
||||
|
||||
function onMag(e) {
|
||||
if (!state.compassHeading) state.compassHeading = e.heading;
|
||||
|
||||
//if (a+180)mod 360 == b then
|
||||
//return (a+b)/2 mod 360 and ((a+b)/2 mod 360) + 180 (they are both the solution, so you may choose one depending if you prefer counterclockwise or clockwise direction)
|
||||
//else
|
||||
//return arctan( (sin(a)+sin(b)) / (cos(a)+cos(b) )
|
||||
|
||||
/*
|
||||
let average;
|
||||
let a = radians(compassHeading);
|
||||
let b = radians(e.heading);
|
||||
if ((a+180) % 360 == b){
|
||||
average = ((a+b)/2 % 360); //can add 180 depending on rotation
|
||||
} else {
|
||||
average = Math.atan( (Math.sin(a)+Math.sin(b))/(Math.cos(a)+Math.cos(b)) );
|
||||
}
|
||||
print("Angle",compassHeading,e.heading, average);
|
||||
compassHeading = (compassHeading + degrees(average)) % 360;
|
||||
*/
|
||||
state.compassHeading = Math.round(e.heading);
|
||||
}
|
||||
|
||||
function onStep(e) {
|
||||
state.steps++;
|
||||
}
|
||||
|
||||
function onPressure(e) {
|
||||
state.pressure = e.pressure;
|
||||
|
||||
if (!state.altitude){
|
||||
state.altitude = e.altitude;
|
||||
state.up = 0;
|
||||
state.down = 0;
|
||||
}
|
||||
let diff = state.altitude - e.altitude;
|
||||
if (Math.abs(diff) > 3){
|
||||
if (diff > 0){
|
||||
state.up += diff;
|
||||
} else {
|
||||
state.down -= diff;
|
||||
}
|
||||
state.altitude = e.altitude;
|
||||
}
|
||||
}
|
||||
|
||||
function start(){
|
||||
Bangle.on('GPS', onGPS);
|
||||
Bangle.on("HRM", onPulse);
|
||||
Bangle.on("mag", onMag);
|
||||
Bangle.on("step", onStep);
|
||||
Bangle.on("pressure", onPressure);
|
||||
|
||||
Bangle.setGPSPower(1, "gpstrek");
|
||||
Bangle.setHRMPower(1, "gpstrek");
|
||||
Bangle.setCompassPower(1, "gpstrek");
|
||||
Bangle.setBarometerPower(1, "gpstrek");
|
||||
state.active = true;
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
|
||||
function stop(){
|
||||
state.active = false;
|
||||
saveState();
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
|
||||
function initState(){
|
||||
//cleanup volatile state here
|
||||
state.currentPos={};
|
||||
state.steps = Bangle.getStepCount();
|
||||
state.calibAltDiff = 0;
|
||||
state.up = 0;
|
||||
state.down = 0;
|
||||
}
|
||||
|
||||
if (state.saved && state.saved < Date.now() - 60000){
|
||||
initState();
|
||||
}
|
||||
|
||||
if (state.active){
|
||||
start();
|
||||
}
|
||||
|
||||
WIDGETS["gpstrek"]={
|
||||
area:"tl",
|
||||
width:state.active?24:0,
|
||||
resetState: initState,
|
||||
getState: function() {
|
||||
return state;
|
||||
},
|
||||
start:start,
|
||||
stop:stop,
|
||||
draw:function() {
|
||||
if (state.active){
|
||||
g.reset();
|
||||
g.drawImage(atob("GBiBAAAAAAAAAAAYAAAYAAAYAAA8AAA8AAB+AAB+AADbAADbAAGZgAGZgAMYwAMYwAcY4AYYYA5+cA3/sB/D+B4AeBAACAAAAAAAAA=="), this.x, this.y);
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
|
@ -9,3 +9,6 @@
|
|||
Show/Hide widgets with swipe up or down
|
||||
0.08: Use default Bangle formatter for booleans
|
||||
0.09: Support new fast app switching
|
||||
0.10: Fix clock not correctly refreshing when drawing in timeouts option is not on
|
||||
0.11: Additional option in customizer to force drawing directly
|
||||
Fix some problems in handling timeouts
|
||||
|
|
|
@ -1,8 +1,21 @@
|
|||
let unlockedDrawInterval = [];
|
||||
let lockedDrawInterval = [];
|
||||
let showWidgets = false;
|
||||
let firstDraw = true;
|
||||
|
||||
{
|
||||
let x = g.getWidth()/2;
|
||||
let y = g.getHeight()/2;
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(x-49, y-19, x+49, y+19);
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawRect(x-50, y-20, x+50, y+20);
|
||||
y -= 4;
|
||||
x -= 4*6;
|
||||
g.setFont("6x8");
|
||||
g.setFontAlign(-1,-1);
|
||||
g.drawString("Loading...", x, y);
|
||||
|
||||
let watchface = require("Storage").readJSON("imageclock.face.json");
|
||||
let watchfaceResources = require("Storage").readJSON("imageclock.resources.json");
|
||||
let precompiledJs = eval(require("Storage").read("imageclock.draw.js"));
|
||||
|
@ -12,37 +25,37 @@ let showWidgets = false;
|
|||
|
||||
let startPerfLog = () => {};
|
||||
let endPerfLog = () => {};
|
||||
let printPerfLog = () => print("Deactivated");
|
||||
let resetPerfLog = () => {performanceLog = {};};
|
||||
Bangle.printPerfLog = () => {print("Deactivated");};
|
||||
Bangle.resetPerfLog = () => {performanceLog = {};};
|
||||
|
||||
let colormap={
|
||||
"#000":0,
|
||||
"#00f":1,
|
||||
"#0f0":2,
|
||||
"#0ff":3,
|
||||
"#f00":4,
|
||||
"#f0f":5,
|
||||
"#ff0":6,
|
||||
"#fff":7
|
||||
"#000":0,
|
||||
"#00f":1,
|
||||
"#0f0":2,
|
||||
"#0ff":3,
|
||||
"#f00":4,
|
||||
"#f0f":5,
|
||||
"#ff0":6,
|
||||
"#fff":7
|
||||
};
|
||||
|
||||
let palette = new Uint16Array([
|
||||
0x0000, //black #000
|
||||
0x001f, //blue #00f
|
||||
0x07e0, //green #0f0
|
||||
0x07ff, //cyan #0ff
|
||||
0xf800, //red #f00
|
||||
0xf81f, //magenta #f0f
|
||||
0xffe0, //yellow #ff0
|
||||
0xffff, //white #fff
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0x0000, //black #000
|
||||
0x001f, //blue #00f
|
||||
0x07e0, //green #0f0
|
||||
0x07ff, //cyan #0ff
|
||||
0xf800, //red #f00
|
||||
0xf81f, //magenta #f0f
|
||||
0xffe0, //yellow #ff0
|
||||
0xffff, //white #fff
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
0xffff, //white
|
||||
]);
|
||||
|
||||
let p0 = g;
|
||||
|
@ -67,7 +80,7 @@ let showWidgets = false;
|
|||
performanceLog.count[name]++;
|
||||
};
|
||||
|
||||
printPerfLog = function(){
|
||||
Bangle.printPerfLog = function(){
|
||||
let result = "";
|
||||
let keys = [];
|
||||
for (let c in performanceLog.cum){
|
||||
|
@ -79,23 +92,33 @@ let showWidgets = false;
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
let delayTimeouts = [];
|
||||
|
||||
function delay(t) {
|
||||
startPerfLog("loadFunctions");
|
||||
|
||||
let delayTimeouts = {};
|
||||
let timeoutCount = 0;
|
||||
|
||||
let delay = function(t) {
|
||||
return new Promise(function (resolve) {
|
||||
delayTimeouts.push(setTimeout(resolve, t));
|
||||
const i = timeoutCount++;
|
||||
let timeout = setTimeout(()=>{
|
||||
resolve();
|
||||
delete delayTimeouts[i];
|
||||
}, t);
|
||||
delayTimeouts[i] = timeout;
|
||||
//print("Add delay timeout", delayTimeouts);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function cleanupDelays(){
|
||||
let cleanupDelays = function(){
|
||||
//print("Cleanup delays", delayTimeouts);
|
||||
for (let t of delayTimeouts){
|
||||
clearTimeout(t);
|
||||
}
|
||||
delayTimeouts = [];
|
||||
}
|
||||
delayTimeouts = {};
|
||||
};
|
||||
|
||||
function prepareImg(resource){
|
||||
let prepareImg = function(resource){
|
||||
startPerfLog("prepareImg");
|
||||
//print("prepareImg: ", resource);
|
||||
|
||||
|
@ -104,15 +127,15 @@ let showWidgets = false;
|
|||
delete resource.dataOffset;
|
||||
delete resource.dataLength;
|
||||
if (resource.paletteData){
|
||||
result.palette = new Uint16Array(resource.paletteData);
|
||||
resource.palette = new Uint16Array(resource.paletteData);
|
||||
delete resource.paletteData;
|
||||
}
|
||||
}
|
||||
endPerfLog("prepareImg");
|
||||
return resource;
|
||||
}
|
||||
};
|
||||
|
||||
function getByPath(object, path, lastElem){
|
||||
let getByPath = function(object, path, lastElem){
|
||||
startPerfLog("getByPath");
|
||||
//print("getByPath", path,lastElem);
|
||||
let current = object;
|
||||
|
@ -133,23 +156,24 @@ let showWidgets = false;
|
|||
return undefined;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
};
|
||||
|
||||
function splitNumberToDigits(num){
|
||||
let splitNumberToDigits = function(num){
|
||||
return String(num).split('').map(item => Number(item));
|
||||
}
|
||||
};
|
||||
|
||||
function isChangedNumber(element){
|
||||
let isChangedNumber = function(element){
|
||||
return element.lastDrawnValue != getValue(element.Value);
|
||||
}
|
||||
};
|
||||
|
||||
function isChangedMultistate(element){
|
||||
let isChangedMultistate = function(element){
|
||||
return element.lastDrawnValue != getMultistate(element.Value);
|
||||
}
|
||||
};
|
||||
|
||||
function drawNumber(graphics, resources, element){
|
||||
let drawNumber = function(graphics, resources, element){
|
||||
startPerfLog("drawNumber");
|
||||
let number = getValue(element.Value);
|
||||
//print("drawNumber: ", number, element);
|
||||
let spacing = element.Spacing ? element.Spacing : 0;
|
||||
let unit = element.Unit;
|
||||
|
||||
|
@ -158,7 +182,6 @@ let showWidgets = false;
|
|||
let numberOfDigits = element.Digits;
|
||||
|
||||
|
||||
//print("drawNumber: ", number, element);
|
||||
if (number) number = number.toFixed(0);
|
||||
|
||||
let isNegative;
|
||||
|
@ -245,7 +268,7 @@ let showWidgets = false;
|
|||
} else {
|
||||
currentDigit = 0;
|
||||
}
|
||||
//print("Digit " + currentDigit + " " + currentX);
|
||||
//print("Digit", currentDigit, currentX);
|
||||
drawElement(graphics, resources, {X:currentX,Y:firstDigitY}, element, currentDigit + imageIndex);
|
||||
currentX += firstImage.width + spacing;
|
||||
}
|
||||
|
@ -258,9 +281,9 @@ let showWidgets = false;
|
|||
element.lastDrawnValue = number;
|
||||
|
||||
endPerfLog("drawNumber");
|
||||
}
|
||||
};
|
||||
|
||||
function drawElement(graphics, resources, pos, element, lastElem){
|
||||
let drawElement = function(graphics, resources, pos, element, lastElem){
|
||||
startPerfLog("drawElement");
|
||||
let cacheKey = "_"+(lastElem?lastElem:"nole");
|
||||
if (!element.cachedImage) element.cachedImage={};
|
||||
|
@ -282,11 +305,9 @@ let showWidgets = false;
|
|||
}
|
||||
}
|
||||
|
||||
//print("cache ",typeof element.cachedImage[cacheKey], element.ImagePath, lastElem);
|
||||
//print("cache ", typeof element.cachedImage[cacheKey], element.ImagePath, lastElem);
|
||||
if(element.cachedImage[cacheKey]){
|
||||
//print("drawElement ",pos, path, lastElem);
|
||||
//print("resource ", resource,pos, path, lastElem);
|
||||
//print("drawImage from drawElement", image, pos);
|
||||
//print("drawElement ", pos, element, lastElem);
|
||||
let options={};
|
||||
if (element.RotationValue){
|
||||
options.rotate = radians(element);
|
||||
|
@ -304,35 +325,36 @@ let showWidgets = false;
|
|||
endPerfLog("drawElement_g.drawImage");
|
||||
}
|
||||
endPerfLog("drawElement");
|
||||
}
|
||||
};
|
||||
|
||||
function getValue(value, defaultValue){
|
||||
let getValue = function(value, defaultValue){
|
||||
startPerfLog("getValue");
|
||||
if (typeof value == "string"){
|
||||
return numbers[value]();
|
||||
}
|
||||
if (value == undefined) return defaultValue;
|
||||
endPerfLog("getValue");
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
function getMultistate(name, defaultValue){
|
||||
let getMultistate = function(name, defaultValue){
|
||||
startPerfLog("getMultistate");
|
||||
if (typeof name == "string"){
|
||||
return multistates[name]();
|
||||
} else {
|
||||
if (name == undefined) return defaultValue;
|
||||
}
|
||||
endPerfLog("getMultistate");
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
function drawScale(graphics, resources, scale){
|
||||
let drawScale = function(graphics, resources, scale){
|
||||
startPerfLog("drawScale");
|
||||
//print("drawScale", scale);
|
||||
let segments = scale.Segments;
|
||||
let imageIndex = scale.ImageIndex !== undefined ? scale.ImageIndex : 0;
|
||||
|
||||
let value = scaledown(scale.Value, scale.MinValue, scale.MaxValue);
|
||||
|
||||
//print("Value is ", value, "(", maxValue, ",", minValue, ")");
|
||||
|
||||
let segmentsToDraw = Math.ceil(value * segments.length);
|
||||
|
||||
for (let i = 0; i < segmentsToDraw; i++){
|
||||
|
@ -341,9 +363,9 @@ let showWidgets = false;
|
|||
scale.lastDrawnValue = segmentsToDraw;
|
||||
|
||||
endPerfLog("drawScale");
|
||||
}
|
||||
};
|
||||
|
||||
function drawImage(graphics, resources, image, name){
|
||||
let drawImage = function(graphics, resources, image, name){
|
||||
startPerfLog("drawImage");
|
||||
//print("drawImage", image.X, image.Y, name);
|
||||
if (image.Value && image.Steps){
|
||||
|
@ -357,9 +379,9 @@ let showWidgets = false;
|
|||
}
|
||||
|
||||
endPerfLog("drawImage");
|
||||
}
|
||||
};
|
||||
|
||||
function drawCodedImage(graphics, resources, image){
|
||||
let drawCodedImage = function(graphics, resources, image){
|
||||
startPerfLog("drawCodedImage");
|
||||
let code = getValue(image.Value);
|
||||
//print("drawCodedImage", image, code);
|
||||
|
@ -386,9 +408,9 @@ let showWidgets = false;
|
|||
image.lastDrawnValue = code;
|
||||
|
||||
startPerfLog("drawCodedImage");
|
||||
}
|
||||
};
|
||||
|
||||
function getWeatherCode(){
|
||||
let getWeatherCode = function(){
|
||||
let jsonWeather = require("Storage").readJSON('weather.json');
|
||||
let weather = (jsonWeather && jsonWeather.weather) ? jsonWeather.weather : undefined;
|
||||
|
||||
|
@ -396,9 +418,9 @@ let showWidgets = false;
|
|||
return weather.code;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
function getWeatherTemperature(){
|
||||
let getWeatherTemperature = function(){
|
||||
let jsonWeather = require("Storage").readJSON('weather.json');
|
||||
let weather = (jsonWeather && jsonWeather.weather) ? jsonWeather.weather : undefined;
|
||||
|
||||
|
@ -415,25 +437,25 @@ let showWidgets = false;
|
|||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
function scaledown(value, min, max){
|
||||
let scaledown = function(value, min, max){
|
||||
//print("scaledown", value, min, max);
|
||||
let scaled = E.clip(getValue(value),getValue(min,0),getValue(max,1));
|
||||
scaled -= getValue(min,0);
|
||||
scaled /= getValue(max,1);
|
||||
return scaled;
|
||||
}
|
||||
};
|
||||
|
||||
function radians(rotation){
|
||||
let radians = function(rotation){
|
||||
let value = scaledown(rotation.RotationValue, rotation.MinRotationValue, rotation.MaxRotationValue);
|
||||
value -= rotation.RotationOffset ? rotation.RotationOffset : 0;
|
||||
value *= 360;
|
||||
value *= Math.PI / 180;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
function drawPoly(graphics, resources, element){
|
||||
let drawPoly = function(graphics, resources, element){
|
||||
startPerfLog("drawPoly");
|
||||
let vertices = [];
|
||||
|
||||
|
@ -463,9 +485,9 @@ let showWidgets = false;
|
|||
}
|
||||
|
||||
endPerfLog("drawPoly");
|
||||
}
|
||||
};
|
||||
|
||||
function drawRect(graphics, resources, element){
|
||||
let drawRect = function(graphics, resources, element){
|
||||
startPerfLog("drawRect");
|
||||
let vertices = [];
|
||||
|
||||
|
@ -479,9 +501,9 @@ let showWidgets = false;
|
|||
endPerfLog("drawRect_g.fillRect");
|
||||
}
|
||||
endPerfLog("drawRect");
|
||||
}
|
||||
};
|
||||
|
||||
function drawCircle(graphics, resources, element){
|
||||
let drawCircle = function(graphics, resources, element){
|
||||
startPerfLog("drawCircle");
|
||||
|
||||
if (element.Filled){
|
||||
|
@ -494,7 +516,7 @@ let showWidgets = false;
|
|||
endPerfLog("drawCircle_g.drawCircle");
|
||||
}
|
||||
endPerfLog("drawCircle");
|
||||
}
|
||||
};
|
||||
|
||||
let numbers = {};
|
||||
numbers.Hour = () => { return new Date().getHours(); };
|
||||
|
@ -547,7 +569,7 @@ let showWidgets = false;
|
|||
multistates.WeatherTemperatureUnit = () => { return getWeatherTemperature().unit; };
|
||||
multistates.StepsGoal = () => { return (numbers.Steps() >= (settings.stepsgoal || 10000)) ? "on": "off"; };
|
||||
|
||||
function drawMultiState(graphics, resources, element){
|
||||
let drawMultiState = function(graphics, resources, element){
|
||||
startPerfLog("drawMultiState");
|
||||
//print("drawMultiState", element);
|
||||
let value = multistates[element.Value]();
|
||||
|
@ -555,7 +577,7 @@ let showWidgets = false;
|
|||
drawImage(graphics, resources, element, value);
|
||||
element.lastDrawnValue = value;
|
||||
endPerfLog("drawMultiState");
|
||||
}
|
||||
};
|
||||
|
||||
let pulse,alt,temp,press;
|
||||
|
||||
|
@ -563,25 +585,20 @@ let showWidgets = false;
|
|||
let requestedDraws = 0;
|
||||
let isDrawing = false;
|
||||
|
||||
let drawingTime;
|
||||
|
||||
let start;
|
||||
|
||||
let deferredTimout;
|
||||
|
||||
function initialDraw(resources, face){
|
||||
let initialDraw = function(resources, face){
|
||||
//print("Free memory", process.memory(false).free);
|
||||
requestedDraws++;
|
||||
if (!isDrawing){
|
||||
cleanupDelays();
|
||||
//print(new Date().toISOString(), "Can draw,", requestedDraws, "draws requested so far");
|
||||
isDrawing = true;
|
||||
resetPerfLog();
|
||||
requestedDraws = 0;
|
||||
//print(new Date().toISOString(), "Drawing start");
|
||||
startPerfLog("initialDraw");
|
||||
//start = Date.now();
|
||||
drawingTime = 0;
|
||||
//print("Precompiled");
|
||||
let promise = precompiledJs(watchfaceResources, watchface);
|
||||
|
||||
|
@ -595,8 +612,6 @@ let showWidgets = false;
|
|||
g.drawLine(0,24,g.getWidth(),24);
|
||||
}
|
||||
lastDrawTime = Date.now() - start;
|
||||
drawingTime += Date.now() - currentDrawingTime;
|
||||
//print(new Date().toISOString(), "Drawing done in", lastDrawTime.toFixed(0), "active:", drawingTime.toFixed(0));
|
||||
isDrawing=false;
|
||||
firstDraw=false;
|
||||
requestRefresh = false;
|
||||
|
@ -608,14 +623,16 @@ let showWidgets = false;
|
|||
if (requestedDraws > 0){
|
||||
//print(new Date().toISOString(), "Had deferred drawing left, drawing again");
|
||||
requestedDraws = 0;
|
||||
//print("Clear deferred timeout", deferredTimout);
|
||||
clearTimeout(deferredTimeout);
|
||||
deferredTimout = setTimeout(()=>{initialDraw(resources, face);}, 10);
|
||||
}
|
||||
} //else {
|
||||
//print("queued draw");
|
||||
//}
|
||||
}
|
||||
};
|
||||
|
||||
function handleHrm(e){
|
||||
let handleHrm = function(e){
|
||||
if (e.confidence > 70){
|
||||
pulse = e.bpm;
|
||||
if (!redrawEvents || redrawEvents.includes("HRM") && !Bangle.isLocked()){
|
||||
|
@ -623,9 +640,9 @@ let showWidgets = false;
|
|||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function handlePressure(e){
|
||||
let handlePressure = function(e){
|
||||
alt = e.altitude;
|
||||
temp = e.temperature;
|
||||
press = e.pressure;
|
||||
|
@ -633,42 +650,46 @@ let showWidgets = false;
|
|||
//print("Redrawing on pressure");
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function handleCharging(e){
|
||||
let handleCharging = function(e){
|
||||
if (!redrawEvents || redrawEvents.includes("charging") && !Bangle.isLocked()){
|
||||
//print("Redrawing on charging");
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function getMatchedWaitingTime(time){
|
||||
let getMatchedWaitingTime = function(time){
|
||||
let result = time - (Date.now() % time);
|
||||
//print("Matched timeout", time, result);
|
||||
//print("Matched wating time", time, result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
function setMatchedInterval(callable, time, intervalHandler, delay){
|
||||
let setMatchedInterval = function(callable, time, intervalHandler, delay){
|
||||
//print("Setting matched interval for", time, intervalHandler);
|
||||
if (!delay) delay = 0;
|
||||
let matchedTime = getMatchedWaitingTime(time + delay);
|
||||
return setTimeout(()=>{
|
||||
let interval = setInterval(callable, time);
|
||||
//print("setMatchedInterval", interval);
|
||||
if (intervalHandler) intervalHandler(interval);
|
||||
callable();
|
||||
}, matchedTime);
|
||||
}
|
||||
};
|
||||
|
||||
endPerfLog("loadFunctions");
|
||||
|
||||
let lastDrawTime = 0;
|
||||
let firstDraw = true;
|
||||
|
||||
startPerfLog("loadProperties");
|
||||
let lockedRedraw = getByPath(watchface, ["Properties","Redraw","Locked"]) || 60000;
|
||||
let unlockedRedraw = getByPath(watchface, ["Properties","Redraw","Unlocked"]) || 1000;
|
||||
let defaultRedraw = getByPath(watchface, ["Properties","Redraw","Default"]) || "Always";
|
||||
let redrawEvents = getByPath(watchface, ["Properties","Redraw","Events"]);
|
||||
let clearOnRedraw = getByPath(watchface, ["Properties","Redraw","Clear"]);
|
||||
let events = getByPath(watchface, ["Properties","Events"]);
|
||||
endPerfLog("loadProperties");
|
||||
|
||||
//print("events", events);
|
||||
//print("redrawEvents", redrawEvents);
|
||||
|
@ -676,7 +697,7 @@ let showWidgets = false;
|
|||
let initialDrawTimeoutUnlocked;
|
||||
let initialDrawTimeoutLocked;
|
||||
|
||||
function handleLock(isLocked, forceRedraw){
|
||||
let handleLock = function(isLocked, forceRedraw){
|
||||
//print("isLocked", Bangle.isLocked());
|
||||
for (let i of unlockedDrawInterval){
|
||||
//print("Clearing unlocked", i);
|
||||
|
@ -694,6 +715,10 @@ let showWidgets = false;
|
|||
//print("Redrawing on unlock", isLocked);
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
if (initialDrawTimeoutUnlocked){
|
||||
//print("clear initialDrawTimeUnlocked timet", initialDrawTimeoutUnlocked);
|
||||
clearTimeout(initialDrawTimeoutUnlocked);
|
||||
}
|
||||
initialDrawTimeoutUnlocked = setMatchedInterval(()=>{
|
||||
//print("Redrawing on unlocked interval");
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
|
@ -708,6 +733,10 @@ let showWidgets = false;
|
|||
//print("Redrawing on lock", isLocked);
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
}
|
||||
if (initialDrawTimeoutLocked){
|
||||
clearTimeout(initialDrawTimeoutLocked);
|
||||
//print("clear initialDrawTimeLocked timet", initialDrawTimeoutLocked);
|
||||
}
|
||||
initialDrawTimeoutLocked = setMatchedInterval(()=>{
|
||||
//print("Redrawing on locked interval");
|
||||
initialDraw(watchfaceResources, watchface);
|
||||
|
@ -718,13 +747,13 @@ let showWidgets = false;
|
|||
Bangle.setHRMPower(0, "imageclock");
|
||||
Bangle.setBarometerPower(0, 'imageclock');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let showWidgetsChanged = false;
|
||||
let currentDragDistance = 0;
|
||||
|
||||
function restoreWidgetDraw(){
|
||||
let restoreWidgetDraw = function(){
|
||||
if (global.WIDGETS) {
|
||||
for (let w in global.WIDGETS) {
|
||||
let wd = global.WIDGETS[w];
|
||||
|
@ -732,9 +761,9 @@ let showWidgets = false;
|
|||
wd.area = originalWidgetArea[w];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function handleDrag(e){
|
||||
let handleDrag = function(e){
|
||||
//print("handleDrag");
|
||||
currentDragDistance += e.dy;
|
||||
if (Math.abs(currentDragDistance) < 10) return;
|
||||
|
@ -757,7 +786,7 @@ let showWidgets = false;
|
|||
showWidgets = dragDown;
|
||||
initialDraw();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Bangle.on('drag', handleDrag);
|
||||
|
||||
|
@ -766,7 +795,7 @@ let showWidgets = false;
|
|||
try{
|
||||
Bangle.setBarometerPower(1, 'imageclock');
|
||||
} catch (e){
|
||||
print("Error during barometer power up", e);
|
||||
//print("Error during barometer power up", e);
|
||||
}
|
||||
}
|
||||
if (!events || events.includes("HRM")) {
|
||||
|
@ -783,7 +812,7 @@ let showWidgets = false;
|
|||
let originalWidgetDraw = {};
|
||||
let originalWidgetArea = {};
|
||||
|
||||
function clearWidgetsDraw(){
|
||||
let clearWidgetsDraw = function(){
|
||||
//print("Clear widget draw calls");
|
||||
if (global.WIDGETS) {
|
||||
originalWidgetDraw = {};
|
||||
|
@ -798,10 +827,7 @@ let showWidgets = false;
|
|||
}
|
||||
}
|
||||
|
||||
if (!global.WIDGETS) Bangle.loadWidgets();
|
||||
clearWidgetsDraw();
|
||||
|
||||
handleLock(Bangle.isLocked());
|
||||
handleLock(Bangle.isLocked(), true);
|
||||
|
||||
Bangle.setUI({
|
||||
mode : "clock",
|
||||
|
@ -832,9 +858,19 @@ let showWidgets = false;
|
|||
}
|
||||
delete lockedDrawInterval;
|
||||
delete showWidgets;
|
||||
delete firstDraw;
|
||||
|
||||
delete Bangle.printPerfLog;
|
||||
if (settings.perflog){
|
||||
delete Bangle.resetPerfLog;
|
||||
delete performanceLog;
|
||||
}
|
||||
|
||||
cleanupDelays();
|
||||
restoreWidgetDraw();
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.loadWidgets();
|
||||
clearWidgetsDraw();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
Options:</br>
|
||||
<input type="checkbox" id="timeoutwrap" name="mode"/>
|
||||
<label for="timeoutwrap">Wrap draw calls in timeouts (Slower, more RAM use, better interactivity)</label></br>
|
||||
<input type="checkbox" id="forceOrigPlane" name="mode" disabled="true"/>
|
||||
<label for="forceOrigPlane">Force use of direct drawing (Even faster, but will produce visible artifacts on not optimized watch faces)</label></br>
|
||||
<input type="checkbox" id="debugprints" name="mode"/>
|
||||
<label for="debugprints">Add debug prints to generated code</label></br>
|
||||
</p>
|
||||
|
@ -579,11 +581,8 @@
|
|||
return result;
|
||||
}
|
||||
|
||||
function convertToCode(elements, properties, wrapInTimeouts){
|
||||
function convertToCode(elements, properties, wrapInTimeouts, forceUseOrigPlane){
|
||||
var code = "(function (wr, wf) {\n";
|
||||
if (!wrapInTimeouts){
|
||||
code += "var ct=Date.now();\n";
|
||||
}
|
||||
code += "var lc;\n";
|
||||
code += "var p = Promise.resolve();\n";
|
||||
|
||||
|
@ -595,7 +594,7 @@
|
|||
var c = elements[i].value;
|
||||
console.log("Check element", c);
|
||||
var name = c.Layer;
|
||||
var plane = wrapInTimeouts ? 1 : 0;
|
||||
var plane = (wrapInTimeouts && !forceUseOrigPlane) ? 1 : 0;
|
||||
if (typeof c.Plane == "number"){
|
||||
plane = c.Plane;
|
||||
}
|
||||
|
@ -610,8 +609,6 @@
|
|||
|
||||
console.log("Found planes", planes, "with numbers", planeNumbers)
|
||||
|
||||
if (wrapInTimeouts && planes == 0) planes = 1;
|
||||
|
||||
code += "p0 = g;\n";
|
||||
|
||||
for (var planeIndex = 0; planeIndex < planeNumbers.length; planeIndex++){
|
||||
|
@ -624,32 +621,25 @@
|
|||
if (plane != 0) code += "if (!p" + plane + ") p" + plane + " = Graphics.createArrayBuffer(g.getWidth(),g.getHeight(),4,{msb:true});\n";
|
||||
|
||||
if (properties.Redraw && properties.Redraw.Clear){
|
||||
if (wrapInTimeouts && plane != 0){
|
||||
if (wrapInTimeouts && (plane != 0 || forceUseOrigPlane)){
|
||||
code += "p = p.then(()=>delay(0)).then(()=>{\n";
|
||||
} else {
|
||||
code += "p = p.then(()=>{\n";
|
||||
}
|
||||
code += "var ct=Date.now();\n"
|
||||
if (addDebug()) code += 'print("Clear for redraw of plane ' + p + '");'+"\n";
|
||||
code += 'startPerfLog("initialDraw_g.clear");'+"\n";
|
||||
code += "p" + plane + ".clear(true);\n";
|
||||
code += 'endPerfLog("initialDraw_g.clear");'+ "\n";
|
||||
|
||||
code += "drawingTime += Date.now() - ct;\n";
|
||||
code += "});\n";
|
||||
}
|
||||
|
||||
var previousPlane = plane + 1;
|
||||
if (previousPlane < planeNumbers.length){
|
||||
code += "p = p.then(()=>{\n";
|
||||
code += "var ct=Date.now();\n";
|
||||
|
||||
if (addDebug()) code += 'print("Copying of plane ' + previousPlane + ' to display");'+"\n";
|
||||
//code += "g.drawImage(p" + i + ".asImage());";
|
||||
code += "p0.drawImage({width: p" + previousPlane + ".getWidth(), height: p" + previousPlane + ".getHeight(), bpp: p" + previousPlane + ".getBPP(), buffer: p" + previousPlane + ".buffer, palette: palette});\n";
|
||||
|
||||
|
||||
code += "drawingTime += Date.now() - ct;\n";
|
||||
code += "});\n";
|
||||
}
|
||||
|
||||
|
@ -660,12 +650,6 @@
|
|||
console.log("Layer elements", layername, layerElements);
|
||||
//code for whole layer
|
||||
|
||||
if (wrapInTimeouts && plane != 0){
|
||||
code += "p = p.then(()=>delay(0)).then(()=>{\n";
|
||||
} else {
|
||||
code += "p = p.then(()=>{\n";
|
||||
}
|
||||
code += "var ct=Date.now();\n";
|
||||
if (addDebug()) code += 'print("Starting layer ' + layername + '");' + "\n";
|
||||
|
||||
var checkForLayerChange = false;
|
||||
|
@ -732,14 +716,17 @@
|
|||
if (addDebug()) code += 'print("Element condition is ' + condition + '");' + "\n";
|
||||
code += "" + colorsetting;
|
||||
code += (condition.length > 0 ? "if (" + condition + "){\n" : "");
|
||||
if (wrapInTimeouts && (plane != 0 || forceUseOrigPlane)){
|
||||
code += "p = p.then(()=>delay(0)).then(()=>{\n";
|
||||
} else {
|
||||
code += "p = p.then(()=>{\n";
|
||||
}
|
||||
if (addDebug()) code += 'print("Drawing element ' + elementIndex + ' with type ' + c.type + ' on plane ' + planeName + '");' + "\n";
|
||||
code += "draw" + c.type + "(" + planeName + ", wr, wf.Collapsed[" + elementIndex + "].value);\n";
|
||||
|
||||
code += "});\n";
|
||||
code += (condition.length > 0 ? "}\n" : "");
|
||||
}
|
||||
|
||||
code += "drawingTime += Date.now() - ct;\n";
|
||||
code += "});\n";
|
||||
}
|
||||
console.log("Current plane is", plane);
|
||||
|
||||
|
@ -759,7 +746,7 @@
|
|||
var properties = faceJson.Properties;
|
||||
faceJson = { Properties: properties, Collapsed: collapseTree(faceJson,{X:0,Y:0})};
|
||||
console.log("After collapsing", faceJson);
|
||||
precompiledJs = convertToCode(faceJson.Collapsed, properties, document.getElementById('timeoutwrap').checked);
|
||||
precompiledJs = convertToCode(faceJson.Collapsed, properties, document.getElementById('timeoutwrap').checked, document.getElementById('forceOrigPlane').checked);
|
||||
console.log("After precompiling", precompiledJs);
|
||||
}
|
||||
|
||||
|
@ -1011,6 +998,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
document.getElementById("timeoutwrap").addEventListener("click", function() {
|
||||
document.getElementById("forceOrigPlane").disabled = !document.getElementById("timeoutwrap").checked;
|
||||
});
|
||||
|
||||
document.getElementById("btnSave").addEventListener("click", function() {
|
||||
var h = document.createElement('a');
|
||||
h.href = 'data:text/json;charset=utf-8,' + encodeURI(JSON.stringify(resultJson));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "imageclock",
|
||||
"name": "Imageclock",
|
||||
"shortName": "Imageclock",
|
||||
"version": "0.09",
|
||||
"version": "0.11",
|
||||
"type": "clock",
|
||||
"description": "BETA!!! File formats still subject to change --- This app is a highly customizable watchface. To use it, you need to select a watchface. You can build the watchfaces yourself without programming anything. All you need to do is write some json and create image files.",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App.
|
||||
0.01: New App.
|
||||
0.02: Performance improvements.
|
|
@ -55,18 +55,18 @@ var H = g.getHeight();
|
|||
show: function() { dateMenu.items[0].emit("redraw"); },
|
||||
hide: function () {}
|
||||
},
|
||||
{ name: "day",
|
||||
get: () => ({ text: getDay(), img: null}),
|
||||
show: function() { dateMenu.items[2].emit("redraw"); },
|
||||
hide: function () {}
|
||||
},
|
||||
{ name: "date",
|
||||
get: () => ({ text: getDate(), img: null}),
|
||||
show: function() { dateMenu.items[1].emit("redraw"); },
|
||||
hide: function () {}
|
||||
},
|
||||
{ name: "steps",
|
||||
get: () => ({ text: Bangle.getHealthStatus("day").steps, img: null}),
|
||||
show: function() { dateMenu.items[2].emit("redraw"); },
|
||||
hide: function () {}
|
||||
},
|
||||
{ name: "battery",
|
||||
get: () => ({ text: E.getBattery() + (Bangle.isCharging() ? "%++" : "%"), img: null}),
|
||||
{ name: "week",
|
||||
get: () => ({ text: weekOfYear(), img: null}),
|
||||
show: function() { dateMenu.items[3].emit("redraw"); },
|
||||
hide: function () {}
|
||||
},
|
||||
|
@ -130,37 +130,53 @@ function getDate(){
|
|||
return twoD(date.getDate()) + "." + twoD(date.getMonth());
|
||||
}
|
||||
|
||||
function getDay(){
|
||||
var date = new Date();
|
||||
return locale.dow(date, true);
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
* Draw
|
||||
*/
|
||||
function draw() {
|
||||
queueDraw();
|
||||
|
||||
g.clear();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
drawMainScreen();
|
||||
}
|
||||
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 drawMainScreen(){
|
||||
g.setFontUbuntuMono();
|
||||
g.setFontAlign(-1, -1);
|
||||
/************************************************
|
||||
* Draw
|
||||
*/
|
||||
function draw() {
|
||||
queueDraw();
|
||||
|
||||
// Get menu item based on x
|
||||
var menuItem = menu[settings.menuPosX];
|
||||
var cmd = menuItem.name.slice(0,5).toLowerCase();
|
||||
drawCmd(cmd);
|
||||
g.setFontUbuntuMono();
|
||||
g.setFontAlign(-1, -1);
|
||||
|
||||
// Draw menu items depending on our y value
|
||||
drawMenuItems(menuItem);
|
||||
g.clearRect(0,24,W,H);
|
||||
|
||||
// And draw the cursor
|
||||
drawCursor();
|
||||
}
|
||||
drawMainScreen();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function drawMainScreen(){
|
||||
// Get menu item based on x
|
||||
var menuItem = menu[settings.menuPosX];
|
||||
var cmd = menuItem.name.slice(0,5).toLowerCase();
|
||||
drawCmd(cmd);
|
||||
|
||||
// Draw menu items depending on our y value
|
||||
drawMenuItems(menuItem);
|
||||
|
||||
// And draw the cursor
|
||||
drawCursor();
|
||||
}
|
||||
|
||||
function drawMenuItems(menuItem) {
|
||||
var start = parseInt(settings.menuPosY / 4) * 4;
|
||||
|
@ -174,194 +190,197 @@ function drawMenuItems(menuItem) {
|
|||
}
|
||||
|
||||
function drawCursor(){
|
||||
g.setFontUbuntuMono();
|
||||
g.setFontAlign(-1, -1);
|
||||
g.setColor(g.theme.fg);
|
||||
|
||||
g.clearRect(0, 27 + 28, 15, H);
|
||||
if(!Bangle.isLocked()){
|
||||
g.drawString(">", -2, ((settings.menuPosY % 4) + 1) * 27 + 28);
|
||||
}
|
||||
}
|
||||
|
||||
function drawText(key, value, line){
|
||||
g.setFontUbuntuMono();
|
||||
var x = 15;
|
||||
var y = line * 27 + 28;
|
||||
g.setColor(g.theme.fg);
|
||||
function drawText(key, value, line){
|
||||
var x = 15;
|
||||
var y = line * 27 + 28;
|
||||
|
||||
if(key){
|
||||
key = (key.toLowerCase() + " ").slice(0, 4) + "|";
|
||||
} else {
|
||||
key = ""
|
||||
g.setFontUbuntuMono();
|
||||
g.setFontAlign(-1, -1);
|
||||
g.setColor(g.theme.fg);
|
||||
|
||||
if(key){
|
||||
key = (key.toLowerCase() + " ").slice(0, 4) + "|";
|
||||
} else {
|
||||
key = ""
|
||||
}
|
||||
|
||||
value = String(value).replace("\n", " ");
|
||||
g.drawString(key + value, x, y);
|
||||
|
||||
lock_input -= 1;
|
||||
}
|
||||
|
||||
|
||||
function drawCmd(cmd){
|
||||
var c = 0;
|
||||
var x = 10;
|
||||
var y = 28;
|
||||
|
||||
g.setColor("#0f0");
|
||||
g.drawString("bjs", x+c, y);
|
||||
c += g.stringWidth("bjs");
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawString(":", x+c, y);
|
||||
c += g.stringWidth(":");
|
||||
|
||||
g.setColor("#0ff");
|
||||
g.drawString("$ ", x+c, y);
|
||||
c += g.stringWidth("$ ");
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawString(cmd, x+c, y);
|
||||
}
|
||||
|
||||
function twoD(str){
|
||||
return ("0" + str).slice(-2)
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
* Listener
|
||||
*/
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Bangle.on('lock', function(isLocked) {
|
||||
drawCursor();
|
||||
});
|
||||
|
||||
|
||||
Bangle.on('charging',function(charging) {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
|
||||
settings.menuPosX=0;
|
||||
settings.menuPosY=0;
|
||||
|
||||
draw();
|
||||
});
|
||||
|
||||
var lock_input = 0;
|
||||
|
||||
Bangle.on('touch', function(btn, e){
|
||||
if(lock_input > 0){
|
||||
return;
|
||||
}
|
||||
lock_input = 0;
|
||||
|
||||
var left = parseInt(g.getWidth() * 0.22);
|
||||
var right = g.getWidth() - left;
|
||||
var upper = parseInt(g.getHeight() * 0.22) + 20;
|
||||
var lower = g.getHeight() - upper;
|
||||
|
||||
var is_upper = e.y < upper;
|
||||
var is_lower = e.y > lower;
|
||||
var is_left = e.x < left && !is_upper && !is_lower;
|
||||
var is_right = e.x > right && !is_upper && !is_lower;
|
||||
var is_center = !is_upper && !is_lower && !is_left && !is_right;
|
||||
|
||||
var oldYScreen = parseInt(settings.menuPosY/4);
|
||||
if(is_lower){
|
||||
if(settings.menuPosY >= menu[settings.menuPosX].items.length-1){
|
||||
return;
|
||||
}
|
||||
|
||||
value = String(value).replace("\n", " ");
|
||||
g.drawString(key + value, x, y);
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosY++;
|
||||
if(parseInt(settings.menuPosY/4) == oldYScreen){
|
||||
drawCursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lock_input -= 1;
|
||||
}
|
||||
|
||||
|
||||
function drawCmd(cmd){
|
||||
var c = 0;
|
||||
var x = 10;
|
||||
var y = 28;
|
||||
|
||||
g.setColor("#0f0");
|
||||
g.drawString("bjs", x+c, y);
|
||||
c += g.stringWidth("bjs");
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawString(":", x+c, y);
|
||||
c += g.stringWidth(":");
|
||||
|
||||
g.setColor("#0ff");
|
||||
g.drawString("~", x+c, y);
|
||||
c += g.stringWidth("~");
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawString("$", x+c, y);
|
||||
c += g.stringWidth("$ ");
|
||||
|
||||
g.drawString(cmd, x+c, y);
|
||||
}
|
||||
|
||||
function twoD(str){
|
||||
return ("0" + str).slice(-2)
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
* Listener
|
||||
*/
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Bangle.on('lock', function(isLocked) {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
|
||||
draw();
|
||||
});
|
||||
|
||||
|
||||
Bangle.on('charging',function(charging) {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
|
||||
draw();
|
||||
});
|
||||
|
||||
var lock_input = 0;
|
||||
|
||||
Bangle.on('touch', function(btn, e){
|
||||
if(lock_input > 0){
|
||||
return;
|
||||
}
|
||||
lock_input = 0;
|
||||
|
||||
var left = parseInt(g.getWidth() * 0.22);
|
||||
var right = g.getWidth() - left;
|
||||
var upper = parseInt(g.getHeight() * 0.22) + 20;
|
||||
var lower = g.getHeight() - upper;
|
||||
|
||||
var is_upper = e.y < upper;
|
||||
var is_lower = e.y > lower;
|
||||
var is_left = e.x < left && !is_upper && !is_lower;
|
||||
var is_right = e.x > right && !is_upper && !is_lower;
|
||||
var is_center = !is_upper && !is_lower && !is_left && !is_right;
|
||||
|
||||
var oldYScreen = parseInt(settings.menuPosY/4);
|
||||
if(is_lower){
|
||||
if(settings.menuPosY >= menu[settings.menuPosX].items.length-1){
|
||||
return;
|
||||
}
|
||||
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosY++;
|
||||
if(parseInt(settings.menuPosY/4) == oldYScreen){
|
||||
drawCursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(is_upper){
|
||||
if(e.y < 20){ // Reserved for widget clicks
|
||||
return;
|
||||
}
|
||||
|
||||
if(settings.menuPosY <= 0){
|
||||
return;
|
||||
}
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosY--;
|
||||
settings.menuPosY = settings.menuPosY < 0 ? 0 : settings.menuPosY;
|
||||
|
||||
if(parseInt(settings.menuPosY/4) == oldYScreen){
|
||||
drawCursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(is_right){
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosX = (settings.menuPosX+1) % menu.length;
|
||||
settings.menuPosY = 0;
|
||||
}
|
||||
|
||||
if(is_left){
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosY = 0;
|
||||
settings.menuPosX = settings.menuPosX-1;
|
||||
settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX;
|
||||
}
|
||||
|
||||
if(is_center){
|
||||
if(!canRunMenuItem()){
|
||||
if(is_upper){
|
||||
if(e.y < 20){ // Reserved for widget clicks
|
||||
return;
|
||||
}
|
||||
runMenuItem();
|
||||
}
|
||||
|
||||
draw();
|
||||
});
|
||||
if(settings.menuPosY <= 0){
|
||||
return;
|
||||
}
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosY--;
|
||||
settings.menuPosY = settings.menuPosY < 0 ? 0 : settings.menuPosY;
|
||||
|
||||
E.on("kill", function(){
|
||||
try{
|
||||
storage.write(SETTINGS_FILE, settings);
|
||||
} catch(ex){
|
||||
// If this fails, we still kill the app...
|
||||
}
|
||||
});
|
||||
if(parseInt(settings.menuPosY/4) == oldYScreen){
|
||||
drawCursor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(is_right){
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosX = (settings.menuPosX+1) % menu.length;
|
||||
settings.menuPosY = 0;
|
||||
}
|
||||
|
||||
if(is_left){
|
||||
Bangle.buzz(40, 0.6);
|
||||
settings.menuPosY = 0;
|
||||
settings.menuPosX = settings.menuPosX-1;
|
||||
settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX;
|
||||
}
|
||||
|
||||
if(is_center){
|
||||
if(!canRunMenuItem()){
|
||||
return;
|
||||
}
|
||||
runMenuItem();
|
||||
}
|
||||
|
||||
draw();
|
||||
});
|
||||
|
||||
E.on("kill", function(){
|
||||
try{
|
||||
storage.write(SETTINGS_FILE, settings);
|
||||
} catch(ex){
|
||||
// If this fails, we still kill the app...
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/************************************************
|
||||
* Startup Clock
|
||||
*/
|
||||
/************************************************
|
||||
* Startup Clock
|
||||
*/
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
// Load and draw widgets
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
// Load and draw widgets
|
||||
Bangle.loadWidgets();
|
||||
|
||||
// Draw first time
|
||||
draw();
|
||||
// Draw first time
|
||||
draw();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "linuxclock",
|
||||
"name": "Linux Clock",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A Linux inspired clock.",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
|
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.8 KiB |
|
@ -1,2 +1,3 @@
|
|||
0.01: Initial release
|
||||
0.02: Removed accelerometer poll interval adjustment, fixed a few issues with detecting the current app
|
||||
0.02: Removed accelerometer poll interval adjustment, fixed a few issues with detecting the current app
|
||||
0.03: Fix a couple of silly mistakes
|
|
@ -12,7 +12,7 @@ E.on("init", () => {
|
|||
Storage.write("powersave.json", {
|
||||
app: __FILE__
|
||||
});
|
||||
}else{
|
||||
}else if(!("__FILE__" in global)){
|
||||
Storage.write("powersave.json", {
|
||||
app: null
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "powersave",
|
||||
"name": "Power Save",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "Halts foreground app execution while screen is off while still allowing background processes.",
|
||||
"readme": "README.md",
|
||||
"icon": "powersave.png",
|
||||
|
@ -10,7 +10,7 @@
|
|||
"supports": ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"powersave.boot.js","url":"boot.js"},
|
||||
{"name":"powersave.screen.js","url":"boot.js"}
|
||||
{"name":"powersave.screen.js","url":"screen.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name": "powersave.json"}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# App Name
|
||||
|
||||
Watchface that displays time and the prime factors of the "military time" (i.e. 21:05 => 2105, shows prime factors of 2105 which are 5 & 421). Displays "Prime Time!" if prime.
|
||||
|
||||

|
||||
|
||||
|
||||
## Creator
|
||||
|
||||
Adapted from simplestclock by [Eve Bury](https://www.github.com/eveeeon)
|
After Width: | Height: | Size: 9.9 KiB |
|
@ -0,0 +1,15 @@
|
|||
{ "id": "primetime",
|
||||
"name": "Prime Time Clock",
|
||||
"version": "0.01",
|
||||
"type": "clock",
|
||||
"description": "A clock that tells you the primes of the time",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"primetime.app.js","url":"primetime.js"},
|
||||
{"name":"primetime.img","url":"primetime-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgVVABVADJMBBf4L/Bf4LMgtQgIHCitAqoHBoEv+EHwALBv/S//4BYO//svwELoP//X/+gLB2E93+Ah9B9f+//QBYMVvv3C4XvvwLDl/0q+AgsB998qt4F4XgHYIXB/1+6ALC//93/4F4I7CI4QLBAIMLoF/6ABBBYNVqgBBgprCAIKz0qkAooLHgP8gXvvALH/EL7e4BY+tz/+vovH3PR1++L9YL/BYdVABQ="))
|
|
@ -0,0 +1,89 @@
|
|||
const h = g.getHeight();
|
||||
const w = g.getWidth();
|
||||
|
||||
|
||||
|
||||
// creates a list of prime factors of n and outputs them as a string, if n is prime outputs "Prime Time!"
|
||||
function primeFactors(n) {
|
||||
const factors = [];
|
||||
let divisor = 2;
|
||||
|
||||
while (n >= 2) {
|
||||
if (n % divisor == 0) {
|
||||
factors.push(divisor);
|
||||
n = n / divisor;
|
||||
} else {
|
||||
divisor++;
|
||||
}
|
||||
}
|
||||
if (factors.length === 1) {
|
||||
return "Prime Time!";
|
||||
}
|
||||
else
|
||||
return factors.toString();
|
||||
}
|
||||
|
||||
|
||||
// converts time HR:MIN to integer HRMIN e.g. 15:35 => 1535
|
||||
function timeToInt(t) {
|
||||
var arr = t.split(':');
|
||||
var intTime = parseInt(arr[0])*100+parseInt(arr[1]);
|
||||
|
||||
return intTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function draw() {
|
||||
var date = new Date();
|
||||
var timeStr = require("locale").time(date,1);
|
||||
var primeStr = primeFactors(timeToInt(timeStr));
|
||||
|
||||
g.reset();
|
||||
g.setColor(0,0,0);
|
||||
g.fillRect(Bangle.appRect);
|
||||
|
||||
g.setFont("6x8", w/30);
|
||||
g.setFontAlign(0, 0);
|
||||
g.setColor(100,100,100);
|
||||
g.drawString(timeStr, w/2, h/2);
|
||||
g.setFont("6x8", w/60);
|
||||
g.drawString(primeStr, w/2, 3*h/4);
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
g.clear();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
// Bangle.setUI("clock");
|
||||
// use clockupdown as it tests for issue #1249
|
||||
Bangle.setUI("clockupdown", btn=> {
|
||||
draw();
|
||||
});
|
||||
|
||||
// Load widgets
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
draw();
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,44 @@
|
|||
# Sensor tools
|
||||
|
||||
This allows to simulate sensor behaviour for development purposes
|
||||
|
||||
|
||||
## Per Sensor settings:
|
||||
|
||||
enabled:
|
||||
true or false
|
||||
mode:
|
||||
emulate: Completely craft events for this sensor
|
||||
modify: Take existing events from real sensor and modify their data
|
||||
name:
|
||||
name of the emulation or modification mode
|
||||
power:
|
||||
emulate: Simulate Bangle._PWR changes, but do not call real power function
|
||||
nop: Do nothing, ignore all power calls for this sensor but return true
|
||||
passthrough: Just pass all power calls unmodified
|
||||
on: Do not allow switching the sensor off, all calls are switching the real sensor on
|
||||
|
||||
### HRM
|
||||
|
||||
Modes: modify, emulate
|
||||
Modification:
|
||||
bpmtrippled: Multiply the bpm value of the original HRM values with 3
|
||||
Emulation:
|
||||
sin: Calculate bpm changes by using sin
|
||||
|
||||
### GPS
|
||||
|
||||
Modes: emulate
|
||||
Emulation:
|
||||
staticfix: static complete fix with all values
|
||||
route: A square route starting in the SW corner and moving SW->NW->NO->SW...
|
||||
routeFuzzy: Roughly the same square as route, but with 100m seqments with some variaton in course
|
||||
nofix: All values NaN but time,sattelites,fix and fix == 0
|
||||
changingfix: A fix with randomly changing values
|
||||
|
||||
### Compass
|
||||
|
||||
Modes: emulate
|
||||
Emulation:
|
||||
static: All values but heading are 1, heading == 0
|
||||
rotate: All values but heading are 1, heading rotates 360°
|
|
@ -0,0 +1,351 @@
|
|||
(function() {
|
||||
var settings = Object.assign(
|
||||
require('Storage').readJSON("sensortools.default.json", true) || {},
|
||||
require('Storage').readJSON("sensortools.json", true) || {}
|
||||
);
|
||||
|
||||
var log = function(text, param) {
|
||||
var logline = new Date().toISOString() + " - " + "Sensortools - " + text;
|
||||
if (param) logline += ": " + JSON.stringify(param);
|
||||
print(logline);
|
||||
};
|
||||
|
||||
if (settings.enabled) {
|
||||
|
||||
log("Enabled");
|
||||
const POWER_DELAY = 10000;
|
||||
|
||||
var onEvents = [];
|
||||
|
||||
Bangle.sensortoolsOrigOn = Bangle.on;
|
||||
Bangle.sensortoolsOrigEmit = Bangle.emit;
|
||||
Bangle.sensortoolsOrigRemoveListener = Bangle.removeListener;
|
||||
|
||||
Bangle.on = function(name, callback) {
|
||||
if (onEvents[name]) {
|
||||
log("Redirecting listener for", name, "to", name + "_mod");
|
||||
Bangle.sensortoolsOrigOn(name + "_mod", callback);
|
||||
Bangle.sensortoolsOrigOn(name, (e) => {
|
||||
log("Redirected event for", name, "to", name + "_mod");
|
||||
Bangle.sensortoolsOrigEmit(name + "_mod", onEvents[name](e));
|
||||
});
|
||||
} else {
|
||||
log("Pass through on call for", name, callback);
|
||||
Bangle.sensortoolsOrigOn(name, callback);
|
||||
}
|
||||
};
|
||||
|
||||
Bangle.removeListener = function(name, callback) {
|
||||
if (onEvents[name]) {
|
||||
log("Removing augmented listener for", name, onEvents[name]);
|
||||
Bangle.sensortoolsOrigRemoveListener(name + "_mod", callback);
|
||||
} else {
|
||||
log("Pass through remove listener for", name);
|
||||
Bangle.sensortoolsOrigRemoveListener(name, callback);
|
||||
}
|
||||
};
|
||||
|
||||
Bangle.emit = function(name, event) {
|
||||
if (onEvents[name]) {
|
||||
log("Augmenting emit call for", name, onEvents[name]);
|
||||
Bangle.sensortoolsOrigEmit(name + "_mod", event);
|
||||
} else {
|
||||
log("Pass through emit call for", name);
|
||||
Bangle.sensortoolsOrigEmit(name, event);
|
||||
}
|
||||
};
|
||||
|
||||
var createPowerFunction = function(type, name, origPower) {
|
||||
return function(isOn, app) {
|
||||
if (type == "nop") {
|
||||
return true;
|
||||
}else if (type == "delay") {
|
||||
setTimeout(() => {
|
||||
origPower(isOn, app);
|
||||
}, POWER_DELAY);
|
||||
} else if (type == "on") {
|
||||
origPower(1, "sensortools_force_on");
|
||||
} else if (type == "passthrough"){
|
||||
origPower(isOn, "app");
|
||||
} else if (type == "emulate"){
|
||||
if (!Bangle._PWR) Bangle._PWR={};
|
||||
if (!Bangle._PWR[name]) Bangle._PWR[name] = [];
|
||||
if (!app) app="?";
|
||||
if (isOn) {
|
||||
Bangle._PWR[name].push(app);
|
||||
return true;
|
||||
} else {
|
||||
Bangle._PWR[name] = Bangle._PWR[name].filter((v)=>{return v == app;});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
if (settings.hrm && settings.hrm.enabled) {
|
||||
log("HRM", settings.hrm);
|
||||
if (settings.hrm.power) {
|
||||
log("HRM power");
|
||||
Bangle.sensortoolsOrigSetHRMPower = Bangle.setHRMPower;
|
||||
Bangle.setHRMPower = createPowerFunction(settings.hrm.power, "HRM", Bangle.sensortoolsOrigSetHRMPower);
|
||||
}
|
||||
if (settings.hrm.mode == "modify") {
|
||||
if (settings.hrm.name == "bpmtrippled") {
|
||||
onEvents.HRM = (e) => {
|
||||
return {
|
||||
bpm: e.bpm * 3
|
||||
};
|
||||
};
|
||||
}
|
||||
} else if (settings.hrm.mode == "emulate") {
|
||||
if (settings.hrm.name == "sin") {
|
||||
setInterval(() => {
|
||||
Bangle.sensortoolsOrigEmit(60 + 3 * Math.sin(Date.now() / 10000));
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (settings.gps && settings.gps.enabled) {
|
||||
log("GPS", settings.gps);
|
||||
let modGps = function(dataProvider) {
|
||||
Bangle.getGPSFix = dataProvider;
|
||||
setInterval(() => {
|
||||
Bangle.sensortoolsOrigEmit("GPS", dataProvider());
|
||||
}, 1000);
|
||||
};
|
||||
if (settings.gps.power) {
|
||||
Bangle.sensortoolsOrigSetGPSPower = Bangle.setGPSPower;
|
||||
Bangle.setGPSPower = createPowerFunction(settings.gps.power, "GPS", Bangle.sensortoolsOrigSetGPSPower);
|
||||
}
|
||||
if (settings.gps.mode == "emulate") {
|
||||
function radians(a) {
|
||||
return a*Math.PI/180;
|
||||
}
|
||||
|
||||
function degrees(a) {
|
||||
var d = a*180/Math.PI;
|
||||
return (d+360)%360;
|
||||
}
|
||||
|
||||
function bearing(a,b){
|
||||
if (!a || !b || !a.lon || !a.lat || !b.lon || !b.lat) return Infinity;
|
||||
var delta = radians(b.lon-a.lon);
|
||||
var alat = radians(a.lat);
|
||||
var blat = radians(b.lat);
|
||||
var y = Math.sin(delta) * Math.cos(blat);
|
||||
var x = Math.cos(alat)*Math.sin(blat) -
|
||||
Math.sin(alat)*Math.cos(blat)*Math.cos(delta);
|
||||
return Math.round(degrees(Math.atan2(y, x)));
|
||||
}
|
||||
|
||||
function interpolate(a,b,progress){
|
||||
return {
|
||||
lat: a.lat * progress + b.lat * (1-progress),
|
||||
lon: a.lon * progress + b.lon * (1-progress),
|
||||
ele: a.ele * progress + b.ele * (1-progress)
|
||||
}
|
||||
}
|
||||
|
||||
function getSquareRoute(){
|
||||
return [
|
||||
{lat:"47.2577411",lon:"11.9927442",ele:2273},
|
||||
{lat:"47.266761",lon:"11.9926673",ele:2166},
|
||||
{lat:"47.2667605",lon:"12.0059511",ele:2245},
|
||||
{lat:"47.2577516",lon:"12.0059925",ele:1994}
|
||||
];
|
||||
}
|
||||
function getSquareRouteFuzzy(){
|
||||
return [
|
||||
{lat:"47.2578455",lon:"11.9929891",ele:2265},
|
||||
{lat:"47.258592",lon:"11.9923341",ele:2256},
|
||||
{lat:"47.2594506",lon:"11.9927412",ele:2230},
|
||||
{lat:"47.2603323",lon:"11.9924949",ele:2219},
|
||||
{lat:"47.2612056",lon:"11.9928175",ele:2199},
|
||||
{lat:"47.2621002",lon:"11.9929817",ele:2182},
|
||||
{lat:"47.2629025",lon:"11.9923915",ele:2189},
|
||||
{lat:"47.2637828",lon:"11.9926486",ele:2180},
|
||||
{lat:"47.2646733",lon:"11.9928167",ele:2191},
|
||||
{lat:"47.2655617",lon:"11.9930357",ele:2185},
|
||||
{lat:"47.2662862",lon:"11.992252",ele:2186},
|
||||
{lat:"47.2669305",lon:"11.993173",ele:2166},
|
||||
{lat:"47.266666",lon:"11.9944419",ele:2171},
|
||||
{lat:"47.2667579",lon:"11.99576",ele:2194},
|
||||
{lat:"47.2669409",lon:"11.9970579",ele:2207},
|
||||
{lat:"47.2666562",lon:"11.9983128",ele:2212},
|
||||
{lat:"47.2666027",lon:"11.9996335",ele:2262},
|
||||
{lat:"47.2667245",lon:"12.0009395",ele:2278},
|
||||
{lat:"47.2668457",lon:"12.002256",ele:2297},
|
||||
{lat:"47.2666126",lon:"12.0035373",ele:2303},
|
||||
{lat:"47.2664554",lon:"12.004841",ele:2251},
|
||||
{lat:"47.2669461",lon:"12.005948",ele:2245},
|
||||
{lat:"47.2660877",lon:"12.006323",ele:2195},
|
||||
{lat:"47.2652729",lon:"12.0057552",ele:2163},
|
||||
{lat:"47.2643926",lon:"12.0060123",ele:2131},
|
||||
{lat:"47.2634978",lon:"12.0058302",ele:2095},
|
||||
{lat:"47.2626129",lon:"12.0060759",ele:2066},
|
||||
{lat:"47.2617325",lon:"12.0058188",ele:2037},
|
||||
{lat:"47.2608668",lon:"12.0061784",ele:1993},
|
||||
{lat:"47.2600155",lon:"12.0057392",ele:1967},
|
||||
{lat:"47.2591203",lon:"12.0058233",ele:1949},
|
||||
{lat:"47.2582307",lon:"12.0059718",ele:1972},
|
||||
{lat:"47.2578014",lon:"12.004804",ele:2011},
|
||||
{lat:"47.2577232",lon:"12.0034834",ele:2044},
|
||||
{lat:"47.257745",lon:"12.0021656",ele:2061},
|
||||
{lat:"47.2578682",lon:"12.0008597",ele:2065},
|
||||
{lat:"47.2577082",lon:"11.9995526",ele:2071},
|
||||
{lat:"47.2575917",lon:"11.9982348",ele:2102},
|
||||
{lat:"47.2577401",lon:"11.996924",ele:2147},
|
||||
{lat:"47.257715",lon:"11.9956061",ele:2197},
|
||||
{lat:"47.2578996",lon:"11.9943081",ele:2228}
|
||||
];
|
||||
}
|
||||
|
||||
if (settings.gps.name == "staticfix") {
|
||||
modGps(() => { return {
|
||||
"lat": 52,
|
||||
"lon": 8,
|
||||
"alt": 100,
|
||||
"speed": 10,
|
||||
"course": 12,
|
||||
"time": Date.now(),
|
||||
"satellites": 7,
|
||||
"fix": 1,
|
||||
"hdop": 1
|
||||
};});
|
||||
} else if (settings.gps.name.includes("route")) {
|
||||
let route;
|
||||
let interpSteps;
|
||||
if (settings.gps.name == "routeFuzzy"){
|
||||
route = getSquareRouteFuzzy();
|
||||
interpSteps = 5;
|
||||
} else {
|
||||
route = getSquareRoute();
|
||||
interpSteps = 50;
|
||||
}
|
||||
|
||||
let step = 0;
|
||||
let routeIndex = 0;
|
||||
modGps(() => {
|
||||
let newIndex = (routeIndex + 1)%route.length;
|
||||
|
||||
let result = {
|
||||
"speed": Math.random() * 3 + 2,
|
||||
"time": Date.now(),
|
||||
"satellites": Math.floor(Math.random()*5)+3,
|
||||
"fix": 1,
|
||||
"hdop": Math.floor(Math.random(30)+1)
|
||||
};
|
||||
|
||||
let oldPos = route[routeIndex];
|
||||
if (step != 0){
|
||||
oldPos = interpolate(route[routeIndex], route[newIndex], E.clip(0,1,step/interpSteps));
|
||||
}
|
||||
let newPos = route[newIndex];
|
||||
if (step < interpSteps - 1){
|
||||
newPos = interpolate(route[routeIndex], route[newIndex], E.clip(0,1,(step+1)%interpSteps/interpSteps));
|
||||
}
|
||||
|
||||
if (step == interpSteps - 1){
|
||||
let followingIndex = (routeIndex + 2)%route.length;
|
||||
newPos = interpolate(route[newIndex], route[followingIndex], E.clip(0,1,1/interpSteps));
|
||||
}
|
||||
|
||||
result.lat = oldPos.lat;
|
||||
result.lon = oldPos.lon;
|
||||
result.alt = oldPos.ele;
|
||||
|
||||
result.course = bearing(oldPos,newPos);
|
||||
|
||||
step++;
|
||||
if (step == interpSteps){
|
||||
routeIndex = (routeIndex + 1) % route.length;
|
||||
step = 0;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
} else if (settings.gps.name == "nofix") {
|
||||
modGps(() => { return {
|
||||
"lat": NaN,
|
||||
"lon": NaN,
|
||||
"alt": NaN,
|
||||
"speed": NaN,
|
||||
"course": NaN,
|
||||
"time": Date.now(),
|
||||
"satellites": 2,
|
||||
"fix": 0,
|
||||
"hdop": NaN
|
||||
};});
|
||||
} else if (settings.gps.name == "changingfix") {
|
||||
let currentSpeed=1;
|
||||
let currentLat=20;
|
||||
let currentLon=10;
|
||||
let currentCourse=10;
|
||||
let currentAlt=-100;
|
||||
let currentSats=5;
|
||||
modGps(() => {
|
||||
currentLat += 0.1;
|
||||
if (currentLat > 50) currentLat = 20;
|
||||
currentLon += 0.1;
|
||||
if (currentLon > 20) currentLon = 10;
|
||||
currentSpeed *= 10;
|
||||
if (currentSpeed > 1000) currentSpeed = 1;
|
||||
currentCourse += 12;
|
||||
if (currentCourse > 360) currentCourse -= 360;
|
||||
currentSats += 1;
|
||||
if (currentSats > 10) currentSats = 5;
|
||||
currentAlt *= 10;
|
||||
if (currentAlt > 1000) currentAlt = -100;
|
||||
return {
|
||||
"lat": currentLat,
|
||||
"lon": currentLon,
|
||||
"alt": currentAlt,
|
||||
"speed": currentSpeed,
|
||||
"course": currentCourse,
|
||||
"time": Date.now(),
|
||||
"satellites": currentSats,
|
||||
"fix": 1,
|
||||
"hdop": 1
|
||||
};});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.mag && settings.mag.enabled) {
|
||||
log("MAG", settings.mag);
|
||||
let modMag = function(data) {
|
||||
setInterval(() => {
|
||||
Bangle.getCompass = data;
|
||||
Bangle.sensortoolsOrigEmit("mag", data());
|
||||
}, 100);
|
||||
};
|
||||
if (settings.mag.power) {
|
||||
Bangle.sensortoolsOrigSetCompassPower = Bangle.setCompassPower;
|
||||
Bangle.setCompassPower = createPowerFunction(settings.mag.power, "Compass", Bangle.sensortoolsOrigSetCompassPower);
|
||||
}
|
||||
if (settings.mag.mode == "emulate") {
|
||||
if (settings.mag.name == "static") {
|
||||
modMag(()=>{return {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1,
|
||||
dx: 1,
|
||||
dy: 1,
|
||||
dz: 1,
|
||||
heading: 0
|
||||
};});
|
||||
} else if (settings.mag.name == "rotate"){
|
||||
let last = 0;
|
||||
modMag(()=>{return {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1,
|
||||
dx: 1,
|
||||
dy: 1,
|
||||
dz: 1,
|
||||
heading: last = (last+1)%360,
|
||||
};});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"enabled": false,
|
||||
"mag": {
|
||||
"enabled": false,
|
||||
"mode": "emulate",
|
||||
"name": "static"
|
||||
},
|
||||
"hrm": {
|
||||
"enabled": false,
|
||||
"mode": "modify",
|
||||
"name": "bpmtrippled"
|
||||
},
|
||||
"gps": {
|
||||
"enabled": false,
|
||||
"mode": "emulate",
|
||||
"name": "changingfix"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 557 B |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "sensortools",
|
||||
"name": "Sensor tools",
|
||||
"shortName": "Sensor tools",
|
||||
"version": "0.01",
|
||||
"description": "Tools for testing and debugging apps that use sensor input",
|
||||
"icon": "icon.png",
|
||||
"type": "bootloader",
|
||||
"tags": "tool,boot,debug",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"sensortools.0.boot.js","url":"boot.js"},
|
||||
{"name":"sensortools.settings.js","url":"settings.js"},
|
||||
{"name":"sensortools.default.json","url":"default.json"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
(function(back) {
|
||||
function writeSettings(key, value) {
|
||||
var s = require('Storage').readJSON(FILE, true) || {};
|
||||
s[key] = value;
|
||||
require('Storage').writeJSON(FILE, s);
|
||||
readSettings();
|
||||
}
|
||||
|
||||
function writeSettingsParent(parent, key, value) {
|
||||
var s = require('Storage').readJSON(FILE, true) || {};
|
||||
if (!s[parent]) s[parent] = {};
|
||||
s[parent][key] = value;
|
||||
require('Storage').writeJSON(FILE, s);
|
||||
readSettings();
|
||||
}
|
||||
|
||||
function readSettings(){
|
||||
settings = Object.assign(
|
||||
require('Storage').readJSON("sensortools.default.json", true) || {},
|
||||
require('Storage').readJSON(FILE, true) || {}
|
||||
);
|
||||
}
|
||||
|
||||
var FILE="sensortools.json";
|
||||
var settings;
|
||||
readSettings();
|
||||
|
||||
|
||||
let modes = ["nop", "emulate", "modify"];
|
||||
let modesPower = ["nop", "emulate", "passthrough", "delay", "on"];
|
||||
|
||||
function showSubMenu(name,key,typesEmulate,typesModify){
|
||||
var menu = {
|
||||
'': { 'title': name,
|
||||
back: ()=>{E.showMenu(buildMainMenu());}},
|
||||
'Enabled': {
|
||||
value: !!settings[key].enabled,
|
||||
onchange: v => {
|
||||
writeSettingsParent(key, "enabled",v);
|
||||
}
|
||||
},
|
||||
'Mode': {
|
||||
value: modes.indexOf(settings[key].mode||"nop"),
|
||||
min: 0, max: modes.length-1,
|
||||
format: v => { return modes[v]; },
|
||||
onchange: v => {
|
||||
writeSettingsParent(key,"mode",modes[v]);
|
||||
showSubMenu(name,key,typesEmulate,typesModify);
|
||||
}
|
||||
},
|
||||
'Name': {},
|
||||
'Power': {
|
||||
value: modesPower.indexOf(settings[key].power||"nop"),
|
||||
min: 0, max: modesPower.length-1,
|
||||
format: v => { return modesPower[v]; },
|
||||
onchange: v => {
|
||||
writeSettingsParent(key,"power",modesPower[v]);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (settings[key].mode != "nop"){
|
||||
let types = typesEmulate;
|
||||
if (settings[key].mode == "modify") types = typesModify;
|
||||
menu.Name = {
|
||||
value: types.indexOf(settings[key].name||"static"),
|
||||
min: 0, max: types.length-1,
|
||||
format: v => { return types[v]; },
|
||||
onchange: v => {
|
||||
writeSettingsParent(key,"name",types[v]);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
delete menu.Name;
|
||||
}
|
||||
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
|
||||
function buildMainMenu(){
|
||||
var mainmenu = {
|
||||
'': { 'title': 'Sensor tools' },
|
||||
'< Back': back,
|
||||
'Enabled': {
|
||||
value: !!settings.enabled,
|
||||
onchange: v => {
|
||||
writeSettings("enabled",v);
|
||||
},
|
||||
},
|
||||
'GPS': ()=>{showSubMenu("GPS","gps",["nop", "staticfix", "nofix", "changingfix", "route", "routeFuzzy"],[]);},
|
||||
'Compass': ()=>{showSubMenu("Compass","mag",["nop", "static", "rotate"],[]);},
|
||||
'HRM': ()=>{showSubMenu("HRM","hrm",["nop", "static"],["bpmtrippled"],["sin"]);}
|
||||
};
|
||||
return mainmenu;
|
||||
}
|
||||
|
||||
E.showMenu(buildMainMenu());
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
0.01: Release
|
||||
0.02: Rewrite with new interface
|
||||
0.03: Added clock infos to expose timer functionality to clocks.
|
||||
0.03: Added clock infos to expose timer functionality to clocks.
|
||||
0.04: Improvements of clock infos.
|
|
@ -69,7 +69,7 @@
|
|||
img: img,
|
||||
items: [
|
||||
{
|
||||
name: "Timer",
|
||||
name: null,
|
||||
get: () => ({ text: getAlarmMinutesText() + (isAlarmEnabled() ? " min" : ""), img: null}),
|
||||
show: function() { smpltmrItems.items[0].emit("redraw"); },
|
||||
hide: function () {},
|
||||
|
@ -78,17 +78,18 @@
|
|||
]
|
||||
};
|
||||
|
||||
var offsets = [+1,+5,-1,-5];
|
||||
var offsets = [+5,-5];
|
||||
offsets.forEach((o, i) => {
|
||||
smpltmrItems.items = smpltmrItems.items.concat({
|
||||
name: String(o),
|
||||
get: () => ({ text: getAlarmMinutesText() + " (" + (o > 0 ? "+" : "") + o + ")", img: null}),
|
||||
name: null,
|
||||
get: () => ({ text: (o > 0 ? "+" : "") + o + " min.", img: null}),
|
||||
show: function() { smpltmrItems.items[i+1].emit("redraw"); },
|
||||
hide: function () {},
|
||||
run: function() {
|
||||
if(o > 0) increaseAlarm(o);
|
||||
else decreaseAlarm(Math.abs(o));
|
||||
this.show();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "smpltmr",
|
||||
"name": "Simple Timer",
|
||||
"shortName": "Simple Timer",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "A very simple app to start a timer.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm,timer",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New widget!
|
|
@ -0,0 +1,9 @@
|
|||
# Close Button Launcher
|
||||
|
||||
Adds a  button to close the current app and go back to the launcher.
|
||||
(Widget is not visible on the clock screen)
|
||||
|
||||
Copied from widclose by @rigrig and slightly modified.
|
||||
|
||||

|
||||

|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"id": "widcloselaunch",
|
||||
"name": "Close Button to launcher",
|
||||
"version": "0.01",
|
||||
"description": "A button to close the current app and go to launcher",
|
||||
"readme": "README.md",
|
||||
"icon": "icon.png",
|
||||
"type": "widget",
|
||||
"tags": "widget,tools",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot_light.png"},{"url":"screenshot_dark.png"}],
|
||||
"storage": [
|
||||
{"name":"widcloselaunch.wid.js","url":"widget.js"}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 261 B |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,14 @@
|
|||
if (!Bangle.CLOCK) WIDGETS.close = {
|
||||
area: "tr", width: 24, sortorder: 10, // we want the right-most spot please
|
||||
draw: function() {
|
||||
Bangle.removeListener("touch", this.touch);
|
||||
Bangle.on("touch", this.touch);
|
||||
g.reset().setColor("#f00").drawImage(atob( // hardcoded red to match setUI back button
|
||||
// b/w version of preview.png, 24x24
|
||||
"GBgBABgAAf+AB//gD//wH//4P//8P//8fn5+fjx+fxj+f4H+/8P//8P/f4H+fxj+fjx+fn5+P//8P//8H//4D//wB//gAf+AABgA"
|
||||
), this.x, this.y);
|
||||
}, touch: function(_, c) {
|
||||
const w = WIDGETS.close;
|
||||
if (w && c.x>=w.x && c.x<=w.x+24 && c.y>=w.y && c.y<=w.y+24) Bangle.showLauncher();
|
||||
}
|
||||
};
|
|
@ -97,10 +97,14 @@ exports.load = function() {
|
|||
// In case there exists already a menu object b with the same name as the next
|
||||
// object a, we append the items. Otherwise we add the new object a to the list.
|
||||
require("Storage").list(/clkinfo.js$/).forEach(fn => {
|
||||
var a = eval(require("Storage").read(fn))();
|
||||
var b = menu.find(x => x.name === a.name)
|
||||
if(b) b.items = b.items.concat(a.items);
|
||||
else menu = menu.concat(a);
|
||||
try{
|
||||
var a = eval(require("Storage").read(fn))();
|
||||
var b = menu.find(x => x.name === a.name)
|
||||
if(b) b.items = b.items.concat(a.items);
|
||||
else menu = menu.concat(a);
|
||||
} catch(e){
|
||||
console.log("Could not load clock info.")
|
||||
}
|
||||
});
|
||||
|
||||
// return it all!
|
||||
|
|