Merge branch 'master' of https://github.com/peerdavid/BangleApps
129
apps.json
|
|
@ -29,6 +29,24 @@
|
|||
],
|
||||
"sortorder": -10
|
||||
},
|
||||
{ "id": "ac_ac",
|
||||
"name": "A Configurable Analog Clock",
|
||||
"shortName":"Configurable Clock",
|
||||
"version":"0.03",
|
||||
"description": "AC-AC, a highly customizable analog clock with several clock faces, hands and complications to choose from",
|
||||
"icon": "app-icon.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"allow_emulator": false,
|
||||
"screenshots": [{"url":"app-screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"custom": "Customizer.html",
|
||||
"storage": [
|
||||
{"name":"ac_ac.app.js","url":"app.js"},
|
||||
{"name":"ac_ac.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "hebrew_calendar",
|
||||
"name": "Hebrew Calendar",
|
||||
|
|
@ -102,7 +120,7 @@
|
|||
"version": "0.06",
|
||||
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,messages,notifications",
|
||||
"tags": "tool,system,messages,notifications,gadgetbridge",
|
||||
"dependencies": {"messages":"app"},
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
|
@ -309,7 +327,7 @@
|
|||
"description": "(NOT RECOMMENDED) Displays Gadgetbridge notifications from Android. Please use the 'Android' Bangle.js app instead.",
|
||||
"icon": "app.png",
|
||||
"type": "widget",
|
||||
"tags": "tool,system,android,widget",
|
||||
"tags": "tool,system,android,widget,gadgetbridge",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"dependencies": {"notify":"type"},
|
||||
"readme": "README.md",
|
||||
|
|
@ -326,7 +344,7 @@
|
|||
"version":"0.01",
|
||||
"description": "Debug info for Gadgetbridge. Run this app and when Gadgetbridge messages arrive they are displayed on-screen.",
|
||||
"icon": "app.png",
|
||||
"tags": "",
|
||||
"tags": "tool,debug,gadgetbridge",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
|
|
@ -770,7 +788,7 @@
|
|||
"id": "recorder",
|
||||
"name": "Recorder (BETA)",
|
||||
"shortName": "Recorder",
|
||||
"version": "0.06",
|
||||
"version": "0.07",
|
||||
"description": "Record GPS position, heart rate and more in the background, then download to your PC.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,outdoors,gps,widget",
|
||||
|
|
@ -1042,7 +1060,7 @@
|
|||
"id": "bthrm",
|
||||
"name": "Bluetooth Heart Rate Monitor",
|
||||
"shortName": "BT HRM",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
@ -1536,7 +1554,7 @@
|
|||
{
|
||||
"id": "assistedgps",
|
||||
"name": "Assisted GPS Update (AGPS)",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "Downloads assisted GPS (AGPS) data to Bangle.js 1 or 2 for faster GPS startup and more accurate fixes. **No app will be installed**, this just uploads new data to the GPS chip.",
|
||||
"icon": "app.png",
|
||||
"type": "RAM",
|
||||
|
|
@ -2277,6 +2295,20 @@
|
|||
{"name":"buffgym.img","url":"buffgym-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "run",
|
||||
"name": "Run",
|
||||
"version":"0.01",
|
||||
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
|
||||
"icon": "app.png",
|
||||
"tags": "run,running,fitness,outdoors,gps",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"run.app.js","url":"app.js"},
|
||||
{"name":"run.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "banglerun",
|
||||
"name": "BangleRun",
|
||||
|
|
@ -2994,6 +3026,20 @@
|
|||
],
|
||||
"data": [{"wildcard":"accellog.?.csv"}]
|
||||
},
|
||||
{ "id": "accelgraph",
|
||||
"name": "Accelerometer Graph",
|
||||
"shortName":"Accel Graph",
|
||||
"version":"0.01",
|
||||
"description": "A simple app to draw a graph of data from the accelerometer on the screen",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,debug",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"accelgraph.app.js","url":"app.js"},
|
||||
{"name":"accelgraph.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cprassist",
|
||||
"name": "CPR Assist",
|
||||
|
|
@ -3923,8 +3969,8 @@
|
|||
"id": "qmsched",
|
||||
"name": "Quiet Mode Schedule and Widget",
|
||||
"shortName": "Quiet Mode",
|
||||
"version": "0.06",
|
||||
"description": "Automatically turn Quiet Mode on or off at set times, and change LCD options while Quiet Mode is active.",
|
||||
"version": "0.07",
|
||||
"description": "Automatically turn Quiet Mode on or off at set times, change theme and LCD options while Quiet Mode is active.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot_b1_main.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_lcd.png"},
|
||||
{"url":"screenshot_b2_main.png"},{"url":"screenshot_b2_edit.png"},{"url":"screenshot_b2_lcd.png"}],
|
||||
|
|
@ -4265,7 +4311,7 @@
|
|||
{
|
||||
"id": "antonclk",
|
||||
"name": "Anton Clock",
|
||||
"version": "0.05",
|
||||
"version": "0.06",
|
||||
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
||||
"readme":"README.md",
|
||||
"icon": "app.png",
|
||||
|
|
@ -4358,8 +4404,10 @@
|
|||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"ffcniftya.app.js","url":"app.js"},
|
||||
{"name":"ffcniftya.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
{"name":"ffcniftya.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"ffcniftya.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [{"name":"ffcniftya.json"}]
|
||||
},
|
||||
{
|
||||
"id": "ffcniftyb",
|
||||
|
|
@ -5032,7 +5080,7 @@
|
|||
{
|
||||
"id": "coretemp",
|
||||
"name": "CoreTemp",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "Display CoreTemp device sensor data",
|
||||
"icon": "coretemp.png",
|
||||
"type": "app",
|
||||
|
|
@ -5042,6 +5090,7 @@
|
|||
"storage": [
|
||||
{"name":"coretemp.wid.js","url":"widget.js"},
|
||||
{"name":"coretemp.app.js","url":"coretemp.js"},
|
||||
{"name":"coretemp.recorder.js","url":"recorder.js"},
|
||||
{"name":"coretemp.settings.js","url":"settings.js"},
|
||||
{"name":"coretemp.img","url":"coretemp-icon.js","evaluate":true},
|
||||
{"name":"coretemp.boot.js","url":"boot.js"}
|
||||
|
|
@ -5155,14 +5204,13 @@
|
|||
{
|
||||
"id": "ftclock",
|
||||
"name": "Four Twenty Clock",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A clock that tells when and where it's going to be 4:20 next",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot1.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"ftclock.app.js","url":"app.js"},
|
||||
|
|
@ -5349,7 +5397,7 @@
|
|||
{ "id": "colorful_clock",
|
||||
"name": "Colorful Analog Clock",
|
||||
"shortName":"Colorful Clock",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "a colorful analog clock",
|
||||
"icon": "app-icon.png",
|
||||
"type": "clock",
|
||||
|
|
@ -5563,5 +5611,54 @@
|
|||
"data": [
|
||||
{"name":"banglexercise.json"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "widpa",
|
||||
"name": "Simple Pedometer",
|
||||
"shortName":"Simple Pedometer",
|
||||
"icon": "screenshot_widpa.png",
|
||||
"screenshots": [{"url":"screenshot_widpa.png"}],
|
||||
"version":"0.01",
|
||||
"type": "widget",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"description": "Displays the current step count from `Bangle.getHealthStatus(\"day\").steps` in 12x16 font, requires firmware v2.11.21 or later",
|
||||
"tags": "widget,battery",
|
||||
"storage": [
|
||||
{"name":"widpa.wid.js","url":"widpa.wid.js"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "widpb",
|
||||
"name": "Lato Pedometer",
|
||||
"shortName":"Lato Pedometer",
|
||||
"icon": "screenshot_widpb.png",
|
||||
"screenshots": [{"url":"screenshot_widpb.png"}],
|
||||
"version":"0.01",
|
||||
"type": "widget",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"description": "Displays the current step count from `Bangle.getHealthStatus(\"day\").steps` in the Lato font, requires firmware v2.11.21 or later",
|
||||
"tags": "widget,battery",
|
||||
"storage": [
|
||||
{"name":"widpb.wid.js","url":"widpb.wid.js"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "timeandlife",
|
||||
"name": "Time and Life",
|
||||
"shortName":"Time and Lfie",
|
||||
"icon": "app.png",
|
||||
"version":"0.1",
|
||||
"description": "A simple watchface which displays the time when the screen is tapped and decays according to the rules of Conway's game of life.",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator":true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"timeandlife.app.js","url":"app.js"},
|
||||
{"name":"timeandlife.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,890 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" style="width:100%; height:100%">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
|
||||
<meta name="viewport" content="minimal-ui, width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
|
||||
<style type="text/css">
|
||||
html {
|
||||
text-size-adjust: 100%;
|
||||
-moz-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-o-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<meta http-equiv="Content-Security-Policy" content="
|
||||
default-src 'self' 'unsafe-inline' 'unsafe-eval' file: https:;
|
||||
">
|
||||
<meta http-equiv="X-Content-Security-Policy" content="
|
||||
default-src 'self' 'unsafe-inline' 'unsafe-eval' file: https:;
|
||||
">
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
<script src="../../core/lib/customize.js"></script>
|
||||
|
||||
<style>
|
||||
body { background:white; color:black }
|
||||
|
||||
table.centered td {
|
||||
text-align:center; vertical-align:top;
|
||||
}
|
||||
|
||||
label.Preview {
|
||||
display:inline-block; position:relative;
|
||||
}
|
||||
label.Preview > input[type="radio"] {
|
||||
display:none; position:relative;
|
||||
-webkit-appearance:none; -moz-appearance:none; appearance:none;
|
||||
width:0px; height:0px;
|
||||
}
|
||||
label.Preview > input[type="radio"] + img {
|
||||
display:inline-block; position:relative;
|
||||
width:88px; height:88px;
|
||||
margin:0px; padding:0px;
|
||||
border:solid 3px white;
|
||||
box-shadow:0px 0px 1px 1px lightgray;
|
||||
}
|
||||
label.Preview > input[type="radio"]:checked + img {
|
||||
border:solid 3px red;
|
||||
}
|
||||
|
||||
|
||||
.ColorPatch {
|
||||
display:inline-block; position:relative;
|
||||
-webkit-appearance:none; -moz-appearance:none; appearance:none;
|
||||
width:30px; height:30px; box-sizing:border-box;
|
||||
border:solid 3px white;
|
||||
margin:0px; padding:0px;
|
||||
box-shadow:inset 0px 0px 1px 1px black;
|
||||
vertical-align:top;
|
||||
}
|
||||
.ColorPatch:checked {
|
||||
border:solid 3px red;
|
||||
}
|
||||
|
||||
|
||||
label.ColorPatch {
|
||||
display:inline-block; position:relative;
|
||||
width:70px;
|
||||
border:none;
|
||||
box-shadow:none;
|
||||
}
|
||||
label.ColorPatch > input[type="radio"] {
|
||||
display:none; position:relative;
|
||||
-webkit-appearance:none; -moz-appearance:none; appearance:none;
|
||||
width:0px; height:0px;
|
||||
}
|
||||
label.ColorPatch > input[type="radio"] + span {
|
||||
display:inline-block; position:absolute;
|
||||
left:0px; top:0px; right:0px; bottom:0px;
|
||||
margin:0px; padding:0px;
|
||||
border:solid 3px white;
|
||||
box-shadow:inset 0px 0px 1px 1px black;
|
||||
font-size:16px; line-height:24px;
|
||||
text-align:center;
|
||||
}
|
||||
label.ColorPatch > input[type="radio"]:checked + span {
|
||||
border:solid 3px red;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
let ClockSize, ClockSizeURL
|
||||
let ClockFace, ClockFaceNumerals, ClockFaceDots, ClockFaceURL
|
||||
let ClockHands, SecondHand, ClockHandsURL, FillColor
|
||||
let ComplicationTL, ComplicationTLURL
|
||||
let ComplicationT, ComplicationTURL
|
||||
let ComplicationTR, ComplicationTRURL
|
||||
let ComplicationL, ComplicationLURL
|
||||
let ComplicationR, ComplicationRURL
|
||||
let ComplicationBL, ComplicationBLURL
|
||||
let ComplicationB, ComplicationBURL
|
||||
let ComplicationBR, ComplicationBRURL
|
||||
let Foreground, Background
|
||||
|
||||
/**** backupConfiguration ****/
|
||||
|
||||
function backupConfiguration () {
|
||||
let Configuration = {
|
||||
ClockSize, ClockSizeURL,
|
||||
ClockFace, ClockFaceNumerals, ClockFaceDots, ClockFaceURL,
|
||||
ClockHands, SecondHand, ClockHandsURL, FillColor,
|
||||
ComplicationTL, ComplicationTLURL,
|
||||
ComplicationT, ComplicationTURL,
|
||||
ComplicationTR, ComplicationTRURL,
|
||||
ComplicationL, ComplicationLURL,
|
||||
ComplicationR, ComplicationRURL,
|
||||
ComplicationBL, ComplicationBLURL,
|
||||
ComplicationB, ComplicationBURL,
|
||||
ComplicationBR, ComplicationBRURL,
|
||||
Foreground, Background
|
||||
}
|
||||
|
||||
try {
|
||||
localStorage.setItem('ac_ac',JSON.stringify(Configuration))
|
||||
} catch (Signal) {
|
||||
console.error('could not backup clock configuration, reason',Signal)
|
||||
}
|
||||
}
|
||||
|
||||
/**** restoreConfiguration - warning: no input validations yet! ****/
|
||||
|
||||
function restoreConfiguration () {
|
||||
let Configuration = {}
|
||||
try {
|
||||
Configuration = JSON.parse(localStorage.getItem('ac_ac') || '')
|
||||
} catch (Signal) { /* nop */ }
|
||||
for (let Key in Configuration) {
|
||||
if (Configuration.hasOwnProperty(Key)) {
|
||||
if ((Key == null) || (typeof Configuration[Key] !== 'string')) {
|
||||
Configuration[Key] = ''
|
||||
} else {
|
||||
Configuration[Key] = Configuration[Key].trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('input[name="clock-size"][value="' + Configuration.ClockSize + '"]').attr('checked','checked')
|
||||
$('#clock-size-custom-url').val(Configuration.ClockSizeURL)
|
||||
|
||||
$('input[name="clock-face"][value="' + Configuration.ClockFace + '"]').attr('checked','checked')
|
||||
$('input[name="clock-face-numerals"][value="' + Configuration.ClockFaceNumerals + '"]').attr('checked','checked')
|
||||
$('input[name="clock-face-dots"][value="' + Configuration.ClockFaceDots + '"]').attr('checked','checked')
|
||||
$('#clock-face-custom-url').val(Configuration.ClockFaceURL)
|
||||
|
||||
$('input[name="clock-hands"][value="' + Configuration.ClockHands + '"]').attr('checked','checked')
|
||||
$('input[name="fill-color"][value="' + Configuration.FillColor + '"]').attr('checked','checked')
|
||||
$('input[name="second-hand"][value="' + Configuration.SecondHand + '"]').attr('checked','checked')
|
||||
$('#clock-hands-custom-url').val(Configuration.ClockHandsURL)
|
||||
|
||||
$('#complication-tl').val(Configuration.ComplicationTL)
|
||||
$('#complication-tl-custom-url').val(Configuration.ComplicationTLURL)
|
||||
|
||||
$('#complication-t').val(Configuration.ComplicationT)
|
||||
$('#complication-t-custom-url').val(Configuration.ComplicationTURL)
|
||||
|
||||
$('#complication-tr').val(Configuration.ComplicationTR)
|
||||
$('#complication-tr-custom-url').val(Configuration.ComplicationTRURL)
|
||||
|
||||
$('#complication-l').val(Configuration.ComplicationL)
|
||||
$('#complication-l-custom-url').val(Configuration.ComplicationLURL)
|
||||
|
||||
$('#complication-r').val(Configuration.ComplicationR)
|
||||
$('#complication-r-custom-url').val(Configuration.ComplicationRURL)
|
||||
|
||||
$('#complication-bl').val(Configuration.ComplicationBL)
|
||||
$('#complication-bl-custom-url').val(Configuration.ComplicationBLURL)
|
||||
|
||||
$('#complication-b').val(Configuration.ComplicationB)
|
||||
$('#complication-b-custom-url').val(Configuration.ComplicationBURL)
|
||||
|
||||
$('#complication-br').val(Configuration.ComplicationBR)
|
||||
$('#complication-br-custom-url').val(Configuration.ComplicationBRURL)
|
||||
|
||||
$('input[name="foreground"][value="' + Configuration.Foreground + '"]').attr('checked','checked')
|
||||
$('input[name="background"][value="' + Configuration.Background + '"]').attr('checked','checked')
|
||||
}
|
||||
restoreConfiguration();
|
||||
|
||||
/**** retrieveInputs ****/
|
||||
|
||||
function retrieveInputs () {
|
||||
ClockSize = $('input[name="clock-size"]:checked').val()
|
||||
ClockSizeURL = ($('#clock-size-custom-url').val() || '').trim()
|
||||
|
||||
ClockFace = $('input[name="clock-face"]:checked').val()
|
||||
ClockFaceNumerals = $('input[name="clock-face-numerals"]:checked').val()
|
||||
ClockFaceDots = $('input[name="clock-face-dots"]:checked').val()
|
||||
ClockFaceURL = ($('#clock-face-custom-url').val() || '').trim()
|
||||
|
||||
ClockHands = $('input[name="clock-hands"]:checked').val()
|
||||
FillColor = $('input[name="fill-color"]:checked').val()
|
||||
SecondHand = $('input[name="second-hand"]:checked').val()
|
||||
ClockHandsURL = ($('#clock-hands-custom-url').val() || '').trim()
|
||||
|
||||
ComplicationTL = $('#complication-tl').val()
|
||||
ComplicationTLURL = ($('#complication-tl-custom-url').val() || '').trim()
|
||||
|
||||
ComplicationT = $('#complication-t').val()
|
||||
ComplicationTURL = ($('#complication-t-custom-url').val() || '').trim()
|
||||
|
||||
ComplicationTR = $('#complication-tr').val()
|
||||
ComplicationTRURL = ($('#complication-tr-custom-url').val() || '').trim()
|
||||
|
||||
ComplicationL = $('#complication-l').val()
|
||||
ComplicationLURL = ($('#complication-l-custom-url').val() || '').trim()
|
||||
|
||||
ComplicationR = $('#complication-r').val()
|
||||
ComplicationRURL = ($('#complication-r-custom-url').val() || '').trim()
|
||||
|
||||
ComplicationBL = $('#complication-bl').val()
|
||||
ComplicationBLURL = ($('#complication-bl-custom-url').val() || '').trim()
|
||||
|
||||
ComplicationB = $('#complication-b').val()
|
||||
ComplicationBURL = ($('#complication-b-custom-url').val() || '').trim()
|
||||
|
||||
ComplicationBR = $('#complication-br').val()
|
||||
ComplicationBRURL = ($('#complication-br-custom-url').val() || '').trim()
|
||||
|
||||
Foreground = $('input[name="foreground"]:checked').val()
|
||||
Background = $('input[name="background"]:checked').val()
|
||||
}
|
||||
retrieveInputs()
|
||||
|
||||
/**** validateInputs ****/
|
||||
|
||||
function validateInputs () {
|
||||
function withError (Message) {
|
||||
showMessage(Message)
|
||||
$('#UploadButton').attr('disabled',true)
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case (ClockSize === 'custom') && (ClockSizeURL === ''):
|
||||
return withError('please enter the URL of your custom "Clock Size Calculator"')
|
||||
|
||||
case (ClockFace === 'custom') && (ClockFaceURL === ''):
|
||||
return withError('please enter the URL of your custom clock face')
|
||||
|
||||
case (ClockHands === 'custom') && (ClockHandsURL === ''):
|
||||
return withError('please enter the URL of your custom clock hands')
|
||||
|
||||
case (ComplicationTL === 'custom') && (ComplicationTLURL === ''):
|
||||
return withError('please enter the URL of your custom complication in the top-left corner')
|
||||
case (ComplicationT === 'custom') && (ComplicationTURL === ''):
|
||||
return withError('please enter the URL of your custom complication at the top edge')
|
||||
case (ComplicationTR === 'custom') && (ComplicationTRURL === ''):
|
||||
return withError('please enter the URL of your custom complication in the top-right corner')
|
||||
case (ComplicationL === 'custom') && (ComplicationLURL === ''):
|
||||
return withError('please enter the URL of your custom complication at the left edge')
|
||||
case (ComplicationR === 'custom') && (ComplicationRURL === ''):
|
||||
return withError('please enter the URL of your custom complication at the right edge')
|
||||
case (ComplicationBL === 'custom') && (ComplicationBLURL === ''):
|
||||
return withError('please enter the URL of your custom complication in the bottom-left corner')
|
||||
case (ComplicationB === 'custom') && (ComplicationBURL === ''):
|
||||
return withError('please enter the URL of your custom complication at the bottom edge')
|
||||
case (ComplicationBR === 'custom') && (ComplicationBRURL === ''):
|
||||
return withError('please enter the URL of your custom complication in the bottom-right corner')
|
||||
}
|
||||
|
||||
hideMessage()
|
||||
$('#UploadButton').removeAttr('disabled')
|
||||
}
|
||||
|
||||
/**** hide/showMesssage ****/
|
||||
|
||||
function hideMessage () { $('#MessageView').hide() }
|
||||
|
||||
function showMessage (Message) {
|
||||
$('#MessageView').text(Message).show()
|
||||
}
|
||||
|
||||
/**** createAndUploadApp ****/
|
||||
|
||||
function createAndUploadApp () {
|
||||
function WidgetsOnBackground () {
|
||||
return (
|
||||
ClockSize === 'smart'
|
||||
? "require('https://raw.githubusercontent.com/rozek/banglejs-2-widgets-on-background/main/drawWidgets.js');"
|
||||
: ''
|
||||
)
|
||||
}
|
||||
|
||||
function chosenClockSize () {
|
||||
switch (ClockSize) {
|
||||
case 'simple': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-simple-clock-size/main/ClockSize.js')"
|
||||
case 'smart': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-smart-clock-size/main/ClockSize.js')"
|
||||
case 'custom': return "require('" + ClockSizeURL + "')"
|
||||
}
|
||||
}
|
||||
|
||||
function chosenClockFace () {
|
||||
switch (ClockFace) {
|
||||
case 'none': return "undefined"
|
||||
case 'four-fold': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-four-fold-clock-face/main/ClockFace.js')"
|
||||
case 'twelve-fold': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-twelve-fold-clock-face/main/ClockFace.js')"
|
||||
case 'rainbow': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-rainbow-clock-face/main/ClockFace.js')"
|
||||
case 'custom': return "require('" + ClockFaceURL + "')"
|
||||
}
|
||||
}
|
||||
|
||||
function chosenClockHands () {
|
||||
switch (ClockHands) {
|
||||
case 'simple': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-simpled-clock-hands/main/ClockHands.js')"
|
||||
case 'rounded': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-rounded-clock-hands/main/ClockHands.js')"
|
||||
case 'hollow': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-hollow-clock-hands/main/ClockHands.js')"
|
||||
case 'custom': return "require('" + ClockHandsURL + "')"
|
||||
}
|
||||
}
|
||||
|
||||
function chosenComplication (Complication, customURL) {
|
||||
switch (Complication) {
|
||||
case 'none': return "undefined"
|
||||
case 'date': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-date-complication/main/Complication.js')"
|
||||
case 'weekday': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-weekday-complication/main/Complication.js')"
|
||||
case 'calendar-week': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-calendar-week-complication/main/Complication.js')"
|
||||
case 'moon-phase': return "require('https://raw.githubusercontent.com/rozek/banglejs-2-moon-phase-complication/main/Complication.js')"
|
||||
case 'custom': return "require('" + customURL + "')"
|
||||
}
|
||||
}
|
||||
function chosenComplicationAt (Position) {
|
||||
switch (Position) {
|
||||
case 'tl': return chosenComplication(ComplicationTL, ComplicationTLURL)
|
||||
case 't': return chosenComplication(ComplicationT, ComplicationTURL)
|
||||
case 'tr': return chosenComplication(ComplicationTR, ComplicationTRURL)
|
||||
case 'l': return chosenComplication(ComplicationL, ComplicationLURL)
|
||||
case 'r': return chosenComplication(ComplicationR, ComplicationRURL)
|
||||
case 'bl': return chosenComplication(ComplicationBL, ComplicationBLURL)
|
||||
case 'b': return chosenComplication(ComplicationB, ComplicationBURL)
|
||||
case 'br': return chosenComplication(ComplicationBR, ComplicationBRURL)
|
||||
}
|
||||
}
|
||||
|
||||
function chosenColor (ColorChoice) {
|
||||
return (ColorChoice === 'none' ? 'undefined' : "'" + ColorChoice + "'")
|
||||
}
|
||||
|
||||
function chosenForeground () { return chosenColor(Foreground) }
|
||||
function chosenBackground () { return chosenColor(Background) }
|
||||
function chosenSecondHand () { return chosenColor(SecondHand) }
|
||||
function chosenFillColor () { return chosenColor(FillColor) }
|
||||
|
||||
function chosenNumerals () { return (ClockFaceNumerals === 'roman' ? 'true' : 'false') }
|
||||
function chosenDots () { return (ClockFaceDots === 'with-dots' ? 'true' : 'false') }
|
||||
|
||||
let AppSource = `
|
||||
${WidgetsOnBackground()}
|
||||
|
||||
let Clockwork = require('https://raw.githubusercontent.com/rozek/banglejs-2-simple-clockwork/main/Clockwork.js');
|
||||
|
||||
Clockwork.windUp({
|
||||
size: ${chosenClockSize()},
|
||||
background:null,
|
||||
face: ${chosenClockFace()},
|
||||
hands: ${chosenClockHands()},
|
||||
complications:{
|
||||
tl:${chosenComplicationAt('tl')},
|
||||
t: ${chosenComplicationAt('t')},
|
||||
tr:${chosenComplicationAt('tr')},
|
||||
l: ${chosenComplicationAt('l')},
|
||||
r: ${chosenComplicationAt('r')},
|
||||
bl:${chosenComplicationAt('bl')},
|
||||
b: ${chosenComplicationAt('b')},
|
||||
br:${chosenComplicationAt('br')},
|
||||
}
|
||||
},{
|
||||
Foreground: ${chosenForeground()},
|
||||
Background: ${chosenBackground()},
|
||||
Seconds: ${chosenSecondHand()},
|
||||
withDots: ${chosenDots()},
|
||||
romanNumerals:${chosenNumerals()},
|
||||
FillColor: ${chosenFillColor()}
|
||||
});
|
||||
`
|
||||
console.log('the configured AC-AC app looks as follows:')
|
||||
console.log(AppSource)
|
||||
|
||||
backupConfiguration()
|
||||
|
||||
sendCustomizedApp({
|
||||
storage:[
|
||||
{name:'ac_ac.app.js', url:'app.js', content:AppSource},
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
/**** register event handlers ****/
|
||||
|
||||
function retrieveAndValidateInputs () {
|
||||
retrieveInputs ()
|
||||
validateInputs ()
|
||||
}
|
||||
|
||||
$('input[type="radio"]').on('change',retrieveAndValidateInputs)
|
||||
$('input[type="url"]'). on('change',retrieveAndValidateInputs)
|
||||
$('select'). on('change',retrieveAndValidateInputs)
|
||||
$('#UploadButton').on('click',createAndUploadApp)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
Please customize your analog clock for the Bangle.js 2 according to your needs.
|
||||
When finished, click on "Upload" at the bottom of this form.
|
||||
</p><p>
|
||||
(Pressing "Upload" will also backup your current configuration so that you
|
||||
won't have to enter the same settings over and over again when you come back
|
||||
to this page later)
|
||||
</p>
|
||||
|
||||
<h3>Clock Size Calculation</h3>
|
||||
|
||||
<p>
|
||||
Click on the desired clock size calculator (if you installed some widgets
|
||||
on your Bangle.js 2, the smart one may produce larger clock faces than the
|
||||
simple one):
|
||||
</p><p>
|
||||
<table class="centered"><tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-size" value="simple">
|
||||
<img src="simpleClockSize.png"/>
|
||||
</label><br>
|
||||
simple
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-size" value="smart" checked>
|
||||
<img src="smartClockSize.png"/>
|
||||
</label><br>
|
||||
smart
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-size" value="custom">
|
||||
<img src="custom.png"/>
|
||||
</label><br>
|
||||
(custom)
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</p><p>
|
||||
If you prefer a "custom" clock size calculator, please enter the URL
|
||||
of its JavaScript module below:
|
||||
</p><p>
|
||||
custom URL: <input type="url" id="clock-size-custom-url" size="50">
|
||||
</p>
|
||||
|
||||
<h3>Clock Face</h3>
|
||||
|
||||
<p>
|
||||
Click on the desired clock face:
|
||||
</p><p>
|
||||
<table class="centered"><tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-face" value="none" checked>
|
||||
<img src="none.png"/>
|
||||
</label><br>
|
||||
none
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-face" value="four-fold">
|
||||
<img src="fourfoldClockFace.png"/>
|
||||
</label><br>
|
||||
four-fold
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-face" value="twelve-fold">
|
||||
<img src="twelvefoldClockFace.png"/>
|
||||
</label><br>
|
||||
twelve-fold
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-face" value="rainbow">
|
||||
<img src="RainbowClockFace.png"/>
|
||||
</label><br>
|
||||
"rainbow"<br>colored
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-face" value="custom">
|
||||
<img src="custom.png"/>
|
||||
</label><br>
|
||||
(custom)
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</p><p>
|
||||
Clock faces are drawn in the configured foreground and background colors
|
||||
(you may select them at the end of this form)
|
||||
</p><p>
|
||||
"Four-fold" clock faces may draw indian-arabic or roman numerals. Which do you prefer?
|
||||
</p><p>
|
||||
<input type="radio" name="clock-face-numerals" value="indian" checked> indian-arabic (3, 6, 9, 12)<br>
|
||||
<input type="radio" name="clock-face-numerals" value="roman"> roman (III, VI, IX, XII)
|
||||
</p><p>
|
||||
The "twelve-fold" and "rainbow"-colored faces may be drawn with or without
|
||||
dots marking the position of every minute. Which variant do you prefer?
|
||||
</p><p>
|
||||
<input type="radio" name="clock-face-dots" value="without-dots" checked> without dots <br>
|
||||
<input type="radio" name="clock-face-dots" value="with-dots"> with dots
|
||||
</p><p>
|
||||
If you prefer a "custom" clock face, please enter the URL
|
||||
of its JavaScript module below:
|
||||
</p><p>
|
||||
custom URL: <input type="url" id="clock-face-custom-url" size="50">
|
||||
</p>
|
||||
|
||||
<h3>Clock Hands</h3>
|
||||
|
||||
<p>
|
||||
Click on the desired clock hands:
|
||||
</p><p>
|
||||
<table class="centered"><tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-hands" value="simple">
|
||||
<img src="simpleClockHands.png"/>
|
||||
</label><br>
|
||||
simple
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-hands" value="rounded" checked>
|
||||
<img src="roundedClockHands.png"/>
|
||||
</label><br>
|
||||
rounded
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-hands" value="hollow">
|
||||
<img src="hollowClockHands.png"/>
|
||||
</label><br>
|
||||
hollow
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<label class="Preview">
|
||||
<input type="radio" name="clock-hands" value="custom">
|
||||
<img src="custom.png"/>
|
||||
</label><br>
|
||||
(custom)
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</p><p>
|
||||
Clock hands are drawn in the configured foreground and background colors
|
||||
(you may select them at the end of this form)
|
||||
</p><p>
|
||||
Hollow clock hands may optionally be filled with a given color. If you have
|
||||
chosen hollow hands, please specify the desired fill mode and color below:
|
||||
</p><p>
|
||||
<b>Hollow Hand Fill Color:</b>
|
||||
</p><p>
|
||||
<label class="ColorPatch">
|
||||
<input type="radio" name="fill-color" value="none" checked/>
|
||||
<span>none</span>
|
||||
</label>
|
||||
<label class="ColorPatch">
|
||||
<input type="radio" name="fill-color" value="Theme"/>
|
||||
<span>themed</span>
|
||||
</label>
|
||||
<input type="radio" name="fill-color" value="#000000" class="ColorPatch" style="background:#000000"/>
|
||||
<input type="radio" name="fill-color" value="#FF0000" class="ColorPatch" style="background:#FF0000"/>
|
||||
<input type="radio" name="fill-color" value="#00FF00" class="ColorPatch" style="background:#00FF00"/>
|
||||
<input type="radio" name="fill-color" value="#0000FF" class="ColorPatch" style="background:#0000FF"/>
|
||||
<input type="radio" name="fill-color" value="#FFFF00" class="ColorPatch" style="background:#FFFF00"/>
|
||||
<input type="radio" name="fill-color" value="#FF00FF" class="ColorPatch" style="background:#FF00FF"/>
|
||||
<input type="radio" name="fill-color" value="#00FFFF" class="ColorPatch" style="background:#00FFFF"/>
|
||||
<input type="radio" name="fill-color" value="#FFFFFF" class="ColorPatch" style="background:#FFFFFF"/>
|
||||
</p><p>
|
||||
Additionally, all clock hands may be drawn with or without second hands.
|
||||
If you want them to be drawn, please click on their desired color below
|
||||
(or choose "themed" to use your Bangle's configured theme) - if not, just
|
||||
select "none":
|
||||
</p><p>
|
||||
<b>Second Hand Color:</b>
|
||||
</p><p>
|
||||
<label class="ColorPatch">
|
||||
<input type="radio" name="second-hand" value="none" checked/>
|
||||
<span>none</span>
|
||||
</label>
|
||||
<label class="ColorPatch">
|
||||
<input type="radio" name="second-hand" value="Theme"/>
|
||||
<span>themed</span>
|
||||
</label>
|
||||
<input type="radio" name="second-hand" value="#000000" class="ColorPatch" style="background:#000000"/>
|
||||
<input type="radio" name="second-hand" value="#FF0000" class="ColorPatch" style="background:#FF0000"/>
|
||||
<input type="radio" name="second-hand" value="#00FF00" class="ColorPatch" style="background:#00FF00"/>
|
||||
<input type="radio" name="second-hand" value="#0000FF" class="ColorPatch" style="background:#0000FF"/>
|
||||
<input type="radio" name="second-hand" value="#FFFF00" class="ColorPatch" style="background:#FFFF00"/>
|
||||
<input type="radio" name="second-hand" value="#FF00FF" class="ColorPatch" style="background:#FF00FF"/>
|
||||
<input type="radio" name="second-hand" value="#00FFFF" class="ColorPatch" style="background:#00FFFF"/>
|
||||
<input type="radio" name="second-hand" value="#FFFFFF" class="ColorPatch" style="background:#FFFFFF"/>
|
||||
</p><p>
|
||||
If you prefer "custom" clock hands, please enter the URL
|
||||
of their JavaScript module below:
|
||||
</p><p>
|
||||
custom URL: <input type="url" id="clock-hands-custom-url" size="50">
|
||||
</p>
|
||||
|
||||
<h3>Complications</h3>
|
||||
|
||||
<p>
|
||||
Complications are small displays for additional information. If you want
|
||||
one or multiple complications to be added to your clock, you'll have to
|
||||
specify which one to be loaded and where it should be placed.
|
||||
</p><p>
|
||||
Up to 6 possible positions exist (top-left, top-right, left, right,
|
||||
bottom-left and bottom-right). Alternatively, the positions "top-left" and
|
||||
"top-right" may be traded for a slightly larger complication at position
|
||||
"top" or "bottom-left" and "bottom-right" for one at the "bottom":
|
||||
</p>
|
||||
<img src="smallPlaceholders.png" width="88" height="88"/>
|
||||
<img src="largePlaceholders.png" width="88" height="88"/>
|
||||
<p>
|
||||
<table><tbody>
|
||||
<tr>
|
||||
<td colspan="3"><b>top-left:</b></td>
|
||||
</tr><tr>
|
||||
<td> </td>
|
||||
<td> Complication:</td>
|
||||
<td>
|
||||
<select id="complication-tl">
|
||||
<option value="none" selected>(none)</option>
|
||||
<option value="date"> date</option>
|
||||
<option value="weekday"> weekday</option>
|
||||
<option value="calendar-week">calendar week</option>
|
||||
<option value="moon-phase"> moon phase</option>
|
||||
<option value="custom"> (custom)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>custom URL:</td>
|
||||
<td><input type="url" id="complication-tl-custom-url" size="50"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3"><b>top:</b></td>
|
||||
</tr><tr>
|
||||
<td> </td>
|
||||
<td> Complication:</td>
|
||||
<td>
|
||||
<select id="complication-t">
|
||||
<option value="none" selected>(none)</option>
|
||||
<option value="date"> date</option>
|
||||
<option value="weekday"> weekday</option>
|
||||
<option value="calendar-week">calendar week</option>
|
||||
<option value="moon-phase"> moon phase</option>
|
||||
<option value="custom"> (custom)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>custom URL:</td>
|
||||
<td><input type="url" id="complication-t-custom-url" size="50"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3"><b>top-right:</b></td>
|
||||
</tr><tr>
|
||||
<td> </td>
|
||||
<td> Complication:</td>
|
||||
<td>
|
||||
<select id="complication-tr">
|
||||
<option value="none" selected>(none)</option>
|
||||
<option value="date"> date</option>
|
||||
<option value="weekday"> weekday</option>
|
||||
<option value="calendar-week">calendar week</option>
|
||||
<option value="moon-phase"> moon phase</option>
|
||||
<option value="custom"> (custom)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>custom URL:</td>
|
||||
<td><input type="url" id="complication-tr-custom-url" size="50"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3"><b>left:</b></td>
|
||||
</tr><tr>
|
||||
<td> </td>
|
||||
<td> Complication:</td>
|
||||
<td>
|
||||
<select id="complication-l">
|
||||
<option value="none" selected>(none)</option>
|
||||
<option value="date"> date</option>
|
||||
<option value="weekday"> weekday</option>
|
||||
<option value="calendar-week">calendar week</option>
|
||||
<option value="moon-phase"> moon phase</option>
|
||||
<option value="custom"> (custom)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>custom URL:</td>
|
||||
<td><input type="url" id="complication-l-custom-url" size="50"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3"><b>right:</b></td>
|
||||
</tr><tr>
|
||||
<td> </td>
|
||||
<td> Complication:</td>
|
||||
<td>
|
||||
<select id="complication-r">
|
||||
<option value="none" selected>(none)</option>
|
||||
<option value="date"> date</option>
|
||||
<option value="weekday"> weekday</option>
|
||||
<option value="calendar-week">calendar week</option>
|
||||
<option value="moon-phase"> moon phase</option>
|
||||
<option value="custom"> (custom)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>custom URL:</td>
|
||||
<td><input type="url" id="complication-r-custom-url" size="50"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3"><b>bottom-left:</b></td>
|
||||
</tr><tr>
|
||||
<td> </td>
|
||||
<td> Complication:</td>
|
||||
<td>
|
||||
<select id="complication-bl">
|
||||
<option value="none" selected>(none)</option>
|
||||
<option value="date"> date</option>
|
||||
<option value="weekday"> weekday</option>
|
||||
<option value="calendar-week">calendar week</option>
|
||||
<option value="moon-phase"> moon phase</option>
|
||||
<option value="custom"> (custom)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>custom URL:</td>
|
||||
<td><input type="url" id="complication-bl-custom-url" size="50"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3"><b>bottom:</b></td>
|
||||
</tr><tr>
|
||||
<td> </td>
|
||||
<td> Complication:</td>
|
||||
<td>
|
||||
<select id="complication-b">
|
||||
<option value="none" selected>(none)</option>
|
||||
<option value="date"> date</option>
|
||||
<option value="weekday"> weekday</option>
|
||||
<option value="calendar-week">calendar week</option>
|
||||
<option value="moon-phase"> moon phase</option>
|
||||
<option value="custom"> (custom)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>custom URL:</td>
|
||||
<td><input type="url" id="complication-b-custom-url" size="50"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3"><b>bottom-right:</b></td>
|
||||
</tr><tr>
|
||||
<td> </td>
|
||||
<td> Complication:</td>
|
||||
<td>
|
||||
<select id="complication-br">
|
||||
<option value="none" selected>(none)</option>
|
||||
<option value="date"> date</option>
|
||||
<option value="weekday"> weekday</option>
|
||||
<option value="calendar-week">calendar week</option>
|
||||
<option value="moon-phase"> moon phase</option>
|
||||
<option value="custom"> (custom)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>custom URL:</td>
|
||||
<td><input type="url" id="complication-br-custom-url" size="50"></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</p>
|
||||
|
||||
<h3>Settings</h3>
|
||||
|
||||
<p>
|
||||
Color faces, hands and complications are often drawn using configurable
|
||||
foreground and background colors.
|
||||
</p><p>
|
||||
Here you may specify these colors. Click on a color to select it - or on
|
||||
"themed" if you want the clock to use the currently configured theme on
|
||||
your Bangle.js 2:
|
||||
</p><p>
|
||||
<b>Background Color:</b>
|
||||
</p><p>
|
||||
<label class="ColorPatch">
|
||||
<input type="radio" name="background" value="Theme" checked/>
|
||||
<span>themed</span>
|
||||
</label>
|
||||
<input type="radio" name="background" value="#000000" class="ColorPatch" style="background:#000000"/>
|
||||
<input type="radio" name="background" value="#FF0000" class="ColorPatch" style="background:#FF0000"/>
|
||||
<input type="radio" name="background" value="#00FF00" class="ColorPatch" style="background:#00FF00"/>
|
||||
<input type="radio" name="background" value="#0000FF" class="ColorPatch" style="background:#0000FF"/>
|
||||
<input type="radio" name="background" value="#FFFF00" class="ColorPatch" style="background:#FFFF00"/>
|
||||
<input type="radio" name="background" value="#FF00FF" class="ColorPatch" style="background:#FF00FF"/>
|
||||
<input type="radio" name="background" value="#00FFFF" class="ColorPatch" style="background:#00FFFF"/>
|
||||
<input type="radio" name="background" value="#FFFFFF" class="ColorPatch" style="background:#FFFFFF"/>
|
||||
</p><p>
|
||||
<b>Foreground Color:</b>
|
||||
</p><p>
|
||||
<label class="ColorPatch">
|
||||
<input type="radio" name="foreground" value="Theme" checked/>
|
||||
<span>themed</span>
|
||||
</label>
|
||||
<input type="radio" name="foreground" value="#000000" class="ColorPatch" style="background:#000000"/>
|
||||
<input type="radio" name="foreground" value="#FF0000" class="ColorPatch" style="background:#FF0000"/>
|
||||
<input type="radio" name="foreground" value="#00FF00" class="ColorPatch" style="background:#00FF00"/>
|
||||
<input type="radio" name="foreground" value="#0000FF" class="ColorPatch" style="background:#0000FF"/>
|
||||
<input type="radio" name="foreground" value="#FFFF00" class="ColorPatch" style="background:#FFFF00"/>
|
||||
<input type="radio" name="foreground" value="#FF00FF" class="ColorPatch" style="background:#FF00FF"/>
|
||||
<input type="radio" name="foreground" value="#00FFFF" class="ColorPatch" style="background:#00FFFF"/>
|
||||
<input type="radio" name="foreground" value="#FFFFFF" class="ColorPatch" style="background:#FFFFFF"/>
|
||||
</p><p>
|
||||
When you are satisfied with your configuration, just click on "Upload" in
|
||||
order to generate the specified clock and upload it to your Bangle.js 2:
|
||||
</p>
|
||||
<p id="MessageView" style="display:none; color:red"></p>
|
||||
|
||||
<button id="UploadButton">Upload</button>
|
||||
</p><p>
|
||||
This application is based on the author's
|
||||
<a href="https://github.com/rozek/banglejs-2-analog-clock-construction-kit">Analog Clock Construction Kit (ACCK)</a>.
|
||||
If you need a different "clockwork", clock size calculation or clock face,
|
||||
or specific clock hands or complications, just follow the link to learn how to
|
||||
implement your own clock parts.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# AC-AC - A Configurable Analog Clock #
|
||||
|
||||
This app implements an analog clock with various faces, hands and complications
|
||||
to choose from before uploading to a Bangle.js 2.
|
||||
|
||||
It is based on the [Analog Clock Construction Kit (ACCK)](https://github.com/rozek/banglejs-2-analog-clock-construction-kit)
|
||||
and makes most of the currently implemented parts available with a few mouse
|
||||
clicks - just click on "Upload" and you will be directed to a web form where
|
||||
you compose your very own, personal analog clock.
|
||||
|
||||
You currently have the choice between
|
||||
|
||||
* 2 different clock sizes,
|
||||
* 4 different clock faces,
|
||||
* 3 different clock hands and
|
||||
* 4 different complications
|
||||
|
||||
Alternatively, you may specify the GitHub URL of ACCK compatible modules for
|
||||
external clock sizes, faces, hands or complications.
|
||||
|
||||
Additionally, you may use the currently configured global theme or configure
|
||||
your own colors for clock fore- and background and second hands.
|
||||
|
||||
Consequently, even without external modules you already have the choice between
|
||||
102144 combinations!
|
||||
|
||||
<!--
|
||||
1 + (8 Fg colors * 7 Bg colors) * 2 sizes * 4(7) faces * 3(4) hands *
|
||||
8 positions * 4 complications (w/o placeholder)
|
||||
-->
|
||||
|
||||
## License ##
|
||||
|
||||
[MIT License](LICENSE)
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgn/ABH+AQPvBpIAI/n8/3f5/PCp/v9oHF7w1CABffGxAYMH4f9z/514YDCxW/O4gFBxwHD/ZEL7/9GgX8GwQLCBQQXH/uP/Hf/2N44IBAgIXJ7oaD/3v/3uAYIIB9wQGAA2+/iRG5oSIM4f+1nrPYgAB3aHIAC77QYYRoCAAP676ICABXYFIntDoPf3+PC5f+BoPOX4vPNBn7IogEB/eu3QXC9wNEAAeKBIP+dgbSCDYMwgEApQVEygPCeRH8iAWBAAMHPwXDgoRGAonACwYABgN5uMAC4q8GC4U0DQsAggRF9gXFgggB/2hC4kdVAQCBVAX7xwXCVAnGCwUadAeeDYfr7IhEAAf93e+A4gpB9yRB/mqcgndRgQAHzqRE1gEC/KoCjLZEsgCB9evO4gOC/RyEgqdC2KnFO4S/KgFYsC/Ga5EBs1AX5bXHgx1C2YXEnp7GCARgB4AfE64WCnawFCgf9VAK/G/3M7zWDz4PF/maXJIAD7D8EVAP85QXN3OP/42DfoQXN/wvE/ySGABa8FAC37AgepVwQ9E1SfBAAJIEAAnrBQ39xgwJ7pRHFQX+3QECCAbyG9bPDzwXC9QMBdgQXIAAf41wEC5pLCJJBcF9fZQ5IAGYYn81q7RJQwWC/wXM9/tA4veCxooDIAPv55PEABwpB97rDAAw"))
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
|
@ -0,0 +1,2 @@
|
|||
let Clockwork = require('https://raw.githubusercontent.com/rozek/banglejs-2-simple-clockwork/main/Clockwork.js');
|
||||
Clockwork.windUp();
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4UA/4AB304ief85L/ABNVAAwKCgILHoALBgoLHqALOrVVr4BEBZIFBBYiaCAAPq2oLQEYlqF5VrBZWnBZWvBZNWz4LGBoQLHJ4O///6v/1BZHa/4LFLYOlr9pR49r1ILJ09qr4ZBBY2vrWdBY5PBq2uyoLIquqBY5bBKoZTFLYILJJ4STDBY77IJ4QLUJ4QLU1QAE0oLPqoAGBZ0BBY9ABYMABY4KCAH4AGA="))
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
Bangle.loadWidgets();
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
var R = Bangle.appRect;
|
||||
|
||||
var x = 0;
|
||||
var last;
|
||||
|
||||
function getY(v) {
|
||||
return (R.y+R.y2 + v*R.h/2)/2;
|
||||
}
|
||||
Bangle.on('accel', a => {
|
||||
g.reset();
|
||||
if (last) {
|
||||
g.setColor("#f00").drawLine(x-1,getY(last.x),x,getY(a.x));
|
||||
g.setColor("#0f0").drawLine(x-1,getY(last.y),x,getY(a.y));
|
||||
g.setColor("#00f").drawLine(x-1,getY(last.z),x,getY(a.z));
|
||||
}
|
||||
last = a;x++;
|
||||
if (x>=g.getWidth()) {
|
||||
x = 1;
|
||||
g.clearRect(R);
|
||||
}
|
||||
});
|
||||
|
After Width: | Height: | Size: 944 B |
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -4,4 +4,7 @@
|
|||
0.04: Clock can optionally show seconds, date optionally in ISO-8601 format, weekdays and uppercase configurable, too.
|
||||
0.05: Clock can optionally show ISO-8601 calendar weeknumber (default: Off)
|
||||
when weekday name "Off": week #:<num>
|
||||
when weekday name "On": weekday name is cut at 6th position and .#<week num> is added
|
||||
when weekday name "On": weekday name is cut at 6th position and .#<week num> is added
|
||||
0.06: fixes #1271 - wrong settings name
|
||||
when weekday name and calendar weeknumber are on then display is <weekday short> #<calweek>
|
||||
week is buffered until date or timezone changes
|
||||
|
|
@ -40,9 +40,9 @@ The main menu contains several settings covering Anton clock in general.
|
|||
* **Show Weekday** - Weekday is shown in the time presentation without seconds.
|
||||
Weekday name depends on the current locale.
|
||||
If seconds are shown, the weekday is never shown as there is not enough space on the watch face.
|
||||
* **Show Weeknumber** - Week-number (ISO-8601) is shown. (default: Off)
|
||||
If "Show Weekday" is "Off" the week-number is displayed as "week #:<num>".
|
||||
If "Show Weekday" is "On" the weekday name is cut at 6th position and suffixed with ".#<week num>".
|
||||
* **Show CalWeek** - Week-number (ISO-8601) is shown. (default: Off)
|
||||
If "Show Weekday" is "Off" displays the week-number as "week #<num>".
|
||||
If "Show Weekday" is "On" displays "weekday name short" with " #<num>" .
|
||||
If seconds are shown, the week number is never shown as there is not enough space on the watch face.
|
||||
* **Vector font** - Use the built-in vector font for dates and weekday.
|
||||
This can improve readability.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Clock with large digits using the "Anton" bold font
|
||||
|
||||
var SETTINGSFILE = "antonclk.json";
|
||||
const SETTINGSFILE = "antonclk.json";
|
||||
|
||||
Graphics.prototype.setFontAnton = function(scale) {
|
||||
// Actual height 69 (68 - 0)
|
||||
|
|
@ -28,7 +28,7 @@ var drawTimeout;
|
|||
var queueMillis = 1000;
|
||||
var secondsScreen = true;
|
||||
|
||||
var isBangle1 = (g.getWidth() == 240);
|
||||
var isBangle1 = (process.env.HWVERSION == 1);
|
||||
|
||||
//For development purposes
|
||||
/*
|
||||
|
|
@ -50,13 +50,11 @@ require('Storage').writeJSON(SETTINGSFILE, {
|
|||
require('Storage').erase(SETTINGSFILE);
|
||||
*/
|
||||
|
||||
// Helper method for loading the settings
|
||||
function def(value, def) {
|
||||
return (value !== undefined ? value : def);
|
||||
}
|
||||
|
||||
// Load settings
|
||||
function loadSettings() {
|
||||
// Helper function default setting
|
||||
function def (value, def) {return value !== undefined ? value : def;}
|
||||
|
||||
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
|
||||
secondsMode = def(settings.secondsMode, "Never");
|
||||
secondsColoured = def(settings.secondsColoured, true);
|
||||
|
|
@ -104,7 +102,12 @@ function isoStr(date) {
|
|||
return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).substr(-2) + "-" + ("0" + date.getDate()).substr(-2);
|
||||
}
|
||||
|
||||
var calWeekBuffer = [false,false,false]; //buffer tz, date, week no (once calculated until other tz or date is requested)
|
||||
function ISO8601calWeek(date) { //copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480
|
||||
dateNoTime = date; dateNoTime.setHours(0,0,0,0);
|
||||
if (calWeekBuffer[0] === date.getTimezoneOffset() && calWeekBuffer[1] === dateNoTime) return calWeekBuffer[2];
|
||||
calWeekBuffer[0] = date.getTimezoneOffset();
|
||||
calWeekBuffer[1] = dateNoTime;
|
||||
var tdt = new Date(date.valueOf());
|
||||
var dayn = (date.getDay() + 6) % 7;
|
||||
tdt.setDate(tdt.getDate() - dayn + 3);
|
||||
|
|
@ -113,7 +116,8 @@ function ISO8601calWeek(date) { //copied from: https://gist.github.com/IamSilviu
|
|||
if (tdt.getDay() !== 4) {
|
||||
tdt.setMonth(0, 1 + ((4 - tdt.getDay()) + 7) % 7);
|
||||
}
|
||||
return 1 + Math.ceil((firstThursday - tdt) / 604800000);
|
||||
calWeekBuffer[2] = 1 + Math.ceil((firstThursday - tdt) / 604800000);
|
||||
return calWeekBuffer[2];
|
||||
}
|
||||
|
||||
function doColor() {
|
||||
|
|
@ -186,13 +190,17 @@ function draw() {
|
|||
else
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString(dateStr, x, y);
|
||||
if (weekDay || calWeek) {
|
||||
var dowwumStr = require("locale").dow(date);
|
||||
if (calWeek || weekDay) {
|
||||
var dowcwStr = "";
|
||||
if (calWeek)
|
||||
dowwumStr = (weekDay ? dowwumStr.substr(0,Math.min(dowwumStr.length,6)) + (dowwumStr.length>=6 ? "." : "") : "week ") + "#" + ISO8601calWeek(date); //TODO: locale for "week"
|
||||
dowcwStr = " #" + ("0" + ISO8601calWeek(date)).substring(-2);
|
||||
if (weekDay)
|
||||
dowcwStr = require("locale").dow(date, calWeek ? 1 : 0) + dowcwStr; //weekDay e.g. Monday or weekDayShort #<calWeek> e.g. Mon #01
|
||||
else //week #01
|
||||
dowcwStr = /*LANG*/"week" + dowcwStr;
|
||||
if (upperCase)
|
||||
dowwumStr = dowwumStr.toUpperCase();
|
||||
g.drawString(dowwumStr, x, y + (vectorFont ? 26 : 16));
|
||||
dowcwStr = dowcwStr.toUpperCase();
|
||||
g.drawString(dowcwStr, x, y + (vectorFont ? 26 : 16));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@
|
|||
writeSettings();
|
||||
}
|
||||
},
|
||||
"Show Weeknumber": {
|
||||
value: (settings.weekNum !== undefined ? settings.weekNum : true),
|
||||
"Show CalWeek": {
|
||||
value: (settings.calWeek !== undefined ? settings.calWeek : false),
|
||||
format: v => v ? "On" : "Off",
|
||||
onchange: v => {
|
||||
settings.weekNum = v;
|
||||
settings.calWeek = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New App!
|
||||
0.02: Update to work with Bangle.js 2
|
||||
0.03: Select GNSS systems to use for Bangle.js 2
|
||||
|
|
|
|||
|
|
@ -27,6 +27,31 @@
|
|||
</div>
|
||||
</div>
|
||||
<div id="banglejs2-info" style="display:none">
|
||||
<p>Using fewer GNSS systems may decrease the time to fix. (If unsure, select only GPS)</p>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Select which GNSS system you want.</label>
|
||||
<label class="form-radio">
|
||||
<input type="radio" name="gnss_select" value="1" checked><i class="form-icon"></i> GPS
|
||||
</label>
|
||||
<label class="form-radio">
|
||||
<input type="radio" name="gnss_select" value="2"><i class="form-icon"></i> BDS
|
||||
</label>
|
||||
<label class="form-radio">
|
||||
<input type="radio" name="gnss_select" value="3"><i class="form-icon"></i> GPS+BDS
|
||||
</label>
|
||||
<label class="form-radio">
|
||||
<input type="radio" name="gnss_select" value="4"><i class="form-icon"></i> GLONASS
|
||||
</label>
|
||||
<label class="form-radio">
|
||||
<input type="radio" name="gnss_select" value="5"><i class="form-icon"></i> GPS+GLONASS
|
||||
</label>
|
||||
<label class="form-radio">
|
||||
<input type="radio" name="gnss_select" value="6"><i class="form-icon"></i> BDS+GLONASS
|
||||
</label>
|
||||
<label class="form-radio">
|
||||
<input type="radio" name="gnss_select" value="6"><i class="form-icon"></i> GPS+BDS+GLONASS
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<p id="upload-wrap" style="display:none">Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||
|
||||
|
|
@ -116,8 +141,13 @@
|
|||
}
|
||||
if (isB2) { // CASIC
|
||||
|
||||
// Disable BDS, use just GPS (supposedly improve lock time)
|
||||
js += `\x10Serial1.println("${CASIC_CHECKSUM("$PCAS04,1")}")\n`; // set GPS-only mode
|
||||
// Select what GNSS System to use for decreased fix time.
|
||||
var radios = document.getElementsByName('gnss_select');
|
||||
var gnss_select="1";
|
||||
for (var i=0; i<radios.length; i++)
|
||||
if (radios[i].checked)
|
||||
gnss_select=radios[i].value;
|
||||
js += `\x10Serial1.println("${CASIC_CHECKSUM("$PCAS04,"+gnss_select)}")\n`; // set GNSS mode
|
||||
// What about:
|
||||
// NAV-TIMEUTC (0x01 0x10)
|
||||
// NAV-PV (0x01 0x03)
|
||||
|
|
|
|||
|
|
@ -2,3 +2,6 @@
|
|||
0.02: Make overriding the HRM event optional
|
||||
Emit BTHRM event for external sensor
|
||||
Add recorder app plugin
|
||||
0.03: Prevent readings from internal sensor mixing into BT values
|
||||
Mark events with src property
|
||||
Show actual source of event in app
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
Bangle.isHRMOn = function() {
|
||||
var settings = require('Storage').readJSON("bthrm.json", true) || {};
|
||||
|
||||
print(settings);
|
||||
if (settings.enabled && !settings.replace){
|
||||
return origIsHRMOn();
|
||||
} else if (settings.enabled && settings.replace){
|
||||
|
|
@ -69,13 +68,11 @@
|
|||
var interval = dv.getUint16(idx,1); // in milliseconds
|
||||
}*/
|
||||
|
||||
|
||||
var eventName = settings.replace ? "HRM" : "BTHRM";
|
||||
|
||||
Bangle.emit(eventName, {
|
||||
Bangle.emit(settings.replace?"HRM":"BTHRM", {
|
||||
bpm:bpm,
|
||||
confidence:100
|
||||
});
|
||||
confidence:100,
|
||||
src:settings.replace?"bthrm":undefined
|
||||
});
|
||||
});
|
||||
return characteristic.startNotifications();
|
||||
}).then(function() {
|
||||
|
|
@ -107,8 +104,20 @@
|
|||
if (settings.enabled || !isOn){
|
||||
Bangle.setBTHRMPower(isOn, app);
|
||||
}
|
||||
if (settings.enabled && !settings.replace || !isOn){
|
||||
if ((settings.enabled && !settings.replace) || !settings.enabled || !isOn){
|
||||
origSetHRMPower(isOn, app);
|
||||
}
|
||||
}
|
||||
|
||||
var settings = require('Storage').readJSON("bthrm.json", true) || {};
|
||||
if (settings.enabled && settings.replace){
|
||||
if (!(Bangle._PWR===undefined) && !(Bangle._PWR.HRM===undefined)){
|
||||
for (var i = 0; i < Bangle._PWR.HRM.length; i++){
|
||||
var app = Bangle._PWR.HRM[i];
|
||||
origSetHRMPower(0, app);
|
||||
Bangle.setBTHRMPower(1, app);
|
||||
if (Bangle._PWR.HRM===undefined) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ function draw(y, event, type, counter) {
|
|||
var px = g.getWidth()/2;
|
||||
g.reset();
|
||||
g.setFontAlign(0,0);
|
||||
g.clearRect(0,y,g.getWidth(),y+80);
|
||||
g.clearRect(0,y,g.getWidth(),y+75);
|
||||
if (type == null || event == null || counter == 0) return;
|
||||
var str = event.bpm + "";
|
||||
g.setFontVector(40).drawString(str,px,y+20);
|
||||
str = "Confidence: " + event.confidence;
|
||||
g.setFontVector(12).drawString(str,px,y+50);
|
||||
str = "Event: " + type;
|
||||
if (type == "HRM") str += " Source: " + (event.src ? event.src : "internal");
|
||||
g.setFontVector(12).drawString(str,px,y+60);
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +36,6 @@ Bangle.on('BTHRM', onBtHrm);
|
|||
Bangle.on('HRM', onHrm);
|
||||
|
||||
Bangle.setHRMPower(1,'bthrm')
|
||||
Bangle.setBTHRMPower(1,'bthrm')
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
(function(recorders) {
|
||||
recorders.bthrm = function() {
|
||||
var bpm = 0;
|
||||
var bpm = "";
|
||||
function onHRM(h) {
|
||||
bpm = h.bpm;
|
||||
bpm = h.bpm;
|
||||
}
|
||||
return {
|
||||
name : "BTHR",
|
||||
fields : ["BT Heartrate"],
|
||||
getValues : () => {
|
||||
result = [bpm];
|
||||
bpm = 0;
|
||||
bpm = "";
|
||||
return result;
|
||||
},
|
||||
start : () => {
|
||||
|
|
|
|||
|
|
@ -136,8 +136,10 @@ function getCirclePosition(type) {
|
|||
if (setting == type) return circlePosX[i - 1];
|
||||
}
|
||||
for (let i = 0; i < defaultCircleTypes.length; i++) {
|
||||
if (type == defaultCircleTypes[i]) return circlePosX[i];
|
||||
}
|
||||
if (type == defaultCircleTypes[i] && (!settings || settings['circle' + (i + 1)] == undefined)) {
|
||||
return circlePosX[i];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Andreas Rozek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Variable Analog Clock #
|
||||
|
||||
This app implements an analog clock with various faces, hands and colors to
|
||||
choose from.
|
||||
|
||||
You have the choice between:
|
||||
|
||||
* 4 different clock faces     and
|
||||
* 3 different clock hands (optionally with or without second hands)   
|
||||
|
||||
Additionally, you may use the currently configured global theme or configure
|
||||
your own colors for clock fore- and background and second hands.
|
||||
|
||||
Just swipe up or down to switch from clock display to configuration screen
|
||||
|
||||
  
|
||||
 
|
||||
|
||||
Chosen settings will be written to the Bangle.js's flash memory and restored
|
||||
whenever the clock is started again.
|
||||
|
||||
This clock also acts as an example for the building blocks found in the author's
|
||||
[GitHub repository](https://github.com/rozek/banglejs-2-activities)
|
||||
|
||||
## License ##
|
||||
|
||||
[MIT License](LICENSE)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Andreas Rozek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# Configurable Analog Clock #
|
||||
|
||||
This app implements an analog clock with various faces, hands and colors to
|
||||
choose from.
|
||||
|
||||
You have the choice between:
|
||||
|
||||
* 4 different clock faces<br>    and
|
||||
* 3 different clock hands (optionally with or without second hands)<br>  
|
||||
|
||||
Additionally, you may use the currently configured global theme or configure
|
||||
your own colors for clock fore- and background and second hands.
|
||||
|
||||
Just swipe up or down to switch from clock display to the first configuration
|
||||
screen and continue from there
|
||||
|
||||
 
|
||||
 
|
||||

|
||||
|
||||
Chosen settings will be written to the Bangle.js's flash memory and restored
|
||||
whenever the clock is started again.
|
||||
|
||||
This clock also acts as an example for the building blocks found in the author's
|
||||
[GitHub repository](https://github.com/rozek/banglejs-2-activities)
|
||||
|
||||
## License ##
|
||||
|
||||
[MIT License](LICENSE)
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgZC/AB1RgkQsAQMyUKAYMIkAPJgNFiEBgACBg0YCRMogEJkGSAwMSEZNAAQMAEAMGgBKHgXAlECwMgzcAmkAhgRGilRssUgMEEYcBwARFiBHBgQKB7AjCawIQEgoCCigDBjEBwwEBEwIAGlmSEYYABI4PAEYhEBNYIjCAYVtwCSElG2xdoAwQjDhpZEEAMUqAHDCIaPBEYlAiwjItkAgYjFqJHDCIdhI4j1CAAhlEZoTUEAAcGEYZKEEYWgCIgjEWYkBoqwCCITLBgcMmPXhgjCgUB2iFDm3pw0YLAMygEgc4QjF49cmA3BbQQjDgGkI5OwNZZ9FEYoRLEYxmBCI5jBEYQACyQRHgmAEYsEEZEka4kAhEEEY8BCIMJCIYjKgGChAFDCwKzDNYyKEJgUDlgRBAoPDRQQjEZQZzEjScIhgjBEwQjEH4aXEgIjBjYCBjQCBMYYADmAjDFIjcGKocAjBKCgJRCAAwaCEARQBmARIhBrEgSMEAApEBmHAAQJrCABUCjFhwwQMI4oA7"))
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New app
|
||||
0.02: Cleanup interface and add settings, widget, add skin temp reporting.
|
||||
0.03: Move code for recording to this app
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
(function(recorders) {
|
||||
recorders.coretemp = function() {
|
||||
var core = "", skin = "";
|
||||
var hasCore = false;
|
||||
function onCore(c) {
|
||||
core=c.core;
|
||||
skin=c.skin;
|
||||
hasCore = true;
|
||||
}
|
||||
return {
|
||||
name : "Core",
|
||||
fields : ["Core","Skin"],
|
||||
getValues : () => {
|
||||
var r = [core,skin];
|
||||
core = "";
|
||||
skin = "";
|
||||
return r;
|
||||
},
|
||||
start : () => {
|
||||
hasCore = false;
|
||||
Bangle.on('CoreTemp', onCore);
|
||||
},
|
||||
stop : () => {
|
||||
hasCore = false;
|
||||
Bangle.removeListener('CoreTemp', onCore);
|
||||
},
|
||||
draw : (x,y) => g.setColor(hasCore?"#0f0":"#8f8").drawImage(atob("DAyBAAHh0js3EuDMA8A8AWBnDj9A8A=="),x,y)
|
||||
};
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -1 +1,2 @@
|
|||
0.01: New Clock Nifty A
|
||||
0.02: Shows the current week number (ISO8601), can be disabled via settings ""
|
||||
|
|
|
|||
|
|
@ -1,4 +1,14 @@
|
|||
# Nifty-A Clock
|
||||
|
||||
Colors are black/white - photos have non correct camera color "blue"
|
||||
|
||||
## This is the clock
|
||||
|
||||

|
||||
|
||||
## The week number (ISO8601) can be turned of in settings
|
||||
(default is **"On"**)
|
||||
|
||||

|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const locale = require("locale");
|
||||
const is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
|
||||
const CFG = require('Storage').readJSON("ffcniftya.json", 1) || {showWeekNum: true};
|
||||
|
||||
/* Clock *********************************************/
|
||||
const scale = g.getWidth() / 176;
|
||||
|
|
@ -16,6 +17,18 @@ const center = {
|
|||
y: Math.round(((viewport.height - widget) / 2) + widget),
|
||||
}
|
||||
|
||||
function ISO8601_week_no(date) { //copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480
|
||||
var tdt = new Date(date.valueOf());
|
||||
var dayn = (date.getDay() + 6) % 7;
|
||||
tdt.setDate(tdt.getDate() - dayn + 3);
|
||||
var firstThursday = tdt.valueOf();
|
||||
tdt.setMonth(0, 1);
|
||||
if (tdt.getDay() !== 4) {
|
||||
tdt.setMonth(0, 1 + ((4 - tdt.getDay()) + 7) % 7);
|
||||
}
|
||||
return 1 + Math.ceil((firstThursday - tdt) / 604800000);
|
||||
}
|
||||
|
||||
function d02(value) {
|
||||
return ('0' + value).substr(-2);
|
||||
}
|
||||
|
|
@ -29,23 +42,26 @@ function draw() {
|
|||
const minutes = d02(now.getMinutes());
|
||||
const day = d02(now.getDate());
|
||||
const month = d02(now.getMonth() + 1);
|
||||
const year = now.getFullYear();
|
||||
|
||||
const month2 = locale.month(now, 3);
|
||||
const day2 = locale.dow(now, 3);
|
||||
const year = now.getFullYear(now);
|
||||
const weekNum = d02(ISO8601_week_no(now));
|
||||
const monthName = locale.month(now, 3);
|
||||
const dayName = locale.dow(now, 3);
|
||||
|
||||
const centerTimeScaleX = center.x + 32 * scale;
|
||||
g.setFontAlign(1, 0).setFont("Vector", 90 * scale);
|
||||
g.drawString(hour, center.x + 32 * scale, center.y - 31 * scale);
|
||||
g.drawString(minutes, center.x + 32 * scale, center.y + 46 * scale);
|
||||
g.drawString(hour, centerTimeScaleX, center.y - 31 * scale);
|
||||
g.drawString(minutes, centerTimeScaleX, center.y + 46 * scale);
|
||||
|
||||
g.fillRect(center.x + 30 * scale, center.y - 72 * scale, center.x + 32 * scale, center.y + 74 * scale);
|
||||
|
||||
const centerDatesScaleX = center.x + 40 * scale;
|
||||
g.setFontAlign(-1, 0).setFont("Vector", 16 * scale);
|
||||
g.drawString(year, center.x + 40 * scale, center.y - 62 * scale);
|
||||
g.drawString(month, center.x + 40 * scale, center.y - 44 * scale);
|
||||
g.drawString(day, center.x + 40 * scale, center.y - 26 * scale);
|
||||
g.drawString(month2, center.x + 40 * scale, center.y + 48 * scale);
|
||||
g.drawString(day2, center.x + 40 * scale, center.y + 66 * scale);
|
||||
g.drawString(year, centerDatesScaleX, center.y - 62 * scale);
|
||||
g.drawString(month, centerDatesScaleX, center.y - 44 * scale);
|
||||
g.drawString(day, centerDatesScaleX, center.y - 26 * scale);
|
||||
if (CFG.showWeekNum) g.drawString(d02(ISO8601_week_no(now)), centerDatesScaleX, center.y + 15 * scale);
|
||||
g.drawString(monthName, centerDatesScaleX, center.y + 48 * scale);
|
||||
g.drawString(dayName, centerDatesScaleX, center.y + 66 * scale);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1,23 @@
|
|||
(function(back) {
|
||||
var FILE = "ffcniftya.json";
|
||||
// Load settings
|
||||
var cfg = require('Storage').readJSON(FILE, 1) || { showWeekNum: true };
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, cfg);
|
||||
}
|
||||
|
||||
// Show the menu
|
||||
E.showMenu({
|
||||
"" : { "title" : "Nifty-A Clock" },
|
||||
"< Back" : () => back(),
|
||||
'week number?': {
|
||||
value: cfg.showWeekNum,
|
||||
format: v => v?"On":"Off",
|
||||
onchange: v => {
|
||||
cfg.showWeekNum = v;
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
@ -1 +1,2 @@
|
|||
0.01: first release
|
||||
0.02: RAM efficient version of `fourTwentyTz.js` (as suggested by @gfwilliams).
|
||||
|
|
|
|||
|
|
@ -17,14 +17,13 @@ function queueDraw() {
|
|||
|
||||
function draw() {
|
||||
g.reset();
|
||||
g.setBgColor("#ffffff");
|
||||
let date = new Date();
|
||||
let timeStr = require("locale").time(date,1);
|
||||
let next420 = getNextFourTwenty();
|
||||
g.clearRect(0,26,g.getWidth(),g.getHeight());
|
||||
g.setColor("#00ff00").setFontAlign(0,-1).setFont("Teletext10x18Ascii",2);
|
||||
g.drawString(next420.minutes? timeStr: `\0${leaf_img}${timeStr}\0${leaf_img}`, g.getWidth()/2, 28);
|
||||
g.setColor("#000000");
|
||||
g.setColor(g.theme.fg);
|
||||
g.setFontAlign(-1,-1).setFont("Teletext10x18Ascii");
|
||||
g.drawString(g.wrapString(next420.text, g.getWidth()-8).join("\n"),4,60);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
let timezones = require("fourTwentyTz").timezones;
|
||||
let ftz = require("fourTwentyTz"),
|
||||
offsets = ftz.offsets,
|
||||
timezones = ftz.timezones;
|
||||
|
||||
function get420offset() {
|
||||
let current_time = Math.floor((Date.now()%(24*3600*1000))/60000);
|
||||
|
|
@ -24,10 +26,10 @@ function makeFourTwentyText(minutes, places) {
|
|||
|
||||
function getNextFourTwenty() {
|
||||
let offs = get420offset();
|
||||
for (let i=0; i<timezones.length; i++) {
|
||||
if (timezones[i][0]<=offs) {
|
||||
let minutes = offs-timezones[i][0];
|
||||
let places = timezones[i][1];
|
||||
for (let i=0; i<offsets.length; i++) {
|
||||
if (offsets[i]<=offs) {
|
||||
let minutes = offs-offsets[i];
|
||||
let places = timezones(offsets[i]);
|
||||
return {
|
||||
minutes: minutes,
|
||||
places: places,
|
||||
|
|
|
|||
|
|
@ -1,463 +1,33 @@
|
|||
// Generated by mkFourTwentyTz.js
|
||||
// Wed Jan 12 2022 19:35:36 GMT+0200 (Israel Standard Time)
|
||||
// Data source: https://timezonedb.com/files/timezonedb.csv.zip
|
||||
// Sun Jan 09 2022 13:21:47 GMT+0200 (Israel Standard Time)
|
||||
exports.timezones = {
|
||||
"0": [
|
||||
"Troll, Antarctica",
|
||||
"Ouagadougou, Burkina Faso",
|
||||
"Abidjan, Côte d'Ivoire",
|
||||
"Canary, Spain",
|
||||
"Faroe, Faroe Islands",
|
||||
"London, United Kingdom of Great Britain and Northern Ireland",
|
||||
"Guernsey, Guernsey",
|
||||
"Accra, Ghana",
|
||||
"Danmarkshavn, Greenland",
|
||||
"Banjul, Gambia",
|
||||
"Conakry, Guinea",
|
||||
"Bissau, Guinea-Bissau",
|
||||
"Dublin, Ireland",
|
||||
"Isle of_Man, Isle of Man",
|
||||
"Reykjavik, Iceland",
|
||||
"Jersey, Jersey",
|
||||
"Monrovia, Liberia",
|
||||
"Bamako, Mali",
|
||||
"Nouakchott, Mauritania",
|
||||
"Lisbon, Portugal",
|
||||
"Madeira, Portugal",
|
||||
"St Helena, Saint Helena, Ascension and Tristan da Cunha",
|
||||
"Freetown, Sierra Leone",
|
||||
"Dakar, Senegal",
|
||||
"Sao Tome, Sao Tome and Principe",
|
||||
"Lome, Togo"
|
||||
],
|
||||
"60": [
|
||||
"Andorra, Andorra",
|
||||
"Tirane, Albania",
|
||||
"Luanda, Angola",
|
||||
"Vienna, Austria",
|
||||
"Sarajevo, Bosnia and Herzegovina",
|
||||
"Brussels, Belgium",
|
||||
"Porto-Novo, Benin",
|
||||
"Kinshasa, Congo, Democratic Republic of the",
|
||||
"Bangui, Central African Republic",
|
||||
"Brazzaville, Congo",
|
||||
"Zurich, Switzerland",
|
||||
"Douala, Cameroon",
|
||||
"Prague, Czechia",
|
||||
"Berlin, Germany",
|
||||
"Busingen, Germany",
|
||||
"Copenhagen, Denmark",
|
||||
"Algiers, Algeria",
|
||||
"El Aaiun, Western Sahara",
|
||||
"Madrid, Spain",
|
||||
"Ceuta, Spain",
|
||||
"Paris, France",
|
||||
"Libreville, Gabon",
|
||||
"Gibraltar, Gibraltar",
|
||||
"Malabo, Equatorial Guinea",
|
||||
"Zagreb, Croatia",
|
||||
"Budapest, Hungary",
|
||||
"Rome, Italy",
|
||||
"Vaduz, Liechtenstein",
|
||||
"Luxembourg, Luxembourg",
|
||||
"Casablanca, Morocco",
|
||||
"Monaco, Monaco",
|
||||
"Podgorica, Montenegro",
|
||||
"Skopje, North Macedonia",
|
||||
"Malta, Malta",
|
||||
"Niamey, Niger",
|
||||
"Lagos, Nigeria",
|
||||
"Amsterdam, Netherlands",
|
||||
"Oslo, Norway",
|
||||
"Warsaw, Poland",
|
||||
"Belgrade, Serbia",
|
||||
"Stockholm, Sweden",
|
||||
"Ljubljana, Slovenia",
|
||||
"Longyearbyen, Svalbard and Jan Mayen",
|
||||
"Bratislava, Slovakia",
|
||||
"San Marino, San Marino",
|
||||
"Ndjamena, Chad",
|
||||
"Tunis, Tunisia",
|
||||
"Vatican, Holy See"
|
||||
],
|
||||
"120": [
|
||||
"Mariehamn, Åland Islands",
|
||||
"Sofia, Bulgaria",
|
||||
"Bujumbura, Burundi",
|
||||
"Gaborone, Botswana",
|
||||
"Lubumbashi, Congo, Democratic Republic of the",
|
||||
"Nicosia, Cyprus",
|
||||
"Famagusta, Cyprus",
|
||||
"Tallinn, Estonia",
|
||||
"Cairo, Egypt",
|
||||
"Helsinki, Finland",
|
||||
"Athens, Greece",
|
||||
"Jerusalem, Israel",
|
||||
"Amman, Jordan",
|
||||
"Beirut, Lebanon",
|
||||
"Maseru, Lesotho",
|
||||
"Vilnius, Lithuania",
|
||||
"Riga, Latvia",
|
||||
"Tripoli, Libya",
|
||||
"Chisinau, Moldova, Republic of",
|
||||
"Blantyre, Malawi",
|
||||
"Maputo, Mozambique",
|
||||
"Windhoek, Namibia",
|
||||
"Gaza, Palestine, State of",
|
||||
"Hebron, Palestine, State of",
|
||||
"Bucharest, Romania",
|
||||
"Kaliningrad, Russian Federation",
|
||||
"Kigali, Rwanda",
|
||||
"Khartoum, Sudan",
|
||||
"Juba, South Sudan",
|
||||
"Damascus, Syrian Arab Republic",
|
||||
"Mbabane, Eswatini",
|
||||
"Kiev, Ukraine",
|
||||
"Uzhgorod, Ukraine",
|
||||
"Zaporozhye, Ukraine",
|
||||
"Johannesburg, South Africa",
|
||||
"Lusaka, Zambia",
|
||||
"Harare, Zimbabwe"
|
||||
],
|
||||
"180": [
|
||||
"Syowa, Antarctica",
|
||||
"Bahrain, Bahrain",
|
||||
"Minsk, Belarus",
|
||||
"Djibouti, Djibouti",
|
||||
"Asmara, Eritrea",
|
||||
"Addis Ababa, Ethiopia",
|
||||
"Baghdad, Iraq",
|
||||
"Nairobi, Kenya",
|
||||
"Comoro, Comoros",
|
||||
"Kuwait, Kuwait",
|
||||
"Antananarivo, Madagascar",
|
||||
"Qatar, Qatar",
|
||||
"Moscow, Russian Federation",
|
||||
"Simferopol, Ukraine",
|
||||
"Kirov, Russian Federation",
|
||||
"Volgograd, Russian Federation",
|
||||
"Riyadh, Saudi Arabia",
|
||||
"Mogadishu, Somalia",
|
||||
"Istanbul, Turkey",
|
||||
"Dar es_Salaam, Tanzania, United Republic of",
|
||||
"Kampala, Uganda",
|
||||
"Aden, Yemen",
|
||||
"Mayotte, Mayotte"
|
||||
],
|
||||
"240": [
|
||||
"Dubai, United Arab Emirates",
|
||||
"Yerevan, Armenia",
|
||||
"Baku, Azerbaijan",
|
||||
"Tbilisi, Georgia",
|
||||
"Mauritius, Mauritius",
|
||||
"Muscat, Oman",
|
||||
"Reunion, Réunion",
|
||||
"Astrakhan, Russian Federation",
|
||||
"Saratov, Russian Federation",
|
||||
"Ulyanovsk, Russian Federation",
|
||||
"Samara, Russian Federation",
|
||||
"Mahe, Seychelles"
|
||||
],
|
||||
"300": [
|
||||
"Mawson, Antarctica",
|
||||
"Qyzylorda, Kazakhstan",
|
||||
"Aqtobe, Kazakhstan",
|
||||
"Aqtau, Kazakhstan",
|
||||
"Atyrau, Kazakhstan",
|
||||
"Oral, Kazakhstan",
|
||||
"Maldives, Maldives",
|
||||
"Karachi, Pakistan",
|
||||
"Yekaterinburg, Russian Federation",
|
||||
"Kerguelen, French Southern Territories",
|
||||
"Dushanbe, Tajikistan",
|
||||
"Ashgabat, Turkmenistan",
|
||||
"Samarkand, Uzbekistan",
|
||||
"Tashkent, Uzbekistan"
|
||||
],
|
||||
"360": [
|
||||
"Vostok, Antarctica",
|
||||
"Dhaka, Bangladesh",
|
||||
"Thimphu, Bhutan",
|
||||
"Urumqi, China",
|
||||
"Chagos, British Indian Ocean Territory",
|
||||
"Bishkek, Kyrgyzstan",
|
||||
"Almaty, Kazakhstan",
|
||||
"Qostanay, Kazakhstan",
|
||||
"Omsk, Russian Federation"
|
||||
],
|
||||
"420": [
|
||||
"Davis, Antarctica",
|
||||
"Christmas, Christmas Island",
|
||||
"Jakarta, Indonesia",
|
||||
"Pontianak, Indonesia",
|
||||
"Phnom Penh, Cambodia",
|
||||
"Vientiane, Lao People's Democratic Republic",
|
||||
"Hovd, Mongolia",
|
||||
"Novosibirsk, Russian Federation",
|
||||
"Barnaul, Russian Federation",
|
||||
"Tomsk, Russian Federation",
|
||||
"Novokuznetsk, Russian Federation",
|
||||
"Krasnoyarsk, Russian Federation",
|
||||
"Bangkok, Thailand",
|
||||
"Ho Chi_Minh, Viet Nam"
|
||||
],
|
||||
"480": [
|
||||
"Perth, Australia",
|
||||
"Brunei, Brunei Darussalam",
|
||||
"Shanghai, China",
|
||||
"Hong Kong, Hong Kong",
|
||||
"Makassar, Indonesia",
|
||||
"Ulaanbaatar, Mongolia",
|
||||
"Choibalsan, Mongolia",
|
||||
"Macau, Macao",
|
||||
"Kuala Lumpur, Malaysia",
|
||||
"Kuching, Malaysia",
|
||||
"Manila, Philippines",
|
||||
"Irkutsk, Russian Federation",
|
||||
"Singapore, Singapore",
|
||||
"Taipei, Taiwan, Province of China"
|
||||
],
|
||||
"540": [
|
||||
"Jayapura, Indonesia",
|
||||
"Tokyo, Japan",
|
||||
"Pyongyang, Korea (Democratic People's Republic of)",
|
||||
"Seoul, Korea, Republic of",
|
||||
"Palau, Palau",
|
||||
"Chita, Russian Federation",
|
||||
"Yakutsk, Russian Federation",
|
||||
"Khandyga, Russian Federation",
|
||||
"Dili, Timor-Leste"
|
||||
],
|
||||
"600": [
|
||||
"DumontDUrville, Antarctica",
|
||||
"Brisbane, Australia",
|
||||
"Lindeman, Australia",
|
||||
"Chuuk, Micronesia (Federated States of)",
|
||||
"Guam, Guam",
|
||||
"Saipan, Northern Mariana Islands",
|
||||
"Port Moresby, Papua New Guinea",
|
||||
"Vladivostok, Russian Federation",
|
||||
"Ust-Nera, Russian Federation"
|
||||
],
|
||||
"660": [
|
||||
"Casey, Antarctica",
|
||||
"Lord Howe, Australia",
|
||||
"Macquarie, Australia",
|
||||
"Hobart, Australia",
|
||||
"Melbourne, Australia",
|
||||
"Sydney, Australia",
|
||||
"Pohnpei, Micronesia (Federated States of)",
|
||||
"Kosrae, Micronesia (Federated States of)",
|
||||
"Noumea, New Caledonia",
|
||||
"Bougainville, Papua New Guinea",
|
||||
"Magadan, Russian Federation",
|
||||
"Sakhalin, Russian Federation",
|
||||
"Srednekolymsk, Russian Federation",
|
||||
"Guadalcanal, Solomon Islands",
|
||||
"Efate, Vanuatu"
|
||||
],
|
||||
"720": [
|
||||
"Tarawa, Kiribati",
|
||||
"Majuro, Marshall Islands",
|
||||
"Kwajalein, Marshall Islands",
|
||||
"Norfolk, Norfolk Island",
|
||||
"Nauru, Nauru",
|
||||
"Kamchatka, Russian Federation",
|
||||
"Anadyr, Russian Federation",
|
||||
"Funafuti, Tuvalu",
|
||||
"Wake, United States Minor Outlying Islands",
|
||||
"Wallis, Wallis and Futuna"
|
||||
],
|
||||
"780": [
|
||||
"McMurdo, Antarctica",
|
||||
"Pago Pago, American Samoa",
|
||||
"Fiji, Fiji",
|
||||
"Kanton, Kiribati",
|
||||
"Niue, Niue",
|
||||
"Auckland, New Zealand",
|
||||
"Fakaofo, Tokelau",
|
||||
"Tongatapu, Tonga",
|
||||
"Midway, United States Minor Outlying Islands",
|
||||
"Apia, Samoa"
|
||||
],
|
||||
"840": [
|
||||
"Rarotonga, Cook Islands",
|
||||
"Kiritimati, Kiribati",
|
||||
"Tahiti, French Polynesia",
|
||||
"Adak, United States of America",
|
||||
"Honolulu, United States of America"
|
||||
],
|
||||
"900": [
|
||||
"Gambier, French Polynesia",
|
||||
"Anchorage, United States of America",
|
||||
"Juneau, United States of America",
|
||||
"Sitka, United States of America",
|
||||
"Metlakatla, United States of America",
|
||||
"Yakutat, United States of America",
|
||||
"Nome, United States of America"
|
||||
],
|
||||
"960": [
|
||||
"Vancouver, Canada",
|
||||
"Tijuana, Mexico",
|
||||
"Pitcairn, Pitcairn",
|
||||
"Los Angeles, United States of America"
|
||||
],
|
||||
"1020": [
|
||||
"Edmonton, Canada",
|
||||
"Cambridge Bay, Canada",
|
||||
"Yellowknife, Canada",
|
||||
"Inuvik, Canada",
|
||||
"Creston, Canada",
|
||||
"Dawson Creek, Canada",
|
||||
"Fort Nelson, Canada",
|
||||
"Whitehorse, Canada",
|
||||
"Dawson, Canada",
|
||||
"Mazatlan, Mexico",
|
||||
"Chihuahua, Mexico",
|
||||
"Ojinaga, Mexico",
|
||||
"Hermosillo, Mexico",
|
||||
"Denver, United States of America",
|
||||
"Boise, United States of America",
|
||||
"Phoenix, United States of America"
|
||||
],
|
||||
"1080": [
|
||||
"Belize, Belize",
|
||||
"Winnipeg, Canada",
|
||||
"Rainy River, Canada",
|
||||
"Resolute, Canada",
|
||||
"Rankin Inlet, Canada",
|
||||
"Regina, Canada",
|
||||
"Swift Current, Canada",
|
||||
"Costa Rica, Costa Rica",
|
||||
"Galapagos, Ecuador",
|
||||
"Guatemala, Guatemala",
|
||||
"Tegucigalpa, Honduras",
|
||||
"Mexico City, Mexico",
|
||||
"Merida, Mexico",
|
||||
"Monterrey, Mexico",
|
||||
"Matamoros, Mexico",
|
||||
"Bahia Banderas, Mexico",
|
||||
"Managua, Nicaragua",
|
||||
"El Salvador, El Salvador",
|
||||
"Chicago, United States of America",
|
||||
"Tell City, Indiana",
|
||||
"Knox, Indiana",
|
||||
"Menominee, United States of America",
|
||||
"Center, North Dakota",
|
||||
"New_Salem, North Dakota",
|
||||
"Beulah, North Dakota"
|
||||
],
|
||||
"1140": [
|
||||
"Eirunepe, Brazil",
|
||||
"Rio Branco, Brazil",
|
||||
"Nassau, Bahamas",
|
||||
"Toronto, Canada",
|
||||
"Nipigon, Canada",
|
||||
"Thunder Bay, Canada",
|
||||
"Iqaluit, Canada",
|
||||
"Pangnirtung, Canada",
|
||||
"Atikokan, Canada",
|
||||
"Easter, Chile",
|
||||
"Bogota, Colombia",
|
||||
"Havana, Cuba",
|
||||
"Guayaquil, Ecuador",
|
||||
"Port-au-Prince, Haiti",
|
||||
"Jamaica, Jamaica",
|
||||
"Cayman, Cayman Islands",
|
||||
"Cancun, Mexico",
|
||||
"Panama, Panama",
|
||||
"Lima, Peru",
|
||||
"Grand Turk, Turks and Caicos Islands",
|
||||
"New York, United States of America",
|
||||
"Detroit, United States of America",
|
||||
"Louisville, Kentucky",
|
||||
"Monticello, Kentucky",
|
||||
"Indianapolis, Indiana",
|
||||
"Vincennes, Indiana",
|
||||
"Winamac, Indiana",
|
||||
"Marengo, Indiana",
|
||||
"Petersburg, Indiana",
|
||||
"Vevay, Indiana"
|
||||
],
|
||||
"1200": [
|
||||
"Antigua, Antigua and Barbuda",
|
||||
"Anguilla, Anguilla",
|
||||
"Aruba, Aruba",
|
||||
"Barbados, Barbados",
|
||||
"St Barthelemy, Saint Barthélemy",
|
||||
"Bermuda, Bermuda",
|
||||
"La Paz, Bolivia (Plurinational State of)",
|
||||
"Kralendijk, Bonaire, Sint Eustatius and Saba",
|
||||
"Campo Grande, Brazil",
|
||||
"Cuiaba, Brazil",
|
||||
"Porto Velho, Brazil",
|
||||
"Boa Vista, Brazil",
|
||||
"Manaus, Brazil",
|
||||
"Halifax, Canada",
|
||||
"Glace Bay, Canada",
|
||||
"Moncton, Canada",
|
||||
"Goose Bay, Canada",
|
||||
"Blanc-Sablon, Canada",
|
||||
"Curacao, Curaçao",
|
||||
"Dominica, Dominica",
|
||||
"Santo Domingo, Dominican Republic",
|
||||
"Grenada, Grenada",
|
||||
"Thule, Greenland",
|
||||
"Guadeloupe, Guadeloupe",
|
||||
"Guyana, Guyana",
|
||||
"St Kitts, Saint Kitts and Nevis",
|
||||
"St Lucia, Saint Lucia",
|
||||
"Marigot, Saint Martin (French part)",
|
||||
"Martinique, Martinique",
|
||||
"Montserrat, Montserrat",
|
||||
"Puerto Rico, Puerto Rico",
|
||||
"Lower Princes, Sint Maarten (Dutch part)",
|
||||
"Port of_Spain, Trinidad and Tobago",
|
||||
"St Vincent, Saint Vincent and the Grenadines",
|
||||
"Caracas, Venezuela (Bolivarian Republic of)",
|
||||
"Tortola, Virgin Islands (British)",
|
||||
"St Thomas, Virgin Islands (U.S.)"
|
||||
],
|
||||
"1260": [
|
||||
"Palmer, Antarctica",
|
||||
"Rothera, Antarctica",
|
||||
"Buenos Aires, Argentina",
|
||||
"Cordoba, Argentina",
|
||||
"Salta, Argentina",
|
||||
"Jujuy, Argentina",
|
||||
"Tucuman, Argentina",
|
||||
"Catamarca, Argentina",
|
||||
"La Rioja, Argentina",
|
||||
"San Juan, Argentina",
|
||||
"Mendoza, Argentina",
|
||||
"San Luis, Argentina",
|
||||
"Rio Gallegos, Argentina",
|
||||
"Ushuaia, Argentina",
|
||||
"Belem, Brazil",
|
||||
"Fortaleza, Brazil",
|
||||
"Recife, Brazil",
|
||||
"Araguaina, Brazil",
|
||||
"Maceio, Brazil",
|
||||
"Bahia, Brazil",
|
||||
"Sao Paulo, Brazil",
|
||||
"Santarem, Brazil",
|
||||
"Santiago, Chile",
|
||||
"Punta Arenas, Chile",
|
||||
"Stanley, Falkland Islands (Malvinas)",
|
||||
"Cayenne, French Guiana",
|
||||
"Nuuk, Greenland",
|
||||
"Miquelon, Saint Pierre and Miquelon",
|
||||
"Asuncion, Paraguay",
|
||||
"Paramaribo, Suriname",
|
||||
"Montevideo, Uruguay"
|
||||
],
|
||||
"1320": [
|
||||
"Noronha, Brazil",
|
||||
"South Georgia, South Georgia and the South Sandwich Islands"
|
||||
],
|
||||
"1380": [
|
||||
"Cape Verde, Cabo Verde",
|
||||
"Scoresbysund, Greenland",
|
||||
"Azores, Portugal"
|
||||
]
|
||||
}
|
||||
exports.offsets = [1380,1320,1260,1200,1140,1080,1020,960,900,840,780,720,660,600,540,480,420,360,300,240,180,120,60,0];
|
||||
exports.timezones = function(offs) {
|
||||
switch (offs) {
|
||||
case 1380: return ["Cape Verde, Cabo Verde","Scoresbysund, Greenland","Azores, Portugal"];
|
||||
case 1320: return ["Noronha, Brazil","South Georgia, South Georgia and the South Sandwich Islands"];
|
||||
case 1260: return ["Palmer, Antarctica","Rothera, Antarctica","Buenos Aires, Argentina","Cordoba, Argentina","Salta, Argentina","Jujuy, Argentina","Tucuman, Argentina","Catamarca, Argentina","La Rioja, Argentina","San Juan, Argentina","Mendoza, Argentina","San Luis, Argentina","Rio Gallegos, Argentina","Ushuaia, Argentina","Belem, Brazil","Fortaleza, Brazil","Recife, Brazil","Araguaina, Brazil","Maceio, Brazil","Bahia, Brazil","Sao Paulo, Brazil","Santarem, Brazil","Santiago, Chile","Punta Arenas, Chile","Stanley, Falkland Islands (Malvinas)","Cayenne, French Guiana","Nuuk, Greenland","Miquelon, Saint Pierre and Miquelon","Asuncion, Paraguay","Paramaribo, Suriname","Montevideo, Uruguay"];
|
||||
case 1200: return ["Antigua, Antigua and Barbuda","Anguilla, Anguilla","Aruba, Aruba","Barbados, Barbados","St Barthelemy, Saint Barthélemy","Bermuda, Bermuda","La Paz, Bolivia (Plurinational State of)","Kralendijk, Bonaire, Sint Eustatius and Saba","Campo Grande, Brazil","Cuiaba, Brazil","Porto Velho, Brazil","Boa Vista, Brazil","Manaus, Brazil","Halifax, Canada","Glace Bay, Canada","Moncton, Canada","Goose Bay, Canada","Blanc-Sablon, Canada","Curacao, Curaçao","Dominica, Dominica","Santo Domingo, Dominican Republic","Grenada, Grenada","Thule, Greenland","Guadeloupe, Guadeloupe","Guyana, Guyana","St Kitts, Saint Kitts and Nevis","St Lucia, Saint Lucia","Marigot, Saint Martin (French part)","Martinique, Martinique","Montserrat, Montserrat","Puerto Rico, Puerto Rico","Lower Princes, Sint Maarten (Dutch part)","Port of_Spain, Trinidad and Tobago","St Vincent, Saint Vincent and the Grenadines","Caracas, Venezuela (Bolivarian Republic of)","Tortola, Virgin Islands (British)","St Thomas, Virgin Islands (U.S.)"];
|
||||
case 1140: return ["Eirunepe, Brazil","Rio Branco, Brazil","Nassau, Bahamas","Toronto, Canada","Nipigon, Canada","Thunder Bay, Canada","Iqaluit, Canada","Pangnirtung, Canada","Atikokan, Canada","Easter, Chile","Bogota, Colombia","Havana, Cuba","Guayaquil, Ecuador","Port-au-Prince, Haiti","Jamaica, Jamaica","Cayman, Cayman Islands","Cancun, Mexico","Panama, Panama","Lima, Peru","Grand Turk, Turks and Caicos Islands","New York, United States of America","Detroit, United States of America","Louisville, Kentucky","Monticello, Kentucky","Indianapolis, Indiana","Vincennes, Indiana","Winamac, Indiana","Marengo, Indiana","Petersburg, Indiana","Vevay, Indiana"];
|
||||
case 1080: return ["Belize, Belize","Winnipeg, Canada","Rainy River, Canada","Resolute, Canada","Rankin Inlet, Canada","Regina, Canada","Swift Current, Canada","Costa Rica, Costa Rica","Galapagos, Ecuador","Guatemala, Guatemala","Tegucigalpa, Honduras","Mexico City, Mexico","Merida, Mexico","Monterrey, Mexico","Matamoros, Mexico","Bahia Banderas, Mexico","Managua, Nicaragua","El Salvador, El Salvador","Chicago, United States of America","Tell City, Indiana","Knox, Indiana","Menominee, United States of America","Center, North Dakota","New_Salem, North Dakota","Beulah, North Dakota"];
|
||||
case 1020: return ["Edmonton, Canada","Cambridge Bay, Canada","Yellowknife, Canada","Inuvik, Canada","Creston, Canada","Dawson Creek, Canada","Fort Nelson, Canada","Whitehorse, Canada","Dawson, Canada","Mazatlan, Mexico","Chihuahua, Mexico","Ojinaga, Mexico","Hermosillo, Mexico","Denver, United States of America","Boise, United States of America","Phoenix, United States of America"];
|
||||
case 960: return ["Vancouver, Canada","Tijuana, Mexico","Pitcairn, Pitcairn","Los Angeles, United States of America"];
|
||||
case 900: return ["Gambier, French Polynesia","Anchorage, United States of America","Juneau, United States of America","Sitka, United States of America","Metlakatla, United States of America","Yakutat, United States of America","Nome, United States of America"];
|
||||
case 840: return ["Rarotonga, Cook Islands","Kiritimati, Kiribati","Tahiti, French Polynesia","Adak, United States of America","Honolulu, United States of America"];
|
||||
case 780: return ["McMurdo, Antarctica","Pago Pago, American Samoa","Fiji, Fiji","Kanton, Kiribati","Niue, Niue","Auckland, New Zealand","Fakaofo, Tokelau","Tongatapu, Tonga","Midway, United States Minor Outlying Islands","Apia, Samoa"];
|
||||
case 720: return ["Tarawa, Kiribati","Majuro, Marshall Islands","Kwajalein, Marshall Islands","Norfolk, Norfolk Island","Nauru, Nauru","Kamchatka, Russian Federation","Anadyr, Russian Federation","Funafuti, Tuvalu","Wake, United States Minor Outlying Islands","Wallis, Wallis and Futuna"];
|
||||
case 660: return ["Casey, Antarctica","Lord Howe, Australia","Macquarie, Australia","Hobart, Australia","Melbourne, Australia","Sydney, Australia","Pohnpei, Micronesia (Federated States of)","Kosrae, Micronesia (Federated States of)","Noumea, New Caledonia","Bougainville, Papua New Guinea","Magadan, Russian Federation","Sakhalin, Russian Federation","Srednekolymsk, Russian Federation","Guadalcanal, Solomon Islands","Efate, Vanuatu"];
|
||||
case 600: return ["DumontDUrville, Antarctica","Brisbane, Australia","Lindeman, Australia","Chuuk, Micronesia (Federated States of)","Guam, Guam","Saipan, Northern Mariana Islands","Port Moresby, Papua New Guinea","Vladivostok, Russian Federation","Ust-Nera, Russian Federation"];
|
||||
case 540: return ["Jayapura, Indonesia","Tokyo, Japan","Pyongyang, Korea (Democratic People's Republic of)","Seoul, Korea, Republic of","Palau, Palau","Chita, Russian Federation","Yakutsk, Russian Federation","Khandyga, Russian Federation","Dili, Timor-Leste"];
|
||||
case 480: return ["Perth, Australia","Brunei, Brunei Darussalam","Shanghai, China","Hong Kong, Hong Kong","Makassar, Indonesia","Ulaanbaatar, Mongolia","Choibalsan, Mongolia","Macau, Macao","Kuala Lumpur, Malaysia","Kuching, Malaysia","Manila, Philippines","Irkutsk, Russian Federation","Singapore, Singapore","Taipei, Taiwan, Province of China"];
|
||||
case 420: return ["Davis, Antarctica","Christmas, Christmas Island","Jakarta, Indonesia","Pontianak, Indonesia","Phnom Penh, Cambodia","Vientiane, Lao People's Democratic Republic","Hovd, Mongolia","Novosibirsk, Russian Federation","Barnaul, Russian Federation","Tomsk, Russian Federation","Novokuznetsk, Russian Federation","Krasnoyarsk, Russian Federation","Bangkok, Thailand","Ho Chi_Minh, Viet Nam"];
|
||||
case 360: return ["Vostok, Antarctica","Dhaka, Bangladesh","Thimphu, Bhutan","Urumqi, China","Chagos, British Indian Ocean Territory","Bishkek, Kyrgyzstan","Almaty, Kazakhstan","Qostanay, Kazakhstan","Omsk, Russian Federation"];
|
||||
case 300: return ["Mawson, Antarctica","Qyzylorda, Kazakhstan","Aqtobe, Kazakhstan","Aqtau, Kazakhstan","Atyrau, Kazakhstan","Oral, Kazakhstan","Maldives, Maldives","Karachi, Pakistan","Yekaterinburg, Russian Federation","Kerguelen, French Southern Territories","Dushanbe, Tajikistan","Ashgabat, Turkmenistan","Samarkand, Uzbekistan","Tashkent, Uzbekistan"];
|
||||
case 240: return ["Dubai, United Arab Emirates","Yerevan, Armenia","Baku, Azerbaijan","Tbilisi, Georgia","Mauritius, Mauritius","Muscat, Oman","Reunion, Réunion","Astrakhan, Russian Federation","Saratov, Russian Federation","Ulyanovsk, Russian Federation","Samara, Russian Federation","Mahe, Seychelles"];
|
||||
case 180: return ["Syowa, Antarctica","Bahrain, Bahrain","Minsk, Belarus","Djibouti, Djibouti","Asmara, Eritrea","Addis Ababa, Ethiopia","Baghdad, Iraq","Nairobi, Kenya","Comoro, Comoros","Kuwait, Kuwait","Antananarivo, Madagascar","Qatar, Qatar","Moscow, Russian Federation","Simferopol, Ukraine","Kirov, Russian Federation","Volgograd, Russian Federation","Riyadh, Saudi Arabia","Mogadishu, Somalia","Istanbul, Turkey","Dar es_Salaam, Tanzania, United Republic of","Kampala, Uganda","Aden, Yemen","Mayotte, Mayotte"];
|
||||
case 120: return ["Mariehamn, Åland Islands","Sofia, Bulgaria","Bujumbura, Burundi","Gaborone, Botswana","Lubumbashi, Congo, Democratic Republic of the","Nicosia, Cyprus","Famagusta, Cyprus","Tallinn, Estonia","Cairo, Egypt","Helsinki, Finland","Athens, Greece","Jerusalem, Israel","Amman, Jordan","Beirut, Lebanon","Maseru, Lesotho","Vilnius, Lithuania","Riga, Latvia","Tripoli, Libya","Chisinau, Moldova, Republic of","Blantyre, Malawi","Maputo, Mozambique","Windhoek, Namibia","Gaza, Palestine, State of","Hebron, Palestine, State of","Bucharest, Romania","Kaliningrad, Russian Federation","Kigali, Rwanda","Khartoum, Sudan","Juba, South Sudan","Damascus, Syrian Arab Republic","Mbabane, Eswatini","Kiev, Ukraine","Uzhgorod, Ukraine","Zaporozhye, Ukraine","Johannesburg, South Africa","Lusaka, Zambia","Harare, Zimbabwe"];
|
||||
case 60: return ["Andorra, Andorra","Tirane, Albania","Luanda, Angola","Vienna, Austria","Sarajevo, Bosnia and Herzegovina","Brussels, Belgium","Porto-Novo, Benin","Kinshasa, Congo, Democratic Republic of the","Bangui, Central African Republic","Brazzaville, Congo","Zurich, Switzerland","Douala, Cameroon","Prague, Czechia","Berlin, Germany","Busingen, Germany","Copenhagen, Denmark","Algiers, Algeria","El Aaiun, Western Sahara","Madrid, Spain","Ceuta, Spain","Paris, France","Libreville, Gabon","Gibraltar, Gibraltar","Malabo, Equatorial Guinea","Zagreb, Croatia","Budapest, Hungary","Rome, Italy","Vaduz, Liechtenstein","Luxembourg, Luxembourg","Casablanca, Morocco","Monaco, Monaco","Podgorica, Montenegro","Skopje, North Macedonia","Malta, Malta","Niamey, Niger","Lagos, Nigeria","Amsterdam, Netherlands","Oslo, Norway","Warsaw, Poland","Belgrade, Serbia","Stockholm, Sweden","Ljubljana, Slovenia","Longyearbyen, Svalbard and Jan Mayen","Bratislava, Slovakia","San Marino, San Marino","Ndjamena, Chad","Tunis, Tunisia","Vatican, Holy See"];
|
||||
case 0: return ["Troll, Antarctica","Ouagadougou, Burkina Faso","Abidjan, Côte d'Ivoire","Canary, Spain","Faroe, Faroe Islands","London, United Kingdom of Great Britain and Northern Ireland","Guernsey, Guernsey","Accra, Ghana","Danmarkshavn, Greenland","Banjul, Gambia","Conakry, Guinea","Bissau, Guinea-Bissau","Dublin, Ireland","Isle of_Man, Isle of Man","Reykjavik, Iceland","Jersey, Jersey","Monrovia, Liberia","Bamako, Mali","Nouakchott, Mauritania","Lisbon, Portugal","Madeira, Portugal","St Helena, Saint Helena, Ascension and Tristan da Cunha","Freetown, Sierra Leone","Dakar, Senegal","Sao Tome, Sao Tome and Principe","Lome, Togo"];
|
||||
default: return ["Houston, we have a bug."];
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ fs.createReadStream(__dirname+'/country.csv')
|
|||
zonelist.push(zone.name);
|
||||
offsdict[zone.offs] = zonelist;
|
||||
}
|
||||
offsets = [];
|
||||
for (o in offsdict) {
|
||||
offsets.unshift(parseInt(o));
|
||||
}
|
||||
fs.open("fourTwentyTz.js","w", (err, fd) => {
|
||||
if (err) {
|
||||
console.log("Can't open output file");
|
||||
|
|
@ -65,8 +69,18 @@ fs.createReadStream(__dirname+'/country.csv')
|
|||
fs.write(fd, "// Generated by mkFourTwentyTz.js\n", handleWrite);
|
||||
fs.write(fd, `// ${Date()}\n`, handleWrite);
|
||||
fs.write(fd, "// Data source: https://timezonedb.com/files/timezonedb.csv.zip\n", handleWrite);
|
||||
fs.write(fd, "exports.timezones = ", handleWrite);
|
||||
fs.write(fd, JSON.stringify(offsdict, null, 4), handleWrite);
|
||||
fs.write(fd, "exports.offsets = ", handleWrite);
|
||||
fs.write(fd, JSON.stringify(offsets), handleWrite);
|
||||
fs.write(fd, ";\n", handleWrite);
|
||||
fs.write(fd, "exports.timezones = function(offs) {\n", handleWrite);
|
||||
fs.write(fd, " switch (offs) {\n", handleWrite);
|
||||
for (i=0; i<offsets.length; i++) {
|
||||
let o = offsets[i].toString();
|
||||
fs.write(fd, ` case ${o}: return ${JSON.stringify(offsdict[o])};\n`, handleWrite);
|
||||
}
|
||||
fs.write(fd, " default: return [\"Houston, we have a bug.\"];\n", handleWrite);
|
||||
fs.write(fd, " }\n", handleWrite);
|
||||
fs.write(fd, "};\n", handleWrite);
|
||||
console.log('Done.');
|
||||
});
|
||||
})
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ TODO:
|
|||
var PREVIEWTILELAYER = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
|
||||
//var TILELAYER = 'http://a.tile.stamen.com/toner/{z}/{x}/{y}.png'; // black and white
|
||||
|
||||
var map = L.map('map').locate({setView: true, maxZoom: 16});
|
||||
// Create map and try and set the location to where the browser thinks we are
|
||||
var map = L.map('map').locate({setView: true, maxZoom: 16, enableHighAccuracy:true});
|
||||
// Tiles used for Bangle.js itself
|
||||
var bangleTileLayer = L.tileLayer(TILELAYER, {
|
||||
maxZoom: 18,
|
||||
|
|
|
|||
|
|
@ -3,4 +3,6 @@
|
|||
0.03: Bangle.js 2 support
|
||||
0.04: Move Quiet Mode LCD options from global settings to this app
|
||||
0.05: Avoid immediately redrawing widgets on load
|
||||
0.06: Fix: don't try to redraw widget when widgets not loaded
|
||||
0.06: Fix: don't try to redraw widget when widgets not loaded
|
||||
0.07: Option to switch theme
|
||||
Changed time selection to 5-minute intervals
|
||||
|
|
@ -9,6 +9,11 @@ Automatically turn Quiet Mode on or off at set times, and display a widget when
|
|||
|  |  |
|
||||
|  |  |
|
||||
|
||||
### Switch Theme:
|
||||
|
||||
Switch to dark theme during Quiet Mode.
|
||||
* **NOTE**: This switches between the default "Dark BW" and "Light BW" themes, so custom theme settings will be lost.
|
||||
|
||||
### LCD Settings:
|
||||
|
||||
If set, these override the default LCD settings while Quiet Mode is active.
|
||||
|
|
@ -3,7 +3,7 @@ Bangle.drawWidgets();
|
|||
|
||||
const modeNames = ["Off", "Alarms", "Silent"];
|
||||
|
||||
// load global brightness setting
|
||||
// load global settings
|
||||
let bSettings = require('Storage').readJSON('setting.json',true)||{};
|
||||
let current = 0|bSettings.quiet;
|
||||
delete bSettings; // we don't need any other global settings
|
||||
|
|
@ -18,6 +18,7 @@ delete bSettings; // we don't need any other global settings
|
|||
*/
|
||||
function save() {
|
||||
require('Storage').writeJSON('qmsched.json', settings);
|
||||
eval(require('Storage').read('qmsched.boot.js')); // apply new schedules right away
|
||||
}
|
||||
function get(key, def) {
|
||||
return (key in settings) ? settings[key] : def;
|
||||
|
|
@ -77,37 +78,66 @@ function formatTime(t) {
|
|||
const mins = Math.round((t-hrs)*60);
|
||||
return (" "+hrs).substr(-2)+":"+("0"+mins).substr(-2);
|
||||
}
|
||||
/**
|
||||
* Apply theme
|
||||
*/
|
||||
function applyTheme() {
|
||||
const theme = (require("Storage").readJSON("setting.json", 1) || {}).theme;
|
||||
if (theme && theme.dark===g.theme.dark) return; // already correct
|
||||
g.theme = theme;
|
||||
delete g.reset;
|
||||
g._reset = g.reset;
|
||||
g.reset = function(n) { return g._reset().setColor(g.theme.fg).setBgColor(g.theme.bg); };
|
||||
g.clear = function(n) { if (n) g.reset(); return g.clearRect(0,0,g.getWidth(),g.getHeight()); };
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
delete m.lastIdx; // force redraw
|
||||
m.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Library uses this to make the app update itself
|
||||
* @param {int} mode New Quite Mode
|
||||
*/
|
||||
function setAppQuietMode(mode) {
|
||||
if (mode === current) return;
|
||||
current = mode;
|
||||
delete m.lastIdx; // force redraw
|
||||
applyTheme();
|
||||
if (m.lastIdx===undefined) m.draw(); // applyTheme didn't redraw menu, but we need to show updated mode
|
||||
}
|
||||
|
||||
let m;
|
||||
function showMainMenu() {
|
||||
let _m, menu = {
|
||||
let menu = {
|
||||
"": {"title": "Quiet Mode"},
|
||||
"< Exit": () => load()
|
||||
};
|
||||
// "Current Mode""Silent" won't fit on Bangle.js 2
|
||||
menu["Current"+((process.env.HWVERSION===2) ? "" : " Mode")] = {
|
||||
value: current,
|
||||
format: v => modeNames[v],
|
||||
onchange: function(v) {
|
||||
if (v<0) {v = 2;}
|
||||
if (v>2) {v = 0;}
|
||||
require("qmsched").setMode(v);
|
||||
current = v;
|
||||
this.value = v;
|
||||
},
|
||||
min:0, max:2, wrap: true,
|
||||
format: () => modeNames[current],
|
||||
onchange: require("qmsched").setMode, // library calls setAppMode(), which updates `current`
|
||||
};
|
||||
scheds.sort((a, b) => (a.hr-b.hr));
|
||||
scheds.forEach((sched, idx) => {
|
||||
menu[formatTime(sched.hr)] = {
|
||||
format: () => modeNames[sched.mode], // abuse format to right-align text
|
||||
onchange: function() {
|
||||
_m.draw = ()=> {}; // prevent redraw of main menu over edit menu
|
||||
onchange: () => {
|
||||
m.draw = ()=> {}; // prevent redraw of main menu over edit menu (needed because we abuse format/onchange)
|
||||
showEditMenu(idx);
|
||||
}
|
||||
};
|
||||
});
|
||||
menu["Add Schedule"] = () => showEditMenu(-1);
|
||||
menu["Switch Theme"] = {
|
||||
value: !!get("switchTheme"),
|
||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
||||
onchange: v => v ? set("switchTheme", v) : unset("switchTheme"),
|
||||
};
|
||||
menu["LCD Settings"] = () => showOptionsMenu();
|
||||
_m = E.showMenu(menu);
|
||||
m = E.showMenu(menu);
|
||||
}
|
||||
|
||||
function showEditMenu(index) {
|
||||
|
|
@ -125,31 +155,19 @@ function showEditMenu(index) {
|
|||
"< Cancel": () => showMainMenu(),
|
||||
"Hours": {
|
||||
value: hrs,
|
||||
onchange: function(v) {
|
||||
if (v<0) {v = 23;}
|
||||
if (v>23) {v = 0;}
|
||||
hrs = v;
|
||||
this.value = v;
|
||||
}, // no arrow fn -> preserve 'this'
|
||||
min:0, max:23, wrap:true,
|
||||
onchange: v => {hrs = v;},
|
||||
},
|
||||
"Minutes": {
|
||||
value: mins,
|
||||
onchange: function(v) {
|
||||
if (v<0) {v = 59;}
|
||||
if (v>59) {v = 0;}
|
||||
mins = v;
|
||||
this.value = v;
|
||||
}, // no arrow fn -> preserve 'this'
|
||||
min:0, max:55, step:5, wrap:true,
|
||||
onchange: v => {mins = v;},
|
||||
},
|
||||
"Switch to": {
|
||||
value: mode,
|
||||
min:0, max:2, wrap:true,
|
||||
format: v => modeNames[v],
|
||||
onchange: function(v) {
|
||||
if (v<0) {v = 2;}
|
||||
if (v>2) {v = 0;}
|
||||
mode = v;
|
||||
this.value = v;
|
||||
}, // no arrow fn -> preserve 'this'
|
||||
onchange: v => {mode = v;},
|
||||
},
|
||||
};
|
||||
function getSched() {
|
||||
|
|
@ -174,7 +192,7 @@ function showEditMenu(index) {
|
|||
showMainMenu();
|
||||
};
|
||||
}
|
||||
return E.showMenu(menu);
|
||||
m = E.showMenu(menu);
|
||||
}
|
||||
|
||||
function showOptionsMenu() {
|
||||
|
|
@ -244,7 +262,7 @@ function showOptionsMenu() {
|
|||
onchange: () => {toggle("wakeOnTwist");},
|
||||
},
|
||||
};
|
||||
return E.showMenu(oMenu);
|
||||
m = E.showMenu(oMenu);
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// apply Quiet Mode schedules
|
||||
(function qm() {
|
||||
if (Bangle.qmTimeout) clearTimeout(Bangle.qmTimeout); // so the app can eval() this file to apply changes right away
|
||||
delete Bangle.qmTimeout;
|
||||
let bSettings = require('Storage').readJSON('setting.json',true)||{};
|
||||
const curr = 0|bSettings.quiet;
|
||||
delete bSettings;
|
||||
|
|
@ -18,7 +20,7 @@
|
|||
let t = 3600000*(next.hr-hr); // timeout in milliseconds
|
||||
if (t<0) {t += 86400000;} // scheduled for tomorrow: add a day
|
||||
/* update quiet mode at the correct time. */
|
||||
setTimeout(() => {
|
||||
Bangle.qmTimeout=setTimeout(() => {
|
||||
require("qmsched").setMode(mode);
|
||||
qm(); // schedule next update
|
||||
}, t);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,31 @@
|
|||
/**
|
||||
* Apply LCD options for given mode
|
||||
* Apply appropriate theme for given mode
|
||||
* @param {int} mode Quiet Mode
|
||||
*/
|
||||
function switchTheme(mode) {
|
||||
if (!!mode === g.theme.dark) return; // nothing to do
|
||||
let s = require("Storage").readJSON("setting.json", 1) || {};
|
||||
// default themes, copied from settings.js:showThemeMenu()
|
||||
function cl(x) { return g.setColor(x).getColor(); }
|
||||
s.theme = mode ? {
|
||||
// 'Dark BW'
|
||||
fg: cl("#fff"), bg: cl("#000"),
|
||||
fg2: cl("#0ff"), bg2: cl("#000"),
|
||||
fgH: cl("#fff"), bgH: cl("#00f"),
|
||||
dark: true
|
||||
} : {
|
||||
// 'Light BW'
|
||||
fg: cl("#000"), bg: cl("#fff"),
|
||||
fg2: cl("#000"), bg2: cl("#cff"),
|
||||
fgH: cl("#000"), bgH: cl("#0ff"),
|
||||
dark: false
|
||||
};
|
||||
require("Storage").writeJSON("setting.json", s);
|
||||
// reload clocks with new theme, otherwise just wait for user to switch apps
|
||||
if (Bangle.CLOCK) load(global.__FILE__);
|
||||
}
|
||||
/**
|
||||
* Apply LCD options and theme for given mode
|
||||
* @param {int} mode Quiet Mode
|
||||
*/
|
||||
exports.applyOptions = function(mode) {
|
||||
|
|
@ -8,6 +34,7 @@ exports.applyOptions = function(mode) {
|
|||
Bangle.setOptions(get("options", {}));
|
||||
Bangle.setLCDBrightness(get("brightness", 1));
|
||||
Bangle.setLCDTimeout(get("timeout", 10));
|
||||
if ((require("Storage").readJSON("qmsched.json", 1) || {}).switchTheme) switchTheme(mode);
|
||||
};
|
||||
/**
|
||||
* Set new Quiet Mode and apply Bangle options
|
||||
|
|
@ -20,4 +47,5 @@ exports.setMode = function(mode) {
|
|||
));
|
||||
exports.applyOptions(mode);
|
||||
if (typeof WIDGETS === "object" && "qmsched" in WIDGETS) WIDGETS["qmsched"].draw();
|
||||
if (global.setAppQuietMode) setAppQuietMode(mode); // current app knows how to update itself
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
return; // drawWidgets will call draw again
|
||||
}
|
||||
let x = this.x, y = this.y;
|
||||
g.clearRect(x, y, x+23, y+23);
|
||||
g.reset().clearRect(x, y, x+23, y+23);
|
||||
// quiet mode: draw red one-way-street sign (dim red on Bangle.js 1)
|
||||
x = this.x+11;y = this.y+11; // center of widget
|
||||
g.setColor(process.env.HWVERSION===2 ? 1 : 0.8, 0, 0).fillCircle(x, y, 8);
|
||||
|
|
|
|||
|
|
@ -8,3 +8,6 @@
|
|||
Fix execution of other recorders (*.recorder.js)
|
||||
Modified icons and colors for better visibility
|
||||
Only show plotting speed if Latitude is available
|
||||
0.07: Add recording for Barometer
|
||||
Record all HRM events
|
||||
Move recording for CoreTemp to its own app
|
||||
|
|
|
|||
|
|
@ -52,19 +52,17 @@
|
|||
};
|
||||
},
|
||||
hrm:function() {
|
||||
var bpm = 0, bpmConfidence = 0;
|
||||
var bpm = "", bpmConfidence = "";
|
||||
function onHRM(h) {
|
||||
if (h.confidence >= bpmConfidence) {
|
||||
bpmConfidence = h.confidence;
|
||||
bpm = h.bpm;
|
||||
}
|
||||
bpmConfidence = h.confidence;
|
||||
bpm = h.bpm;
|
||||
}
|
||||
return {
|
||||
name : "HR",
|
||||
fields : ["Heartrate", "Confidence"],
|
||||
getValues : () => {
|
||||
var r = [bpm,bpmConfidence];
|
||||
bpm = 0; bpmConfidence = 0;
|
||||
bpm = ""; bpmConfidence = "";
|
||||
return r;
|
||||
},
|
||||
start : () => {
|
||||
|
|
@ -92,32 +90,6 @@
|
|||
draw : (x,y) => g.setColor(Bangle.isCharging() ? "#0f0" : "#ff0").drawImage(atob("DAwBAABgH4G4EYG4H4H4H4GIH4AA"),x,y)
|
||||
};
|
||||
},
|
||||
temp:function() {
|
||||
var core = 0, skin = 0;
|
||||
var hasCore = false;
|
||||
function onCore(c) {
|
||||
core=c.core;
|
||||
skin=c.skin;
|
||||
hasCore = true;
|
||||
}
|
||||
return {
|
||||
name : "Core",
|
||||
fields : ["Core","Skin"],
|
||||
getValues : () => {
|
||||
var r = [core,skin];
|
||||
return r;
|
||||
},
|
||||
start : () => {
|
||||
hasCore = false;
|
||||
Bangle.on('CoreTemp', onCore);
|
||||
},
|
||||
stop : () => {
|
||||
hasCore = false;
|
||||
Bangle.removeListener('CoreTemp', onCore);
|
||||
},
|
||||
draw : (x,y) => g.setColor(hasCore?"#0f0":"#8f8").drawImage(atob("DAwBAAAOAKPOfgZgZgZgZgfgPAAA"),x,y)
|
||||
};
|
||||
},
|
||||
steps:function() {
|
||||
var lastSteps = 0;
|
||||
return {
|
||||
|
|
@ -133,8 +105,38 @@
|
|||
draw : (x,y) => g.reset().drawImage(atob("DAwBAAMMeeeeeeeecOMMAAMMMMAA"),x,y)
|
||||
};
|
||||
}
|
||||
// TODO: recAltitude from pressure sensor
|
||||
};
|
||||
if (Bangle.getPressure){
|
||||
recorders['baro'] = function() {
|
||||
var temp="",press="",alt="";
|
||||
function onPress(c) {
|
||||
temp=c.temperature;
|
||||
press=c.pressure;
|
||||
alt=c.altitude;
|
||||
}
|
||||
return {
|
||||
name : "Baro",
|
||||
fields : ["Barometer Temperature", "Barometer Pressure", "Barometer Altitude"],
|
||||
getValues : () => {
|
||||
var r = [temp,press,alt];
|
||||
temp="";
|
||||
press="";
|
||||
alt="";
|
||||
return r;
|
||||
},
|
||||
start : () => {
|
||||
Bangle.setBarometerPower(1,"recorder");
|
||||
Bangle.on('pressure', onPress);
|
||||
},
|
||||
stop : () => {
|
||||
Bangle.setBarometerPower(0,"recorder");
|
||||
Bangle.removeListener('pressure', onPress);
|
||||
},
|
||||
draw : (x,y) => g.setColor("#0f0").drawImage(atob("DAwBAAH4EIHIEIHIEIHIEIEIH4AA"),x,y)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/* eg. foobar.recorder.js
|
||||
(function(recorders) {
|
||||
recorders.foobar = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# Run App
|
||||
|
||||
This app allows you to display the status of your run, it
|
||||
shows distance, time, steps, cadence, pace and more.
|
||||
|
||||
To use it, start the app and press the middle button so that
|
||||
the red `STOP` in the bottom right turns to a green `RUN`.
|
||||
|
||||
## Display
|
||||
|
||||
* `DIST` - the distance travelled based on the GPS (if you have a GPS lock).
|
||||
* NOTE: this is based on the GPS coordinates which are not 100% accurate, especially initially. As
|
||||
the GPS updates your position as it gets more satellites your position changes and the distance
|
||||
shown will increase, even if you are standing still.
|
||||
* `TIME` - the elapsed time for your run
|
||||
* `PACE` - the number of minutes it takes you to run a kilometer **based on your run so far**
|
||||
* `HEART` - Your heart rate
|
||||
* `STEPS` - Steps since you started exercising
|
||||
* `CADENCE` - Steps per second based on your step rate *over the last minute*
|
||||
* `GPS` - this is green if you have a GPS lock. GPS is turned on automatically
|
||||
so if you have no GPS lock you just need to wait.
|
||||
* The current time is displayed right at the bottom of the screen
|
||||
* `RUN/STOP` - whether the distance for your run is being displayed or not
|
||||
|
||||
## Recording Tracks
|
||||
|
||||
`Run` doesn't directly allow you to record your tracks at the moment.
|
||||
However you can just install the `Recorder` app, turn recording on in
|
||||
that, and then start the `Run` app.
|
||||
|
||||
## TODO
|
||||
|
||||
* Allow this app to trigger the `Recorder` app on and off directly.
|
||||
* Keep a log of each run's stats (distance/steps/etc)
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4UA///pH9vEFt9TIW0FqALJitUBZNVqoLqgo4BHZAUBtBTHgILB1XAEREV1WsEQ9AgWq1ALHgEO1WtBYxCBhWq0pdInWq2tABY8q1WVBZGq1XFBZS/IKQRvCDIsP9WsBZP60CTCBYs//+wLxALBTQ4AB///+AKHgYLB/gLK/4LHh//AIIwFitVr/8DIIwFLANXBAILIqogBn7DBEYrXBeQRgIBYKmHDgYLLZRBACBZYKJZIILKKRZeWgJGKAFQA=="))
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
var B2 = process.env.HWVERSION==2;
|
||||
var Layout = require("Layout");
|
||||
var locale = require("locale")
|
||||
var fontHeading = "6x8:2";
|
||||
var fontValue = B2 ? "6x15:2" : "6x8:3";
|
||||
var headingCol = "#888";
|
||||
var running = false;
|
||||
var startTime;
|
||||
var startSteps;
|
||||
// This & previous GPS readings
|
||||
var lastGPS, thisGPS;
|
||||
var distance = 0; ///< distance in meters
|
||||
var startSteps = Bangle.getStepCount(); ///< number of steps when we started
|
||||
var lastStepCount = startSteps; // last time 'step' was called
|
||||
var stepHistory = new Uint8Array(60); // steps each second for the last minute (0 = current minute)
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
// ---------------------------
|
||||
|
||||
function formatTime(ms) {
|
||||
var s = Math.round(ms/1000);
|
||||
var min = Math.floor(s/60).toString();
|
||||
s = (s%60).toString();
|
||||
return min.padStart(2,0)+":"+s.padStart(2,0);
|
||||
}
|
||||
|
||||
// Format speed in meters/second
|
||||
function formatPace(speed) {
|
||||
if (speed < 0.1667) {
|
||||
return `__'__"`;
|
||||
}
|
||||
const pace = Math.round(1000 / speed); // seconds for 1km
|
||||
const min = Math.floor(pace / 60); // minutes for 1km
|
||||
const sec = pace % 60;
|
||||
return ('0' + min).substr(-2) + `'` + ('0' + sec).substr(-2) + `"`;
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
|
||||
function clearState() {
|
||||
distance = 0;
|
||||
startSteps = Bangle.getStepCount();
|
||||
stepHistory.fill(0);
|
||||
layout.dist.label=locale.distance(distance);
|
||||
layout.time.label="00:00";
|
||||
layout.pace.label=formatPace(0);
|
||||
layout.hrm.label="--";
|
||||
layout.steps.label=0;
|
||||
layout.cadence.label= "0";
|
||||
layout.status.bgCol = "#f00";
|
||||
}
|
||||
|
||||
function onStartStop() {
|
||||
running = !running;
|
||||
if (running) {
|
||||
clearState();
|
||||
startTime = Date.now();
|
||||
}
|
||||
layout.button.label = running ? "STOP" : "START";
|
||||
layout.status.label = running ? "RUN" : "STOP";
|
||||
layout.status.bgCol = running ? "#0f0" : "#f00";
|
||||
// if stopping running, don't clear state
|
||||
// so we can at least refer to what we've done
|
||||
layout.render();
|
||||
}
|
||||
|
||||
var layout = new Layout( {
|
||||
type:"v", c: [
|
||||
{ type:"h", filly:1, c:[
|
||||
{type:"txt", font:fontHeading, label:"DIST", fillx:1, col:headingCol },
|
||||
{type:"txt", font:fontHeading, label:"TIME", fillx:1, col:headingCol }
|
||||
]}, { type:"h", filly:1, c:[
|
||||
{type:"txt", font:fontValue, label:"0.00", id:"dist", fillx:1 },
|
||||
{type:"txt", font:fontValue, label:"00:00", id:"time", fillx:1 }
|
||||
]}, { type:"h", filly:1, c:[
|
||||
{type:"txt", font:fontHeading, label:"PACE", fillx:1, col:headingCol },
|
||||
{type:"txt", font:fontHeading, label:"HEART", fillx:1, col:headingCol }
|
||||
]}, { type:"h", filly:1, c:[
|
||||
{type:"txt", font:fontValue, label:`__'__"`, id:"pace", fillx:1 },
|
||||
{type:"txt", font:fontValue, label:"--", id:"hrm", fillx:1 }
|
||||
]}, { type:"h", filly:1, c:[
|
||||
{type:"txt", font:fontHeading, label:"STEPS", fillx:1, col:headingCol },
|
||||
{type:"txt", font:fontHeading, label:"CADENCE", fillx:1, col:headingCol }
|
||||
]}, { type:"h", filly:1, c:[
|
||||
{type:"txt", font:fontValue, label:"0", id:"steps", fillx:1 },
|
||||
{type:"txt", font:fontValue, label:"0", id:"cadence", fillx:1 }
|
||||
]}, { type:"h", filly:1, c:[
|
||||
{type:"txt", font:fontHeading, label:"GPS", id:"gps", fillx:1, bgCol:"#f00" },
|
||||
{type:"txt", font:fontHeading, label:"00:00", id:"clock", fillx:1, bgCol:g.theme.fg, col:g.theme.bg },
|
||||
{type:"txt", font:fontHeading, label:"STOP", id:"status", fillx:1 }
|
||||
]},
|
||||
|
||||
]
|
||||
},{lazy:true, btns:[{ label:"START", cb: onStartStop, id:"button"}]});
|
||||
clearState();
|
||||
layout.render();
|
||||
|
||||
|
||||
|
||||
function onTimer() {
|
||||
layout.clock.label = locale.time(new Date(),1);
|
||||
if (!running) {
|
||||
layout.render();
|
||||
return;
|
||||
}
|
||||
// called once a second
|
||||
var duration = Date.now() - startTime; // in ms
|
||||
// set cadence based on steps over last minute
|
||||
var stepsInMinute = E.sum(stepHistory);
|
||||
var cadence = 60000 * stepsInMinute / Math.min(duration,60000);
|
||||
// update layout
|
||||
layout.time.label = formatTime(duration);
|
||||
layout.steps.label = Bangle.getStepCount()-startSteps;
|
||||
layout.cadence.label = Math.round(cadence);
|
||||
layout.render();
|
||||
// move step history onwards
|
||||
stepHistory.set(stepHistory,1);
|
||||
stepHistory[0]=0;
|
||||
}
|
||||
|
||||
Bangle.on("GPS", function(fix) {
|
||||
layout.gps.bgCol = fix.fix ? "#0f0" : "#f00";
|
||||
lastGPS = thisGPS;
|
||||
thisGPS = fix;
|
||||
if (running && fix.fix && lastGPS.fix) {
|
||||
// work out distance - moving from a to b
|
||||
var a = Bangle.project(lastGPS);
|
||||
var b = Bangle.project(thisGPS);
|
||||
var dx = a.x-b.x, dy = a.y-b.y;
|
||||
var d = Math.sqrt(dx*dx+dy*dy); // this should be the distance in meters
|
||||
distance += d;
|
||||
layout.dist.label=locale.distance(distance);
|
||||
var duration = Date.now() - startTime; // in ms
|
||||
var speed = distance * 1000 / duration; // meters/sec
|
||||
layout.pace.label = formatPace(speed);
|
||||
}
|
||||
});
|
||||
Bangle.on("HRM", function(h) {
|
||||
layout.hrm.label = h.bpm;
|
||||
});
|
||||
Bangle.on("step", function(steps) {
|
||||
if (running) {
|
||||
layout.steps.label = steps-Bangle.getStepCount();
|
||||
stepHistory[0] += steps-lastStepCount;
|
||||
}
|
||||
lastStepCount = steps;
|
||||
});
|
||||
|
||||
// We always call ourselves once a second, if only to update the time
|
||||
setInterval(onTimer, 1000);
|
||||
|
||||
/* Turn GPS and HRM on right at the start to ensure
|
||||
we get the highest chance of a lock. */
|
||||
Bangle.setHRMPower(true,"app");
|
||||
Bangle.setGPSPower(true,"app");
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Andreas Rozek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Andreas Rozek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
0.1: New app
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Time and Life
|
||||
|
||||
A simple watchface which displays the time when the screen is tapped and decays according to the rules of [Conway's game of life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life).
|
||||
|
||||

|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkB/4AGCY4PHC/4X/C/4X/C/4XvJ/4X/C/4X/C/4X3AH4A/AH4A/AH4A/"))
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
// Globals
|
||||
const X = 176,
|
||||
Y = 176; // screen resolution of bangle 2
|
||||
const STEP_TIMEOUT = 1000;
|
||||
const PAUSE_TIME = 3000;
|
||||
|
||||
const ONE = [
|
||||
[0, 1, 0],
|
||||
[1, 1, 0],
|
||||
[0, 1, 0],
|
||||
[0, 1, 0],
|
||||
[0, 1, 0],
|
||||
[0, 1, 0],
|
||||
[1, 1, 1],
|
||||
];
|
||||
const TWO = [
|
||||
[0, 1, 0],
|
||||
[1, 0, 1],
|
||||
[0, 0, 1],
|
||||
[0, 1, 0],
|
||||
[1, 0, 0],
|
||||
[1, 0, 0],
|
||||
[1, 1, 1],
|
||||
];
|
||||
const THREE = [
|
||||
[0, 1, 0],
|
||||
[1, 0, 1],
|
||||
[0, 0, 1],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1],
|
||||
[1, 0, 1],
|
||||
[0, 1, 0],
|
||||
];
|
||||
const FOUR = [
|
||||
[0, 0, 1],
|
||||
[1, 0, 1],
|
||||
[1, 0, 1],
|
||||
[1, 1, 1],
|
||||
[0, 0, 1],
|
||||
[0, 0, 1],
|
||||
[0, 0, 1],
|
||||
];
|
||||
const FIVE = [
|
||||
[1, 1, 1],
|
||||
[1, 0, 0],
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1],
|
||||
[1, 0, 1],
|
||||
[0, 1, 0],
|
||||
];
|
||||
const SIX = [
|
||||
[0, 1, 0],
|
||||
[1, 0, 1],
|
||||
[1, 0, 0],
|
||||
[1, 1, 0],
|
||||
[1, 0, 1],
|
||||
[1, 0, 1],
|
||||
[0, 1, 0],
|
||||
];
|
||||
const SEVEN = [
|
||||
[1, 1, 1],
|
||||
[1, 0, 1],
|
||||
[0, 0, 1],
|
||||
[0, 1, 0],
|
||||
[0, 1, 0],
|
||||
[0, 1, 0],
|
||||
[0, 1, 0],
|
||||
];
|
||||
const EIGHT = [
|
||||
[0, 1, 0],
|
||||
[1, 0, 1],
|
||||
[1, 0, 1],
|
||||
[0, 1, 0],
|
||||
[1, 0, 1],
|
||||
[1, 0, 1],
|
||||
[0, 1, 0],
|
||||
];
|
||||
const NINE = [
|
||||
[0, 1, 0],
|
||||
[1, 0, 1],
|
||||
[1, 0, 1],
|
||||
[0, 1, 1],
|
||||
[0, 0, 1],
|
||||
[1, 0, 1],
|
||||
[0, 1, 0],
|
||||
];
|
||||
const ZERO = [
|
||||
[0, 1, 0],
|
||||
[1, 0, 1],
|
||||
[1, 0, 1],
|
||||
[1, 0, 1],
|
||||
[1, 0, 1],
|
||||
[1, 0, 1],
|
||||
[0, 1, 0],
|
||||
];
|
||||
const NUMBERS = [ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE];
|
||||
|
||||
// Arraybuffers to store game state
|
||||
// 484 8 bit integers that are either 1 or 0 form the 22 x 22 grid
|
||||
let data = new Uint8Array(484);
|
||||
let nextData = new Uint8Array(484);
|
||||
|
||||
let palette = new Uint16Array(256); // palette for rendering data
|
||||
palette[0] = g.theme.bg;
|
||||
palette[1] = g.theme.fg;
|
||||
|
||||
let lastPaused = new Date();
|
||||
|
||||
// Conway's game of life
|
||||
// if < 2 neighbours, set off
|
||||
// if 2 or 3 neighbours, set on
|
||||
// if > 3 neighbours, set off
|
||||
/*const updateStateC = E.compiledC(`
|
||||
// void run(int, int)
|
||||
void run(char* n, char* m){
|
||||
// n is a pointer to the first byte in data, m is for nextdata
|
||||
int count = 0;
|
||||
for (int i=0;i<484;i++) {
|
||||
// Add 8 neighbours, wrapping around
|
||||
count =
|
||||
*(n+(i+484-23)%484) +
|
||||
*(n+(i+484-22)%484) +
|
||||
*(n+(i+484-21)%484) +
|
||||
*(n+(i+484-1)%484) +
|
||||
*(n+(i+484+1)%484) +
|
||||
*(n+(i+484+21)%484) +
|
||||
*(n+(i+484+22)%484) +
|
||||
*(n+(i+484+23)%484);
|
||||
if (count < 2 || count > 3) {
|
||||
*(m+i) = 0;
|
||||
} else {
|
||||
*(m+i) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
`);*/
|
||||
// precompiled - taken from file downloaded from Bangle.js storage after
|
||||
// Web IDE upload
|
||||
const updateStateC=function(a){return a=atob('ACLwtU/08nMBJxZGAvLNFQL1536V+/P0A/sUVJ778/UD+xXlAvLPHhD4BMBEXZ778/UD+xXlZERFXQLy4x4sRJ778/UD+xXlAvLlHkVdLESe+/P1A/sV5QLy+R5FXSxEnvvz9QP7FeUC9f1+RV0sRJ778/UD+xXlAvL7HkVdLESe+/P1A/sV5UVdLEQCPAIsNL88RjRGjFQBMrL18n+10fC9AAA='),{run:E.nativeCall(1,'void(int, int)',a)}}();
|
||||
|
||||
function draw() {
|
||||
g.drawImage({
|
||||
width:22, height:22, bpp: 8,
|
||||
palette : palette, // ideally we'd just have BPP 1 and would render direct but it makes the code tricky
|
||||
buffer : data.buffer,
|
||||
},0,0,{scale:8});
|
||||
}
|
||||
|
||||
const step = () => {
|
||||
if (new Date() - lastPaused < PAUSE_TIME) {
|
||||
return;
|
||||
}
|
||||
let startTime = new Date();
|
||||
const dataAddr = E.getAddressOf(data, true);
|
||||
const nextDataAddr = E.getAddressOf(nextData, true);
|
||||
updateStateC.run(dataAddr, nextDataAddr);
|
||||
draw();
|
||||
data.set(nextData);
|
||||
};
|
||||
|
||||
const setPixel = (i, j) => {
|
||||
data[i * 22 + j] = 1;
|
||||
nextData[i * 22 + j] = 1;
|
||||
};
|
||||
|
||||
const setNum = (character, i, j) => {
|
||||
const startJ = j;
|
||||
character.forEach(row => {
|
||||
j = startJ;
|
||||
row.forEach(pixel => {
|
||||
if (pixel) setPixel(i, j);
|
||||
j++;
|
||||
});
|
||||
i++;
|
||||
});
|
||||
};
|
||||
|
||||
const setDots = () => {
|
||||
setPixel(10, 10);
|
||||
setPixel(12, 10);
|
||||
};
|
||||
|
||||
const drawTime = () => {
|
||||
lastPaused = new Date();
|
||||
g.clear();
|
||||
data.fill(0);
|
||||
const d = new Date();
|
||||
const hourTens = Math.floor(d.getHours() / 10);
|
||||
const hourOnes = d.getHours() % 10;
|
||||
const minuteTens = Math.floor(d.getMinutes() / 10);
|
||||
const minuteOnes = d.getMinutes() % 10;
|
||||
setNum(NUMBERS[hourTens], 8, 1);
|
||||
setNum(NUMBERS[hourOnes], 8, 6);
|
||||
setDots();
|
||||
setNum(NUMBERS[minuteTens], 8, 13);
|
||||
setNum(NUMBERS[minuteOnes], 8, 18);
|
||||
draw();
|
||||
};
|
||||
|
||||
const start = () => {
|
||||
Bangle.setUI("clock"); // Show launcher when middle button pressed
|
||||
g.clear();
|
||||
Bangle.setLCDTimeout(20); // backlight/lock timeout in seconds
|
||||
let stepInterval = setInterval(step, STEP_TIMEOUT);
|
||||
|
||||
// Handlers
|
||||
Bangle.on('touch', drawTime);
|
||||
|
||||
// Sleep mode
|
||||
Bangle.on('lock', isLocked => {
|
||||
if (stepInterval) {
|
||||
clearInterval(stepInterval);
|
||||
}
|
||||
stepInterval = undefined;
|
||||
if (!isLocked) {
|
||||
drawTime();
|
||||
stepInterval = setInterval(step, STEP_TIMEOUT);
|
||||
}
|
||||
});
|
||||
|
||||
drawTime();
|
||||
};
|
||||
|
||||
start();
|
||||
|
After Width: | Height: | Size: 605 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -0,0 +1 @@
|
|||
0.01: First release
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# Simple Pedometer Widget
|
||||
|
||||
*Displays the current step count from `Bangle.getHealthStatus("day").steps` in (6x8,2) font, Requires firmware v2.11.21 or later*
|
||||
|
||||
* Designed to be small, minimal, does one thing well, no settings
|
||||
* Supports Bangle 1 and Bangle 2
|
||||
|
||||
## Notes
|
||||
|
||||
* Requires firmware v2.11.21 or later
|
||||
* `Bangle.getHealthStatus("day").steps` is reset to zero if you reboot your watch with a long BTN Press
|
||||
* The step count displayed may be a few steps more than that reported by widpedpm as widpedom may not always be loaded.
|
||||
|
||||

|
||||
|
||||
Written by: [Hugh Barney](https://github.com/hughbarney) For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
|
After Width: | Height: | Size: 246 B |
|
|
@ -0,0 +1,17 @@
|
|||
Bangle.on('step', function(s) { WIDGETS["widpa"].draw(); });
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (on) WIDGETS["widpa"].draw();
|
||||
});
|
||||
WIDGETS["widpa"]={area:"tl",width:13,draw:function() {
|
||||
if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off
|
||||
var steps = Bangle.getHealthStatus("day").steps;
|
||||
var w = 1 + (steps.toString().length)*12;
|
||||
if (w > this.width) {this.width = w; setTimeout(() => Bangle.drawWidgets(),10); return;}
|
||||
g.reset();
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(this.x, this.y, this.x + this.width, this.y + 23);
|
||||
g.setColor(g.theme.fg);
|
||||
g.setFont('6x8',2);
|
||||
g.setFontAlign(-1, 0);
|
||||
g.drawString(steps, this.x, this.y + 12);
|
||||
}};
|
||||
|
|
@ -0,0 +1 @@
|
|||
0.01: First release
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Lato Pedometer Widget
|
||||
|
||||
*Displays the current step count from `Bangle.getHealthStatus("day").steps` in the Lato font, Requires firmware v2.11.21 or later*
|
||||
|
||||
* Designed to be minimal, does one thing well, no settings
|
||||
* Supports Bangle 1 and Bangle 2
|
||||
|
||||
## Notes
|
||||
|
||||
* Requires firmware v2.11.21 or later
|
||||
* Uses the Lato custom font, so memory footprint is 500 bytes larger than 'Simple Pedometer Widget'
|
||||
* `Bangle.getHealthStatus("day").steps` is reset to zero if you reboot your watch with a long BTN Press
|
||||
* The step count displayed may be a few steps more than that reported by widpedpm as widpedom may not always be loaded.
|
||||
|
||||

|
||||
|
||||
Written by: [Hugh Barney](https://github.com/hughbarney) For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
|
After Width: | Height: | Size: 338 B |
|
|
@ -0,0 +1,20 @@
|
|||
// on.step version
|
||||
Bangle.on('step', function(s) { WIDGETS["bata"].draw(); });
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (on) WIDGETS["bata"].draw();
|
||||
});
|
||||
WIDGETS["bata"]={area:"tl",width:13,draw:function() {
|
||||
if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off
|
||||
var steps = Bangle.getHealthStatus("day").steps;
|
||||
var w = 1 + (steps.toString().length)*12;
|
||||
if (w > this.width) {this.width = w; setTimeout(() => Bangle.drawWidgets(),10); return;}
|
||||
g.reset();
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(this.x, this.y, this.x + this.width, this.y + 23); // erase background
|
||||
g.setColor(g.theme.fg);
|
||||
// Lato from fonts.google.com, Actual height 17 (17 - 1), Numeric only
|
||||
const scale = 1;
|
||||
g.setFontCustom(atob("AAAAABwAAOAAAgAAHAADwAD4AB8AB8AA+AAeAADAAAAOAAP+AH/8B4DwMAGBgAwMAGBgAwOAOA//gD/4AD4AAAAAAAABgAAcAwDAGAwAwP/+B//wAAGAAAwAAGAAAAAAAAIAwHgOA4DwMA+BgOwMDmBg4wOeGA/gwDwGAAAAAAAAAGAHA8A4DwMAGBhAwMMGBjgwOcOA+/gDj4AAAAABgAAcAAHgADsAA5gAOMAHBgBwMAP/+B//wABgAAMAAAAAAAgD4OB/AwOYGBjAwMYGBjBwMe8Bh/AIHwAAAAAAAAAfAAP8AHxwB8GAdgwPMGBxgwMOOAB/gAH4AAAAAAABgAAMAABgAwMAeBgPgMHwBj4AN8AB+AAPAABAAAAAAAMfAH38B/xwMcGBhgwMMGBjgwP+OA+/gDj4AAAAAAAAOAAH4AA/gQMMGBgzwME8BhvAOPgA/4AD8AAEAAAAAAGAwA4OAHBwAAA="), 46, atob("BAgMDAwMDAwMDAwMBQ=="), 21+(scale<<8)+(1<<16));
|
||||
g.setFontAlign(-1, 0);
|
||||
g.drawString(steps, this.x, this.y + 12);
|
||||
}};
|
||||
2
core
|
|
@ -1 +1 @@
|
|||
Subproject commit b05af96b2522a7a7225a56d804faf9383f8a8f97
|
||||
Subproject commit 649489412e27ef770bc0c8ed12cfca6a17a98c0d
|
||||