1
0
Fork 0

Big refactor to attempt to merge all the waypoints code into one place. At least 3 apps had basically identical interface.html files, and yet wpmoto had a great map-based editor.

master
Gordon Williams 2022-07-26 16:14:04 +01:00
parent e9fdb1dee5
commit 91728c147f
39 changed files with 485 additions and 828 deletions

View File

@ -3,4 +3,4 @@
0.03: Add Waypoint Editor
0.04: Fix great circle formula
0.05: Use locale for speed and distance + fix Vector font sizes
0.06: Move waypoints.json (and editor) to 'waypoints' app

View File

@ -207,7 +207,7 @@ Bangle.on('lcdPower',function(on) {
}
});
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
var waypoints = require("waypoints").load();
wp=waypoints[0];
function nextwp(inc){
@ -223,7 +223,7 @@ function doselect(){
if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) {
waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
wp = waypoints[wpindex];
require("Storage").writeJSON("waypoints.json", waypoints);
require("waypoints").save(waypoints);
}
selected=!selected;
drawN();

View File

@ -6,6 +6,6 @@ function drawN(){var a=loc.speed(speed);buf.setColor(1);buf.setFont("6x8",2);buf
0,30);buf.setColor(selected?1:2);buf.drawString(wp.name,140,0);buf.setColor(1);buf.drawString(a,60,0);buf.drawString(loc.distance(dist),60,30);flip(buf,Yoff+130);g.setFont("6x8",1);g.setColor(0,0,0);g.fillRect(10,230,60,239);g.setColor(1,1,1);g.drawString("Sats "+satellites.toString(),10,230)}var savedfix;
function onGPS(a){savedfix=a;void 0!==a&&(course=isNaN(a.course)?course:Math.round(a.course),speed=isNaN(a.speed)?speed:a.speed,satellites=a.satellites);candraw&&(void 0!==a&&1==a.fix&&(dist=distance(a,wp),isNaN(dist)&&(dist=0),brg=bearing(a,wp),isNaN(brg)&&(brg=0)),drawN())}var intervalRef;function stopdraw(){candraw=!1;intervalRef&&clearInterval(intervalRef)}
function startTimers(){candraw=!0;intervalRefSec=setInterval(function(){heading=newHeading(course,heading);course!=heading&&drawCompass(heading)},200)}function drawAll(){g.setColor(1,.5,.5);g.fillPoly([120,Yoff+50,110,Yoff+70,130,Yoff+70]);g.setColor(1,1,1);drawN();drawCompass(heading)}function startdraw(){g.clear();Bangle.drawWidgets();startTimers();drawAll()}
function setButtons(){setWatch(nextwp.bind(null,-1),BTN1,{repeat:!0,edge:"falling"});setWatch(doselect,BTN2,{repeat:!0,edge:"falling"});setWatch(nextwp.bind(null,1),BTN3,{repeat:!0,edge:"falling"})}var SCREENACCESS={withApp:!0,request:function(){this.withApp=!1;stopdraw();clearWatch()},release:function(){this.withApp=!0;startdraw();setButtons()}};Bangle.on("lcdPower",function(a){SCREENACCESS.withApp&&(a?startdraw():stopdraw())});var waypoints=require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
wp=waypoints[0];function nextwp(a){selected&&(wpindex+=a,wpindex>=waypoints.length&&(wpindex=0),0>wpindex&&(wpindex=waypoints.length-1),wp=waypoints[wpindex],drawN())}function doselect(){selected&&0!=wpindex&&void 0===waypoints[wpindex].lat&&savedfix.fix&&(waypoints[wpindex]={name:"@"+wp.name,lat:savedfix.lat,lon:savedfix.lon},wp=waypoints[wpindex],require("Storage").writeJSON("waypoints.json",waypoints));selected=!selected;drawN()}g.clear();Bangle.setLCDBrightness(1);Bangle.loadWidgets();Bangle.drawWidgets();
function setButtons(){setWatch(nextwp.bind(null,-1),BTN1,{repeat:!0,edge:"falling"});setWatch(doselect,BTN2,{repeat:!0,edge:"falling"});setWatch(nextwp.bind(null,1),BTN3,{repeat:!0,edge:"falling"})}var SCREENACCESS={withApp:!0,request:function(){this.withApp=!1;stopdraw();clearWatch()},release:function(){this.withApp=!0;startdraw();setButtons()}};Bangle.on("lcdPower",function(a){SCREENACCESS.withApp&&(a?startdraw():stopdraw())});var waypoints=require("waypoints").load();
wp=waypoints[0];function nextwp(a){selected&&(wpindex+=a,wpindex>=waypoints.length&&(wpindex=0),0>wpindex&&(wpindex=waypoints.length-1),wp=waypoints[wpindex],drawN())}function doselect(){selected&&0!=wpindex&&void 0===waypoints[wpindex].lat&&savedfix.fix&&(waypoints[wpindex]={name:"@"+wp.name,lat:savedfix.lat,lon:savedfix.lon},wp=waypoints[wpindex],require("waypoints").save(waypoints));selected=!selected;drawN()}g.clear();Bangle.setLCDBrightness(1);Bangle.loadWidgets();Bangle.drawWidgets();
Bangle.setGPSPower(1);drawAll();startTimers();Bangle.on("GPS",onGPS);setButtons();

View File

@ -1,16 +1,15 @@
{
"id": "gpsnav",
"name": "GPS Navigation",
"version": "0.05",
"version": "0.06",
"description": "Displays GPS Course and Speed, + Directions to waypoint and waypoint recording, now with waypoint editor",
"icon": "icon.png",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS"],
"readme": "README.md",
"interface": "waypoints.html",
"dependencies" : { "waypoints":"type" },
"storage": [
{"name":"gpsnav.app.js","url":"app.min.js"},
{"name":"gpsnav.img","url":"app-icon.js","evaluate":true}
],
"data": [{"name":"waypoints.json","url":"waypoints.json"}]
]
}

View File

@ -1,170 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
</head>
<body>
<h4>List of waypoints</h4>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Lat.</th>
<th>Long.</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="waypoints">
</tbody>
</table>
<br>
<h4>Add a new waypoint</h4>
<form id="add_waypoint_form">
<div class="columns">
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" type="text" id="add_waypoint_name" placeholder="Name">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longtitude" placeholder="Long">
</div>
</div>
<div class="columns">
<div class="column col-3 col-xs-8">
<button id="add_name_button" class="btn btn-primary btn-sm">Add Name Only</button>
</div>
<div class="column col-3 col-xs-8">
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
</div>
</div>
</form>
<br>
<button id="Download" class="btn btn-error">Reload</button> <button id="Upload" class="btn btn-primary">Upload</button>
<script src="../../core/lib/interface.js"></script>
<script>
var waypoints = []
var $name = document.getElementById('add_waypoint_name')
var $form = document.getElementById('add_waypoint_form')
var $button = document.getElementById('add_waypoint_button')
var $name_button = document.getElementById('add_name_button')
var $latitude = document.getElementById('add_latitude')
var $longtitude = document.getElementById('add_longtitude')
var $list = document.getElementById('waypoints')
function compare(a, b){
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
if (x=="none") {return -1};
if (y=="none") {return 1};
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
}
$button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
var lat = parseFloat($latitude.value).toPrecision(5);
var lon = parseFloat($longtitude.value).toPrecision(5);
waypoints.push({
name, lat,lon,
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = (0).toPrecision(5);
$longtitude.value = (0).toPrecision(5);
});
$name_button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
waypoints.push({
name
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = 0.0000
$longtitude.value = 0.0000
});
function removeWaypoint(index){
$name.value = waypoints[index].name
$latitude.value = waypoints[index].lat
$longtitude.value = waypoints[index].lon
waypoints = waypoints.filter((p,i) => i!==index)
renderWaypoints()
}
function renderWaypoints(){
$list.innerHTML = ''
waypoints.forEach((waypoint,index) => {
var $waypoint = document.createElement('tr')
if (index==0){
$waypoint.innerHTML = `<td>${waypoint.name}</td>`
} else if(waypoint.lat==undefined){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>------</td><td>-----</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
} else {
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>${waypoint.lat}</td><td>${waypoint.lon}</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
}
$list.appendChild($waypoint)
})
$name.focus()
}
function downloadJSONfile(fileid, callback) {
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`,contents=>{
var storedpts = JSON.parse(contents);
callback(storedpts);
});
}
function uploadFile(fileid, contents) {
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`,ret=>{
console.log("uploadFile",ret);
});
}
function gotStored(pts){
waypoints = pts;
renderWaypoints();
}
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
document.getElementById("Download").addEventListener("click", function() {
downloadJSONfile("waypoints.json", gotStored);
});
document.getElementById("Upload").addEventListener("click", function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
</script>
</body>
</html>

View File

@ -1,20 +0,0 @@
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]

View File

@ -11,3 +11,4 @@
0.11: Detect when waypoints.json is not present, error E-WPT
0.12: Added stepo2 as a replacement for stepo and digi
0.13: Added long press BTN2 toggle gpsrec status in GPS clock
0.14: Move waypoints.json (and editor) to 'waypoints' app

View File

@ -208,14 +208,8 @@ which will obviously limit this.
### Waypoint Editor
Clicking on the download icon of gpsnav in the app loader invokes the
waypoint editor. The editor downloads and displays the current
`waypoints.json` file. Clicking the `Edit` button beside an entry
causes the entry to be deleted from the list and displayed in the
edit boxes. It can be restored - by clicking the `Add waypoint`
button. A new markable entry is created by using the `Add name`
button. The edited `waypoints.json` file is uploaded to the Bangle by
clicking the `Upload` button.
Clicking on the download icon of `Waypoints` in the app loader invokes the
waypoint editor. See the `Waypoints` app for more information.
### Calibration of the Compass

View File

@ -321,7 +321,7 @@ GPS.prototype.getWPbearing = function() {
}
GPS.prototype.loadFirstWaypoint = function() {
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}];
var waypoints = require("waypoints").load();
this.wp_index = 0;
this.wp_current = waypoints[this.wp_index];
log_debug(this.wp_current);
@ -346,7 +346,7 @@ GPS.prototype.markWaypoint = function() {
log_debug("GPS::markWaypoint()");
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}];
var waypoints = require("waypoints").load();
this.wp_current = waypoints[this.wp_index];
if (this.waypointHasLocation()) {
@ -356,12 +356,12 @@ GPS.prototype.markWaypoint = function() {
}
this.wp_current = waypoints[this.wp_index];
require("Storage").writeJSON("waypoints.json", waypoints);
require("waypoints").save(waypoints);
log_debug("GPS::markWaypoint() written");
}
GPS.prototype.nextWaypoint = function(inc) {
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}];
var waypoints = require("waypoints").load();
this.wp_index+=inc;
if (this.wp_index>=waypoints.length) this.wp_index=0;
if (this.wp_index<0) this.wp_index = waypoints.length-1;

View File

@ -1,14 +1,14 @@
{
"id": "kitchen",
"name": "Kitchen Combo",
"version": "0.13",
"version": "0.14",
"description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'",
"icon": "kitchen.png",
"type": "clock",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS"],
"readme": "README.md",
"interface": "waypoints.html",
"dependencies" : { "waypoints":"type" },
"storage": [
{"name":"kitchen.app.js","url":"kitchen.app.js"},
{"name":"stepo2.kit.js","url":"stepo2.kit.js"},
@ -16,6 +16,5 @@
{"name":"gps.kit.js","url":"gps.kit.js"},
{"name":"compass.kit.js","url":"compass.kit.js"},
{"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true}
],
"data": [{"name":"waypoints.json","url":"waypoints.json"}]
]
}

View File

@ -1,170 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
</head>
<body>
<h4>List of waypoints</h4>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Lat.</th>
<th>Long.</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="waypoints">
</tbody>
</table>
<br>
<h4>Add a new waypoint</h4>
<form id="add_waypoint_form">
<div class="columns">
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" type="text" id="add_waypoint_name" placeholder="Name">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longtitude" placeholder="Long">
</div>
</div>
<div class="columns">
<div class="column col-3 col-xs-8">
<button id="add_name_button" class="btn btn-primary btn-sm">Add Name Only</button>
</div>
<div class="column col-3 col-xs-8">
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
</div>
</div>
</form>
<br>
<button id="Download" class="btn btn-error">Reload</button> <button id="Upload" class="btn btn-primary">Upload</button>
<script src="../../core/lib/interface.js"></script>
<script>
var waypoints = []
var $name = document.getElementById('add_waypoint_name')
var $form = document.getElementById('add_waypoint_form')
var $button = document.getElementById('add_waypoint_button')
var $name_button = document.getElementById('add_name_button')
var $latitude = document.getElementById('add_latitude')
var $longtitude = document.getElementById('add_longtitude')
var $list = document.getElementById('waypoints')
function compare(a, b){
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
if (x=="none") {return -1};
if (y=="none") {return 1};
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
}
$button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
var lat = parseFloat($latitude.value).toPrecision(5);
var lon = parseFloat($longtitude.value).toPrecision(5);
waypoints.push({
name, lat,lon,
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = (0).toPrecision(5);
$longtitude.value = (0).toPrecision(5);
});
$name_button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
waypoints.push({
name
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = 0.0000
$longtitude.value = 0.0000
});
function removeWaypoint(index){
$name.value = waypoints[index].name
$latitude.value = waypoints[index].lat
$longtitude.value = waypoints[index].lon
waypoints = waypoints.filter((p,i) => i!==index)
renderWaypoints()
}
function renderWaypoints(){
$list.innerHTML = ''
waypoints.forEach((waypoint,index) => {
var $waypoint = document.createElement('tr')
if (index==0){
$waypoint.innerHTML = `<td>${waypoint.name}</td>`
} else if(waypoint.lat==undefined){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>------</td><td>-----</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
} else {
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>${waypoint.lat}</td><td>${waypoint.lon}</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
}
$list.appendChild($waypoint)
})
$name.focus()
}
function downloadJSONfile(fileid, callback) {
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`,contents=>{
var storedpts = JSON.parse(contents);
callback(storedpts);
});
}
function uploadFile(fileid, contents) {
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`,ret=>{
console.log("uploadFile",ret);
});
}
function gotStored(pts){
waypoints = pts;
renderWaypoints();
}
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
document.getElementById("Download").addEventListener("click", function() {
downloadJSONfile("waypoints.json", gotStored);
});
document.getElementById("Upload").addEventListener("click", function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
</script>
</body>
</html>

View File

@ -1,20 +0,0 @@
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]

View File

@ -71,9 +71,7 @@ The MAX values continue to be collected with the display off so may appear a lit
## Waypoints
Waypoints are used in [D]istance mode. Create a file waypoints.json and write to storage on the Bangle.js using the IDE. The first 6 characters of the name are displayed in Speed+[D]istance mode.
The [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app in the App Loader has a really nice waypoints file editor. (Must be connected to your Bangle.JS and then click on the Download icon.)
Waypoints are used in Distance and VMG modes. See the `Waypoints` app for information on how to create/edit waypoints. The first 6 characters of the name are displayed in Speed+[D]istance mode.
Sample waypoints.json (My sailing waypoints)
@ -149,5 +147,3 @@ Developed for my use in sailing, cycling and motorcycling. If you find this soft
Many thanks to Gordon Williams. Awesome job.
Special thanks also to @jeffmer, for the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app and @hughbarney for the Low power GPS code development and Wouter Bulten for the Kalman filter code.

View File

@ -209,7 +209,7 @@ function nxtWp(inc){
}
function loadWp() {
var w = require("Storage").readJSON('waypoints.json')||[{name:"NONE"}];
var w = require("waypoints").load();
if (cfg.wp>=w.length) cfg.wp=0;
if (cfg.wp<0) cfg.wp = w.length-1;
savSettings();

View File

@ -8,12 +8,12 @@
"type": "app",
"tags": "tool,outdoors",
"supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : { "waypoints":"type" },
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"speedalt.app.js","url":"app.js"},
{"name":"speedalt.img","url":"app-icon.js","evaluate":true},
{"name":"speedalt.settings.js","url":"settings.js"}
],
"data": [{"name":"speedalt.json"}]
]
}

View File

@ -15,3 +15,4 @@
0.15: Droidscript mirroring prog automatically uses last connection address. Auto connects when run.
0.16: Add configuration item Wpt File Suffix. A one character suffix to append to the waypoints.json file. A number of other apps also use this file name. Using the file name suffix allows the speedalt2 waypoints to be retained if one of these other apps is installed for a different use.
0.17: Use default Bangle formatter for booleans
0.18: Move waypoints.json to 'waypoints' app (with editor)

View File

@ -74,13 +74,11 @@ Android Screen Mirroring:<br>
## Waypoints
Waypoints are used in Distance and VMG modes. Create a file waypoints.json and write to storage on the Bangle.js using the IDE. The first 6 characters of the name are displayed in Speed+[D]istance mode.
The [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app in the App Loader has a really nice waypoints file editor. (Must be connected to your Bangle.JS and then click on the Download icon.)
Waypoints are used in Distance and VMG modes. See the `Waypoints` app for information on how to create/edit waypoints. The first 6 characters of the name are displayed in Speed+[D]istance mode.
By default the waypoints file is called waypoints.json
**Note** : The waypoints.json file is used by a number of different gps apps. The setting 'Wpt File Suffix' allows one of waypoints1.json, waypoints2.json or waypoints3.json to be used instead. This allows the other apps to be used with a different set of waypoints without losing the speedalt2 waypoint set.
**Note** : The waypoints.json file is used by a number of different gps apps. The setting 'Wpt File Suffix' allows one of waypoints.1.json, waypoints.2.json or waypoints.3.json to be used instead. This allows the other apps to be used with a different set of waypoints without losing the speedalt2 waypoint set.
Sample waypoints.json (My sailing waypoints)
@ -156,4 +154,3 @@ Developed for my use in sailing, cycling and motorcycling. If you find this soft
Many thanks to Gordon Williams. Awesome job.
Special thanks also to @jeffmer, for the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app and @hughbarney for the Low power GPS code development and Wouter Bulten for the Kalman filter code.

View File

@ -210,7 +210,7 @@ function nxtWp(){
}
function loadWp() {
var w = require("Storage").readJSON('waypoints'+cfg.wptSfx+'.json')||[{name:"NONE"}];
var w = require("waypoints").load(cfg.wptSfx);
if (cfg.wp>=w.length) cfg.wp=0;
if (cfg.wp<0) cfg.wp = w.length-1;
savSettings();

View File

@ -2,12 +2,13 @@
"id": "speedalt2",
"name": "GPS Adventure Sports II",
"shortName":"GPS Adv Sport II",
"version":"0.17",
"version":"0.18",
"description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.",
"icon": "app.png",
"type": "app",
"tags": "tool,outdoors",
"supports": ["BANGLEJS"],
"dependencies" : { "waypoints":"type" },
"readme": "README.md",
"allow_emulator": true,
"storage": [
@ -16,10 +17,6 @@
{"name":"speedalt2.settings.js","url":"settings.js"}
],
"data": [
{"name":"speedalt2.json"},
{"name":"waypoints.json"},
{"name":"waypoints1.json"},
{"name":"waypoints2.json"},
{"name":"waypoints3.json"}
{"name":"speedalt2.json"}
]
}

View File

@ -1,3 +1,4 @@
0.01: New app!
0.02: Make Bangle.js 2 compatible
0.03: Silently use built in heading when no magnav calibration file is present
0.04: Move waypoints.json (and editor) to 'waypoints' app

View File

@ -61,58 +61,10 @@ The app indicates that WP2 is now marked by adding the prefix @ to
it's name. The distance should be small as shown in the screen shot
as you have just marked your current location.
## Waypoint JSON file
When the app is loaded from the app loader, a file named
`waypoints.json` is loaded along with the javascript etc. The file
has the following contents:
```
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]
```
The file contains the initial NONE waypoint which is useful if you
just want to display course and speed. The next two entries are
waypoints to No 10 Downing Street and to Stone Henge - obtained from
Google Maps. The last five entries are entries which can be *marked*.
You add and delete entries using the Web IDE to load and then save
the file from and to watch storage. The app itself does not limit the
number of entries although it does load the entire file into RAM
which will obviously limit this.
## Waypoint Editor
Clicking on the download icon of gpsnav in the app loader invokes the
waypoint editor. The editor downloads and displays the current
`waypoints.json` file. Clicking the `Edit` button beside an entry
causes the entry to be deleted from the list and displayed in the
edit boxes. It can be restored - by clicking the `Add waypoint`
button. A new markable entry is created by using the `Add name`
button. The edited `waypoints.json` file is uploaded to the Bangle by
clicking the `Upload` button.
## Setting Waypoints
Check out the documentation for the `Waypoints` app. This provides
the ability to set waypoints from your browser.
## Calibration of the Compass

View File

@ -250,7 +250,7 @@ Bangle.on('lcdPower',function(on) {
}
});
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
var waypoints = require("waypoints").load();
wp=waypoints[0];
function nextwp(inc){
@ -266,7 +266,7 @@ function doselect(){
if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) {
waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
wp = waypoints[wpindex];
require("Storage").writeJSON("waypoints.json", waypoints);
require("waypoints").save(waypoints);
}
selected=!selected;
drawN();

View File

@ -1,16 +1,15 @@
{
"id": "waypointer",
"name": "Way Pointer",
"version": "0.03",
"version": "0.04",
"description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation",
"icon": "waypointer.png",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS", "BANGLEJS2"],
"dependencies" : { "waypoints":"type" },
"readme": "README.md",
"interface": "waypoints.html",
"storage": [
{"name":"waypointer.app.js","url":"app.js"},
{"name":"waypointer.img","url":"icon.js","evaluate":true}
],
"data": [{"name":"waypoints.json","url":"waypoints.json"}]
]
}

View File

@ -1,170 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
</head>
<body>
<h4>List of waypoints</h4>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Lat.</th>
<th>Long.</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="waypoints">
</tbody>
</table>
<br>
<h4>Add a new waypoint</h4>
<form id="add_waypoint_form">
<div class="columns">
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" type="text" id="add_waypoint_name" placeholder="Name">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longtitude" placeholder="Long">
</div>
</div>
<div class="columns">
<div class="column col-3 col-xs-8">
<button id="add_name_button" class="btn btn-primary btn-sm">Add Name Only</button>
</div>
<div class="column col-3 col-xs-8">
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
</div>
</div>
</form>
<br>
<button id="Download" class="btn btn-error">Reload</button> <button id="Upload" class="btn btn-primary">Upload</button>
<script src="../../core/lib/interface.js"></script>
<script>
var waypoints = []
var $name = document.getElementById('add_waypoint_name')
var $form = document.getElementById('add_waypoint_form')
var $button = document.getElementById('add_waypoint_button')
var $name_button = document.getElementById('add_name_button')
var $latitude = document.getElementById('add_latitude')
var $longtitude = document.getElementById('add_longtitude')
var $list = document.getElementById('waypoints')
function compare(a, b){
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
if (x=="none") {return -1};
if (y=="none") {return 1};
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
}
$button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
var lat = parseFloat($latitude.value);
var lon = parseFloat($longtitude.value);
waypoints.push({
name, lat,lon,
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = (0).toPrecision(5);
$longtitude.value = (0).toPrecision(5);
});
$name_button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
waypoints.push({
name
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = 0.0000
$longtitude.value = 0.0000
});
function removeWaypoint(index){
$name.value = waypoints[index].name
$latitude.value = waypoints[index].lat
$longtitude.value = waypoints[index].lon
waypoints = waypoints.filter((p,i) => i!==index)
renderWaypoints()
}
function renderWaypoints(){
$list.innerHTML = ''
waypoints.forEach((waypoint,index) => {
var $waypoint = document.createElement('tr')
if (index==0){
$waypoint.innerHTML = `<td>${waypoint.name}</td>`
} else if(waypoint.lat==undefined){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>------</td><td>-----</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
} else {
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>${waypoint.lat}</td><td>${waypoint.lon}</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
}
$list.appendChild($waypoint)
})
$name.focus()
}
function downloadJSONfile(fileid, callback) {
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`,contents=>{
var storedpts = JSON.parse(contents);
callback(storedpts);
});
}
function uploadFile(fileid, contents) {
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`,ret=>{
console.log("uploadFile",ret);
});
}
function gotStored(pts){
waypoints = pts;
renderWaypoints();
}
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
document.getElementById("Download").addEventListener("click", function() {
downloadJSONfile("waypoints.json", gotStored);
});
document.getElementById("Upload").addEventListener("click", function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
</script>
</body>
</html>

View File

@ -1,20 +0,0 @@
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]

1
apps/waypoints/ChangeLog Normal file
View File

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

56
apps/waypoints/README.md Normal file
View File

@ -0,0 +1,56 @@
# Waypoints
This app provides a common way to set up the `waypoints.json` file,
which several other apps rely on for navigation.
## Waypoint JSON file
When the app is loaded from the app loader, a file named
`waypoints.json` is loaded along with the javascript etc. The file
has the following contents:
```
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]
```
The file contains the initial NONE waypoint which is useful if you
just want to display course and speed. The next two entries are
waypoints to No 10 Downing Street and to Stone Henge - obtained from
Google Maps. The last five entries are entries which can be *marked*.
You add and delete entries using the Web IDE to load and then save
the file from and to watch storage. The app itself does not limit the
number of entries although it does load the entire file into RAM
which will obviously limit this.
## Waypoint Editor
Clicking on the download icon of `Waypoints` in the app loader invokes the
waypoint editor. The editor downloads and displays the current
`waypoints.json` file. Clicking the `Edit` button beside an entry
causes the entry to be deleted from the list and displayed in the
edit boxes. It can be restored - by clicking the `Add waypoint`
button. A new markable entry is created by using the `Add name`
button. The edited `waypoints.json` file is uploaded to the Bangle by
clicking the `Upload` button.

View File

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

34
apps/waypoints/app.js Normal file
View File

@ -0,0 +1,34 @@
// place your const, vars, functions or classes here
// clear the screen
g.clear();
var n = 0;
// redraw the screen
function draw() {
g.reset().clearRect(Bangle.appRect);
g.setFont("6x8").setFontAlign(0,0).drawString("Up / Down",g.getWidth()/2,g.getHeight()/2 - 20);
g.setFont("Vector",60).setFontAlign(0,0).drawString(n,g.getWidth()/2,g.getHeight()/2 + 30);
}
// Respond to user input
Bangle.setUI({mode: "updown"}, function(dir) {
if (dir<0) {
n--;
draw();
} else if (dir>0) {
n++;
draw();
} else {
n = 0;
draw();
}
});
// First draw...
draw();
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();

BIN
apps/waypoints/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -4,6 +4,7 @@
<meta charset="utf-8">
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css">
@ -11,6 +12,8 @@
html, body { height: 100% }
.flex-col { display:flex; flex-direction:column; height:100% }
#map { width:100%; height:100% }
#tab-map { width:100%; height:100% }
#tab-list { width:100%; height:100% }
/* https://stackoverflow.com/a/58686215 */
.arrow-icon {
@ -23,6 +26,7 @@
transform-origin: center center;
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
}
</style>
</head>
<body>
@ -32,8 +36,57 @@
<span id="status"></span>
<span id="routestatus"></span>
</div>
<div>
<ul class="tab tab-block">
<li class="tab-item active" id="tabitem-map">
<a href="#">Map</a>
</li>
<li class="tab-item" id="tabitem-list">
<a href="#">List</a>
</li>
</ul>
</div>
<div style="flex: 1">
<div id="tab-map">
<div id="map"></div>
</div>
<div id="tab-list" style="display:none">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Lat.</th>
<th>Long.</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="waypoints">
</tbody>
</table>
<br>
<h4>Add a new waypoint</h4>
<form id="add_waypoint_form">
<div class="columns">
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" type="text" id="add_waypoint_name" placeholder="Name">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longtitude" placeholder="Long">
</div>
</div>
<div class="columns">
<div class="column col-3 col-xs-8">
<button id="add_name_button" class="btn btn-primary btn-sm">Add Name Only</button>
</div>
<div class="column col-3 col-xs-8">
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
</div>
</div>
</form>
</div>
</div>
</div>
@ -44,8 +97,23 @@
<script src="../../core/lib/interface.js"></script>
<script>
var map;
var waypoints = [];
// ========================================================================== tabs
document.getElementById('tabitem-map').addEventListener('click',function() {
document.getElementById('tabitem-map').classList.remove("active");
document.getElementById('tabitem-list').classList.add("active");
document.getElementById('tab-map').style.display="block";
document.getElementById('tab-list').style.display="none";
});
document.getElementById('tabitem-list').addEventListener('click',function() {
document.getElementById('tabitem-map').classList.add("active");
document.getElementById('tabitem-list').classList.remove("active");
document.getElementById('tab-map').style.display="none";
document.getElementById('tab-list').style.display="block";
});
// ========================================================================== MAP
var map;
var mapmarkers = L.layerGroup();
var searchresult = L.layerGroup();
var dynamicarrow = L.layerGroup();
@ -100,9 +168,6 @@
L.featureGroup(getArrows(latlngs, 'black', 2, map)).addTo(dynamicarrow)
map.addLayer(dynamicarrow);
});
clean();
renderAllWaypoints();
/*** status ***/
function clean() {
@ -152,11 +217,11 @@
dirty();
}
function renderWaypoints(wps, isroute, parentidx) {
function renderWaypointsMap(wps, isroute, parentidx) {
var latlngs = [];
for (var i = 0; i < wps.length; i++) {
if (wps[i].route) {
renderWaypoints(wps[i].route, true, i);
renderWaypointsMap(wps[i].route, true, i);
continue;
}
if (wps[i].lat == null || wps[i].lon == null)
@ -182,7 +247,8 @@
function renderAllWaypoints() {
mapmarkers.clearLayers();
renderWaypoints(waypoints, false, 0);
renderWaypointsMap(waypoints, false, 0);
renderWaypointsList();
map.addLayer(mapmarkers);
}
@ -234,51 +300,18 @@
/*** Bangle.js ***/
function downloadJSONfile(fileid, callback) {
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`, contents => {
var storedpts = JSON.parse(contents);
callback(storedpts);
clean();
});
}
function uploadFile(fileid, contents) {
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`, ret => {
console.log("uploadFile", ret);
if (ret == "OK")
clean();
});
}
function gotStored(pts) {
waypoints = pts;
var latlngs = waypoints.map(p => [p.lat, p.lon]);
var latlngs = waypoints.filter(p => isFinite(p.lat)&&isFinite(p.lon)).map(p => [p.lat, p.lon]);
var poly = L.polygon(latlngs);
map.fitBounds(poly.getBounds());
var bounds = poly.getBounds();
if (bounds.isValid())
map.fitBounds(bounds);
renderAllWaypoints();
}
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
$('#download').on('click', function() {
downloadJSONfile("waypoints.json", gotStored);
});
$('#upload').click(function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
$('#statusarea').click(closeRoute);
/*** map arrows ***/
@ -348,5 +381,136 @@
this.x = (round ? Math.round(x) : x);
this.y = (round ? Math.round(y) : y);
}
// ========================================================================== LIST
var $name = document.getElementById('add_waypoint_name')
var $form = document.getElementById('add_waypoint_form')
var $button = document.getElementById('add_waypoint_button')
var $name_button = document.getElementById('add_name_button')
var $latitude = document.getElementById('add_latitude')
var $longtitude = document.getElementById('add_longtitude')
var $list = document.getElementById('waypoints')
function compare(a, b){
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
if (x=="none") {return -1};
if (y=="none") {return 1};
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
}
$button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
var lat = parseFloat($latitude.value);
var lon = parseFloat($longtitude.value);
waypoints.push({
name, lat,lon,
});
waypoints.sort(compare);
renderAllWaypoints()
$name.value = ''
$latitude.value = (0).toPrecision(5);
$longtitude.value = (0).toPrecision(5);
});
$name_button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
waypoints.push({
name
});
waypoints.sort(compare);
renderAllWaypoints()
$name.value = ''
$latitude.value = 0.0000
$longtitude.value = 0.0000
});
function removeWaypoint(index){
$name.value = waypoints[index].name
$latitude.value = waypoints[index].lat
$longtitude.value = waypoints[index].lon
waypoints = waypoints.filter((p,i) => i!==index)
renderAllWaypoints()
}
function renderWaypointsList(){
$list.innerHTML = ''
waypoints.forEach((waypoint,index) => {
var $waypoint = document.createElement('tr')
if (index==0){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td></td><td></td>`
} else if(waypoint.lat==undefined){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>------</td><td>-----</td>`;
} else {
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>${waypoint.lat.toFixed(6)}</td><td>${waypoint.lon.toFixed(6)}</td>`;
}
$waypoint.innerHTML += `<td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-delete"></i></button></td>`;
$list.appendChild($waypoint)
})
$name.focus()
}
function renderWaypoints() {
renderWaypointsList();
renderWaypointsMap();
}
// ========================================================================== UPLOAD/DOWNLOAD
function downloadJSONfile(fileid, callback) {
// TODO: use interface.js-provided stuff?
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`, contents => {
if (contents=='[{name:"NONE"}]') contents="[]";
var storedpts = JSON.parse(contents);
callback(storedpts);
clean();
});
}
function uploadFile(fileid, contents) {
// TODO: use interface.js-provided stuff?
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`, ret => {
console.log("uploadFile", ret);
if (ret == "OK")
clean();
});
}
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
$('#download').on('click', function() {
downloadJSONfile("waypoints.json", gotStored);
});
$('#upload').click(function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
// ========================================================================== FINALLY...
clean();
renderAllWaypoints();
</script>
</body>
</body>
</html>

7
apps/waypoints/lib.js Normal file
View File

@ -0,0 +1,7 @@
exports.load = (num) => {
return require("Storage").readJSON(`waypoints${num?`.${num}`:""}.json`)||[{name:"NONE"}];
};
exports.save = (waypoints,num) => {
require("Storage").writeJSON(`waypoints${num?`.${num}`:""}.json`, waypoints);
};

View File

@ -0,0 +1,20 @@
{ "id": "waypoints",
"name": "Waypoints",
"version":"0.01",
"description": "Provides 'waypoints.json' used by various navigation apps, as well as a way to edit it from the App Loader with maps or a list",
"icon": "app.png",
"tags": "tool,outdoors,gps",
"type": "waypoints",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"interface": "interface.html",
"storage": [
{"name":"waypoints","url":"lib.js"}
],
"data": [
{"name":"waypoints.json","url":"waypoints.json"},
{"name":"waypoints.1.json"},
{"name":"waypoints.2.json"},
{"name":"waypoints.3.json"}
]
}

View File

@ -0,0 +1,12 @@
[
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
}
]

View File

@ -1,2 +1,3 @@
...
0.02: First update with ChangeLog Added
0.03: Move waypoints.json (and editor) to 'waypoints' app

View File

@ -1,6 +1,6 @@
var loc = require("locale");
var waypoints = require("Storage").readJSON("waypoints.json") || [];
var waypoints = require("waypoints").load();
var wp = waypoints[0];
if (wp == undefined) wp = {name:"NONE"};
var wp_bearing = 0;
@ -196,7 +196,7 @@ function addCurrentWaypoint() {
}
function saveWaypoints() {
require("Storage").writeJSON("waypoints.json", waypoints);
require("waypoints").save(waypoints);
}
function deleteWaypoint(w) {

View File

@ -2,17 +2,16 @@
"id": "wpmoto",
"name": "Waypointer Moto",
"shortName": "Waypointer Moto",
"version": "0.02",
"version": "0.03",
"description": "Waypoint-based motorcycle navigation aid",
"icon": "wpmoto.png",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS","BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot-menu.png"},{"url":"screenshot-delete.png"}],
"readme": "README.md",
"interface": "wpmoto.html",
"dependencies" : { "waypoints":"type" },
"storage": [
{"name":"wpmoto.app.js","url":"app.js"},
{"name":"wpmoto.img","url":"icon.js","evaluate":true}
],
"data": [{"name":"waypoints.json","url":"waypoints.json"}]
]
}

View File

@ -1,5 +0,0 @@
[
{
"name":"NONE"
},
]

View File

@ -77,12 +77,13 @@ const APP_KEYS = [
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports'];
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports'
const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale","settings"]; // values allowed for "type" field
const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale","settings","waypoints"]; // values allowed for "type" field
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
const GRANDFATHERED_ICONS = ["s7clk", "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"];
const INTERNAL_FILES_IN_APP_TYPE = { // list of app types and files they SHOULD provide...
'textinput' : ['textinput'],
'waypoints' : ['waypoints'],
// notify?
};