mirror of https://github.com/espruino/BangleApps
Merge pull request #3171 from flaparoo/aviatorclk
New app: aviatorclk (and module "avwx")pull/3182/head
commit
ed2d6e8794
|
@ -0,0 +1 @@
|
||||||
|
aviatorclk.json
|
|
@ -0,0 +1 @@
|
||||||
|
1.00: initial release
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Aviator Clock
|
||||||
|
|
||||||
|
A clock for aviators, with local time and UTC - and the latest METAR
|
||||||
|
(Meteorological Aerodrome Report) for the nearest airport
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
This app depends on the [AVWX module](?id=avwx). Make sure to configure that
|
||||||
|
module after installing this app.
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Local time (with optional seconds)
|
||||||
|
- UTC / Zulu time
|
||||||
|
- Weekday and day of the month
|
||||||
|
- Latest METAR for the nearest airport (scrollable)
|
||||||
|
|
||||||
|
Tap the screen in the top or bottom half to scroll the METAR text (in case not
|
||||||
|
the whole report fits on the screen).
|
||||||
|
|
||||||
|
The colour of the METAR text will change to orange if the report is more than
|
||||||
|
1h old, and red if it's older than 1.5h.
|
||||||
|
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
- **Show Seconds**: to conserve battery power, you can turn the seconds display off
|
||||||
|
- **Invert Scrolling**: swaps the METAR scrolling direction of the top and bottom taps
|
||||||
|
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Flaparoo [github](https://github.com/flaparoo)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwg96iIACCqMBCwYABiAWQiUiAAUhDBwWGDCAWHDAYuMCw4ABGBYWKGBYuLGBcBLpAXNFxhIKFxgwCIyhIJC58hC44WNC5B2NPBIXbBYIAHNgIXKCpAYEC5AhBII8SDAQXJMI5EEC6ZREC6EhFwkRO4zuCC46AFAgLYEC4YCBIoaADF4gXEKgYXDVBAcCXxBZDkcyDRAXHmILCif//4GEC5f/PQQWB//zbAX/C5gAKC78BC6K/In4WJ+YXW+QXHMAURl4XJeQYWEGALhBC4q+BYYLbDFwowCkLTCRIyNHGArNBC48SFxIXCMApHDOwQXIJAIQCAAaWCDYJGIDAipGFwQWKDAUSDAnzUoIWMDAcjn/zUgQWOPYYADOZJjKFqIAp"))
|
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
* Aviator Clock - Bangle.js
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const COLOUR_DARK_GREY = 0x4208; // same as: g.setColor(0.25, 0.25, 0.25)
|
||||||
|
const COLOUR_GREY = 0x8410; // same as: g.setColor(0.5, 0.5, 0.5)
|
||||||
|
const COLOUR_LIGHT_GREY = 0xc618; // same as: g.setColor(0.75, 0.75, 0.75)
|
||||||
|
const COLOUR_RED = 0xf800; // same as: g.setColor(1, 0, 0)
|
||||||
|
const COLOUR_BLUE = 0x001f; // same as: g.setColor(0, 0, 1)
|
||||||
|
const COLOUR_YELLOW = 0xffe0; // same as: g.setColor(1, 1, 0)
|
||||||
|
const COLOUR_LIGHT_CYAN = 0x87ff; // same as: g.setColor(0.5, 1, 1)
|
||||||
|
const COLOUR_DARK_YELLOW = 0x8400; // same as: g.setColor(0.5, 0.5, 0)
|
||||||
|
const COLOUR_DARK_CYAN = 0x0410; // same as: g.setColor(0, 0.5, 0.5)
|
||||||
|
const COLOUR_ORANGE = 0xfc00; // same as: g.setColor(1, 0.5, 0)
|
||||||
|
|
||||||
|
const APP_NAME = 'aviatorclk';
|
||||||
|
|
||||||
|
const horizontalCenter = g.getWidth()/2;
|
||||||
|
const mainTimeHeight = 38;
|
||||||
|
const secondaryFontHeight = 22;
|
||||||
|
const dateColour = ( g.theme.dark ? COLOUR_YELLOW : COLOUR_BLUE );
|
||||||
|
const UTCColour = ( g.theme.dark ? COLOUR_LIGHT_CYAN : COLOUR_DARK_CYAN );
|
||||||
|
const separatorColour = ( g.theme.dark ? COLOUR_LIGHT_GREY : COLOUR_DARK_GREY );
|
||||||
|
|
||||||
|
const avwx = require('avwx');
|
||||||
|
|
||||||
|
|
||||||
|
// read in the settings
|
||||||
|
var settings = Object.assign({
|
||||||
|
showSeconds: true,
|
||||||
|
invertScrolling: false,
|
||||||
|
}, require('Storage').readJSON(APP_NAME+'.json', true) || {});
|
||||||
|
|
||||||
|
|
||||||
|
// globals
|
||||||
|
var drawTimeout;
|
||||||
|
var secondsInterval;
|
||||||
|
var avwxTimeout;
|
||||||
|
|
||||||
|
var AVWXrequest;
|
||||||
|
var METAR = '';
|
||||||
|
var METARlinesCount = 0;
|
||||||
|
var METARscollLines = 0;
|
||||||
|
var METARts;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// date object to time string in format HH:MM[:SS]
|
||||||
|
// (with a leading 0 for hours if required, unlike the "locale" time() function)
|
||||||
|
function timeStr(date, seconds) {
|
||||||
|
let timeStr = date.getHours().toString();
|
||||||
|
if (timeStr.length == 1) timeStr = '0' + timeStr;
|
||||||
|
let minutes = date.getMinutes().toString();
|
||||||
|
if (minutes.length == 1) minutes = '0' + minutes;
|
||||||
|
timeStr += ':' + minutes;
|
||||||
|
if (seconds) {
|
||||||
|
let seconds = date.getSeconds().toString();
|
||||||
|
if (seconds.length == 1) seconds = '0' + seconds;
|
||||||
|
timeStr += ':' + seconds;
|
||||||
|
}
|
||||||
|
return timeStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// draw the METAR info
|
||||||
|
function drawAVWX() {
|
||||||
|
let now = new Date();
|
||||||
|
let METARage = 0; // in minutes
|
||||||
|
if (METARts) {
|
||||||
|
METARage = Math.floor((now - METARts) / 60000);
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setBgColor(g.theme.bg);
|
||||||
|
|
||||||
|
let y = Bangle.appRect.y + mainTimeHeight + secondaryFontHeight + 4;
|
||||||
|
g.clearRect(0, y, g.getWidth(), y + (secondaryFontHeight * 4));
|
||||||
|
|
||||||
|
g.setFontAlign(0, -1).setFont("Vector", secondaryFontHeight);
|
||||||
|
if (METARage > 90) { // older than 1.5h
|
||||||
|
g.setColor(COLOUR_RED);
|
||||||
|
} else if (METARage > 60) { // older than 1h
|
||||||
|
g.setColor( g.theme.dark ? COLOUR_ORANGE : COLOUR_DARK_YELLOW );
|
||||||
|
} else {
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
}
|
||||||
|
let METARlines = g.wrapString(METAR, g.getWidth());
|
||||||
|
METARlinesCount = METARlines.length;
|
||||||
|
METARlines.splice(0, METARscollLines);
|
||||||
|
g.drawString(METARlines.join("\n"), horizontalCenter, y, true);
|
||||||
|
|
||||||
|
if (! avwxTimeout) { avwxTimeout = setTimeout(updateAVWX, 5 * 60000); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the METAR info
|
||||||
|
function updateAVWX() {
|
||||||
|
if (avwxTimeout) clearTimeout(avwxTimeout);
|
||||||
|
avwxTimeout = undefined;
|
||||||
|
|
||||||
|
METAR = '\nGetting GPS fix';
|
||||||
|
METARlinesCount = 0; METARscollLines = 0;
|
||||||
|
METARts = undefined;
|
||||||
|
drawAVWX();
|
||||||
|
|
||||||
|
Bangle.setGPSPower(true, APP_NAME);
|
||||||
|
Bangle.on('GPS', fix => {
|
||||||
|
// prevent multiple, simultaneous requests
|
||||||
|
if (AVWXrequest) { return; }
|
||||||
|
|
||||||
|
if ('fix' in fix && fix.fix != 0 && fix.satellites >= 4) {
|
||||||
|
Bangle.setGPSPower(false, APP_NAME);
|
||||||
|
let lat = fix.lat;
|
||||||
|
let lon = fix.lon;
|
||||||
|
|
||||||
|
METAR = '\nRequesting METAR';
|
||||||
|
METARlinesCount = 0; METARscollLines = 0;
|
||||||
|
METARts = undefined;
|
||||||
|
drawAVWX();
|
||||||
|
|
||||||
|
// get latest METAR from nearest airport (via AVWX API)
|
||||||
|
AVWXrequest = avwx.request('metar/'+lat+','+lon, 'onfail=nearest', data => {
|
||||||
|
if (avwxTimeout) clearTimeout(avwxTimeout);
|
||||||
|
avwxTimeout = undefined;
|
||||||
|
|
||||||
|
let METARjson = JSON.parse(data.resp);
|
||||||
|
|
||||||
|
if ('sanitized' in METARjson) {
|
||||||
|
METAR = METARjson.sanitized;
|
||||||
|
} else {
|
||||||
|
METAR = 'No "sanitized" METAR data found!';
|
||||||
|
}
|
||||||
|
METARlinesCount = 0; METARscollLines = 0;
|
||||||
|
|
||||||
|
if ('time' in METARjson) {
|
||||||
|
METARts = new Date(METARjson.time.dt);
|
||||||
|
let now = new Date();
|
||||||
|
let METARage = Math.floor((now - METARts) / 60000); // in minutes
|
||||||
|
if (METARage <= 30) {
|
||||||
|
// some METARs update every 30 min -> attempt to update after METAR is 35min old
|
||||||
|
avwxTimeout = setTimeout(updateAVWX, (35 - METARage) * 60000);
|
||||||
|
} else if (METARage <= 60) {
|
||||||
|
// otherwise, attempt METAR update after it's 65min old
|
||||||
|
avwxTimeout = setTimeout(updateAVWX, (65 - METARage) * 60000);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
METARts = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawAVWX();
|
||||||
|
AVWXrequest = undefined;
|
||||||
|
|
||||||
|
}, error => {
|
||||||
|
// AVWX API request failed
|
||||||
|
console.log(error);
|
||||||
|
METAR = 'ERR: ' + error;
|
||||||
|
METARlinesCount = 0; METARscollLines = 0;
|
||||||
|
METARts = undefined;
|
||||||
|
drawAVWX();
|
||||||
|
AVWXrequest = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// draw only the seconds part of the main clock
|
||||||
|
function drawSeconds() {
|
||||||
|
let now = new Date();
|
||||||
|
let seconds = now.getSeconds().toString();
|
||||||
|
if (seconds.length == 1) seconds = '0' + seconds;
|
||||||
|
let y = Bangle.appRect.y + mainTimeHeight - 3;
|
||||||
|
g.setFontAlign(-1, 1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_GREY);
|
||||||
|
g.drawString(seconds, horizontalCenter + 54, y, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sync seconds update
|
||||||
|
function syncSecondsUpdate() {
|
||||||
|
drawSeconds();
|
||||||
|
setTimeout(function() {
|
||||||
|
drawSeconds();
|
||||||
|
secondsInterval = setInterval(drawSeconds, 1000);
|
||||||
|
}, 1000 - (Date.now() % 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
// set timeout for per-minute updates
|
||||||
|
function queueDraw() {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function() {
|
||||||
|
drawTimeout = undefined;
|
||||||
|
if (METARts) {
|
||||||
|
let now = new Date();
|
||||||
|
let METARage = Math.floor((now - METARts) / 60000);
|
||||||
|
if (METARage > 60) {
|
||||||
|
// the METAR colour might have to be updated:
|
||||||
|
drawAVWX();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw();
|
||||||
|
}, 60000 - (Date.now() % 60000));
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw top part of clock (main time, date and UTC)
|
||||||
|
function draw() {
|
||||||
|
let now = new Date();
|
||||||
|
let nowUTC = new Date(now + (now.getTimezoneOffset() * 1000 * 60));
|
||||||
|
|
||||||
|
// prepare main clock area
|
||||||
|
let y = Bangle.appRect.y;
|
||||||
|
|
||||||
|
g.setBgColor(g.theme.bg);
|
||||||
|
|
||||||
|
// main time display
|
||||||
|
g.setFontAlign(0, -1).setFont("Vector", mainTimeHeight).setColor(g.theme.fg);
|
||||||
|
g.drawString(timeStr(now, false), horizontalCenter, y, true);
|
||||||
|
|
||||||
|
// prepare second line (UTC and date)
|
||||||
|
y += mainTimeHeight;
|
||||||
|
g.clearRect(0, y, g.getWidth(), y + secondaryFontHeight - 1);
|
||||||
|
|
||||||
|
// weekday and day of the month
|
||||||
|
g.setFontAlign(-1, -1).setFont("Vector", secondaryFontHeight).setColor(dateColour);
|
||||||
|
g.drawString(require("locale").dow(now, 1).toUpperCase() + ' ' + now.getDate(), 0, y, false);
|
||||||
|
|
||||||
|
// UTC
|
||||||
|
g.setFontAlign(1, -1).setFont("Vector", secondaryFontHeight).setColor(UTCColour);
|
||||||
|
g.drawString(timeStr(nowUTC, false) + "Z", g.getWidth(), y, false);
|
||||||
|
|
||||||
|
queueDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// initialise
|
||||||
|
g.clear(true);
|
||||||
|
|
||||||
|
// scroll METAR lines on taps
|
||||||
|
Bangle.setUI("clockupdown", action => {
|
||||||
|
switch (action) {
|
||||||
|
case -1: // top tap
|
||||||
|
if (settings.invertScrolling) {
|
||||||
|
if (METARscollLines > 0)
|
||||||
|
METARscollLines--;
|
||||||
|
} else {
|
||||||
|
if (METARscollLines < METARlinesCount - 4)
|
||||||
|
METARscollLines++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: // bottom tap
|
||||||
|
if (settings.invertScrolling) {
|
||||||
|
if (METARscollLines < METARlinesCount - 4)
|
||||||
|
METARscollLines++;
|
||||||
|
} else {
|
||||||
|
if (METARscollLines > 0)
|
||||||
|
METARscollLines--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
drawAVWX();
|
||||||
|
});
|
||||||
|
|
||||||
|
// load widgets
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
// draw static separator line
|
||||||
|
y = Bangle.appRect.y + mainTimeHeight + secondaryFontHeight;
|
||||||
|
g.setColor(separatorColour);
|
||||||
|
g.drawLine(0, y, g.getWidth(), y);
|
||||||
|
|
||||||
|
// draw times and request METAR
|
||||||
|
draw();
|
||||||
|
if (settings.showSeconds)
|
||||||
|
syncSecondsUpdate();
|
||||||
|
updateAVWX();
|
||||||
|
|
||||||
|
|
||||||
|
// TMP for debugging:
|
||||||
|
//METAR = 'YAAA 011100Z 21014KT CAVOK 23/08 Q1018 RMK RF000/0000';
|
||||||
|
//METAR = 'YAAA 150900Z 14012KT 9999 SCT045 BKN064 26/14 Q1012 RMK RF000/0000 DL-W/DL-NW';
|
||||||
|
//METAR = 'YAAA 020030Z VRB CAVOK';
|
||||||
|
//METARts = new Date(Date.now() - 61 * 60000); // 61 to trigger warning, 91 to trigger alert
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1,35 @@
|
||||||
|
(function(back) {
|
||||||
|
var FILE = "aviatorclk.json";
|
||||||
|
|
||||||
|
// Load settings
|
||||||
|
var settings = Object.assign({
|
||||||
|
showSeconds: true,
|
||||||
|
invertScrolling: false,
|
||||||
|
}, require('Storage').readJSON(FILE, true) || {});
|
||||||
|
|
||||||
|
function writeSettings() {
|
||||||
|
require('Storage').writeJSON(FILE, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the menu
|
||||||
|
E.showMenu({
|
||||||
|
"" : { "title" : "AV8R Clock" },
|
||||||
|
"< Back" : () => back(),
|
||||||
|
'Show Seconds': {
|
||||||
|
value: !!settings.showSeconds, // !! converts undefined to false
|
||||||
|
format: v => v ? "On" : "Off",
|
||||||
|
onchange: v => {
|
||||||
|
settings.showSeconds = v;
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Invert Scrolling': {
|
||||||
|
value: !!settings.invertScrolling, // !! converts undefined to false
|
||||||
|
format: v => v ? "On" : "Off",
|
||||||
|
onchange: v => {
|
||||||
|
settings.invertScrolling = v;
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"id": "aviatorclk",
|
||||||
|
"name": "Aviator Clock",
|
||||||
|
"shortName":"AV8R Clock",
|
||||||
|
"version":"1.00",
|
||||||
|
"description": "A clock for aviators, with local time and UTC - and the latest METAR for the nearest airport",
|
||||||
|
"icon": "aviatorclk.png",
|
||||||
|
"screenshots": [{ "url": "screenshot.png" }, { "url": "screenshot2.png" }],
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"dependencies" : { "avwx": "module" },
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{ "name":"aviatorclk.app.js", "url":"aviatorclk.app.js" },
|
||||||
|
{ "name":"aviatorclk.settings.js", "url":"aviatorclk.settings.js" },
|
||||||
|
{ "name":"aviatorclk.img", "url":"aviatorclk-icon.js", "evaluate":true }
|
||||||
|
],
|
||||||
|
"data": [{ "name":"aviatorclk.json" }]
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
|
@ -0,0 +1 @@
|
||||||
|
1.00: initial release
|
|
@ -0,0 +1,41 @@
|
||||||
|
# AVWX Module
|
||||||
|
|
||||||
|
This is a module/library to use the [AVWX](https://account.avwx.rest/) Aviation
|
||||||
|
Weather API. It doesn't include an app.
|
||||||
|
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
You will need an AVWX account (see above for link) and generate an API token.
|
||||||
|
The free "Hobby" plan is normally sufficient, but please consider supporting
|
||||||
|
the AVWX project.
|
||||||
|
|
||||||
|
After installing the module on your Bangle, use the "interface" page (floppy
|
||||||
|
disk icon) in the App Loader to set the API token.
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Include the module in your app with:
|
||||||
|
|
||||||
|
const avwx = require('avwx');
|
||||||
|
|
||||||
|
Then use the exported function, for example to get the "sanitized" METAR from
|
||||||
|
the nearest station to a lat/lon coordinate pair:
|
||||||
|
|
||||||
|
reqID = avwx.request('metar/'+lat+','+lon,
|
||||||
|
'filter=sanitized&onfail=nearest',
|
||||||
|
data => { console.log(data); },
|
||||||
|
error => { console.log(error); });
|
||||||
|
|
||||||
|
The returned reqID can be useful to track whether a request has already been
|
||||||
|
made (ie. the app is still waiting on a response).
|
||||||
|
|
||||||
|
Please consult the [AVWX documentation](https://avwx.docs.apiary.io/) for
|
||||||
|
information about the available end-points and request parameters.
|
||||||
|
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Flaparoo [github](https://github.com/flaparoo)
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* AVWX Bangle Module
|
||||||
|
*
|
||||||
|
* AVWX doco: https://avwx.docs.apiary.io/
|
||||||
|
* test AVWX API request with eg.: curl -X GET 'https://avwx.rest/api/metar/43.9844,-88.5570?token=...'
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const AVWX_BASE_URL = 'https://avwx.rest/api/'; // must end with a slash
|
||||||
|
const AVWX_CONFIG_FILE = 'avwx.json';
|
||||||
|
|
||||||
|
|
||||||
|
// read in the settings
|
||||||
|
var AVWXsettings = Object.assign({
|
||||||
|
AVWXtoken: '',
|
||||||
|
}, require('Storage').readJSON(AVWX_CONFIG_FILE, true) || {});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an AVWX API request
|
||||||
|
*
|
||||||
|
* @param {string} requestPath API path (after /api/), eg. 'meta/KOSH'
|
||||||
|
* @param {string} params optional request parameters, eg. 'onfail=nearest' (use '&' in the string to combine multiple params)
|
||||||
|
* @param {function} successCB callback if the API request was successful - will supply the returned data: successCB(data)
|
||||||
|
* @param {function} failCB callback in case the API request failed - will supply the error: failCB(error)
|
||||||
|
*
|
||||||
|
* @returns {number} the HTTP request ID
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* reqID = avwx.request('metar/'+lat+','+lon,
|
||||||
|
* 'filter=sanitized&onfail=nearest',
|
||||||
|
* data => { console.log(data); },
|
||||||
|
* error => { console.log(error); });
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
exports.request = function(requestPath, optParams, successCB, failCB) {
|
||||||
|
if (! AVWXsettings.AVWXtoken) {
|
||||||
|
failCB('No AVWX API Token defined!');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let params = 'token='+AVWXsettings.AVWXtoken;
|
||||||
|
if (optParams)
|
||||||
|
params += '&'+optParams;
|
||||||
|
return Bangle.http(AVWX_BASE_URL+requestPath+'?'+params).then(successCB).catch(failCB);
|
||||||
|
};
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,47 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<p>To use the <a href="https://account.avwx.rest/">AVWX</a> API, you need an account and generate an API token. The free "Hobby" plan is sufficient, but please consider supporting the AVWX project.</p>
|
||||||
|
<p>
|
||||||
|
<label class="form-label" for="AVWXtoken">AVWX API Token:</label>
|
||||||
|
<input class="form-input" type="text" id="AVWXtoken" placeholder="Your personal AVWX API Token" />
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<button id="upload" class="btn btn-primary">Configure</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><div id="status"></div></p>
|
||||||
|
|
||||||
|
<script src="../../core/lib/interface.js"></script>
|
||||||
|
<script>
|
||||||
|
var AVWXsettings = {};
|
||||||
|
|
||||||
|
function onInit() {
|
||||||
|
// read in existing settings to preserve them during an update
|
||||||
|
try {
|
||||||
|
Util.readStorageJSON('avwx.json', currentSettings => {
|
||||||
|
if (currentSettings) {
|
||||||
|
AVWXsettings = currentSettings;
|
||||||
|
if ('AVWXtoken' in AVWXsettings) {
|
||||||
|
document.getElementById('AVWXtoken').value = AVWXsettings.AVWXtoken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Failed to read existing settings: "+e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("upload").addEventListener("click", function() {
|
||||||
|
AVWXsettings.AVWXtoken = document.getElementById('AVWXtoken').value;
|
||||||
|
Util.writeStorage('avwx.json', JSON.stringify(AVWXsettings), () => {
|
||||||
|
document.getElementById("status").innerHTML = 'AVWX configuration successfully uploaded to Bangle!';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"id": "avwx",
|
||||||
|
"name": "AVWX Module",
|
||||||
|
"shortName":"AVWX",
|
||||||
|
"version":"1.00",
|
||||||
|
"description": "Module/library for the AVWX API",
|
||||||
|
"icon": "avwx.png",
|
||||||
|
"type": "module",
|
||||||
|
"tags": "outdoors",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"provides_modules": ["avwx"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"interface": "interface.html",
|
||||||
|
"storage": [
|
||||||
|
{ "name":"avwx", "url":"avwx.js" }
|
||||||
|
],
|
||||||
|
"data": [{ "name":"avwx.json" }]
|
||||||
|
}
|
Loading…
Reference in New Issue