Merge remote-tracking branch 'upstream/master'
|
|
@ -29,3 +29,4 @@ Changed for individual apps are listed in `apps/appname/ChangeLog`
|
||||||
* Added progress bar on Bangle.js for uploads
|
* Added progress bar on Bangle.js for uploads
|
||||||
* Provide a proper error message in case JSON decode fails
|
* Provide a proper error message in case JSON decode fails
|
||||||
* Check you're connecting with a Bangle.js of the correct version
|
* Check you're connecting with a Bangle.js of the correct version
|
||||||
|
* Allow 'data' style app files to be uploaded with the app (and switch over settings files for various apps)
|
||||||
|
|
|
||||||
10
README.md
|
|
@ -249,14 +249,20 @@ and which gives information about the app for the Launcher.
|
||||||
{"name":"appid.js", // filename to use in storage.
|
{"name":"appid.js", // filename to use in storage.
|
||||||
// If name=='RAM', the code is sent directly to Bangle.js and is not saved to a file
|
// If name=='RAM', the code is sent directly to Bangle.js and is not saved to a file
|
||||||
"url":"", // URL of file to load (currently relative to apps/)
|
"url":"", // URL of file to load (currently relative to apps/)
|
||||||
"content":"..." // if supplied, this content is loaded directly
|
"content":"...", // if supplied, this content is loaded directly
|
||||||
"evaluate":true // if supplied, data isn't quoted into a String before upload
|
"evaluate":true, // if supplied, data isn't quoted into a String before upload
|
||||||
// (eg it's evaluated as JS)
|
// (eg it's evaluated as JS)
|
||||||
|
"noOverwrite":true // if supplied, this file will not be overwritten if it
|
||||||
|
// already exists
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"data": [ // list of files the app writes to
|
"data": [ // list of files the app writes to
|
||||||
{"name":"appid.data.json", // filename used in storage
|
{"name":"appid.data.json", // filename used in storage
|
||||||
"storageFile":true // if supplied, file is treated as storageFile
|
"storageFile":true // if supplied, file is treated as storageFile
|
||||||
|
"url":"", // if supplied URL of file to load (currently relative to apps/)
|
||||||
|
"content":"...", // if supplied, this content is loaded directly
|
||||||
|
"evaluate":true, // if supplied, data isn't quoted into a String before upload
|
||||||
|
// (eg it's evaluated as JS)
|
||||||
},
|
},
|
||||||
{"wildcard":"appid.data.*" // wildcard of filenames used in storage
|
{"wildcard":"appid.data.*" // wildcard of filenames used in storage
|
||||||
}, // this is mutually exclusive with using "name"
|
}, // this is mutually exclusive with using "name"
|
||||||
|
|
|
||||||
133
apps.json
|
|
@ -80,7 +80,7 @@
|
||||||
"name": "Notifications (default)",
|
"name": "Notifications (default)",
|
||||||
"shortName":"Notifications",
|
"shortName":"Notifications",
|
||||||
"icon": "notify.png",
|
"icon": "notify.png",
|
||||||
"version":"0.07",
|
"version":"0.08",
|
||||||
"description": "A handler for displaying notifications that displays them in a bar at the top of the screen",
|
"description": "A handler for displaying notifications that displays them in a bar at the top of the screen",
|
||||||
"tags": "widget",
|
"tags": "widget",
|
||||||
"type": "notify",
|
"type": "notify",
|
||||||
|
|
@ -93,7 +93,7 @@
|
||||||
"name": "Fullscreen Notifications",
|
"name": "Fullscreen Notifications",
|
||||||
"shortName":"Notifications",
|
"shortName":"Notifications",
|
||||||
"icon": "notify.png",
|
"icon": "notify.png",
|
||||||
"version":"0.07",
|
"version":"0.08",
|
||||||
"description": "A handler for displaying notifications that displays them fullscreen. This may not fully restore the screen after on some apps. See `Notifications (default)` for more information about the notifications library.",
|
"description": "A handler for displaying notifications that displays them fullscreen. This may not fully restore the screen after on some apps. See `Notifications (default)` for more information about the notifications library.",
|
||||||
"tags": "widget",
|
"tags": "widget",
|
||||||
"type": "notify",
|
"type": "notify",
|
||||||
|
|
@ -139,7 +139,7 @@
|
||||||
{ "id": "gbridge",
|
{ "id": "gbridge",
|
||||||
"name": "Gadgetbridge",
|
"name": "Gadgetbridge",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.21",
|
"version":"0.22",
|
||||||
"description": "The default notification handler for Gadgetbridge notifications from Android",
|
"description": "The default notification handler for Gadgetbridge notifications from Android",
|
||||||
"tags": "tool,system,android,widget",
|
"tags": "tool,system,android,widget",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
|
@ -171,7 +171,7 @@
|
||||||
{ "id": "setting",
|
{ "id": "setting",
|
||||||
"name": "Settings",
|
"name": "Settings",
|
||||||
"icon": "settings.png",
|
"icon": "settings.png",
|
||||||
"version":"0.23",
|
"version":"0.24",
|
||||||
"description": "A menu for setting up Bangle.js",
|
"description": "A menu for setting up Bangle.js",
|
||||||
"tags": "tool,system",
|
"tags": "tool,system",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
|
@ -180,13 +180,16 @@
|
||||||
{"name":"setting.boot.js","url":"boot.js"},
|
{"name":"setting.boot.js","url":"boot.js"},
|
||||||
{"name":"setting.img","url":"settings-icon.js","evaluate":true}
|
{"name":"setting.img","url":"settings-icon.js","evaluate":true}
|
||||||
],
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"setting.json", "url":"settings.min.json","evaluate":true}
|
||||||
|
],
|
||||||
"sortorder" : -2
|
"sortorder" : -2
|
||||||
},
|
},
|
||||||
{ "id": "alarm",
|
{ "id": "alarm",
|
||||||
"name": "Default Alarm",
|
"name": "Default Alarm",
|
||||||
"shortName":"Alarms",
|
"shortName":"Alarms",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.10",
|
"version":"0.11",
|
||||||
"description": "Set and respond to alarms",
|
"description": "Set and respond to alarms",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm,widget",
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
@ -216,24 +219,33 @@
|
||||||
{ "id": "slidingtext",
|
{ "id": "slidingtext",
|
||||||
"name": "Sliding Clock",
|
"name": "Sliding Clock",
|
||||||
"icon": "slidingtext.png",
|
"icon": "slidingtext.png",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently only English, French and Japanese are supported",
|
"description": "Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently only English, French and Japanese are supported",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"type":"clock",
|
"type":"clock",
|
||||||
"allow_emulator":true,
|
"allow_emulator":true,
|
||||||
|
"readme": "README.md",
|
||||||
|
"custom":"custom.html",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"slidingtext.app.js","url":"slidingtext.js"},
|
{"name":"slidingtext.app.js","url":"slidingtext.js"},
|
||||||
{"name":"slidingtext.img","url":"slidingtext-icon.js","evaluate":true}
|
{"name":"slidingtext.img","url":"slidingtext-icon.js","evaluate":true},
|
||||||
|
{"name":"slidingtext.locale.en.js","url":"slidingtext.locale.en.js"},
|
||||||
|
{"name":"slidingtext.locale.en2.js","url":"slidingtext.locale.en2.js"},
|
||||||
|
{"name":"slidingtext.utils.en.js","url":"slidingtext.utils.en.js"},
|
||||||
|
{"name":"slidingtext.locale.fr.js","url":"slidingtext.locale.fr.js"},
|
||||||
|
{"name":"slidingtext.locale.jp.js","url":"slidingtext.locale.jp.js"},
|
||||||
|
{"name":"slidingtext.dtfmt.js","url":"slidingtext.dtfmt.js"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ "id": "sweepclock",
|
{ "id": "sweepclock",
|
||||||
"name": "Sweep Clock",
|
"name": "Sweep Clock",
|
||||||
"icon": "sweepclock.png",
|
"icon": "sweepclock.png",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "Smooth sweep secondhand with single hour numeral. Use button1 to toggle the numeral font",
|
"description": "Smooth sweep secondhand with single hour numeral. Use button1 to toggle the numeral font and button3 to change the colour theme",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"type":"clock",
|
"type":"clock",
|
||||||
"allow_emulator":true,
|
"allow_emulator":true,
|
||||||
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"sweepclock.app.js","url":"sweepclock.js"},
|
{"name":"sweepclock.app.js","url":"sweepclock.js"},
|
||||||
{"name":"sweepclock.img","url":"sweepclock-icon.js","evaluate":true}
|
{"name":"sweepclock.img","url":"sweepclock-icon.js","evaluate":true}
|
||||||
|
|
@ -413,7 +425,7 @@
|
||||||
{ "id": "gpsrec",
|
{ "id": "gpsrec",
|
||||||
"name": "GPS Recorder",
|
"name": "GPS Recorder",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.18",
|
"version":"0.19",
|
||||||
"interface": "interface.html",
|
"interface": "interface.html",
|
||||||
"description": "Application that allows you to record a GPS track. Can run in background",
|
"description": "Application that allows you to record a GPS track. Can run in background",
|
||||||
"tags": "tool,outdoors,gps,widget",
|
"tags": "tool,outdoors,gps,widget",
|
||||||
|
|
@ -438,14 +450,16 @@
|
||||||
"interface":"waypoints.html",
|
"interface":"waypoints.html",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"gpsnav.app.js","url":"app.min.js"},
|
{"name":"gpsnav.app.js","url":"app.min.js"},
|
||||||
{"name":"waypoints.json","url":"waypoints.json","evaluate":false},
|
|
||||||
{"name":"gpsnav.img","url":"app-icon.js","evaluate":true}
|
{"name":"gpsnav.img","url":"app-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"waypoints.json","url":"waypoints.json"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ "id": "heart",
|
{ "id": "heart",
|
||||||
"name": "Heart Rate Recorder",
|
"name": "Heart Rate Recorder",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.02",
|
"version":"0.04",
|
||||||
"interface": "interface.html",
|
"interface": "interface.html",
|
||||||
"description": "Application that allows you to record your heart rate. Can run in background",
|
"description": "Application that allows you to record your heart rate. Can run in background",
|
||||||
"tags": "tool,health,widget",
|
"tags": "tool,health,widget",
|
||||||
|
|
@ -558,7 +572,7 @@
|
||||||
"shortName": "Battery Warning",
|
"shortName": "Battery Warning",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "Show a warning when the battery runs low.",
|
"description": "Show a warning when the battery runs low.",
|
||||||
"tags": "tool,battery",
|
"tags": "tool,battery",
|
||||||
"type":"widget",
|
"type":"widget",
|
||||||
|
|
@ -738,7 +752,7 @@
|
||||||
{ "id": "route",
|
{ "id": "route",
|
||||||
"name": "Route Viewer",
|
"name": "Route Viewer",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "Upload a KML file of a route, and have your watch display a map with how far around it you are",
|
"description": "Upload a KML file of a route, and have your watch display a map with how far around it you are",
|
||||||
"tags": "",
|
"tags": "",
|
||||||
"custom": "custom.html",
|
"custom": "custom.html",
|
||||||
|
|
@ -1072,7 +1086,7 @@
|
||||||
{ "id": "widpedom",
|
{ "id": "widpedom",
|
||||||
"name": "Pedometer widget",
|
"name": "Pedometer widget",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"version":"0.11",
|
"version":"0.12",
|
||||||
"description": "Daily pedometer widget",
|
"description": "Daily pedometer widget",
|
||||||
"tags": "widget",
|
"tags": "widget",
|
||||||
"type":"widget",
|
"type":"widget",
|
||||||
|
|
@ -1203,7 +1217,7 @@
|
||||||
{ "id": "marioclock",
|
{ "id": "marioclock",
|
||||||
"name": "Mario Clock",
|
"name": "Mario Clock",
|
||||||
"icon": "marioclock.png",
|
"icon": "marioclock.png",
|
||||||
"version":"0.14",
|
"version":"0.15",
|
||||||
"description": "Animated retro Mario clock, with Gameboy style 8-bit grey-scale graphics.",
|
"description": "Animated retro Mario clock, with Gameboy style 8-bit grey-scale graphics.",
|
||||||
"tags": "clock,mario,retro",
|
"tags": "clock,mario,retro",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
@ -1701,9 +1715,9 @@
|
||||||
{
|
{
|
||||||
"id": "rclock",
|
"id": "rclock",
|
||||||
"name": "Round clock with seconds, minutes and date",
|
"name": "Round clock with seconds, minutes and date",
|
||||||
"shortName":"Round Clock",
|
"shortName": "Round Clock",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.04",
|
"version": "0.05",
|
||||||
"description": "Designed round clock with ticks for minutes and seconds and heart rate indication",
|
"description": "Designed round clock with ticks for minutes and seconds and heart rate indication",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
@ -1712,6 +1726,20 @@
|
||||||
{"name":"rclock.img","url":"app-icon.js","evaluate":true}
|
{"name":"rclock.img","url":"app-icon.js","evaluate":true}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "fclock",
|
||||||
|
"name": "fclock",
|
||||||
|
"shortName": "F Clock",
|
||||||
|
"icon": "app.png",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "Simple design of a digital clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"type": "clock",
|
||||||
|
"storage": [
|
||||||
|
{"name":"fclock.app.js","url":"fclock.app.js"},
|
||||||
|
{"name":"fclock.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
},
|
||||||
{ "id": "hamloc",
|
{ "id": "hamloc",
|
||||||
"name": "QTH Locator / Maidenhead Locator System",
|
"name": "QTH Locator / Maidenhead Locator System",
|
||||||
"shortName": "QTH Locator",
|
"shortName": "QTH Locator",
|
||||||
|
|
@ -2100,7 +2128,7 @@
|
||||||
"name": "SleepPhaseAlarm",
|
"name": "SleepPhaseAlarm",
|
||||||
"shortName":"SleepPhaseAlarm",
|
"shortName":"SleepPhaseAlarm",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
|
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
|
||||||
"tags": "alarm",
|
"tags": "alarm",
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
@ -2225,7 +2253,7 @@
|
||||||
"name": "Apple Notification Widget",
|
"name": "Apple Notification Widget",
|
||||||
"shortName":"ANCS Widget",
|
"shortName":"ANCS Widget",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"version":"0.06",
|
"version":"0.07",
|
||||||
"description": "Displays call, message etc notifications from a paired iPhone. Read README before installation as it only works with compatible apps",
|
"description": "Displays call, message etc notifications from a paired iPhone. Read README before installation as it only works with compatible apps",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"tags": "widget",
|
"tags": "widget",
|
||||||
|
|
@ -2399,9 +2427,11 @@
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"worldclock.app.js","url":"app.js"},
|
{"name":"worldclock.app.js","url":"app.js"},
|
||||||
{"name":"worldclock.settings.json"},
|
|
||||||
{"name":"worldclock.img","url":"worldclock-icon.js","evaluate":true}
|
{"name":"worldclock.img","url":"worldclock-icon.js","evaluate":true}
|
||||||
]
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"worldclock.settings.json"}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{ "id": "digiclock",
|
{ "id": "digiclock",
|
||||||
"name": "Digital Clock Face",
|
"name": "Digital Clock Face",
|
||||||
|
|
@ -2593,7 +2623,7 @@
|
||||||
"name": "Hard Alarm",
|
"name": "Hard Alarm",
|
||||||
"shortName":"HardAlarm",
|
"shortName":"HardAlarm",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "Make sure you wake up! Count to the right number to turn off the alarm",
|
"description": "Make sure you wake up! Count to the right number to turn off the alarm",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm,widget",
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
@ -2644,8 +2674,10 @@
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"breath.app.js","url":"app.js"},
|
{"name":"breath.app.js","url":"app.js"},
|
||||||
{"name":"breath.settings.json","url":"settings.json"},
|
|
||||||
{"name":"breath.img","url":"app-icon.js","evaluate":true}
|
{"name":"breath.img","url":"app-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"breath.settings.json","url":"settings.json"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ "id": "lazyclock",
|
{ "id": "lazyclock",
|
||||||
|
|
@ -2742,9 +2774,11 @@
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"gpsservice.app.js","url":"app.js"},
|
{"name":"gpsservice.app.js","url":"app.js"},
|
||||||
{"name":"gpsservice.settings.js","url":"settings.js"},
|
{"name":"gpsservice.settings.js","url":"settings.js"},
|
||||||
{"name":"gpsservice.settings.json","url":"settings.json"},
|
|
||||||
{"name":"gpsservice.wid.js","url":"widget.js"},
|
{"name":"gpsservice.wid.js","url":"widget.js"},
|
||||||
{"name":"gpsservice.img","url":"gpsservice-icon.js","evaluate":true}
|
{"name":"gpsservice.img","url":"gpsservice-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"gpsservice.settings.json","url":"settings.json"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ "id": "mclockplus",
|
{ "id": "mclockplus",
|
||||||
|
|
@ -2841,16 +2875,18 @@
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"gpssetup","url":"gpssetup.js"},
|
{"name":"gpssetup","url":"gpssetup.js"},
|
||||||
{"name":"gpssetup.settings.js","url":"settings.js"},
|
{"name":"gpssetup.settings.js","url":"settings.js"},
|
||||||
{"name":"gpssetup.settings.json","url":"settings.json"},
|
|
||||||
{"name":"gpssetup.app.js","url":"app.js"},
|
{"name":"gpssetup.app.js","url":"app.js"},
|
||||||
{"name":"gpssetup.img","url":"icon.js","evaluate":true}
|
{"name":"gpssetup.img","url":"icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"gpssetup.settings.json","url":"settings.json"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ "id": "walkersclock",
|
{ "id": "walkersclock",
|
||||||
"name": "Walkers Clock",
|
"name": "Walkers Clock",
|
||||||
"shortName":"Walkers Clock",
|
"shortName":"Walkers Clock",
|
||||||
"icon": "walkersclock48.png",
|
"icon": "walkersclock48.png",
|
||||||
"version":"0.03",
|
"version":"0.04",
|
||||||
"description": "A large font watch, displays steps, can switch GPS on/off, displays grid reference",
|
"description": "A large font watch, displays steps, can switch GPS on/off, displays grid reference",
|
||||||
"type":"clock",
|
"type":"clock",
|
||||||
"tags": "clock, gps, tools, outdoors",
|
"tags": "clock, gps, tools, outdoors",
|
||||||
|
|
@ -2944,8 +2980,10 @@
|
||||||
"interface":"waypoints.html",
|
"interface":"waypoints.html",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"waypointer.app.js","url":"app.js"},
|
{"name":"waypointer.app.js","url":"app.js"},
|
||||||
{"name":"waypoints.json","url":"waypoints.json","evaluate":false},
|
|
||||||
{"name":"waypointer.img","url":"icon.js","evaluate":true}
|
{"name":"waypointer.img","url":"icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"waypoints.json","url":"waypoints.json"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ "id": "color_catalog",
|
{ "id": "color_catalog",
|
||||||
|
|
@ -3004,7 +3042,7 @@
|
||||||
"name": "Gadgetbridge Music Controls",
|
"name": "Gadgetbridge Music Controls",
|
||||||
"shortName":"Music Controls",
|
"shortName":"Music Controls",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"version":"0.01",
|
"version":"0.02",
|
||||||
"description": "Control the music on your Gadgetbridge-connected phone",
|
"description": "Control the music on your Gadgetbridge-connected phone",
|
||||||
"tags": "tools,bluetooth,gadgetbridge,music",
|
"tags": "tools,bluetooth,gadgetbridge,music",
|
||||||
"type":"app",
|
"type":"app",
|
||||||
|
|
@ -3045,7 +3083,7 @@
|
||||||
{ "id": "kitchen",
|
{ "id": "kitchen",
|
||||||
"name": "Kitchen Combo",
|
"name": "Kitchen Combo",
|
||||||
"icon": "kitchen.png",
|
"icon": "kitchen.png",
|
||||||
"version":"0.02",
|
"version":"0.03",
|
||||||
"description": "Combination of the stepo, walkersclock, arrow and waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later",
|
"description": "Combination of the stepo, walkersclock, arrow and waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later",
|
||||||
"tags": "tool,outdoors,gps",
|
"tags": "tool,outdoors,gps",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
|
@ -3059,5 +3097,38 @@
|
||||||
{"name":"waypoints.json","url":"waypoints.json","evaluate":false},
|
{"name":"waypoints.json","url":"waypoints.json","evaluate":false},
|
||||||
{"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true}
|
{"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
{ "id": "qmsched",
|
||||||
|
"name": "Quiet Mode Schedule",
|
||||||
|
"shortName":"Quiet Mode",
|
||||||
|
"icon": "app.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Automatically turn Quiet Mode on or off at set times",
|
||||||
|
"readme": "README.md",
|
||||||
|
"tags": "tool",
|
||||||
|
"storage": [
|
||||||
|
{"name":"qmsched","url":"lib.js"},
|
||||||
|
{"name":"qmsched.app.js","url":"app.js"},
|
||||||
|
{"name":"qmsched.boot.js","url":"boot.js"},
|
||||||
|
{"name":"qmsched.img","url":"icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name":"qmsched.json"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "hourstrike",
|
||||||
|
"name": "Hour Strike",
|
||||||
|
"shortName": "Hour Strike",
|
||||||
|
"icon": "app-icon.png",
|
||||||
|
"version": "0.07",
|
||||||
|
"description": "Strike the clock on the hour. A great tool to remind you an hour has passed!",
|
||||||
|
"tags": "tool,alarm",
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"hourstrike.app.js","url":"app.js"},
|
||||||
|
{"name":"hourstrike.boot.js","url":"boot.js"},
|
||||||
|
{"name":"hourstrike.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@
|
||||||
0.08: Make alarm scheduling more reliable
|
0.08: Make alarm scheduling more reliable
|
||||||
0.09: Add per alarm auto-snooze option
|
0.09: Add per alarm auto-snooze option
|
||||||
0.10: Fix auto-snooze option (this stopped new alarms being added) (fix #506)
|
0.10: Fix auto-snooze option (this stopped new alarms being added) (fix #506)
|
||||||
|
0.11: Respect Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ function showAlarm(alarm) {
|
||||||
load();
|
load();
|
||||||
});
|
});
|
||||||
function buzz() {
|
function buzz() {
|
||||||
|
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence
|
||||||
Bangle.buzz(100).then(()=>{
|
Bangle.buzz(100).then(()=>{
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
Bangle.buzz(100).then(function() {
|
Bangle.buzz(100).then(function() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: First published version of app
|
||||||
|
After Width: | Height: | Size: 6.4 KiB |
|
|
@ -0,0 +1,206 @@
|
||||||
|
{
|
||||||
|
var minutes;
|
||||||
|
var seconds;
|
||||||
|
var hours;
|
||||||
|
var date;
|
||||||
|
var first = true;
|
||||||
|
var locale = require('locale');
|
||||||
|
var _12hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"] || false;
|
||||||
|
|
||||||
|
//HR variables
|
||||||
|
var id = 0;
|
||||||
|
var grow = true;
|
||||||
|
var size=10;
|
||||||
|
|
||||||
|
//Screen dimensions
|
||||||
|
const screen = {
|
||||||
|
width: g.getWidth(),
|
||||||
|
height: g.getWidth(),
|
||||||
|
middle: g.getWidth() / 2,
|
||||||
|
center: g.getHeight() / 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ssettings
|
||||||
|
const settings = {
|
||||||
|
time: {
|
||||||
|
color: '#dddddd',
|
||||||
|
font: 'Vector',
|
||||||
|
size: 100,
|
||||||
|
middle: screen.middle,
|
||||||
|
center: screen.center,
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
color: '#dddddd',
|
||||||
|
font: 'Vector',
|
||||||
|
size: 15,
|
||||||
|
middle: screen.height-17, // at bottom of screen
|
||||||
|
center: screen.center,
|
||||||
|
},
|
||||||
|
circle: {
|
||||||
|
colormin: '#ffffff',
|
||||||
|
colorsec: '#ffffff',
|
||||||
|
width: 10,
|
||||||
|
middle: screen.middle,
|
||||||
|
center: screen.center,
|
||||||
|
height: screen.height
|
||||||
|
},
|
||||||
|
hr: {
|
||||||
|
color: '#333333',
|
||||||
|
size: 20,
|
||||||
|
x: screen.center,
|
||||||
|
y: screen.middle + 65
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dateStr = function (date) {
|
||||||
|
return locale.date(new Date(), 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFormated = function(val) {
|
||||||
|
if (val<10) {
|
||||||
|
val='0'+val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawMin = function (sections, color) {
|
||||||
|
|
||||||
|
g.setFontAlign(0, 0, 0);
|
||||||
|
g.setColor('#000000');
|
||||||
|
g.setFont(settings.time.font, settings.time.size/2);
|
||||||
|
g.drawString(getFormated(sections-1), settings.time.center+50, settings.time.middle);
|
||||||
|
g.setColor(settings.time.color);
|
||||||
|
g.setFont(settings.time.font, settings.time.size/2);
|
||||||
|
g.drawString(getFormated(sections), settings.time.center+50, settings.time.middle);
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawSec = function (sections, color) {
|
||||||
|
g.setFontAlign(0, 0, 0);
|
||||||
|
g.setColor('#000000');
|
||||||
|
g.setFont(settings.time.font, settings.time.size/4);
|
||||||
|
g.drawString(getFormated(sections-1), settings.time.center+100, settings.time.middle);
|
||||||
|
g.setColor(settings.time.color);
|
||||||
|
g.setFont(settings.time.font, settings.time.size/4);
|
||||||
|
g.drawString(getFormated(sections), settings.time.center+100, settings.time.middle);
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawClock = function () {
|
||||||
|
|
||||||
|
currentTime = new Date();
|
||||||
|
|
||||||
|
//Get date as a string
|
||||||
|
date = dateStr(currentTime);
|
||||||
|
|
||||||
|
if(seconds==59) {
|
||||||
|
g.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update minutes when needed
|
||||||
|
if (minutes != currentTime.getMinutes()) {
|
||||||
|
minutes = currentTime.getMinutes();
|
||||||
|
drawMin(minutes, settings.circle.colormin);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update seconds when needed
|
||||||
|
if (seconds != currentTime.getSeconds()) {
|
||||||
|
seconds = currentTime.getSeconds();
|
||||||
|
drawSec(seconds, settings.circle.colorsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write the time as configured in the settings
|
||||||
|
hours = currentTime.getHours();
|
||||||
|
if (_12hour && hours > 13) {
|
||||||
|
hours = hours - 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
var meridian;
|
||||||
|
|
||||||
|
if (typeof locale.meridian === "function") {
|
||||||
|
meridian = locale.meridian(new Date());
|
||||||
|
} else {
|
||||||
|
meridian = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var timestr;
|
||||||
|
|
||||||
|
if (meridian.length > 0 && _12hour) {
|
||||||
|
timestr = hours + " " + meridian;
|
||||||
|
} else {
|
||||||
|
timestr = hours;
|
||||||
|
}
|
||||||
|
g.setFontAlign(0, 0, 0);
|
||||||
|
g.setColor(settings.time.color);
|
||||||
|
g.setFont(settings.time.font, settings.time.size);
|
||||||
|
g.drawString(timestr, settings.time.center-40, settings.time.middle);
|
||||||
|
|
||||||
|
//Write the date as configured in the settings
|
||||||
|
g.setColor(settings.date.color);
|
||||||
|
g.setFont(settings.date.font, settings.date.size);
|
||||||
|
g.drawString(date, settings.date.center, settings.date.middle);
|
||||||
|
};
|
||||||
|
|
||||||
|
//setInterval for HR visualisation
|
||||||
|
const newBeats = function (hr) {
|
||||||
|
if (id != 0) {
|
||||||
|
changeInterval(id, 6e3 / hr.bpm);
|
||||||
|
} else {
|
||||||
|
id = setInterval(drawHR, 6e3 / hr.bpm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//visualize HR with circles pulsating
|
||||||
|
const drawHR = function () {
|
||||||
|
if (grow && size < settings.hr.size) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!grow && size > 3) {
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == settings.hr.size || size == 3) {
|
||||||
|
grow = !grow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grow) {
|
||||||
|
color = settings.hr.color;
|
||||||
|
g.setColor(color);
|
||||||
|
g.fillCircle(settings.hr.x, settings.hr.y, size);
|
||||||
|
} else {
|
||||||
|
color = "#000000";
|
||||||
|
g.setColor(color);
|
||||||
|
g.drawCircle(settings.hr.x, settings.hr.y, size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// clean app screen
|
||||||
|
g.clear();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
//manage when things should be enabled and not
|
||||||
|
Bangle.on('lcdPower', function (on) {
|
||||||
|
if (on) {
|
||||||
|
Bangle.setHRMPower(1);
|
||||||
|
} else {
|
||||||
|
Bangle.setHRMPower(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// refesh every second
|
||||||
|
setInterval(drawClock, 1E3);
|
||||||
|
|
||||||
|
//start HR monitor and update frequency of update
|
||||||
|
Bangle.setHRMPower(1);
|
||||||
|
Bangle.on('HRM', function (d) {
|
||||||
|
newBeats(d);
|
||||||
|
});
|
||||||
|
|
||||||
|
// draw now
|
||||||
|
drawClock();
|
||||||
|
|
||||||
|
// Show launcher when middle button pressed
|
||||||
|
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
0.01: Initial version
|
0.01: Initial version
|
||||||
|
0.02: Increase text brightness, improve controls, (try to) reduce memory usage
|
||||||
|
|
@ -23,9 +23,13 @@ You can change this under `Settings`->`App/Widget Settings`->`Music Controls`.
|
||||||
## Controls
|
## Controls
|
||||||
|
|
||||||
### Buttons
|
### Buttons
|
||||||
* Button 1: Volume up (hold to repeat)
|
* Button 1: Volume up
|
||||||
* Button 2: Toggle play/pause, long-press for menu
|
* Button 2:
|
||||||
* Button 3: Volume down (hold to repeat, but remember that holding for too long resets your watch)
|
- Single press: toggle play/pause
|
||||||
|
- Double press: next song
|
||||||
|
- Triple press: previous song
|
||||||
|
- Long-press: open application launcher
|
||||||
|
* Button 3: Volume down
|
||||||
|
|
||||||
### Touch
|
### Touch
|
||||||
* Left: pause/previous song
|
* Left: pause/previous song
|
||||||
|
|
|
||||||
1158
apps/gbmusic/app.js
|
|
@ -21,3 +21,4 @@
|
||||||
0.19: Support for call incoming/start/end
|
0.19: Support for call incoming/start/end
|
||||||
0.20: Reduce memory usage
|
0.20: Reduce memory usage
|
||||||
0.21: Fix HRM setting
|
0.21: Fix HRM setting
|
||||||
|
0.22: Respect Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,9 @@
|
||||||
case "notify-":
|
case "notify-":
|
||||||
if (event.t === "notify") {
|
if (event.t === "notify") {
|
||||||
require("notify").show(prettifyNotificationEvent(event));
|
require("notify").show(prettifyNotificationEvent(event));
|
||||||
Bangle.buzz();
|
if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||||
|
Bangle.buzz();
|
||||||
|
}
|
||||||
} else { // notify-
|
} else { // notify-
|
||||||
require("notify").hide(event);
|
require("notify").hide(event);
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +176,9 @@
|
||||||
body: event.number, icon:require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw="))}
|
body: event.number, icon:require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw="))}
|
||||||
if (event.cmd === "incoming") {
|
if (event.cmd === "incoming") {
|
||||||
require("notify").show(note);
|
require("notify").show(note);
|
||||||
Bangle.buzz();
|
if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||||
|
Bangle.buzz();
|
||||||
|
}
|
||||||
} else if (event.cmd === "start") {
|
} else if (event.cmd === "start") {
|
||||||
require("notify").show(Object.assign(note, {
|
require("notify").show(Object.assign(note, {
|
||||||
bgColor : "#008000", titleBgColor : "#00C000",
|
bgColor : "#008000", titleBgColor : "#00C000",
|
||||||
|
|
@ -194,6 +198,7 @@
|
||||||
delete state.find;
|
delete state.find;
|
||||||
}
|
}
|
||||||
if (event.n)
|
if (event.n)
|
||||||
|
// Ignore quiet mode: we always want to find our watch
|
||||||
state.find = setInterval(_=>{
|
state.find = setInterval(_=>{
|
||||||
Bangle.buzz();
|
Bangle.buzz();
|
||||||
setTimeout(_=>Bangle.beep(), 1000);
|
setTimeout(_=>Bangle.beep(), 1000);
|
||||||
|
|
|
||||||
|
|
@ -20,3 +20,4 @@
|
||||||
0.16: Add gpsrec app to Settings menu
|
0.16: Add gpsrec app to Settings menu
|
||||||
0.17: Disable recording if storage is full (fix #574)
|
0.17: Disable recording if storage is full (fix #574)
|
||||||
0.18: Period counter now uses GPS time rather than counting packets (allows use with GPS Setup)
|
0.18: Period counter now uses GPS time rather than counting packets (allows use with GPS Setup)
|
||||||
|
0.19: Fix memory usage issues inside track viewer app
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ function showMainMenu() {
|
||||||
updateSettings();
|
updateSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'View Tracks': viewTracks,
|
'View Tracks': ()=>{viewTracks();},
|
||||||
'< Back': ()=>{load();}
|
'< Back': ()=>{load();}
|
||||||
};
|
};
|
||||||
return E.showMenu(mainmenu);
|
return E.showMenu(mainmenu);
|
||||||
|
|
@ -65,13 +65,13 @@ function viewTracks() {
|
||||||
for (var n=0;n<36;n++) {
|
for (var n=0;n<36;n++) {
|
||||||
var f = require("Storage").open(getFN(n),"r");
|
var f = require("Storage").open(getFN(n),"r");
|
||||||
if (f.readLine()!==undefined) {
|
if (f.readLine()!==undefined) {
|
||||||
menu["Track "+n] = viewTrack.bind(null,n,false);
|
menu["Track "+n] = (n=>viewTrack(n)).bind(null,n,false);
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
menu["No Tracks found"] = function(){};
|
menu["No Tracks found"] = function(){};
|
||||||
menu['< Back'] = showMainMenu;
|
menu['< Back'] = () => { showMainMenu(); };
|
||||||
return E.showMenu(menu);
|
return E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,7 +161,7 @@ function viewTrack(n, info) {
|
||||||
viewTrack(n, info);
|
viewTrack(n, info);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
menu['< Back'] = viewTracks;
|
menu['< Back'] = () => { viewTracks(); };
|
||||||
return E.showMenu(menu);
|
return E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
0.01: Add a number to match to turn off alarm
|
0.01: Add a number to match to turn off alarm
|
||||||
|
0.02: Respect Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ function showPrompt(msg, buzzCount, alarm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAlarm(alarm) {
|
function showAlarm(alarm) {
|
||||||
|
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence
|
||||||
var msg = formatTime(alarm.hr);
|
var msg = formatTime(alarm.hr);
|
||||||
var buzzCount = 20;
|
var buzzCount = 20;
|
||||||
if (alarm.msg)
|
if (alarm.msg)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Don't overwrite existing settings on app update
|
0.02: Don't overwrite existing settings on app update
|
||||||
Clean up recordings on app removal
|
Clean up recordings on app removal
|
||||||
|
0.03: added graphing feature of 164 latest measurements
|
||||||
|
0.04: Fix memory usage when viewing HRM traces
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,25 @@
|
||||||
|
const GraphXZero = 40;
|
||||||
|
const GraphYZero = 200;
|
||||||
|
const GraphY100 = 80;
|
||||||
|
|
||||||
|
const GraphMarkerOffset = 5;
|
||||||
|
const MaxValueCount = 164;
|
||||||
|
const GraphXMax = GraphXZero + MaxValueCount;
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
var settings = require("Storage").readJSON("heart.json",1)||{};
|
var settings = require("Storage").readJSON("heart.json",1)||{};
|
||||||
|
|
||||||
|
var globalSettings = require('Storage').readJSON('setting.json', true) || {timezone: 0};
|
||||||
|
require('DateExt').locale({
|
||||||
|
str: "0D.0M. 0h:0m",
|
||||||
|
offset: [
|
||||||
|
globalSettings.timezone * 60,
|
||||||
|
globalSettings.timezone * 60
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
function getFileNbr(n) {
|
function getFileNbr(n) {
|
||||||
return ".heart"+n.toString(36);
|
return ".heart"+n.toString(36);
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +52,8 @@ function showMainMenu() {
|
||||||
updateSettings();
|
updateSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'View Records': viewRecords,
|
'View Records': ()=>{viewRecords()},
|
||||||
|
'Graph Records': ()=>{graphRecords()},
|
||||||
'< Back': ()=>{load();}
|
'< Back': ()=>{load();}
|
||||||
};
|
};
|
||||||
return E.showMenu(mainMenu);
|
return E.showMenu(mainMenu);
|
||||||
|
|
@ -55,7 +73,7 @@ function viewRecords() {
|
||||||
}
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
menu["No Records Found"] = function(){};
|
menu["No Records Found"] = function(){};
|
||||||
menu['< Back'] = showMainMenu;
|
menu['< Back'] = ()=>{showMainMenu()};
|
||||||
return E.showMenu(menu);
|
return E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,9 +110,188 @@ function viewRecord(n) {
|
||||||
viewRecord(n);
|
viewRecord(n);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
menu['< Back'] = viewRecords;
|
menu['< Back'] = ()=>{viewRecords()};
|
||||||
print(menu);
|
print(menu);
|
||||||
return E.showMenu(menu);
|
return E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function graphRecords() {
|
||||||
|
const menu = {
|
||||||
|
'': { 'title': 'Heart Records' }
|
||||||
|
};
|
||||||
|
var found = false;
|
||||||
|
for (var n=0;n<36;n++) {
|
||||||
|
var f = require("Storage").open(getFileNbr(n),"r");
|
||||||
|
var line = f.readLine();
|
||||||
|
if (line!==undefined) {
|
||||||
|
menu["#"+n+" "+Date(line.split(",")[0]*1000).as().str] = graphRecord.bind(null,n);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
menu["No Records Found"] = function(){};
|
||||||
|
menu['< Back'] = ()=>{showMainMenu()};
|
||||||
|
return E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// based on batchart
|
||||||
|
function renderHomeIcon() {
|
||||||
|
//Home for Btn2
|
||||||
|
g.setColor(1, 1, 1);
|
||||||
|
g.drawLine(220, 118, 227, 110);
|
||||||
|
g.drawLine(227, 110, 234, 118);
|
||||||
|
|
||||||
|
g.drawPoly([222,117,222,125,232,125,232,117], false);
|
||||||
|
g.drawRect(226,120,229,125);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderChart() {
|
||||||
|
// Left Y axis (Battery)
|
||||||
|
g.setColor(1, 1, 0);
|
||||||
|
g.drawLine(GraphXZero, GraphYZero + GraphMarkerOffset, GraphXZero, GraphY100);
|
||||||
|
|
||||||
|
g.setFontAlign(1, -1, 0);
|
||||||
|
g.drawString("150", 35, GraphY100 - GraphMarkerOffset);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, GraphY100, GraphXZero, GraphY100);
|
||||||
|
|
||||||
|
g.drawString("125", 35, GraphYZero - 110 - GraphMarkerOffset);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
|
||||||
|
|
||||||
|
g.drawString("100", 35, GraphYZero - 100 - GraphMarkerOffset);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
|
||||||
|
|
||||||
|
g.drawString("90", 35, GraphYZero - 90 - GraphMarkerOffset);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
|
||||||
|
|
||||||
|
g.drawString("80", 35, GraphYZero - 70 - GraphMarkerOffset);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
|
||||||
|
|
||||||
|
g.drawString("70", 35, GraphYZero - 50 - GraphMarkerOffset);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
|
||||||
|
|
||||||
|
g.drawString("60", 35, GraphYZero - 30 - GraphMarkerOffset);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
|
||||||
|
|
||||||
|
g.drawString("50", 35, GraphYZero - 20 - GraphMarkerOffset);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
|
||||||
|
|
||||||
|
g.drawString("40", 35, GraphYZero - 10 - GraphMarkerOffset);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, 150, GraphXZero, 150);
|
||||||
|
|
||||||
|
g.drawString("30", 35, GraphYZero - GraphMarkerOffset);
|
||||||
|
|
||||||
|
g.setColor(1, 1, 1);
|
||||||
|
g.drawLine(GraphXZero - GraphMarkerOffset, GraphYZero, GraphXMax + GraphMarkerOffset, GraphYZero);
|
||||||
|
|
||||||
|
console.log("Finished drawing chart");
|
||||||
|
}
|
||||||
|
|
||||||
|
// as drawing starts at 30 HRM decreasing measrure by 30
|
||||||
|
// recalculate for range 110-150 as only 20 pixels are available
|
||||||
|
function getY(measure) {
|
||||||
|
positionY = GraphYZero - measure + 30;
|
||||||
|
if (100 < measure < 150) {
|
||||||
|
positionY = GraphYZero - ( 100 + Math.round((measure - 100)/2) ) + 30;
|
||||||
|
g.setColor(1, 0, 0);
|
||||||
|
} else if (60 < measrure < 100) {
|
||||||
|
positionY = GraphYZero - ( 30 + Math.round((measure - 30)/2) ) + 30;
|
||||||
|
g.setColor(0, 1, 0);
|
||||||
|
}
|
||||||
|
if (positionY > GraphYZero) {
|
||||||
|
positionY = GraphYZero;
|
||||||
|
g.setColor(1, 0, 0);
|
||||||
|
}
|
||||||
|
if (positionY < GraphY100) {
|
||||||
|
positionY = GraphY100;
|
||||||
|
g.setColor(1, 0, 0);
|
||||||
|
}
|
||||||
|
return positionY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop() {
|
||||||
|
E.showMenu();
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
function graphRecord(n) {
|
||||||
|
E.showMenu({'': 'Heart Record '+n});
|
||||||
|
E.showMessage(
|
||||||
|
"Loading Data ...\n\nMay take a while,\nwill vibrate\nwhen done.",
|
||||||
|
'Heart Record '+n
|
||||||
|
);
|
||||||
|
g.setFont("Vector", 10);
|
||||||
|
|
||||||
|
var lastPixel;
|
||||||
|
var lineCount = 0;
|
||||||
|
var positionX = GraphXZero;
|
||||||
|
var positionY = GraphYZero;
|
||||||
|
var startLine = 1;
|
||||||
|
var tempCount = 0;
|
||||||
|
var f = require("Storage").open(getFileNbr(n),"r");
|
||||||
|
var line = f.readLine();
|
||||||
|
var times = Array(2);
|
||||||
|
console.log("Counting lines");
|
||||||
|
while (line !== undefined) {
|
||||||
|
lineCount++;
|
||||||
|
line = f.readLine();
|
||||||
|
}
|
||||||
|
console.log(`Line count: ${lineCount}`);
|
||||||
|
if (lineCount > MaxValueCount) {
|
||||||
|
startLine = lineCount - MaxValueCount;
|
||||||
|
}
|
||||||
|
console.log(`start: ${startLine}`);
|
||||||
|
|
||||||
|
f = require("Storage").open(getFileNbr(n),"r");
|
||||||
|
line = f.readLine();
|
||||||
|
while (line !== undefined) {
|
||||||
|
currentLine = line;
|
||||||
|
line = f.readLine();
|
||||||
|
tempCount++;
|
||||||
|
if (tempCount == startLine) {
|
||||||
|
g.clear();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
renderHomeIcon();
|
||||||
|
renderChart();
|
||||||
|
} else if (tempCount > startLine) {
|
||||||
|
positionX++;
|
||||||
|
if (parseInt(currentLine.split(",")[2]) >= 70) {
|
||||||
|
g.setColor(1, 1, 1);
|
||||||
|
oldPositionY = positionY;
|
||||||
|
positionY = getY(parseInt(currentLine.split(",")[1]));
|
||||||
|
if (times[0] === undefined) {
|
||||||
|
times[0] = parseInt(currentLine.split(",")[0]);
|
||||||
|
}
|
||||||
|
if (tempCount == startLine + 1) {
|
||||||
|
g.setPixel(positionX, positionY);
|
||||||
|
} else {
|
||||||
|
g.drawLine(positionX - 1, oldPositionY, positionX, positionY);
|
||||||
|
times[1] = parseInt(currentLine.split(",")[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setColor(1, 1, 0);
|
||||||
|
g.setFont("Vector", 10);
|
||||||
|
console.log('start: ' + times[0]);
|
||||||
|
console.log('end: ' + times[1]);
|
||||||
|
if (times[0] !== undefined) {
|
||||||
|
g.setFontAlign(-1, -1, 0);
|
||||||
|
var startdate = new Date(times[0]*1000);
|
||||||
|
g.drawString(startdate.local().as("0h:0m").str, 15, GraphYZero + 12);
|
||||||
|
}
|
||||||
|
if (times[1] !== undefined) {
|
||||||
|
g.setFontAlign(1, -1, 0);
|
||||||
|
var enddate = new Date(times[1]*1000);
|
||||||
|
g.drawString(enddate.local().as().str, GraphXMax, GraphYZero + 12);
|
||||||
|
}
|
||||||
|
console.log("Finished rendering data");
|
||||||
|
Bangle.buzz(200, 0.3);
|
||||||
|
setWatch(stop, BTN2, {edge:"falling", debounce:50, repeat:false});
|
||||||
|
}
|
||||||
|
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
|
|
||||||
|
// vim: et ts=2 sw=2
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
0.01: New App
|
||||||
|
0.02: Add different strike intervals and support for quiet time
|
||||||
|
0.03: Bug fixes for setting attributes
|
||||||
|
0.04: Add more time to strike and the strength
|
||||||
|
0.05: Add display for the next strike time
|
||||||
|
0.06: Move the next strike time to the first row of display
|
||||||
|
0.07: Change the boot function to avoid reloading the entire watch
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Hour Strike
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Time passes too fast!
|
||||||
|
|
||||||
|
This app configures your `Bangle.js` so that it buzzes on the hour or on the half hour.
|
||||||
|
|
||||||
|
This app is slightly different from [Hour Chime](https://github.com/espruino/BangleApps/tree/master/apps/widchime). `Hour Chimee` runs as a widget but `Hour Strike` runs as a background task, without showing a widget.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Strike the hour, the half hour, the quarter hour, and more
|
||||||
|
- Set up a range of hours that clock will strike
|
||||||
|
- Set up the strength of the strike
|
||||||
|
- Preview when the next strike will happen
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
- This app does not know or check whether your clock already chimes on the hour.
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
|
||||||
|
[Weiming Hu](https://weiming-hu.github.io/), using coding from the [Default Alarm](https://github.com/espruino/BangleApps/tree/master/apps/alarm).
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwkGswAHogAEBxAAHsgXFowXPCwowQFwwwQCIUjn/zmQwPFwUj/4ACDAQwMBwNDCgPwh4DBmgwMFwU/C4vzGBgMBoRECC4f/kgwLIwgXFJBgLBl4XH+QXNLwQXFMAQXPmEDC6K8DiEBAoYXSgA1DI6MxgETL6gYBgIGBC5ynDCYMQAwKnOa4YABmTXQoQXEAAUkC5dkMAx2EowXJJBBGNAAMUBwMjMAgHBoIXLiIPBDAM/+YWCokRC5gwCAAtBC5owDAAgJBC5dBiAwGoMBigXLokQgIXFA4QXMoEAAANNqAECggXR7oXXAYQXSgvUC6sEC60N6oXW6AXUinu8IXTgPuAAMQC6UEC4SsDC58OC4XgC9RHXO66nCoLXVAAoXmABQX1A"))
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,48 @@
|
||||||
|
const storage = require('Storage');
|
||||||
|
let settings;
|
||||||
|
|
||||||
|
function updateSettings() {
|
||||||
|
storage.write('hourstrike.json', settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetSettings() {
|
||||||
|
settings = {
|
||||||
|
interval: 3600,
|
||||||
|
start: 9,
|
||||||
|
end: 21,
|
||||||
|
vlevel: 0.5,
|
||||||
|
next_hour: -1,
|
||||||
|
next_minute: -1,
|
||||||
|
};
|
||||||
|
updateSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = storage.readJSON('hourstrike.json', 1);
|
||||||
|
if (!settings) resetSettings();
|
||||||
|
|
||||||
|
function showMainMenu() {
|
||||||
|
var mode_txt = ['Off','1 min','5 min','10 min','1/4 h','1/2 h','1 h'];
|
||||||
|
var mode_interval = [-1,60,300,600,900,1800,3600];
|
||||||
|
const mainmenu = {'': { 'title': 'Hour Strike' }};
|
||||||
|
mainmenu['Next strike '+settings.next_hour+':'+settings.next_minute] = function(){};
|
||||||
|
mainmenu['Notify every'] = {
|
||||||
|
value: mode_interval.indexOf(settings.interval),
|
||||||
|
min: 0, max: 6, format: v => mode_txt[v],
|
||||||
|
onchange: v => {
|
||||||
|
settings.interval = mode_interval[v];
|
||||||
|
if (v===0) {settings.next_hour = -1; settings.next_minute = -1;}
|
||||||
|
updateSettings();}};
|
||||||
|
mainmenu.Start = {
|
||||||
|
value: settings.start, min: 0, max: 23, format: v=>v+':00',
|
||||||
|
onchange: v=> {settings.start = v; updateSettings();}};
|
||||||
|
mainmenu.End = {
|
||||||
|
value: settings.end, min: 0, max: 23, format: v=>v+':59',
|
||||||
|
onchange: v=> {settings.end = v; updateSettings();}};
|
||||||
|
mainmenu.Strength = {
|
||||||
|
value: settings.vlevel*10, min: 1, max: 10, format: v=>v/10,
|
||||||
|
onchange: v=> {settings.vlevel = v/10; updateSettings();}};
|
||||||
|
mainmenu['< Back'] = ()=>load();
|
||||||
|
return E.showMenu(mainmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
showMainMenu();
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
(function() {
|
||||||
|
function setup () {
|
||||||
|
var settings = require('Storage').readJSON('hourstrike.json',1)||[];
|
||||||
|
var t = new Date();
|
||||||
|
var t_min_sec = t.getMinutes()*60+t.getSeconds();
|
||||||
|
var wait_msec = settings.interval>0?(settings.interval-t_min_sec%settings.interval)*1000:-1;
|
||||||
|
if (wait_msec>0) {
|
||||||
|
t.setMilliseconds(t.getMilliseconds()+wait_msec);
|
||||||
|
var t_hour = t.getHours();
|
||||||
|
if (t_hour<settings.start||t_hour>settings.end) {
|
||||||
|
var strike = new Date(t.getTime());
|
||||||
|
strike.setHours(settings.start);
|
||||||
|
strike.setMinutes(0);
|
||||||
|
if (t_hour>settings.end) {
|
||||||
|
strike.setDate(strike.getDate()+1);
|
||||||
|
}
|
||||||
|
wait_msec += strike-t;
|
||||||
|
settings.next_hour = strike.getHours();
|
||||||
|
settings.next_minute = strike.getMinutes();
|
||||||
|
} else {
|
||||||
|
settings.next_hour = t_hour;
|
||||||
|
settings.next_minute = t.getMinutes();
|
||||||
|
}
|
||||||
|
setTimeout(strike_func, wait_msec);
|
||||||
|
} else {
|
||||||
|
settings.next_hour = -1;
|
||||||
|
settings.next_minute = -1;
|
||||||
|
}
|
||||||
|
require('Storage').write('hourstrike.json', settings);
|
||||||
|
}
|
||||||
|
function strike_func () {
|
||||||
|
var setting = require('Storage').readJSON('hourstrike.json',1)||[];
|
||||||
|
Bangle.buzz(200, setting.vlevel||0.5)
|
||||||
|
.then(() => new Promise(resolve => setTimeout(resolve,200)))
|
||||||
|
.then(() => Bangle.buzz(200, setting.vlevel||0.5));
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
setup();
|
||||||
|
})();
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: First version
|
0.01: First version
|
||||||
0.02: compass disable BTN1,BTN2 while waiting for GPS to reach RUNNING status
|
0.02: compass disable BTN1,BTN2 while waiting for GPS to reach RUNNING status
|
||||||
|
0.03: Don't buzz for GPS fix in Quiet Mode
|
||||||
|
|
@ -246,7 +246,9 @@ GPS.prototype.processFix = function(fix) {
|
||||||
if (fix.fix) {
|
if (fix.fix) {
|
||||||
//this.log_debug("Got fix - setting state to GPS_RUNNING");
|
//this.log_debug("Got fix - setting state to GPS_RUNNING");
|
||||||
this.gpsState = this.GPS_RUNNING;
|
this.gpsState = this.GPS_RUNNING;
|
||||||
if (!this.last_fix.fix) Bangle.buzz(); // buzz on first position
|
if (!this.last_fix.fix && !(require("Storage").readJSON("setting.json", 1) || {}).quiet) {
|
||||||
|
Bangle.buzz(); // buzz on first position
|
||||||
|
}
|
||||||
this.last_fix = fix;
|
this.last_fix = fix;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,4 @@
|
||||||
0.12: Add info banner message when phone (dis)connects. Display low-battery warning (<=10%)
|
0.12: Add info banner message when phone (dis)connects. Display low-battery warning (<=10%)
|
||||||
0.13: Fix drawPyramid function so pyramids are drawn in correct Y position
|
0.13: Fix drawPyramid function so pyramids are drawn in correct Y position
|
||||||
0.14: Add jumping frame for characters
|
0.14: Add jumping frame for characters
|
||||||
|
0.15: Disable notification buzz during Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@
|
||||||
0.05: Adjust position of notification src text
|
0.05: Adjust position of notification src text
|
||||||
0.06: Support background color
|
0.06: Support background color
|
||||||
0.07: Auto-calculate height, and pad text down even when there's no title (so it stays on-screen)
|
0.07: Auto-calculate height, and pad text down even when there's no title (so it stays on-screen)
|
||||||
|
0.08: Don't turn on screen during Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ other applications or widgets to display messages.
|
||||||
|
|
||||||
```JS
|
```JS
|
||||||
options = {
|
options = {
|
||||||
on : bool, // turn screen on, default true
|
on : bool, // turn screen on, default true (But not if Quiet Mode is enabled)
|
||||||
size : int, // height of notification, default is fit to height (80 max)
|
size : int, // height of notification, default is fit to height (80 max)
|
||||||
title : string, // optional title
|
title : string, // optional title
|
||||||
id // optional notification ID, used with hide()
|
id // optional notification ID, used with hide()
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,9 @@ exports.show = function(options) {
|
||||||
options.render({x:x, y:y, w:w, h:h});
|
options.render({x:x, y:y, w:w, h:h});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.on) Bangle.setLCDPower(1); // light up
|
if (options.on && !(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||||
|
Bangle.setLCDPower(1); // light up
|
||||||
|
}
|
||||||
Bangle.setLCDMode(oldMode); // clears cliprect
|
Bangle.setLCDMode(oldMode); // clears cliprect
|
||||||
|
|
||||||
function anim() {
|
function anim() {
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,4 @@
|
||||||
0.05: Fix `g` corruption issue if .hide gets called twice
|
0.05: Fix `g` corruption issue if .hide gets called twice
|
||||||
0.06: Adjust position of notification src text and notifications without title
|
0.06: Adjust position of notification src text and notifications without title
|
||||||
0.07: Support background color
|
0.07: Support background color
|
||||||
|
0.08: Don't turn on screen during Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -90,8 +90,9 @@ exports.show = function(options) {
|
||||||
const area={x:x, y:y, w:w, h:h}
|
const area={x:x, y:y, w:w, h:h}
|
||||||
options.render(area);
|
options.render(area);
|
||||||
}
|
}
|
||||||
|
if (options.on && !(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||||
if (options.on) Bangle.setLCDPower(1); // light up
|
Bangle.setLCDPower(1); // light up
|
||||||
|
}
|
||||||
Bangle.on("touch", exports.hide);
|
Bangle.on("touch", exports.hide);
|
||||||
// Create a fake graphics to hide draw attempts
|
// Create a fake graphics to hide draw attempts
|
||||||
oldg = g;
|
oldg = g;
|
||||||
|
|
@ -115,9 +116,11 @@ exports.hide = function(options) {
|
||||||
Bangle.removeListener("touch", exports.hide);
|
Bangle.removeListener("touch", exports.hide);
|
||||||
g.clear();
|
g.clear();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
// flipping the screen off then on often triggers a redraw - it may not!
|
if (Bangle.isLCDOn() || !(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||||
Bangle.setLCDPower(0);
|
// flipping the screen off then on often triggers a redraw - it may not!
|
||||||
Bangle.setLCDPower(1);
|
Bangle.setLCDPower(0);
|
||||||
|
Bangle.setLCDPower(1);
|
||||||
|
}
|
||||||
// hack for E.showMenu/showAlert/showPrompt - can force a redraw by faking next/back
|
// hack for E.showMenu/showAlert/showPrompt - can force a redraw by faking next/back
|
||||||
if (Bangle.btnWatches) {
|
if (Bangle.btnWatches) {
|
||||||
global["\xff"].watches[Bangle.btnWatches[0]].callback();
|
global["\xff"].watches[Bangle.btnWatches[0]].callback();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: First version
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Quiet Mode Schedule
|
||||||
|
|
||||||
|
Automatically turn Quiet Mode on or off at set times.
|
||||||
|
|
||||||
|
 
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
const modeNames = ["Off", "Alarms", "Silent"];
|
||||||
|
let scheds = require("Storage").readJSON("qmsched.json", 1);
|
||||||
|
/*scheds = [
|
||||||
|
{ hr : 6.5, // hours + minutes/60
|
||||||
|
last : 0, // last day of the month we fired on - so we don't switch twice in one day!
|
||||||
|
mode : 1, // quiet mode (0/1/2)
|
||||||
|
}
|
||||||
|
];*/
|
||||||
|
if (!scheds) {
|
||||||
|
// set default schedule on first load of app
|
||||||
|
scheds = [
|
||||||
|
{"hr": 8, "mode": 0, "last": 25},
|
||||||
|
{"hr": 22, "mode": 1, "last": 25},
|
||||||
|
];
|
||||||
|
require("Storage").writeJSON("qmsched.json", scheds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(t) {
|
||||||
|
const hrs = 0|t;
|
||||||
|
const mins = Math.round((t-hrs)*60);
|
||||||
|
return (" "+hrs).substr(-2)+":"+("0"+mins).substr(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentHr() {
|
||||||
|
const time = new Date();
|
||||||
|
return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMainMenu() {
|
||||||
|
const menu = {
|
||||||
|
"": {"title": "Quiet Mode"},
|
||||||
|
"Current Mode": {
|
||||||
|
value: (require("Storage").readJSON("setting.json", 1) || {}).quiet|0,
|
||||||
|
format: v => modeNames[v],
|
||||||
|
onchange: function(v) {
|
||||||
|
if (v<0) v = 2;
|
||||||
|
if (v>2) v = 0;
|
||||||
|
require("qmsched").setMode(v);
|
||||||
|
this.value = v;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
scheds.sort((a, b) => (a.hr-b.hr));
|
||||||
|
scheds.forEach((sched, idx) => {
|
||||||
|
const name = modeNames[sched.mode];
|
||||||
|
const txt = formatTime(sched.hr)+" ".repeat(14-name.length)+name;
|
||||||
|
menu[txt] = function() {
|
||||||
|
showEditMenu(idx);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
menu["Add Schedule"] = () => showEditMenu(-1);
|
||||||
|
menu["< Back"] = () => {load();};
|
||||||
|
return E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showEditMenu(index) {
|
||||||
|
const isNew = index<0;
|
||||||
|
let hrs = 12, mins = 0;
|
||||||
|
let mode = 1;
|
||||||
|
if (!isNew) {
|
||||||
|
const s = scheds[index];
|
||||||
|
hrs = 0|s.hr;
|
||||||
|
mins = Math.round((s.hr-hrs)*60);
|
||||||
|
mode = s.mode;
|
||||||
|
}
|
||||||
|
const menu = {
|
||||||
|
"": {"title": (isNew ? "Add" : "Edit")+" Schedule"},
|
||||||
|
"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'
|
||||||
|
},
|
||||||
|
"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'
|
||||||
|
},
|
||||||
|
"Switch to": {
|
||||||
|
value: mode,
|
||||||
|
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'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
function getSched() {
|
||||||
|
const hr = hrs+(mins/60);
|
||||||
|
let day = 0;
|
||||||
|
// If schedule is for tomorrow not today (eg, in the past), set day
|
||||||
|
if (hr<getCurrentHr()) {
|
||||||
|
day = (new Date()).getDate();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
hr: hr,
|
||||||
|
mode: mode,
|
||||||
|
last: day,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
menu["> Save"] = function() {
|
||||||
|
if (isNew) {
|
||||||
|
scheds.push(getSched());
|
||||||
|
} else {
|
||||||
|
scheds[index] = getSched();
|
||||||
|
}
|
||||||
|
require("Storage").writeJSON("qmsched.json", scheds);
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
if (!isNew) {
|
||||||
|
menu["> Delete"] = function() {
|
||||||
|
scheds.splice(index, 1);
|
||||||
|
require("Storage").writeJSON("qmsched.json", scheds);
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
menu["< Cancel"] = showMainMenu;
|
||||||
|
return E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
showMainMenu();
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -0,0 +1,24 @@
|
||||||
|
// apply Quiet Mode schedules
|
||||||
|
(function qm() {
|
||||||
|
let scheds = require("Storage").readJSON("qmsched.json", 1) || [];
|
||||||
|
if (!scheds.length) return;
|
||||||
|
let next,idx;
|
||||||
|
scheds.forEach(function(s, i) {
|
||||||
|
if (!next || (s.hr+s.last*24)<(next.hr+next.last*24)) {
|
||||||
|
next = s;
|
||||||
|
idx = i;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const now = new Date(),
|
||||||
|
hr = now.getHours()+(now.getMinutes()/60)+(now.getSeconds()/3600);
|
||||||
|
let t = 3600000*(next.hr-hr);
|
||||||
|
if (next.last===now.getDate()) t += 86400000;
|
||||||
|
/* update quiet mode at the correct time. */
|
||||||
|
setTimeout(function() {
|
||||||
|
let scheds = require("Storage").readJSON("qmsched.json", 1) || [];
|
||||||
|
require("qmsched").setMode(scheds[idx].mode);
|
||||||
|
scheds[idx].last = (new Date()).getDate();
|
||||||
|
require("Storage").writeJSON("qmsched.json", scheds);
|
||||||
|
qm(); // schedule next update
|
||||||
|
}, t);
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
// https://icons8.com/icon/19324/no-reminders
|
||||||
|
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AElksF1wwtF4YwO0WiGFguBGFovfGB3MAAgwnFooxfGBAuJGEguLGEV/F5owh0YvpGH4vhGCQvd0YwQF7vMGCAveGCAvfGB4vgGBwvhGBouhGFLkIGEouIGEwvKGBguiGEQuNGEHN5owa5ouQ53P5/O5wyOGA3NDAIbBLyAUCAAQzCNBQwF0gVDXiQoBGQgAEEIILE0iSJdiozCFQw1FGBJgSABSVIeg7wQGSDDMFyQ0VCQQwdAAWcAAwPHGD4vPGD+iAAwRJGEgRLGEQRNeTwARF1wA/AH4AX"))
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* Set new Quiet Mode and apply Bangle options
|
||||||
|
* @param {int} mode Quiet Mode
|
||||||
|
*/
|
||||||
|
exports.setMode = function(mode) {
|
||||||
|
let s = require("Storage").readJSON("setting.json", 1) || {};
|
||||||
|
s.quiet = mode;
|
||||||
|
require("Storage").writeJSON("setting.json", s);
|
||||||
|
if (s.options) Bangle.setOptions(s.options);
|
||||||
|
if (mode && s.qmOptions) Bangle.setOptions(s.qmOptions);
|
||||||
|
if (mode && s.qmBrightness) {
|
||||||
|
if (s.qmBrightness!=1) Bangle.setLCDBrightness(s.qmBrightness);
|
||||||
|
} else {
|
||||||
|
if (s.brightness && s.brightness!=1) Bangle.setLCDBrightness(s.brightness);
|
||||||
|
}
|
||||||
|
if (mode && s.qmTimeout) Bangle.setLCDTimeout(s.qmTimeout);
|
||||||
|
};
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -1,4 +1,5 @@
|
||||||
0.01: First published version of app
|
0.01: First published version of app
|
||||||
0.02: Added support for locale and 12H clock
|
0.02: Added support for locale and 12H clock
|
||||||
0.03: Added HR indication to clock
|
0.03: Added HR indication to clock
|
||||||
0.04: Update font size and alignment
|
0.04: Update font size and alignment
|
||||||
|
0.05: Changes which circle show minutes and seconds
|
||||||
|
|
@ -23,29 +23,29 @@
|
||||||
// Ssettings
|
// Ssettings
|
||||||
const settings = {
|
const settings = {
|
||||||
time: {
|
time: {
|
||||||
color: 0xD6ED17,
|
color: '#D6ED17',
|
||||||
font: 'Vector',
|
font: 'Vector',
|
||||||
size: 60,
|
size: 60,
|
||||||
middle: screen.middle,
|
middle: screen.middle,
|
||||||
center: screen.center,
|
center: screen.center,
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
color: 0xD6ED17,
|
color: '#D6ED17',
|
||||||
font: 'Vector',
|
font: 'Vector',
|
||||||
size: 15,
|
size: 15,
|
||||||
middle: screen.height-17, // at bottom of screen
|
middle: screen.height-17, // at bottom of screen
|
||||||
center: screen.center,
|
center: screen.center,
|
||||||
},
|
},
|
||||||
circle: {
|
circle: {
|
||||||
colormin: 0x606060,
|
colormin: '#ffffff',
|
||||||
colorsec: 0x656565,
|
colorsec: '#ffffff',
|
||||||
width: 10,
|
width: 10,
|
||||||
middle: screen.middle,
|
middle: screen.middle,
|
||||||
center: screen.center,
|
center: screen.center,
|
||||||
height: screen.height
|
height: screen.height
|
||||||
},
|
},
|
||||||
hr: {
|
hr: {
|
||||||
color: 0x333333,
|
color: '#333333',
|
||||||
size: 10,
|
size: 10,
|
||||||
x: screen.center,
|
x: screen.center,
|
||||||
y: screen.middle + 45
|
y: screen.middle + 45
|
||||||
|
|
@ -66,18 +66,6 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawMinArc = function (sections, color) {
|
const drawMinArc = function (sections, color) {
|
||||||
g.setColor(color);
|
|
||||||
rad = (settings.circle.height / 2) - 20;
|
|
||||||
r1 = getArcXY(settings.circle.middle, settings.circle.center, rad, sections * (360 / 60) - 90);
|
|
||||||
//g.setPixel(r[0],r[1]);
|
|
||||||
r2 = getArcXY(settings.circle.middle, settings.circle.center, rad - settings.circle.width, sections * (360 / 60) - 90);
|
|
||||||
//g.setPixel(r[0],r[1]);
|
|
||||||
g.drawLine(r1[0], r1[1], r2[0], r2[1]);
|
|
||||||
g.setColor('#333333');
|
|
||||||
g.drawCircle(settings.circle.middle, settings.circle.center, rad - settings.circle.width - 4)
|
|
||||||
};
|
|
||||||
|
|
||||||
const drawSecArc = function (sections, color) {
|
|
||||||
g.setColor(color);
|
g.setColor(color);
|
||||||
rad = (settings.circle.height / 2) - 40;
|
rad = (settings.circle.height / 2) - 40;
|
||||||
r1 = getArcXY(settings.circle.middle, settings.circle.center, rad, sections * (360 / 60) - 90);
|
r1 = getArcXY(settings.circle.middle, settings.circle.center, rad, sections * (360 / 60) - 90);
|
||||||
|
|
@ -86,7 +74,19 @@
|
||||||
//g.setPixel(r[0],r[1]);
|
//g.setPixel(r[0],r[1]);
|
||||||
g.drawLine(r1[0], r1[1], r2[0], r2[1]);
|
g.drawLine(r1[0], r1[1], r2[0], r2[1]);
|
||||||
g.setColor('#333333');
|
g.setColor('#333333');
|
||||||
g.drawCircle(settings.circle.middle, settings.circle.center, rad - settings.circle.width - 4)
|
g.drawCircle(settings.circle.middle, settings.circle.center, rad - settings.circle.width - 4);
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawSecArc = function (sections, color) {
|
||||||
|
g.setColor(color);
|
||||||
|
rad = (settings.circle.height / 2) - 20;
|
||||||
|
r1 = getArcXY(settings.circle.middle, settings.circle.center, rad, sections * (360 / 60) - 90);
|
||||||
|
//g.setPixel(r[0],r[1]);
|
||||||
|
r2 = getArcXY(settings.circle.middle, settings.circle.center, rad - settings.circle.width, sections * (360 / 60) - 90);
|
||||||
|
//g.setPixel(r[0],r[1]);
|
||||||
|
g.drawLine(r1[0], r1[1], r2[0], r2[1]);
|
||||||
|
g.setColor('#333333');
|
||||||
|
g.drawCircle(settings.circle.middle, settings.circle.center, rad - settings.circle.width - 4);
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawClock = function () {
|
const drawClock = function () {
|
||||||
|
|
@ -107,15 +107,13 @@
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset seconds
|
// Reset
|
||||||
if (seconds == 59) {
|
if (seconds == 59) {
|
||||||
g.setColor('#000000');
|
g.setColor('#000000');
|
||||||
g.fillCircle(settings.circle.middle, settings.circle.center, (settings.circle.height / 2) - 40);
|
g.fillCircle(settings.circle.middle, settings.circle.center, (settings.circle.height / 2));
|
||||||
}
|
for (count = 0; count <= minutes; count++) {
|
||||||
// Reset minutes
|
drawMinArc(count, settings.circle.colormin);
|
||||||
if (minutes == 59 && seconds == 59) {
|
}
|
||||||
g.setColor('#000000');
|
|
||||||
g.fillCircle(settings.circle.middle, settings.circle.center, (settings.circle.height / 2) - 20);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get date as a string
|
//Get date as a string
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
|
0.02: Change color from red->yellow to ease readability (fix #710)
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ var currentDist = 0;
|
||||||
function drawMap() {
|
function drawMap() {
|
||||||
g.clearRect(0,0,239,120);
|
g.clearRect(0,0,239,120);
|
||||||
g.setFontAlign(0,0);
|
g.setFontAlign(0,0);
|
||||||
g.setColor(1,0,0);
|
g.setColor(1,1,0);
|
||||||
g.setFontVector(40);
|
g.setFontVector(40);
|
||||||
g.drawString((currentDist===undefined)?"?":(Math.round(currentDist)+"m"), 160, 30);
|
g.drawString((currentDist===undefined)?"?":(Math.round(currentDist)+"m"), 160, 30);
|
||||||
g.setColor(1,1,1);
|
g.setColor(1,1,1);
|
||||||
|
|
@ -151,7 +151,7 @@ function drawMap() {
|
||||||
g.drawString(Math.round(totalDistance)+"m", 160, 70);
|
g.drawString(Math.round(totalDistance)+"m", 160, 70);
|
||||||
g.drawString((nextPtIdx/2)+"/"+coordDistance.length, 50, 20);
|
g.drawString((nextPtIdx/2)+"/"+coordDistance.length, 50, 20);
|
||||||
if (!fix.fix) {
|
if (!fix.fix) {
|
||||||
g.setColor(1,0,0);
|
g.setColor(1,1,0);
|
||||||
g.drawString("No GPS", 50, 50);
|
g.drawString("No GPS", 50, 50);
|
||||||
g.setFont("6x8",1);
|
g.setFont("6x8",1);
|
||||||
g.drawString(fix.satellites+" Sats", 50, 70);
|
g.drawString(fix.satellites+" Sats", 50, 70);
|
||||||
|
|
@ -161,17 +161,17 @@ function drawMap() {
|
||||||
g.setColor(0,0,0);
|
g.setColor(0,0,0);
|
||||||
g.drawCircle(lastFix.s.x,lastFix.s.y,10);
|
g.drawCircle(lastFix.s.x,lastFix.s.y,10);
|
||||||
}
|
}
|
||||||
for (var i=0;i<gcoords.length;i+=2) {
|
var c1 = g.toColor(1,1,0);
|
||||||
g.setColor((i<=nextPtIdx) ? 63488 : 46486); // red/grey
|
var c2 = g.toColor(0.7,0.7,0.7);
|
||||||
g.fillRect(gcoords[i]-2,gcoords[i+1]-2,gcoords[i]+2,gcoords[i+1]+2);
|
for (var i=0;i<gcoords.length;i+=2)
|
||||||
}
|
g.setColor((i<=nextPtIdx) ? c1 : c2).fillRect(gcoords[i]-2,gcoords[i+1]-2,gcoords[i]+2,gcoords[i+1]+2);
|
||||||
g.setColor(1,0,0); // first part of path
|
g.setColor(1,1,0); // first part of path
|
||||||
g.drawPoly(new Uint8Array(gcoords.buffer, 0, nextPtIdx+2));
|
g.drawPoly(new Uint8Array(gcoords.buffer, 0, nextPtIdx+2));
|
||||||
g.setColor(1,1,1); // remaining part of path
|
g.setColor(1,1,1); // remaining part of path
|
||||||
g.drawPoly(new Uint8Array(gcoords.buffer, nextPtIdx));
|
g.drawPoly(new Uint8Array(gcoords.buffer, nextPtIdx));
|
||||||
|
|
||||||
if (fix && fix.fix) {
|
if (fix && fix.fix) {
|
||||||
g.setColor(1,0,0);
|
g.setColor(1,1,0);
|
||||||
g.drawCircle(fix.s.x,fix.s.y,10);
|
g.drawCircle(fix.s.x,fix.s.y,10);
|
||||||
}
|
}
|
||||||
lastFix = fix;
|
lastFix = fix;
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,4 @@
|
||||||
Add whitelist option (fix #78)
|
Add whitelist option (fix #78)
|
||||||
0.22: Move HID to BLE menu
|
0.22: Move HID to BLE menu
|
||||||
0.23: Change max time offset to 13 for NZ summer daylight time (NZDT)
|
0.23: Change max time offset to 13 for NZ summer daylight time (NZDT)
|
||||||
|
0.24: Add Quiet Mode settings
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ This is Bangle.js's settings menu
|
||||||
* **Debug Info** should debug info be shown on the watch's screen or not?
|
* **Debug Info** should debug info be shown on the watch's screen or not?
|
||||||
* **Beep** most Bangle.js do not have a speaker inside, but they can use the vibration motor to beep in different pitches. You can change the behaviour here to use a Piezo speaker if one is connected
|
* **Beep** most Bangle.js do not have a speaker inside, but they can use the vibration motor to beep in different pitches. You can change the behaviour here to use a Piezo speaker if one is connected
|
||||||
* **Vibration** enable/disable the vibration motor
|
* **Vibration** enable/disable the vibration motor
|
||||||
|
* **Quiet Mode** prevent notifications/alarms from vibrating/beeping/turning the screen on - see below
|
||||||
* **Locale** set time zone/whether the clock is 12/24 hour (for supported clocks)
|
* **Locale** set time zone/whether the clock is 12/24 hour (for supported clocks)
|
||||||
* **Select Clock** if you have more than one clock face, select the default one
|
* **Select Clock** if you have more than one clock face, select the default one
|
||||||
* **HID** When Bluetooth is enabled, Bangle.js can appear as a Bluetooth Keyboard/Joystick/etc to send keypresses to a connected device.
|
* **HID** When Bluetooth is enabled, Bangle.js can appear as a Bluetooth Keyboard/Joystick/etc to send keypresses to a connected device.
|
||||||
|
|
@ -31,3 +32,17 @@ This is Bangle.js's settings menu
|
||||||
* **LCD Timeout** how long should the LCD stay on for if no activity is detected. 0=stay on forever
|
* **LCD Timeout** how long should the LCD stay on for if no activity is detected. 0=stay on forever
|
||||||
* **Wake on X** should the given activity wake up the Bangle.js LCD?
|
* **Wake on X** should the given activity wake up the Bangle.js LCD?
|
||||||
* **Twist X** these options adjust the sensitivity of `Wake on Twist` to ensure Bangle.js wakes up with just the right amount of wrist movement.
|
* **Twist X** these options adjust the sensitivity of `Wake on Twist` to ensure Bangle.js wakes up with just the right amount of wrist movement.
|
||||||
|
|
||||||
|
|
||||||
|
## Quiet Mode
|
||||||
|
|
||||||
|
Quiet Mode is a hint to apps and widgets that you do not want to be disturbed.
|
||||||
|
The exact effects depend on the app. In general the watch will not wake up by itself, but will still respond to button presses.
|
||||||
|
|
||||||
|
* **Quiet Mode**
|
||||||
|
- Off: Normal operation
|
||||||
|
- Alarms: Stops notifications, but "alarm" apps will still work
|
||||||
|
- Silent: Blocks even alarms
|
||||||
|
* **LCD Brightness**, **LCD Timeout**, **Wake on X**:
|
||||||
|
Override default settings while Quit Mode is active (either as *Alarms* or *Silent*)
|
||||||
|
|
||||||
|
|
@ -2,7 +2,13 @@
|
||||||
var settings = require('Storage').readJSON('setting.json', true);
|
var settings = require('Storage').readJSON('setting.json', true);
|
||||||
if (!settings) return;
|
if (!settings) return;
|
||||||
if (settings.options) Bangle.setOptions(settings.options);
|
if (settings.options) Bangle.setOptions(settings.options);
|
||||||
if (settings.brightness && settings.brightness!=1) Bangle.setLCDBrightness(settings.brightness);
|
if (settings.quiet && settings.qmOptions) Bangle.setOptions(settings.qmOptions);
|
||||||
|
if (settings.quiet && settings.qmBrightness) {
|
||||||
|
if (settings.qmBrightness!=1) Bangle.setLCDBrightness(settings.qmBrightness);
|
||||||
|
} else {
|
||||||
|
if (settings.brightness && settings.brightness!=1) Bangle.setLCDBrightness(settings.brightness);
|
||||||
|
}
|
||||||
|
if (settings.quiet && settings.qmTimeout) Bangle.setLCDTimeout(s.qmTimeout);
|
||||||
if (settings.passkey!==undefined && settings.passkey.length==6) NRF.setSecurity({passkey:settings.passkey, mitm:1, display:1});
|
if (settings.passkey!==undefined && settings.passkey.length==6) NRF.setSecurity({passkey:settings.passkey, mitm:1, display:1});
|
||||||
if (settings.whitelist) NRF.on('connect', function(addr) { if (!settings.whitelist.includes(addr)) NRF.disconnect(); });
|
if (settings.whitelist) NRF.on('connect', function(addr) { if (!settings.whitelist.includes(addr)) NRF.disconnect(); });
|
||||||
delete settings;
|
delete settings;
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,17 @@ let settings;
|
||||||
|
|
||||||
function updateSettings() {
|
function updateSettings() {
|
||||||
//storage.erase('setting.json'); // - not needed, just causes extra writes if settings were the same
|
//storage.erase('setting.json'); // - not needed, just causes extra writes if settings were the same
|
||||||
|
if (Object.keys(settings.qmOptions).length === 0) delete settings.qmOptions;
|
||||||
storage.write('setting.json', settings);
|
storage.write('setting.json', settings);
|
||||||
|
if (!('qmOptions' in settings)) settings.qmOptions = {}; // easier if this always exists in this file
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateOptions() {
|
function updateOptions() {
|
||||||
updateSettings();
|
updateSettings();
|
||||||
Bangle.setOptions(settings.options)
|
Bangle.setOptions(settings.options)
|
||||||
|
if (settings.quiet) {
|
||||||
|
Bangle.setOptions(settings.qmOptions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function gToInternal(g) {
|
function gToInternal(g) {
|
||||||
|
|
@ -29,6 +34,7 @@ function resetSettings() {
|
||||||
ble: true, // Bluetooth enabled by default
|
ble: true, // Bluetooth enabled by default
|
||||||
blerepl: true, // Is REPL on Bluetooth - can Espruino IDE be used?
|
blerepl: true, // Is REPL on Bluetooth - can Espruino IDE be used?
|
||||||
log: false, // Do log messages appear on screen?
|
log: false, // Do log messages appear on screen?
|
||||||
|
quiet: 0, // quiet mode: 0: off, 1: priority only, 2: total silence
|
||||||
timeout: 10, // Default LCD timeout in seconds
|
timeout: 10, // Default LCD timeout in seconds
|
||||||
vibrate: true, // Vibration enabled by default. App must support
|
vibrate: true, // Vibration enabled by default. App must support
|
||||||
beep: "vib", // Beep enabled by default. App must support
|
beep: "vib", // Beep enabled by default. App must support
|
||||||
|
|
@ -48,13 +54,19 @@ function resetSettings() {
|
||||||
twistThreshold: 819.2,
|
twistThreshold: 819.2,
|
||||||
twistMaxY: -800,
|
twistMaxY: -800,
|
||||||
twistTimeout: 1000
|
twistTimeout: 1000
|
||||||
}
|
},
|
||||||
|
// Quiet Mode options:
|
||||||
|
// we only set these if we want to override the default value
|
||||||
|
// qmOptions: {},
|
||||||
|
// qmBrightness: undefined,
|
||||||
|
// qmTimeout: undefined,
|
||||||
};
|
};
|
||||||
updateSettings();
|
updateSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
settings = storage.readJSON('setting.json', 1);
|
settings = storage.readJSON('setting.json', 1);
|
||||||
if (!settings) resetSettings();
|
if (!settings) resetSettings();
|
||||||
|
if (!('qmOptions' in settings)) settings.qmOptions = {}; // easier if this always exists in here
|
||||||
|
|
||||||
const boolFormat = v => v ? "On" : "Off";
|
const boolFormat = v => v ? "On" : "Off";
|
||||||
|
|
||||||
|
|
@ -97,6 +109,7 @@ function showMainMenu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Quiet Mode": ()=>showQuietModeMenu(),
|
||||||
'Locale': ()=>showLocaleMenu(),
|
'Locale': ()=>showLocaleMenu(),
|
||||||
'Select Clock': ()=>showClockMenu(),
|
'Select Clock': ()=>showClockMenu(),
|
||||||
'Set Time': ()=>showSetTimeMenu(),
|
'Set Time': ()=>showSetTimeMenu(),
|
||||||
|
|
@ -224,7 +237,9 @@ function showLCDMenu() {
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.brightness = v || 1;
|
settings.brightness = v || 1;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
Bangle.setLCDBrightness(settings.brightness);
|
if (!(settings.quiet && "qmBrightness" in settings)) {
|
||||||
|
Bangle.setLCDBrightness(settings.brightness);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'LCD Timeout': {
|
'LCD Timeout': {
|
||||||
|
|
@ -235,7 +250,9 @@ function showLCDMenu() {
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.timeout = 0 | v;
|
settings.timeout = 0 | v;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
Bangle.setLCDTimeout(settings.timeout);
|
if (!(settings.quiet && "qmTimeout" in settings)) {
|
||||||
|
Bangle.setLCDTimeout(settings.timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Wake on BTN1': {
|
'Wake on BTN1': {
|
||||||
|
|
@ -319,6 +336,104 @@ function showLCDMenu() {
|
||||||
}
|
}
|
||||||
return E.showMenu(lcdMenu)
|
return E.showMenu(lcdMenu)
|
||||||
}
|
}
|
||||||
|
function showQuietModeMenu() {
|
||||||
|
// we always keep settings.quiet and settings.qmOptions
|
||||||
|
// other qm values are deleted when not set
|
||||||
|
const modes = ["Off", "Alarms", "Silent"];
|
||||||
|
const qmDisabledFormat = v => v ? "Off" : "-";
|
||||||
|
const qmMenu = {
|
||||||
|
"": {"title": "Quiet Mode"},
|
||||||
|
"< Back": () => showMainMenu(),
|
||||||
|
"Quiet Mode": {
|
||||||
|
value: settings.quiet|0,
|
||||||
|
format: v => modes[v%3],
|
||||||
|
onchange: v => {
|
||||||
|
settings.quiet = v%3;
|
||||||
|
updateSettings();
|
||||||
|
updateOptions();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"LCD Brightness": {
|
||||||
|
value: settings.qmBrightness || 0,
|
||||||
|
min: 0, // 0 = use default
|
||||||
|
max: 1,
|
||||||
|
step: 0.1,
|
||||||
|
format: v => (v>0.05) ? v : "-",
|
||||||
|
onchange: v => {
|
||||||
|
if (v>0.05) { // prevent v=0.000000000000001 bugs
|
||||||
|
settings.qmBrightness = v;
|
||||||
|
} else {
|
||||||
|
delete settings.qmBrightness;
|
||||||
|
}
|
||||||
|
updateSettings();
|
||||||
|
if (settings.qmBrightness) { // show result, even if not quiet right now
|
||||||
|
Bangle.setLCDBrightness(v);
|
||||||
|
} else {
|
||||||
|
Bangle.setLCDBrightness(settings.brightness);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"LCD Timeout": {
|
||||||
|
value: settings.qmTimeout || 0,
|
||||||
|
min: 0, // 0 = use default (no constant on for quiet mode)
|
||||||
|
max: 60,
|
||||||
|
step: 5,
|
||||||
|
format: v => v>1 ? v : "-",
|
||||||
|
onchange: v => {
|
||||||
|
if (v>1) {
|
||||||
|
settings.qmTimeout = v;
|
||||||
|
} else {
|
||||||
|
delete settings.qmTimeout;
|
||||||
|
}
|
||||||
|
updateSettings();
|
||||||
|
if (settings.quiet && v>1) {
|
||||||
|
Bangle.setLCDTimeout(v);
|
||||||
|
} else {
|
||||||
|
Bangle.setLCDTimeout(settings.timeout);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// we disable wakeOn* events by overwriting them as false in qmOptions
|
||||||
|
// not disabled = not present in qmOptions at all
|
||||||
|
"Wake on FaceUp": {
|
||||||
|
value: "wakeOnFaceUp" in settings.qmOptions,
|
||||||
|
format: qmDisabledFormat,
|
||||||
|
onchange: () => {
|
||||||
|
if ("wakeOnFaceUp" in settings.qmOptions) {
|
||||||
|
delete settings.qmOptions.wakeOnFaceUp;
|
||||||
|
} else {
|
||||||
|
settings.qmOptions.wakeOnFaceUp = false;
|
||||||
|
}
|
||||||
|
updateOptions();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Wake on Touch": {
|
||||||
|
value: "wakeOnTouch" in settings.qmOptions,
|
||||||
|
format: qmDisabledFormat,
|
||||||
|
onchange: () => {
|
||||||
|
if ("wakeOnTouch" in settings.qmOptions) {
|
||||||
|
delete settings.qmOptions.wakeOnTouch;
|
||||||
|
} else {
|
||||||
|
settings.qmOptions.wakeOnTouch = false;
|
||||||
|
}
|
||||||
|
updateOptions();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Wake on Twist": {
|
||||||
|
value: "wakeOnTwist" in settings.qmOptions,
|
||||||
|
format: qmDisabledFormat,
|
||||||
|
onchange: () => {
|
||||||
|
if ("wakeOnTwist" in settings.qmOptions) {
|
||||||
|
delete settings.qmOptions.wakeOnTwist;
|
||||||
|
} else {
|
||||||
|
settings.qmOptions.wakeOnTwist = false;
|
||||||
|
}
|
||||||
|
updateOptions();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return E.showMenu(qmMenu);
|
||||||
|
}
|
||||||
|
|
||||||
function showLocaleMenu() {
|
function showLocaleMenu() {
|
||||||
const localemenu = {
|
const localemenu = {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
{"ble":true,"blerepl":true,"log":false,"timeout":10,"vibrate":true,"beep":"vib","timezone":0,"HID":false,"clock":null,"12hour":false,"brightness":1,"options":{"wakeOnBTN1":true,"wakeOnBTN2":true,"wakeOnBTN3":true,"wakeOnFaceUp":false,"wakeOnTouch":false,"wakeOnTwist":true,"twistThreshold":819.2,"twistMaxY":-800,"twistTimeout":1000}}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
|
0.02: Respect Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ function drawApp() {
|
||||||
|
|
||||||
var buzzCount = 19;
|
var buzzCount = 19;
|
||||||
function buzz() {
|
function buzz() {
|
||||||
|
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence
|
||||||
Bangle.setLCDPower(1);
|
Bangle.setLCDPower(1);
|
||||||
Bangle.buzz().then(()=>{
|
Bangle.buzz().then(()=>{
|
||||||
if (buzzCount--) {
|
if (buzzCount--) {
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
0.01: Initial Release
|
0.01: Initial Release
|
||||||
|
0.02: Color Themes, Smoother scrolling
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,34 @@
|
||||||
# Sliding Text Clock - See the time in different languages
|
# Sliding Text Clock - See the time in different languages
|
||||||
|
|
||||||
Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently only English, French and Japanese are supported
|
Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Please use the upload page to choose which languages you want loaded.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### Button 1
|
||||||
|
|
||||||
Use Button 1 (the top right button) to change the language
|
Use Button 1 (the top right button) to change the language
|
||||||
|
|
||||||
|
| English | English (Traditional) | French | Japanese (Romanji) |
|
||||||
|
| ---- | ---- | ---- | ---- |
|
||||||
|
|  |  |  | |
|
||||||
|
|
||||||
|
### Button 3
|
||||||
|
Button 3 (bottom right button) is used to change the colour
|
||||||
|
|
||||||
|
| Black | Red | Gray | Purple |
|
||||||
|
| ---- | ---- | ---- | ---- |
|
||||||
|
|  |  |  |  |
|
||||||
|
|
||||||
|
## Further Details
|
||||||
|
|
||||||
|
For further details of design and working please visit [The Project Page](https://www.notion.so/adrianwkirk/Sliding-Text-Clock-a8fe556f03624a619656ddbc4f36f41b)
|
||||||
|
|
||||||
## Requests
|
## Requests
|
||||||
|
|
||||||
[Reach out to Adrian](https://www.github.com/awkirk71) if you have feature requests or notice bugs.
|
Reach out to adrian@adriankirk.com if you have feature requests or notice bugs.
|
||||||
|
|
||||||
## Creator
|
## Creator
|
||||||
|
|
||||||
Made by [Adrian Kirk](https://www.github.com/awkirk71).
|
Made by [Adrian Kirk](mailto:adrian@adriankirk.com)
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
|
@ -0,0 +1,69 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<p>Please select watch languages</p>
|
||||||
|
|
||||||
|
<table id="language_selection">
|
||||||
|
<tr>
|
||||||
|
<th>Enabled</th>
|
||||||
|
<th>Name</th>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||||
|
|
||||||
|
<script src="../../core/lib/customize.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var slidingtext_languages=[
|
||||||
|
{name:"English", shortname:"en"},
|
||||||
|
{name:"English(Traditional)",shortname:"en2"},
|
||||||
|
{name:"French",shortname:"fr"},
|
||||||
|
{name:"Japanese",shortname:"jp"}
|
||||||
|
];
|
||||||
|
var selected_languages = ["en","fr","jp"];
|
||||||
|
try{
|
||||||
|
var stored = localStorage.getItem('slidingtext_stored')
|
||||||
|
if(stored) selected_languages = JSON.parse(stored);
|
||||||
|
} catch(e){
|
||||||
|
console.log("failed to load languages:" + e);
|
||||||
|
}
|
||||||
|
console.log("selected languages:" + selected_languages);
|
||||||
|
var tbl=document.getElementById("language_selection");
|
||||||
|
for (var i=0; i<slidingtext_languages.length; i++) {
|
||||||
|
var curr_language = slidingtext_languages[i];
|
||||||
|
var language_selected = selected_languages.includes(curr_language["shortname"])
|
||||||
|
var $offset = document.createElement('tr')
|
||||||
|
$offset.innerHTML = `
|
||||||
|
<td><input type="checkbox" id="enabled_${i}" ${language_selected? "checked" : ""}></td>
|
||||||
|
<td>${curr_language['name']}</td>`
|
||||||
|
tbl.append($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the 'upload' button is clicked...
|
||||||
|
document.getElementById("upload").addEventListener("click", function() {
|
||||||
|
var new_selected_languages=[];
|
||||||
|
for (var i=0; i<slidingtext_languages.length; i++) {
|
||||||
|
var curr_language = slidingtext_languages[i];
|
||||||
|
var checked=document.getElementById("enabled_"+i).checked;
|
||||||
|
if (checked) {
|
||||||
|
new_selected_languages.push(curr_language.shortname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("new selected languages:" + new_selected_languages);
|
||||||
|
localStorage.setItem('slidingtext_stored',JSON.stringify(new_selected_languages));
|
||||||
|
// send finished app (in addition to contents of app.json)
|
||||||
|
sendCustomizedApp({
|
||||||
|
storage:[
|
||||||
|
{name:"slidingtext.languages.json", content:JSON.stringify(new_selected_languages)},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
|
@ -0,0 +1,15 @@
|
||||||
|
class DateFormatter {
|
||||||
|
/**
|
||||||
|
* A pure virtual class which all the other date formatters will
|
||||||
|
* inherit from.
|
||||||
|
* The name will be used to declare the date format when selected
|
||||||
|
* and the date formatDate methid will return the time formated
|
||||||
|
* to the lines of text on the screen
|
||||||
|
*/
|
||||||
|
name(){return "no name";}
|
||||||
|
formatDate(date){
|
||||||
|
return ["no","date","defined"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DateFormatter;
|
||||||
|
|
@ -1,21 +1,102 @@
|
||||||
/**
|
/**
|
||||||
* Adrian Kirk 2021-02
|
* Adrian Kirk 2021-02
|
||||||
* Sliding text clock inspired by the Pebble
|
* Sliding text clock inspired by the Pebble
|
||||||
* clock with the same name
|
* clock with the same name
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const color_schemes = [
|
||||||
|
{
|
||||||
|
name: "black",
|
||||||
|
background : [0.0,0.0,0.0],
|
||||||
|
main_bar: [1.0,1.0,1.0],
|
||||||
|
other_bars: [0.85,0.85,0.85],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "red",
|
||||||
|
background : [1.0,0.0,0.0],
|
||||||
|
main_bar: [1.0,1.0,0.0],
|
||||||
|
other_bars: [0.85,0.85,0.85]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grey",
|
||||||
|
background : [0.5,0.5,0.5],
|
||||||
|
main_bar: [1.0,1.0,1.0],
|
||||||
|
other_bars: [0.0,0.0,0.0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "purple",
|
||||||
|
background : [1.0,0.0,1.0],
|
||||||
|
main_bar: [1.0,1.0,0.0],
|
||||||
|
other_bars: [0.85,0.85,0.85]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "blue",
|
||||||
|
background : [0.4,0.7,1.0],
|
||||||
|
main_bar: [1.0,1.0,1.0],
|
||||||
|
other_bars: [0.9,0.9,0.9]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let color_scheme_index = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Watch Display
|
||||||
|
*/
|
||||||
|
|
||||||
|
function bg_color(){
|
||||||
|
return color_schemes[color_scheme_index].background;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main_color(){
|
||||||
|
return color_schemes[color_scheme_index].main_bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
function other_color(){
|
||||||
|
return color_schemes[color_scheme_index].other_bars;
|
||||||
|
}
|
||||||
|
|
||||||
|
let command_stack_high_priority = [];
|
||||||
|
let command_stack_low_priority = [];
|
||||||
|
|
||||||
|
function next_command(){
|
||||||
|
command = command_stack_high_priority.pop();
|
||||||
|
if(command == null){
|
||||||
|
//console.log("Low priority command");
|
||||||
|
command = command_stack_low_priority.pop();
|
||||||
|
} else {
|
||||||
|
//console.log("High priority command");
|
||||||
|
}
|
||||||
|
if(command != null){
|
||||||
|
command.call();
|
||||||
|
} else {
|
||||||
|
//console.log("no command");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset_commands(){
|
||||||
|
command_stack_high_priority = [];
|
||||||
|
command_stack_low_priority = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function has_commands(){
|
||||||
|
return command_stack_high_priority.length > 0 ||
|
||||||
|
command_stack_low_priority.lenth > 0;
|
||||||
|
}
|
||||||
|
|
||||||
class ShiftText {
|
class ShiftText {
|
||||||
/**
|
/**
|
||||||
* Class Responsible for shifting text around the screen
|
* Class Responsible for shifting text around the screen
|
||||||
*
|
*
|
||||||
* This is a object that initializes itself with a position and
|
* This is a object that initializes itself with a position and
|
||||||
* text after which you can tell it where you want to move to
|
* text after which you can tell it where you want to move to
|
||||||
* using the moveTo method and it will smoothly move the text across
|
* using the moveTo method and it will smoothly move the text across
|
||||||
* at the selected frame rate and speed
|
* at the selected frame rate and speed
|
||||||
*/
|
*/
|
||||||
constructor(x,y,txt,font_name,
|
constructor(x,y,txt,font_name,
|
||||||
font_size,speed_x,speed_y,freq_millis, color){
|
font_size,speed_x,speed_y,freq_millis,
|
||||||
|
color,
|
||||||
|
bg_color){
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.tgt_x = x;
|
this.tgt_x = x;
|
||||||
this.init_x = x;
|
this.init_x = x;
|
||||||
|
|
@ -29,29 +110,44 @@ class ShiftText {
|
||||||
this.speed_x = Math.abs(speed_x);
|
this.speed_x = Math.abs(speed_x);
|
||||||
this.speed_y = Math.abs(speed_y);
|
this.speed_y = Math.abs(speed_y);
|
||||||
this.freq_millis = freq_millis;
|
this.freq_millis = freq_millis;
|
||||||
this.colour = color;
|
this.color = color;
|
||||||
|
this.bg_color = bg_color;
|
||||||
this.finished_callback=null;
|
this.finished_callback=null;
|
||||||
this.timeoutId = null;
|
this.timeoutId = null;
|
||||||
}
|
}
|
||||||
|
setColor(color){
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
setBgColor(bg_color){
|
||||||
|
this.bg_color = bg_color;
|
||||||
|
}
|
||||||
reset(){
|
reset(){
|
||||||
|
//console.log("reset");
|
||||||
this.hide();
|
this.hide();
|
||||||
this.x = this.init_x;
|
this.x = this.init_x;
|
||||||
this.y = this.init_y;
|
this.y = this.init_y;
|
||||||
this.txt = this.init_txt;
|
this.txt = this.init_txt;
|
||||||
this.show();
|
this.show();
|
||||||
if(this.timeoutId != null){
|
if(this.timeoutId != null){
|
||||||
clearTimeout(this.timeoutId);
|
clearTimeout(this.timeoutId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show() {
|
show() {
|
||||||
g.setFont(this.font_name,this.font_size);
|
g.setFont(this.font_name,this.font_size);
|
||||||
g.setColor(this.colour[0],this.colour[1],this.colour[2]);
|
g.setColor(this.color[0],this.color[1],this.color[2]);
|
||||||
g.drawString(this.txt, this.x, this.y);
|
g.drawString(this.txt, this.x, this.y);
|
||||||
}
|
}
|
||||||
hide(){
|
hide(){
|
||||||
g.setFont(this.font_name,this.font_size);
|
g.setFont(this.font_name,this.font_size);
|
||||||
g.setColor(0,0,0);
|
//console.log("bgcolor:" + this.bg_color);
|
||||||
|
g.setColor(this.bg_color[0],this.bg_color[1],this.bg_color[2]);
|
||||||
g.drawString(this.txt, this.x, this.y);
|
g.drawString(this.txt, this.x, this.y);
|
||||||
|
/*g.fillPoly([this.x - 1, this.y,
|
||||||
|
240, this.y,
|
||||||
|
240, this.y + this.font_size,
|
||||||
|
this.x -1 , this.y + this.font_size,
|
||||||
|
]);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
setText(txt){
|
setText(txt){
|
||||||
this.txt = txt;
|
this.txt = txt;
|
||||||
|
|
@ -92,15 +188,15 @@ class ShiftText {
|
||||||
this.finished_callback = finished_callback;
|
this.finished_callback = finished_callback;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* private internal method for directing the text move.
|
* private internal method for directing the text move.
|
||||||
* It will see how far away we are from the target coords
|
* It will see how far away we are from the target coords
|
||||||
* and move towards the target at the defined speed.
|
* and move towards the target at the defined speed.
|
||||||
*/
|
*/
|
||||||
_doMove(){
|
_doMove(){
|
||||||
this.hide();
|
this.hide();
|
||||||
// move closer to the target in the x direction
|
// move closer to the target in the x direction
|
||||||
diff_x = this.tgt_x - this.x;
|
var diff_x = this.tgt_x - this.x;
|
||||||
finished_x = false;
|
var finished_x = false;
|
||||||
if(Math.abs(diff_x) <= this.speed_x){
|
if(Math.abs(diff_x) <= this.speed_x){
|
||||||
this.x = this.tgt_x;
|
this.x = this.tgt_x;
|
||||||
finished_x = true;
|
finished_x = true;
|
||||||
|
|
@ -112,8 +208,8 @@ class ShiftText {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// move closer to the target in the y direction
|
// move closer to the target in the y direction
|
||||||
diff_y = this.tgt_y - this.y;
|
var diff_y = this.tgt_y - this.y;
|
||||||
finished_y = false;
|
var finished_y = false;
|
||||||
if(Math.abs(diff_y) <= this.speed_y){
|
if(Math.abs(diff_y) <= this.speed_y){
|
||||||
this.y = this.tgt_y;
|
this.y = this.tgt_y;
|
||||||
finished_y = true;
|
finished_y = true;
|
||||||
|
|
@ -126,235 +222,90 @@ class ShiftText {
|
||||||
}
|
}
|
||||||
this.show();
|
this.show();
|
||||||
this.timeoutId = null;
|
this.timeoutId = null;
|
||||||
finished = finished_x & finished_y;
|
var finished = finished_x & finished_y;
|
||||||
if(!finished){
|
if(!finished){
|
||||||
this.timeoutId = setTimeout(this._doMove.bind(this), this.freq_millis);
|
this.timeoutId = setTimeout(this._doMove.bind(this), this.freq_millis);
|
||||||
} else if(this.finished_callback != null){
|
} else if(this.finished_callback != null){
|
||||||
|
//console.log("finished - calling:" + this.finished_callback);
|
||||||
this.finished_callback.call();
|
this.finished_callback.call();
|
||||||
this.finished_callback = null;
|
this.finished_callback = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DateFormatter {
|
const CLOCK_TEXT_SPEED_X = 10;
|
||||||
/**
|
// a list of display rows
|
||||||
* A pure virtual class which all the other date formatters will
|
let row_displays = [
|
||||||
* inherit from.
|
new ShiftText(240,50,'',"Vector",40,CLOCK_TEXT_SPEED_X,1,10,main_color(),bg_color()),
|
||||||
* The name will be used to declare the date format when selected
|
new ShiftText(240,90,'',"Vector",30,CLOCK_TEXT_SPEED_X,1,10,other_color(),bg_color()),
|
||||||
* and the date formatDate methid will return the time formated
|
new ShiftText(240,120,'',"Vector",30,CLOCK_TEXT_SPEED_X,1,10,other_color(),bg_color()),
|
||||||
* to the lines of text on the screen
|
new ShiftText(240,150,'',"Vector",30,CLOCK_TEXT_SPEED_X,1,10,other_color(),bg_color()),
|
||||||
*/
|
new ShiftText(240,180,'',"Vector",40,CLOCK_TEXT_SPEED_X,1,10,main_color(),bg_color())
|
||||||
name(){"no name";}
|
|
||||||
formatDate(date){
|
|
||||||
return ["","",""];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* English date formatting
|
|
||||||
*/
|
|
||||||
|
|
||||||
// English String Numbers
|
|
||||||
const numberStr = ["ZERO","ONE", "TWO", "THREE", "FOUR", "FIVE",
|
|
||||||
"SIX", "SEVEN","EIGHT", "NINE", "TEN",
|
|
||||||
"ELEVEN", "TWELVE", "THIRTEEN", "FOURTEEN",
|
|
||||||
"FIFTEEN", "SIXTEEN", "SEVENTEEN", "EIGHTEEN",
|
|
||||||
"NINETEEN", "TWENTY"];
|
|
||||||
const tensStr = ["ZERO", "TEN", "TWENTY", "THIRTY", "FOURTY",
|
|
||||||
"FIFTY"];
|
|
||||||
|
|
||||||
function hoursToText(hours){
|
|
||||||
hours = hours % 12;
|
|
||||||
if(hours == 0){
|
|
||||||
hours = 12;
|
|
||||||
}
|
|
||||||
return numberStr[hours];
|
|
||||||
}
|
|
||||||
|
|
||||||
function numberToText(value){
|
|
||||||
word1 = '';
|
|
||||||
word2 = '';
|
|
||||||
if(value > 20){
|
|
||||||
tens = (value / 10 | 0);
|
|
||||||
word1 = tensStr[tens];
|
|
||||||
remainder = value - tens * 10;
|
|
||||||
if(remainder > 0){
|
|
||||||
word2 = numberStr[remainder];
|
|
||||||
}
|
|
||||||
} else if(value > 0) {
|
|
||||||
word1 = numberStr[value];
|
|
||||||
}
|
|
||||||
return [word1,word2];
|
|
||||||
}
|
|
||||||
|
|
||||||
class EnglishDateFormatter extends DateFormatter{
|
|
||||||
name(){return "English";}
|
|
||||||
formatDate(date){
|
|
||||||
hours_txt = hoursToText(date.getHours());
|
|
||||||
mins_txt = numberToText(date.getMinutes());
|
|
||||||
return [hours_txt,mins_txt[0],mins_txt[1]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* French date formatting
|
|
||||||
*/
|
|
||||||
const frenchNumberStr = [ "ZERO", "UNE", "DEUX", "TROIS", "QUATRE",
|
|
||||||
"CINQ", "SIX", "SEPT", "HUIT", "NEUF", "DIX",
|
|
||||||
"ONZE", "DOUZE", "TREIZE", "QUATORZE","QUINZE",
|
|
||||||
"SEIZE", "DIX SEPT", "DIX HUIT","DIX NEUF", "VINGT",
|
|
||||||
"VINGT ET UN", "VINGT DEUX", "VINGT TROIS",
|
|
||||||
"VINGT QUATRE", "VINGT CINQ", "VINGT SIX",
|
|
||||||
"VINGT SEPT", "VINGT HUIT", "VINGT NEUF"
|
|
||||||
];
|
|
||||||
|
|
||||||
function frenchHoursToText(hours){
|
|
||||||
hours = hours % 12;
|
|
||||||
if(hours == 0){
|
|
||||||
hours = 12;
|
|
||||||
}
|
|
||||||
return frenchNumberStr[hours];
|
|
||||||
}
|
|
||||||
|
|
||||||
function frenchHeures(hours){
|
|
||||||
if(hours % 12 == 1){
|
|
||||||
return 'HEURE';
|
|
||||||
} else {
|
|
||||||
return 'HEURES';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FrenchDateFormatter extends DateFormatter {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
name(){return "French";}
|
|
||||||
formatDate(date){
|
|
||||||
hours = frenchHoursToText(date.getHours());
|
|
||||||
heures = frenchHeures(date.getHours());
|
|
||||||
mins = date.getMinutes();
|
|
||||||
if(mins == 0){
|
|
||||||
if(hours == 0){
|
|
||||||
return ["MINUIT", "",""];
|
|
||||||
} else if(hours == 12){
|
|
||||||
return ["MIDI", "",""];
|
|
||||||
} else {
|
|
||||||
return [hours, heures,""];
|
|
||||||
}
|
|
||||||
} else if(mins == 30){
|
|
||||||
return [hours, heures,'ET DEMIE'];
|
|
||||||
} else if(mins == 15){
|
|
||||||
return [hours, heures,'ET QUERT'];
|
|
||||||
} else if(mins == 45){
|
|
||||||
next_hour = date.getHours() + 1;
|
|
||||||
hours = frenchHoursToText(next_hour);
|
|
||||||
heures = frenchHeures(next_hour);
|
|
||||||
return [hours, heures,"MOINS",'LET QUERT'];
|
|
||||||
}
|
|
||||||
if(mins > 30){
|
|
||||||
to_mins = 60-mins;
|
|
||||||
mins_txt = frenchNumberStr[to_mins];
|
|
||||||
next_hour = date.getHours() + 1;
|
|
||||||
hours = frenchHoursToText(next_hour);
|
|
||||||
heures = frenchHeures(next_hour);
|
|
||||||
return [ hours, heures , "MOINS", mins_txt ];
|
|
||||||
} else {
|
|
||||||
mins_txt = frenchNumberStr[mins];
|
|
||||||
return [ hours, heures , mins_txt ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Japanese date formatting
|
|
||||||
*/
|
|
||||||
const japaneseHourStr = [ "ZERO", "ICHII", "NI", "SAN", "YO",
|
|
||||||
"GO", "ROKU", "SHICHI", "HACHI", "KU", "JUU",
|
|
||||||
'JUU ICHI', 'JUU NI'];
|
|
||||||
const tensPrefixStr = [ "",
|
|
||||||
"JUU",
|
|
||||||
'NIJUU',
|
|
||||||
'SAN JUU',
|
|
||||||
'YON JUU',
|
|
||||||
'GO JUU'];
|
|
||||||
|
|
||||||
const japaneseMinuteStr = [ ["", "PUN"],
|
|
||||||
["IP","PUN" ],
|
|
||||||
["NI", "FUN"],
|
|
||||||
["SAN", "PUN"],
|
|
||||||
["YON","FUN"],
|
|
||||||
["GO", "HUN"],
|
|
||||||
["RO", "PUN"],
|
|
||||||
["NANA", "FUN"],
|
|
||||||
["HAP", "PUN"],
|
|
||||||
["KYU","FUN"],
|
|
||||||
["JUP", "PUN"]
|
|
||||||
];
|
|
||||||
|
|
||||||
function japaneseHoursToText(hours){
|
|
||||||
hours = hours % 12;
|
|
||||||
if(hours == 0){
|
|
||||||
hours = 12;
|
|
||||||
}
|
|
||||||
return japaneseHourStr[hours];
|
|
||||||
}
|
|
||||||
|
|
||||||
function japaneseMinsToText(mins){
|
|
||||||
if(mins == 0){
|
|
||||||
return ["",""];
|
|
||||||
} else if(mins == 30)
|
|
||||||
return ["HAN",""];
|
|
||||||
else {
|
|
||||||
units = mins % 10;
|
|
||||||
mins_txt = japaneseMinuteStr[units];
|
|
||||||
tens = mins /10 | 0;
|
|
||||||
if(tens > 0){
|
|
||||||
tens_txt = tensPrefixStr[tens];
|
|
||||||
return [tens_txt + ' ' + mins_txt[0], mins_txt[1]];
|
|
||||||
} else {
|
|
||||||
return [mins_txt[0], mins_txt[1]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JapaneseDateFormatter extends DateFormatter {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
name(){return "Japanese (Romanji)";}
|
|
||||||
formatDate(date){
|
|
||||||
hours_txt = japaneseHoursToText(date.getHours());
|
|
||||||
mins_txt = japaneseMinsToText(date.getMinutes());
|
|
||||||
return [hours_txt,"JI", mins_txt[0], mins_txt[1] ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Watch Display
|
|
||||||
*/
|
|
||||||
|
|
||||||
// a list of display rows
|
|
||||||
let row_displays = [
|
|
||||||
new ShiftText(240,60,'',"Vector",40,10,10,40,[1,1,1]),
|
|
||||||
new ShiftText(240,100,'',"Vector",20,10,10,50,[0.85,0.85,0.85]),
|
|
||||||
new ShiftText(240,120,'',"Vector",20,10,10,60,[0.85,0.85,0.85]),
|
|
||||||
new ShiftText(240,140,'',"Vector",20,10,10,70,[0.85,0.85,0.85])
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// a list of the formatters to cycle through
|
function nextColorTheme(){
|
||||||
let date_formatters = [
|
//console.log("next color theme");
|
||||||
new EnglishDateFormatter(),
|
color_scheme_index += 1;
|
||||||
new FrenchDateFormatter(),
|
if(color_scheme_index >= row_displays.length){
|
||||||
new JapaneseDateFormatter()
|
color_scheme_index = 0;
|
||||||
];
|
}
|
||||||
|
var color_scheme = color_schemes[color_scheme_index];
|
||||||
|
setColor(color_scheme.main_bar,
|
||||||
|
color_scheme.other_bars,
|
||||||
|
color_scheme.background);
|
||||||
|
reset_clock();
|
||||||
|
draw_clock();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setColor(main_color,other_color,bg_color){
|
||||||
|
row_displays[0].setColor(main_color);
|
||||||
|
row_displays[0].setBgColor(bg_color);
|
||||||
|
for(var i=1; i<row_displays.length - 1; i++){
|
||||||
|
row_displays[i].setColor(other_color);
|
||||||
|
row_displays[i].setBgColor(bg_color);
|
||||||
|
}
|
||||||
|
row_displays[row_displays.length - 1].setColor(main_color);
|
||||||
|
row_displays[row_displays.length - 1].setBgColor(bg_color);
|
||||||
|
g.setColor(bg_color[0],bg_color[1],bg_color[2]);
|
||||||
|
g.fillPoly([0,25,
|
||||||
|
0,240,
|
||||||
|
240,240,
|
||||||
|
240,25
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the date formats required
|
||||||
|
|
||||||
|
LANGUAGES_FILE = "slidingtext.languages.json";
|
||||||
|
var LANGUAGES_DEFAULT = ["en","en2"];
|
||||||
|
var locales = null;
|
||||||
|
try{
|
||||||
|
locales = require("Storage").readJSON(LANGUAGES_FILE);
|
||||||
|
if(locales != null){
|
||||||
|
console.log("loaded languages:" + JSON.stringify(locales));
|
||||||
|
} else {
|
||||||
|
console.log("no languages loaded");
|
||||||
|
locales = LANGUAGES_DEFAULT;
|
||||||
|
}
|
||||||
|
} catch(e){
|
||||||
|
console.log("failed to load languages:" + e);
|
||||||
|
}
|
||||||
|
if(locales == null || locales.length == 0){
|
||||||
|
locales = LANGUAGES_DEFAULT;
|
||||||
|
console.log("defaulting languages to locale:" + locales);
|
||||||
|
}
|
||||||
|
|
||||||
|
let date_formatters = [];
|
||||||
|
for(var i=0; i< locales.length; i++){
|
||||||
|
console.log("loading locale:" + locales[i]);
|
||||||
|
var Formatter = require("slidingtext.locale." + locales[i] + ".js");
|
||||||
|
date_formatters.push(new Formatter());
|
||||||
|
}
|
||||||
|
|
||||||
// current index of the date formatter to display
|
// current index of the date formatter to display
|
||||||
let date_formatter_idx = 0;
|
let date_formatter_idx = 0;
|
||||||
let date_formatter = date_formatters[date_formatter_idx];
|
let date_formatter = date_formatters[date_formatter_idx];
|
||||||
|
|
||||||
// The small display at the top which announces the date format
|
|
||||||
let format_name_display = new ShiftText(55,0,'',"Vector",10,1,1,50,[1,1,1]);
|
|
||||||
|
|
||||||
function changeFormatter(){
|
function changeFormatter(){
|
||||||
date_formatter_idx += 1;
|
date_formatter_idx += 1;
|
||||||
if(date_formatter_idx >= date_formatters.length){
|
if(date_formatter_idx >= date_formatters.length){
|
||||||
|
|
@ -364,61 +315,204 @@ function changeFormatter(){
|
||||||
date_formatter = date_formatters[date_formatter_idx];
|
date_formatter = date_formatters[date_formatter_idx];
|
||||||
reset_clock();
|
reset_clock();
|
||||||
draw_clock();
|
draw_clock();
|
||||||
// now announce the formatter by name
|
command_stack_high_priority.unshift(
|
||||||
format_name_display.setTextYPosition(date_formatter.name(),-10);
|
function() {
|
||||||
format_name_display.moveToY(15);
|
//console.log("move in new:" + txt);
|
||||||
// and then move back
|
// first select the top or bottom to display the formatter name
|
||||||
format_name_display.onFinished(
|
// We choose the first spare row without text
|
||||||
function(){
|
var format_name_display = row_displays[row_displays.length - 1];
|
||||||
format_name_display.moveToY(-10);
|
if (format_name_display.txt != '') {
|
||||||
|
format_name_display = row_displays[0];
|
||||||
|
}
|
||||||
|
if (format_name_display.txt != ''){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
format_name_display.speed_x = 3;
|
||||||
|
format_name_display.onFinished(function(){
|
||||||
|
format_name_display.speed_x = CLOCK_TEXT_SPEED_X;
|
||||||
|
console.log("return speed to:" + format_name_display.speed_x)
|
||||||
|
next_command();
|
||||||
|
});
|
||||||
|
format_name_display.setTextXPosition(date_formatter.name(),220);
|
||||||
|
format_name_display.moveToX(-date_formatter.name().length * format_name_display.font_size);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function reset_clock(){
|
function reset_clock(){
|
||||||
//console.log("reset_clock");
|
//console.log("reset_clock");
|
||||||
var i;
|
for (var i = 0; i < row_displays.length; i++) {
|
||||||
for (i = 0; i < row_displays.length; i++) {
|
row_displays[i].speed_x = CLOCK_TEXT_SPEED_X;
|
||||||
row_displays[i].reset();
|
row_displays[i].reset();
|
||||||
}
|
}
|
||||||
|
reset_commands();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let last_draw_time = null;
|
||||||
|
const next_minute_boundary_secs = 7.5;
|
||||||
|
|
||||||
function draw_clock(){
|
function draw_clock(){
|
||||||
//console.log("draw_clock");
|
var date = new Date();
|
||||||
date = new Date();
|
if(last_draw_time != null &&
|
||||||
rows = date_formatter.formatDate(date);
|
date.getTime() - last_draw_time.getTime() < next_minute_boundary_secs * 1000 &&
|
||||||
var i;
|
has_commands() ){
|
||||||
for (i = 0; i < rows.length; i++) {
|
console.log("skipping draw clock");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
last_draw_time = date;
|
||||||
|
}
|
||||||
|
reset_commands();
|
||||||
|
console.log("draw_clock:" + date.toISOString());
|
||||||
|
// we don't want the time to be displayed
|
||||||
|
// and then immediately be trigger another time
|
||||||
|
if(date.getSeconds() > 60 - next_minute_boundary_secs){
|
||||||
|
console.log("forwarding to next minute");
|
||||||
|
date = new Date(date.getTime() + next_minute_boundary_secs * 1000);
|
||||||
|
}
|
||||||
|
//date.setMinutes(37);
|
||||||
|
var rows = date_formatter.formatDate(date);
|
||||||
|
var display;
|
||||||
|
for (var i = 0; i < rows.length; i++) {
|
||||||
display = row_displays[i];
|
display = row_displays[i];
|
||||||
txt = rows[i];
|
var txt = rows[i];
|
||||||
|
//console.log(i + "->" + txt);
|
||||||
display_row(display,txt);
|
display_row(display,txt);
|
||||||
}
|
}
|
||||||
// If the dateformatter has not returned enough
|
// If the dateformatter has not returned enough
|
||||||
// rows then treat the reamining rows as empty
|
// rows then treat the reamining rows as empty
|
||||||
for (j = i; j < row_displays.length; j++) {
|
for (var j = i; j < row_displays.length; j++) {
|
||||||
display = row_displays[j];
|
display = row_displays[j];
|
||||||
|
//console.log(i + "->''(empty)");
|
||||||
display_row(display,'');
|
display_row(display,'');
|
||||||
}
|
}
|
||||||
|
next_command();
|
||||||
//console.log(date);
|
//console.log(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
function display_row(display,txt){
|
function display_row(display,txt){
|
||||||
if(display.txt == ''){
|
if(display == null) {
|
||||||
display.setTextXPosition(txt,240);
|
console.log("no display for text:" + txt)
|
||||||
display.moveToX(20);
|
return;
|
||||||
} else if(txt != display.txt){
|
}
|
||||||
display.moveToX(-100);
|
|
||||||
display.onFinished(
|
if(display.txt == null || display.txt == ''){
|
||||||
function(){
|
if(txt != '') {
|
||||||
display.setTextXPosition(txt,240);
|
command_stack_high_priority.unshift(
|
||||||
display.moveToX(20);
|
function () {
|
||||||
}
|
//console.log("move in new:" + txt);
|
||||||
|
display.onFinished(next_command);
|
||||||
|
display.setTextXPosition(txt, 240);
|
||||||
|
display.moveToX(20);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if(txt != display.txt && display.txt != null){
|
||||||
|
command_stack_high_priority.push(
|
||||||
|
function(){
|
||||||
|
//console.log("move out:" + txt);
|
||||||
|
display.onFinished(next_command);
|
||||||
|
display.moveToX(-display.txt.length * display.font_size);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
command_stack_low_priority.push(
|
||||||
|
function(){
|
||||||
|
//console.log("move in:" + txt);
|
||||||
|
display.onFinished(next_command);
|
||||||
|
display.setTextXPosition(txt,240);
|
||||||
|
display.moveToX(20);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
display.setTextXPosition(txt,20);
|
command_stack_high_priority.push(
|
||||||
|
function(){
|
||||||
|
//console.log("move in2:" + txt);
|
||||||
|
display.setTextXPosition(txt,20);
|
||||||
|
next_command();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called from load_settings on startup to
|
||||||
|
* set the color scheme to named value
|
||||||
|
*/
|
||||||
|
function set_colorscheme(colorscheme_name){
|
||||||
|
console.log("setting color scheme:" + colorscheme_name);
|
||||||
|
for (var i=0; i < color_schemes.length; i++) {
|
||||||
|
if(color_schemes[i].name == colorscheme_name){
|
||||||
|
color_scheme_index = i;
|
||||||
|
console.log("match");
|
||||||
|
var color_scheme = color_schemes[color_scheme_index];
|
||||||
|
setColor(color_scheme.main_bar,
|
||||||
|
color_scheme.other_bars,
|
||||||
|
color_scheme.background);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_dateformat(dateformat_name){
|
||||||
|
console.log("setting date format:" + dateformat_name);
|
||||||
|
for (var i=0; i < date_formatters.length; i++) {
|
||||||
|
if(date_formatters[i].name() == dateformat_name){
|
||||||
|
date_formatter_idx = i;
|
||||||
|
date_formatter = date_formatters[date_formatter_idx];
|
||||||
|
console.log("match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PREFERENCE_FILE = "slidingtext.settings.json";
|
||||||
|
/**
|
||||||
|
* Called on startup to set the watch to the last preference settings
|
||||||
|
*/
|
||||||
|
function load_settings(){
|
||||||
|
try{
|
||||||
|
settings = require("Storage").readJSON(PREFERENCE_FILE);
|
||||||
|
if(settings != null){
|
||||||
|
console.log("loaded:" + JSON.stringify(settings));
|
||||||
|
if(settings.color_scheme != null){
|
||||||
|
set_colorscheme(settings.color_scheme);
|
||||||
|
}
|
||||||
|
if(settings.date_format != null){
|
||||||
|
set_dateformat(settings.date_format);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("no settings to load");
|
||||||
|
}
|
||||||
|
} catch(e){
|
||||||
|
console.log("failed to load settings:" + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on button press to save down the last preference settings
|
||||||
|
*/
|
||||||
|
function save_settings(){
|
||||||
|
var settings = {
|
||||||
|
date_format : date_formatter.name(),
|
||||||
|
color_scheme : color_schemes[color_scheme_index].name,
|
||||||
|
};
|
||||||
|
console.log("saving:" + JSON.stringify(settings));
|
||||||
|
require("Storage").writeJSON(PREFERENCE_FILE,settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
function button1pressed() {
|
||||||
|
changeFormatter();
|
||||||
|
save_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function button3pressed() {
|
||||||
|
console.log("button3pressed");
|
||||||
|
nextColorTheme();
|
||||||
|
reset_clock();
|
||||||
|
draw_clock();
|
||||||
|
save_settings();
|
||||||
|
}
|
||||||
|
|
||||||
// The interval reference for updating the clock
|
// The interval reference for updating the clock
|
||||||
let intervalRef = null;
|
let intervalRef = null;
|
||||||
|
|
||||||
|
|
@ -430,9 +524,9 @@ function clearTimers(){
|
||||||
}
|
}
|
||||||
|
|
||||||
function startTimers(){
|
function startTimers(){
|
||||||
let date = new Date();
|
var date = new Date();
|
||||||
let secs = date.getSeconds();
|
var secs = date.getSeconds();
|
||||||
let nextMinuteStart = 60 - secs;
|
var nextMinuteStart = 60 - secs;
|
||||||
//console.log("scheduling clock draw in " + nextMinuteStart + " seconds");
|
//console.log("scheduling clock draw in " + nextMinuteStart + " seconds");
|
||||||
setTimeout(scheduleDrawClock,nextMinuteStart * 1000);
|
setTimeout(scheduleDrawClock,nextMinuteStart * 1000);
|
||||||
draw_clock();
|
draw_clock();
|
||||||
|
|
@ -457,6 +551,7 @@ Bangle.on('lcdPower', (on) => {
|
||||||
clearTimers();
|
clearTimers();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Bangle.on('faceUp',function(up){
|
Bangle.on('faceUp',function(up){
|
||||||
//console.log("faceUp: " + up + " LCD: " + Bangle.isLCDOn());
|
//console.log("faceUp: " + up + " LCD: " + Bangle.isLCDOn());
|
||||||
if (up && !Bangle.isLCDOn()) {
|
if (up && !Bangle.isLCDOn()) {
|
||||||
|
|
@ -467,9 +562,17 @@ Bangle.on('faceUp',function(up){
|
||||||
});
|
});
|
||||||
|
|
||||||
g.clear();
|
g.clear();
|
||||||
|
load_settings();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
startTimers();
|
startTimers();
|
||||||
// Show launcher when middle button pressed
|
// Show launcher when middle button pressed
|
||||||
setWatch(Bangle.showLauncher, BTN2,{repeat:false,edge:"falling"});
|
setWatch(Bangle.showLauncher, BTN2,{repeat:false,edge:"falling"});
|
||||||
setWatch(changeFormatter, BTN1,{repeat:true,edge:"falling"});
|
|
||||||
|
|
||||||
|
// Handle button 1 being pressed
|
||||||
|
setWatch(button1pressed, BTN1,{repeat:true,edge:"falling"});
|
||||||
|
|
||||||
|
// Handle button 3 being pressed
|
||||||
|
setWatch(button3pressed, BTN3,{repeat:true,edge:"falling"});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
var DateFormatter = require("slidingtext.dtfmt.js");
|
||||||
|
const hoursToText = require("slidingtext.utils.en.js").hoursToText;
|
||||||
|
const numberToText = require("slidingtext.utils.en.js").numberToText;
|
||||||
|
|
||||||
|
class EnglishDateFormatter extends DateFormatter {
|
||||||
|
constructor() { super();}
|
||||||
|
name(){return "English";}
|
||||||
|
formatDate(date){
|
||||||
|
var hours_txt = hoursToText(date.getHours());
|
||||||
|
var mins_txt = numberToText(date.getMinutes());
|
||||||
|
return [hours_txt,mins_txt[0],mins_txt[1]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = EnglishDateFormatter;
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
var DateFormatter = require("slidingtext.dtfmt.js");
|
||||||
|
const hoursToText = require("slidingtext.utils.en.js").hoursToText;
|
||||||
|
const numberToText = require("slidingtext.utils.en.js").numberToText;
|
||||||
|
|
||||||
|
class EnglishTraditionalDateFormatter extends DateFormatter {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
name(){return "English (Traditional)";}
|
||||||
|
formatDate(date){
|
||||||
|
var mins = date.getMinutes();
|
||||||
|
var hourOfDay = date.getHours();
|
||||||
|
if(mins > 30){
|
||||||
|
hourOfDay += 1;
|
||||||
|
}
|
||||||
|
var hours = hoursToText(hourOfDay);
|
||||||
|
// Deal with the special times first
|
||||||
|
if(mins == 0){
|
||||||
|
return [hours,"", "O'","CLOCK"];
|
||||||
|
} else if(mins == 30){
|
||||||
|
return ["","HALF", "PAST", "", hours];
|
||||||
|
} else if(mins == 15){
|
||||||
|
return ["","QUARTER", "PAST", "", hours];
|
||||||
|
} else if(mins == 45) {
|
||||||
|
return ["", "QUARTER", "TO", "", hours];
|
||||||
|
}
|
||||||
|
var mins_txt;
|
||||||
|
var from_to;
|
||||||
|
var mins_value;
|
||||||
|
if(mins > 30){
|
||||||
|
mins_value = 60-mins;
|
||||||
|
from_to = "TO";
|
||||||
|
mins_txt = numberToText(mins_value);
|
||||||
|
} else {
|
||||||
|
mins_value = mins;
|
||||||
|
from_to = "PAST";
|
||||||
|
mins_txt = numberToText(mins_value);
|
||||||
|
}
|
||||||
|
if(mins_txt[1] != '') {
|
||||||
|
return ['', mins_txt[0], mins_txt[1], from_to, hours];
|
||||||
|
} else {
|
||||||
|
if(mins_value % 5 == 0) {
|
||||||
|
return ['', mins_txt[0], from_to, '', hours];
|
||||||
|
} else if(mins_value == 1){
|
||||||
|
return ['', mins_txt[0], 'MINUTE', from_to, hours];
|
||||||
|
} else {
|
||||||
|
return ['', mins_txt[0], 'MINUTES', from_to, hours];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = EnglishTraditionalDateFormatter;
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
var DateFormatter = require("slidingtext.dtfmt.js");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* French date formatting
|
||||||
|
*/
|
||||||
|
const frenchNumberStr = [ "ZERO", "UNE", "DEUX", "TROIS", "QUATRE",
|
||||||
|
"CINQ", "SIX", "SEPT", "HUIT", "NEUF", "DIX",
|
||||||
|
"ONZE", "DOUZE", "TREIZE", "QUATORZE","QUINZE",
|
||||||
|
"SEIZE", "DIX SEPT", "DIX HUIT","DIX NEUF", "VINGT",
|
||||||
|
"VINGT ET UN", "VINGT DEUX", "VINGT TROIS",
|
||||||
|
"VINGT QUATRE", "VINGT CINQ", "VINGT SIX",
|
||||||
|
"VINGT SEPT", "VINGT HUIT", "VINGT NEUF"
|
||||||
|
];
|
||||||
|
|
||||||
|
function frenchHoursToText(hours){
|
||||||
|
hours = hours % 12;
|
||||||
|
if(hours == 0){
|
||||||
|
hours = 12;
|
||||||
|
}
|
||||||
|
return frenchNumberStr[hours];
|
||||||
|
}
|
||||||
|
|
||||||
|
function frenchHeures(hours){
|
||||||
|
if(hours % 12 == 1){
|
||||||
|
return 'HEURE';
|
||||||
|
} else {
|
||||||
|
return 'HEURES';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FrenchDateFormatter extends DateFormatter {
|
||||||
|
constructor() { super(); }
|
||||||
|
name(){return "French";}
|
||||||
|
formatDate(date){
|
||||||
|
var hours = frenchHoursToText(date.getHours());
|
||||||
|
var heures = frenchHeures(date.getHours());
|
||||||
|
var mins = date.getMinutes();
|
||||||
|
if(mins == 0){
|
||||||
|
if(hours == 0){
|
||||||
|
return ["MINUIT", "",""];
|
||||||
|
} else if(hours == 12){
|
||||||
|
return ["MIDI", "",""];
|
||||||
|
} else {
|
||||||
|
return [hours, heures,""];
|
||||||
|
}
|
||||||
|
} else if(mins == 30){
|
||||||
|
return [hours, heures,'ET DEMIE'];
|
||||||
|
} else if(mins == 15){
|
||||||
|
return [hours, heures,'ET QUERT'];
|
||||||
|
} else if(mins == 45){
|
||||||
|
var next_hour = date.getHours() + 1;
|
||||||
|
hours = frenchHoursToText(next_hour);
|
||||||
|
heures = frenchHeures(next_hour);
|
||||||
|
return [hours, heures,"MOINS",'LET QUERT'];
|
||||||
|
}
|
||||||
|
if(mins > 30){
|
||||||
|
var to_mins = 60-mins;
|
||||||
|
var mins_txt = frenchNumberStr[to_mins];
|
||||||
|
next_hour = date.getHours() + 1;
|
||||||
|
hours = frenchHoursToText(next_hour);
|
||||||
|
heures = frenchHeures(next_hour);
|
||||||
|
return [ hours, heures , "MOINS", mins_txt ];
|
||||||
|
} else {
|
||||||
|
mins_txt = frenchNumberStr[mins];
|
||||||
|
return [ hours, heures , mins_txt ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = FrenchDateFormatter;
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
var DateFormatter = require("slidingtext.dtfmt.js");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Japanese date formatting
|
||||||
|
*/
|
||||||
|
const japaneseHourStr = [ "ZERO", "ICHII", "NI", "SAN", "YO",
|
||||||
|
"GO", "ROKU", "SHICHI", "HACHI", "KU", "JUU",
|
||||||
|
'JUU ICHI', 'JUU NI'];
|
||||||
|
const tensPrefixStr = [ "",
|
||||||
|
"JUU",
|
||||||
|
'NIJUU',
|
||||||
|
'SAN JUU',
|
||||||
|
'YON JUU',
|
||||||
|
'GO JUU'];
|
||||||
|
|
||||||
|
const japaneseMinuteStr = [ ["", "PUN"],
|
||||||
|
["IP","PUN" ],
|
||||||
|
["NI", "FUN"],
|
||||||
|
["SAN", "PUN"],
|
||||||
|
["YON","FUN"],
|
||||||
|
["GO", "HUN"],
|
||||||
|
["RO", "PUN"],
|
||||||
|
["NANA", "FUN"],
|
||||||
|
["HAP", "PUN"],
|
||||||
|
["KYU","FUN"],
|
||||||
|
["JUP", "PUN"]
|
||||||
|
];
|
||||||
|
|
||||||
|
function japaneseHoursToText(hours){
|
||||||
|
hours = hours % 12;
|
||||||
|
if(hours == 0){
|
||||||
|
hours = 12;
|
||||||
|
}
|
||||||
|
return japaneseHourStr[hours];
|
||||||
|
}
|
||||||
|
|
||||||
|
function japaneseMinsToText(mins){
|
||||||
|
if(mins == 0){
|
||||||
|
return ["",""];
|
||||||
|
} else if(mins == 30)
|
||||||
|
return ["HAN",""];
|
||||||
|
else {
|
||||||
|
var units = mins % 10;
|
||||||
|
var mins_txt = japaneseMinuteStr[units];
|
||||||
|
var tens = mins /10 | 0;
|
||||||
|
if(tens > 0){
|
||||||
|
var tens_txt = tensPrefixStr[tens];
|
||||||
|
var minutes_txt;
|
||||||
|
if(mins_txt[0] != ''){
|
||||||
|
minutes_txt = mins_txt[0] + ' ' + mins_txt[1];
|
||||||
|
} else {
|
||||||
|
minutes_txt = mins_txt[1];
|
||||||
|
}
|
||||||
|
return [tens_txt, minutes_txt];
|
||||||
|
} else {
|
||||||
|
return [mins_txt[0], mins_txt[1]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JapaneseDateFormatter extends DateFormatter {
|
||||||
|
constructor() { super(); }
|
||||||
|
name(){return "Japanese (Romanji)";}
|
||||||
|
formatDate(date){
|
||||||
|
var hours_txt = japaneseHoursToText(date.getHours());
|
||||||
|
var mins_txt = japaneseMinsToText(date.getMinutes());
|
||||||
|
return [hours_txt,"JI", mins_txt[0], mins_txt[1] ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = JapaneseDateFormatter;
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
const numberStr = ["ZERO","ONE", "TWO", "THREE", "FOUR", "FIVE",
|
||||||
|
"SIX", "SEVEN","EIGHT", "NINE", "TEN",
|
||||||
|
"ELEVEN", "TWELVE", "THIRTEEN", "FOURTEEN",
|
||||||
|
"FIFTEEN", "SIXTEEN", "SEVENTEEN", "EIGHTEEN",
|
||||||
|
"NINETEEN", "TWENTY"];
|
||||||
|
const tensStr = ["ZERO", "TEN", "TWENTY", "THIRTY", "FOURTY",
|
||||||
|
"FIFTY"];
|
||||||
|
|
||||||
|
const hoursToText = (hours)=>{
|
||||||
|
hours = hours % 12;
|
||||||
|
if(hours == 0){
|
||||||
|
hours = 12;
|
||||||
|
}
|
||||||
|
return numberStr[hours];
|
||||||
|
}
|
||||||
|
|
||||||
|
const numberToText = (value)=> {
|
||||||
|
var word1 = '';
|
||||||
|
var word2 = '';
|
||||||
|
if(value > 20){
|
||||||
|
var tens = (value / 10 | 0);
|
||||||
|
word1 = tensStr[tens];
|
||||||
|
var remainder = value - tens * 10;
|
||||||
|
if(remainder > 0){
|
||||||
|
word2 = numberStr[remainder];
|
||||||
|
}
|
||||||
|
} else if(value > 0) {
|
||||||
|
word1 = numberStr[value];
|
||||||
|
}
|
||||||
|
return [word1,word2];
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.hoursToText = hoursToText;
|
||||||
|
exports.numberToText = numberToText;
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
0.01: Initial Release
|
0.01: Initial Release
|
||||||
|
0.02: Added Colour Themes
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,24 @@ The Sweep Clock provides a clock with a perfectly smooth sweep second hand with
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Use Button 1 (the top right button) to change the numeral types (currently European and Roman)
|
### Button 1
|
||||||
|
|
||||||
|
Use Button 1 (the top right button) to change the numeral type
|
||||||
|
|
||||||
|
| Default clock face | Roman Numeral Font | No Digits |
|
||||||
|
| ---- | ---- | ---- |
|
||||||
|
|  |  |  |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Button 3
|
||||||
|
Button 3 (bottom right button) is used to change the colour
|
||||||
|
|
||||||
|
| Red | Grey | Purple |
|
||||||
|
| ---- | ---- | ---- |
|
||||||
|
|  |  |  |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Further Details
|
## Further Details
|
||||||
|
|
||||||
|
|
@ -14,8 +31,8 @@ For further details of design and working please visit [The Project Page](https:
|
||||||
|
|
||||||
## Requests
|
## Requests
|
||||||
|
|
||||||
[Reach out to Adrian](https://www.github.com/awkirk71) if you have feature requests or notice bugs.
|
Reach out to adrian@adriankirk.com if you have feature requests or notice bugs.
|
||||||
|
|
||||||
## Creator
|
## Creator
|
||||||
|
|
||||||
Made by [Adrian Kirk](https://www.github.com/awkirk71).
|
Made by [Adrian Kirk](mailto:adrian@adriankirk.com)
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
|
@ -1 +1 @@
|
||||||
require("heatshrink").decompress(atob("lEowkE/4AdmU/CaMgCaUQCaPzgQmR+UDCaPxCaUwj525gJ2/CZ0vMSJ2SkEACaCJBgEPRKEAgJ3Q+cxE6Myn8yO6EggMjMSR3Q+ASBgCdRO4KJQCaMwCaPzCQQTPEwYTOJoYTO+cQCaKHCAAizLkEQYgQTF+YTHmMAkITEj//l8wl4UHkcwiIRBAQMD/8/CZKLBiUAiEigMCBAMzCwMyPI8zPQMzgMBn/yh/zgZSICoMxn5kC+ZmBPhY3CHAIAbA"))
|
require("heatshrink").decompress(atob("lEowkA/4AGmYIHABHzmVCCaE0kUin4TPmUimQTQ+UzmcvJ6EjCaP/kYABCaEymYTl+Q7SMgITTmQTQPAK0RMgITm+QTS+ciPCcikQpPY4MjmYTO+czmcyHh4TCmcvJ54nCPCBjBJx4oECc8zJ6ATTn48RE4YTTHh4SDH4ImRFBwTGFBgTGFBgSGFBYmHUgITRmcyFBASFAoUjE5PzkQLBHJxiDAQP/GxAA=="))
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,55 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const screen_center_x = g.getWidth()/2;
|
const screen_center_x = g.getWidth()/2;
|
||||||
const screen_center_y = g.getHeight()/2;
|
const screen_center_y = 10 + g.getHeight()/2;
|
||||||
|
|
||||||
require("FontCopasetic40x58Numeric").add(Graphics);
|
require("FontCopasetic40x58Numeric").add(Graphics);
|
||||||
|
|
||||||
|
const color_schemes = [
|
||||||
|
{
|
||||||
|
name: "black",
|
||||||
|
background : [0.0,0.0,0.0],
|
||||||
|
second_hand: [1.0,0.0,0.0],
|
||||||
|
minute_hand: [1.0,1.0,1.0],
|
||||||
|
hour_hand: [1.0,1.0,1.0],
|
||||||
|
numeral:[1.0,1.0,1.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "red",
|
||||||
|
background : [1.0,0.0,0.0],
|
||||||
|
second_hand: [1.0,1.0,0.0],
|
||||||
|
minute_hand: [1.0,1.0,1.0],
|
||||||
|
hour_hand: [1.0,1.0,1.0],
|
||||||
|
numeral:[1.0,1.0,1.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grey",
|
||||||
|
background : [0.5,0.5,0.5],
|
||||||
|
second_hand: [0.0,0.0,0.0],
|
||||||
|
minute_hand: [1.0,1.0,1.0],
|
||||||
|
hour_hand: [1.0,1.0,1.0],
|
||||||
|
numeral:[1.0,1.0,1.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "purple",
|
||||||
|
background : [1.0,0.0,1.0],
|
||||||
|
second_hand: [1.0,1.0,0.0],
|
||||||
|
minute_hand: [1.0,1.0,1.0],
|
||||||
|
hour_hand: [1.0,1.0,1.0],
|
||||||
|
numeral:[1.0,1.0,1.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "blue",
|
||||||
|
background : [0.4,0.7,1.0],
|
||||||
|
second_hand: [0.5,0.5,0.5],
|
||||||
|
minute_hand: [1.0,1.0,1.0],
|
||||||
|
hour_hand: [1.0,1.0,1.0],
|
||||||
|
numeral:[1.0,1.0,1.0]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let color_scheme_index = 0;
|
||||||
|
|
||||||
class Hand {
|
class Hand {
|
||||||
/**
|
/**
|
||||||
* Pure virtual class for all Hand classes to extend.
|
* Pure virtual class for all Hand classes to extend.
|
||||||
|
|
@ -28,16 +73,12 @@ class ThinHand extends Hand {
|
||||||
length,
|
length,
|
||||||
tolerance,
|
tolerance,
|
||||||
draw_test,
|
draw_test,
|
||||||
red,
|
color_theme){
|
||||||
green,
|
|
||||||
blue){
|
|
||||||
super();
|
super();
|
||||||
this.centerX = centerX;
|
this.centerX = centerX;
|
||||||
this.centerY = centerY;
|
this.centerY = centerY;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.red = red;
|
this.color_theme = color_theme;
|
||||||
this.green = green;
|
|
||||||
this.blue = blue;
|
|
||||||
// The last x and y coordinates (not the centre) of the last draw
|
// The last x and y coordinates (not the centre) of the last draw
|
||||||
this.last_x = centerX;
|
this.last_x = centerX;
|
||||||
this.last_y = centerY;
|
this.last_y = centerY;
|
||||||
|
|
@ -60,13 +101,14 @@ class ThinHand extends Hand {
|
||||||
// and then call the predicate to see if a redraw is needed
|
// and then call the predicate to see if a redraw is needed
|
||||||
this.draw_test(this.angle,this.last_draw_time) ){
|
this.draw_test(this.angle,this.last_draw_time) ){
|
||||||
// rub out the old hand line
|
// rub out the old hand line
|
||||||
g.setColor(0,0,0);
|
background = color_schemes[color_scheme_index].background;
|
||||||
|
g.setColor(background[0],background[1],background[2]);
|
||||||
g.drawLine(this.centerX, this.centerY, this.last_x, this.last_y);
|
g.drawLine(this.centerX, this.centerY, this.last_x, this.last_y);
|
||||||
// Now draw the new hand line
|
// Now draw the new hand line
|
||||||
g.setColor(this.red,this.green,this.blue);
|
hand_color = color_schemes[color_scheme_index][this.color_theme];
|
||||||
|
g.setColor(hand_color[0],hand_color[1],hand_color[2]);
|
||||||
x2 = this.centerX + this.length*Math.sin(angle);
|
x2 = this.centerX + this.length*Math.sin(angle);
|
||||||
y2 = this.centerY - this.length*Math.cos(angle);
|
y2 = this.centerY - this.length*Math.cos(angle);
|
||||||
g.setColor(this.red,this.green,this.blue);
|
|
||||||
g.drawLine(this.centerX, this.centerY, x2, y2);
|
g.drawLine(this.centerX, this.centerY, x2, y2);
|
||||||
// and store the last draw details for the next call
|
// and store the last draw details for the next call
|
||||||
this.last_x = x2;
|
this.last_x = x2;
|
||||||
|
|
@ -90,18 +132,14 @@ class ThickHand extends Hand {
|
||||||
length,
|
length,
|
||||||
tolerance,
|
tolerance,
|
||||||
draw_test,
|
draw_test,
|
||||||
red,
|
color_theme,
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
base_height,
|
base_height,
|
||||||
thickness){
|
thickness){
|
||||||
super();
|
super();
|
||||||
this.centerX = centerX;
|
this.centerX = centerX;
|
||||||
this.centerY = centerY;
|
this.centerY = centerY;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.red = red;
|
this.color_theme = color_theme;
|
||||||
this.green = green;
|
|
||||||
this.blue = blue;
|
|
||||||
this.thickness = thickness;
|
this.thickness = thickness;
|
||||||
this.base_height = base_height;
|
this.base_height = base_height;
|
||||||
// angle from the center to the top corners of the rectangle
|
// angle from the center to the top corners of the rectangle
|
||||||
|
|
@ -132,7 +170,8 @@ class ThickHand extends Hand {
|
||||||
// method to move the hand to a new angle
|
// method to move the hand to a new angle
|
||||||
moveTo(angle){
|
moveTo(angle){
|
||||||
if(Math.abs(angle - this.angle) > this.tolerance || this.draw_test(this.angle - this.delta_base,this.angle + this.delta_base ,this.last_draw_time) ){
|
if(Math.abs(angle - this.angle) > this.tolerance || this.draw_test(this.angle - this.delta_base,this.angle + this.delta_base ,this.last_draw_time) ){
|
||||||
g.setColor(0,0,0);
|
background = color_schemes[color_scheme_index].background;
|
||||||
|
g.setColor(background[0],background[1],background[2]);
|
||||||
g.fillPoly([this.last_x1,
|
g.fillPoly([this.last_x1,
|
||||||
this.last_y1,
|
this.last_y1,
|
||||||
this.last_x2,
|
this.last_x2,
|
||||||
|
|
@ -142,7 +181,6 @@ class ThickHand extends Hand {
|
||||||
this.last_x4,
|
this.last_x4,
|
||||||
this.last_y4
|
this.last_y4
|
||||||
]);
|
]);
|
||||||
g.setColor(this.red,this.green,this.blue);
|
|
||||||
// bottom left
|
// bottom left
|
||||||
x1 = this.centerX +
|
x1 = this.centerX +
|
||||||
this.vertex_radius_base*Math.sin(angle - this.delta_base);
|
this.vertex_radius_base*Math.sin(angle - this.delta_base);
|
||||||
|
|
@ -157,7 +195,8 @@ class ThickHand extends Hand {
|
||||||
// top left
|
// top left
|
||||||
x4 = this.centerX + this.vertex_radius_top*Math.sin(angle - this.delta_top);
|
x4 = this.centerX + this.vertex_radius_top*Math.sin(angle - this.delta_top);
|
||||||
y4 = this.centerY - this.vertex_radius_top*Math.cos(angle - this.delta_top);
|
y4 = this.centerY - this.vertex_radius_top*Math.cos(angle - this.delta_top);
|
||||||
g.setColor(this.red,this.green,this.blue);
|
hand_color = color_schemes[color_scheme_index][this.color_theme];
|
||||||
|
g.setColor(hand_color[0],hand_color[1],hand_color[2]);
|
||||||
g.fillPoly([x1,y1,
|
g.fillPoly([x1,y1,
|
||||||
x2,y2,
|
x2,y2,
|
||||||
x3,y3,
|
x3,y3,
|
||||||
|
|
@ -184,10 +223,10 @@ let force_redraw = false;
|
||||||
// The seconds hand is the main focus and is set to redraw on every cycle
|
// The seconds hand is the main focus and is set to redraw on every cycle
|
||||||
let seconds_hand = new ThinHand(screen_center_x,
|
let seconds_hand = new ThinHand(screen_center_x,
|
||||||
screen_center_y,
|
screen_center_y,
|
||||||
100,
|
95,
|
||||||
0,
|
0,
|
||||||
(angle, last_draw_time) => false,
|
(angle, last_draw_time) => false,
|
||||||
1.0,0.0,0.0);
|
"second_hand");
|
||||||
// The minute hand is set to redraw at a 250th of a circle,
|
// The minute hand is set to redraw at a 250th of a circle,
|
||||||
// when the second hand is ontop or slighly overtaking
|
// when the second hand is ontop or slighly overtaking
|
||||||
// or when a force_redraw is called
|
// or when a force_redraw is called
|
||||||
|
|
@ -201,7 +240,7 @@ let minutes_hand = new ThinHand(screen_center_x,
|
||||||
80,
|
80,
|
||||||
2*Math.PI/250,
|
2*Math.PI/250,
|
||||||
minutes_hand_redraw,
|
minutes_hand_redraw,
|
||||||
1.0,1.0,1.0);
|
"minute_hand");
|
||||||
// The hour hand is a thick hand so we have to redraw when the minute hand
|
// The hour hand is a thick hand so we have to redraw when the minute hand
|
||||||
// overlaps from its behind andle coverage to its ahead angle coverage.
|
// overlaps from its behind andle coverage to its ahead angle coverage.
|
||||||
let hour_hand_redraw = function(angle_from, angle_to, last_draw_time){
|
let hour_hand_redraw = function(angle_from, angle_to, last_draw_time){
|
||||||
|
|
@ -214,12 +253,13 @@ let hours_hand = new ThickHand(screen_center_x,
|
||||||
40,
|
40,
|
||||||
2*Math.PI/600,
|
2*Math.PI/600,
|
||||||
hour_hand_redraw,
|
hour_hand_redraw,
|
||||||
1.0,1.0,1.0,
|
"hour_hand",
|
||||||
5,
|
5,
|
||||||
4);
|
4);
|
||||||
|
|
||||||
function draw_clock(){
|
function draw_clock(){
|
||||||
date = new Date();
|
date = new Date();
|
||||||
|
draw_background();
|
||||||
draw_hour_digit(date);
|
draw_hour_digit(date);
|
||||||
draw_seconds(date);
|
draw_seconds(date);
|
||||||
draw_mins(date);
|
draw_mins(date);
|
||||||
|
|
@ -242,7 +282,7 @@ function draw_mins(date,seconds_angle){
|
||||||
mins_angle = 2*Math.PI*mins_frac;
|
mins_angle = 2*Math.PI*mins_frac;
|
||||||
redraw = minutes_hand.moveTo(mins_angle);
|
redraw = minutes_hand.moveTo(mins_angle);
|
||||||
if(redraw){
|
if(redraw){
|
||||||
console.log("redraw mins");
|
//console.log("redraw mins");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,7 +292,7 @@ function draw_hours(date){
|
||||||
hours_angle = 2*Math.PI*hours_frac;
|
hours_angle = 2*Math.PI*hours_frac;
|
||||||
redraw = hours_hand.moveTo(hours_angle);
|
redraw = hours_hand.moveTo(hours_angle);
|
||||||
if(redraw){
|
if(redraw){
|
||||||
console.log("redraw hours");
|
//console.log("redraw hours");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,6 +315,18 @@ class NumeralFont {
|
||||||
* method to draw text at the required coordinates
|
* method to draw text at the required coordinates
|
||||||
*/
|
*/
|
||||||
draw(hour_txt,x,y){ return "";}
|
draw(hour_txt,x,y){ return "";}
|
||||||
|
/**
|
||||||
|
* Called from the settings loader to identify the font
|
||||||
|
*/
|
||||||
|
getName(){return "";}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoFont extends NumeralFont{
|
||||||
|
constructor(){super();}
|
||||||
|
getDimensions(hour){return [0,0];}
|
||||||
|
hour_txt(hour){ return ""; }
|
||||||
|
draw(hour_txt,x,y){ return "";}
|
||||||
|
getName(){return "NoFont";}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CopasetFont extends NumeralFont{
|
class CopasetFont extends NumeralFont{
|
||||||
|
|
@ -314,6 +366,7 @@ class CopasetFont extends NumeralFont{
|
||||||
g.setFontCopasetic40x58Numeric();
|
g.setFontCopasetic40x58Numeric();
|
||||||
g.drawString(hour_txt,x,y);
|
g.drawString(hour_txt,x,y);
|
||||||
}
|
}
|
||||||
|
getName(){return "Copaset";}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -358,6 +411,7 @@ class RomanNumeralFont extends NumeralFont{
|
||||||
g.setFont("Vector",40);
|
g.setFont("Vector",40);
|
||||||
g.drawString(hour_txt,x,y);
|
g.drawString(hour_txt,x,y);
|
||||||
}
|
}
|
||||||
|
getName(){return "Roman";}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The problem with the trig inverse functions on
|
// The problem with the trig inverse functions on
|
||||||
|
|
@ -419,11 +473,12 @@ class HourScriber {
|
||||||
drawHour(hours){
|
drawHour(hours){
|
||||||
changed = false;
|
changed = false;
|
||||||
if(this.curr_hours != hours || this.curr_numeral_font !=this.numeral_font){
|
if(this.curr_hours != hours || this.curr_numeral_font !=this.numeral_font){
|
||||||
g.setColor(0,0,0);
|
background = color_schemes[color_scheme_index].background;
|
||||||
|
g.setColor(background[0],background[1],background[2]);
|
||||||
this.curr_numeral_font.draw(this.curr_hour_str,
|
this.curr_numeral_font.draw(this.curr_hour_str,
|
||||||
this.curr_hour_x,
|
this.curr_hour_x,
|
||||||
this.curr_hour_y);
|
this.curr_hour_y);
|
||||||
console.log("erasing old hour");
|
//console.log("erasing old hour");
|
||||||
hours_frac = hours / 12;
|
hours_frac = hours / 12;
|
||||||
angle = 2*Math.PI*hours_frac;
|
angle = 2*Math.PI*hours_frac;
|
||||||
dimensions = this.numeral_font.getDimensions(hours);
|
dimensions = this.numeral_font.getDimensions(hours);
|
||||||
|
|
@ -477,15 +532,16 @@ class HourScriber {
|
||||||
}
|
}
|
||||||
if(changed ||
|
if(changed ||
|
||||||
this.draw_test(this.angle_from, this.angle_to, this.last_draw_time) ){
|
this.draw_test(this.angle_from, this.angle_to, this.last_draw_time) ){
|
||||||
g.setColor(1,1,1);
|
numeral_color = color_schemes[color_scheme_index].numeral;
|
||||||
|
g.setColor(numeral_color[0],numeral_color[1],numeral_color[2]);
|
||||||
this.numeral_font.draw(this.curr_hour_str,this.curr_hour_x,this.curr_hour_y);
|
this.numeral_font.draw(this.curr_hour_str,this.curr_hour_x,this.curr_hour_y);
|
||||||
this.last_draw_time = new Date();
|
this.last_draw_time = new Date();
|
||||||
console.log("redraw digit");
|
//console.log("redraw digit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let numeral_fonts = [new CopasetFont(), new RomanNumeralFont()];
|
let numeral_fonts = [new CopasetFont(), new RomanNumeralFont(), new NoFont()];
|
||||||
let numeral_fonts_index = 0;
|
let numeral_fonts_index = 0;
|
||||||
/**
|
/**
|
||||||
* predicate for deciding when the digit has to be redrawn
|
* predicate for deciding when the digit has to be redrawn
|
||||||
|
|
@ -540,7 +596,93 @@ function draw_hour_digit(date){
|
||||||
hour_scriber.drawHour(hours);
|
hour_scriber.drawHour(hours);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Boiler plate code for setting up the clock
|
function draw_background(){
|
||||||
|
if(force_redraw){
|
||||||
|
background = color_schemes[color_scheme_index].background;
|
||||||
|
g.setColor(background[0],background[1],background[2]);
|
||||||
|
g.fillPoly([0,25,
|
||||||
|
0,240,
|
||||||
|
240,240,
|
||||||
|
240,25
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function next_colorscheme(){
|
||||||
|
color_scheme_index += 1;
|
||||||
|
color_scheme_index = color_scheme_index % color_schemes.length;
|
||||||
|
//console.log("color_scheme_index=" + color_scheme_index);
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called from load_settings on startup to
|
||||||
|
* set the color scheme to named value
|
||||||
|
*/
|
||||||
|
function set_colorscheme(colorscheme_name){
|
||||||
|
console.log("setting color scheme:" + colorscheme_name);
|
||||||
|
for (var i=0; i < color_schemes.length; i++) {
|
||||||
|
if(color_schemes[i].name == colorscheme_name){
|
||||||
|
color_scheme_index = i;
|
||||||
|
force_redraw = true;
|
||||||
|
console.log("match");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called from load_settings on startup
|
||||||
|
* to set the font to named value
|
||||||
|
*/
|
||||||
|
function set_font(font_name){
|
||||||
|
console.log("setting font:" + font_name);
|
||||||
|
for (var i=0; i < numeral_fonts.length; i++) {
|
||||||
|
if(numeral_fonts[i].getName() == font_name){
|
||||||
|
numeral_fonts_index = i;
|
||||||
|
force_redraw = true;
|
||||||
|
console.log("match");
|
||||||
|
hour_scriber.setNumeralFont(numeral_fonts[numeral_fonts_index]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on startup to set the watch to the last preference settings
|
||||||
|
*/
|
||||||
|
function load_settings(){
|
||||||
|
try{
|
||||||
|
settings = require("Storage").readJSON("sweepclock.settings.json");
|
||||||
|
if(settings != null){
|
||||||
|
console.log("loaded:" + JSON.stringify(settings));
|
||||||
|
if(settings.color_scheme != null){
|
||||||
|
set_colorscheme(settings.color_scheme);
|
||||||
|
}
|
||||||
|
if(settings.font != null){
|
||||||
|
set_font(settings.font);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("no settings to load");
|
||||||
|
}
|
||||||
|
} catch(e){
|
||||||
|
console.log("failed to load settings:" + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on button press to save down the last preference settings
|
||||||
|
*/
|
||||||
|
function save_settings(){
|
||||||
|
settings = {
|
||||||
|
font : numeral_fonts[numeral_fonts_index].getName(),
|
||||||
|
color_scheme : color_schemes[color_scheme_index].name,
|
||||||
|
};
|
||||||
|
console.log("saving:" + JSON.stringify(settings));
|
||||||
|
require("Storage").writeJSON("sweepclock.settings.json",settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boiler plate code for setting up the clock,
|
||||||
// below
|
// below
|
||||||
let intervalRef = null;
|
let intervalRef = null;
|
||||||
|
|
||||||
|
|
@ -593,6 +735,7 @@ Bangle.on('faceUp',function(up){
|
||||||
});
|
});
|
||||||
|
|
||||||
g.clear();
|
g.clear();
|
||||||
|
load_settings();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
startTimers();
|
startTimers();
|
||||||
|
|
@ -602,8 +745,17 @@ setWatch(Bangle.showLauncher, BTN2,{repeat:false,edge:"falling"});
|
||||||
|
|
||||||
function button1pressed(){
|
function button1pressed(){
|
||||||
next_font();
|
next_font();
|
||||||
|
save_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function button3pressed(){
|
||||||
|
next_colorscheme();
|
||||||
|
save_settings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle button 1 being pressed
|
// Handle button 1 being pressed
|
||||||
setWatch(button1pressed, BTN1,{repeat:true,edge:"falling"});
|
setWatch(button1pressed, BTN1,{repeat:true,edge:"falling"});
|
||||||
|
|
||||||
|
// Handle button 3 being pressed
|
||||||
|
setWatch(button3pressed, BTN3,{repeat:true,edge:"falling"});
|
||||||
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.6 KiB |
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: First version of the Walkers Clock
|
0.01: First version of the Walkers Clock
|
||||||
0.02: Fixed screen flicker
|
0.02: Fixed screen flicker
|
||||||
0.03: Added display of GPS fix lat/lon and course
|
0.03: Added display of GPS fix lat/lon and course
|
||||||
|
0.04: Don't buzz for GPS fix in Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -379,7 +379,9 @@ function processFix(fix) {
|
||||||
|
|
||||||
if (fix.fix) {
|
if (fix.fix) {
|
||||||
if (!last_fix.fix) {
|
if (!last_fix.fix) {
|
||||||
Bangle.buzz(); // buzz on first position
|
if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||||
|
Bangle.buzz(); // buzz on first position
|
||||||
|
}
|
||||||
clearActivityArea = true;
|
clearActivityArea = true;
|
||||||
}
|
}
|
||||||
gpsState = GPS_RUNNING;
|
gpsState = GPS_RUNNING;
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,4 @@
|
||||||
0.04: Works on both standard and modified firmware
|
0.04: Works on both standard and modified firmware
|
||||||
0.05: Bug fixes w.r.t. reconnection
|
0.05: Bug fixes w.r.t. reconnection
|
||||||
0.06: Update README - Release version
|
0.06: Update README - Release version
|
||||||
|
0.07: Respect Quiet Mode
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -187,9 +187,11 @@
|
||||||
//we may already be displaying a prompt, so clear it
|
//we may already be displaying a prompt, so clear it
|
||||||
E.showPrompt();
|
E.showPrompt();
|
||||||
if (screentimeout) clearTimeout(screentimeout);
|
if (screentimeout) clearTimeout(screentimeout);
|
||||||
Bangle.setLCDPower(true);
|
if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||||
|
Bangle.setLCDPower(true);
|
||||||
|
}
|
||||||
SCREENACCESS.request();
|
SCREENACCESS.request();
|
||||||
if (!buzzing){
|
if (!buzzing && !(require('Storage').readJSON('setting.json',1)||{}).quiet){
|
||||||
buzzing=true;
|
buzzing=true;
|
||||||
Bangle.buzz(500).then(()=>{buzzing=false;});
|
Bangle.buzz(500).then(()=>{buzzing=false;});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
0.01: New Battery Warning!
|
0.01: New Battery Warning!
|
||||||
|
0.02: Respect Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,10 @@
|
||||||
.setColor(0xF800).drawString(`${E.getBattery()}%`, a.x+8+100, a.y+a.h/2);
|
.setColor(0xF800).drawString(`${E.getBattery()}%`, a.x+8+100, a.y+a.h/2);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (setting("buzz")) Bangle.buzz();
|
if (setting("buzz")
|
||||||
|
&& !(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||||
|
Bangle.buzz();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bangle.on("charging", check);
|
Bangle.on("charging", check);
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@
|
||||||
0.09: Add daily goal
|
0.09: Add daily goal
|
||||||
0.10: Fix daily goal, don't store settings in separate file
|
0.10: Fix daily goal, don't store settings in separate file
|
||||||
0.11: added getSteps() method for apps to retrieve step count
|
0.11: added getSteps() method for apps to retrieve step count
|
||||||
|
0.12: Respect Quiet Mode
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,8 @@
|
||||||
// TODO: could save this to PEDOMFILE for lastUpdate's day?
|
// TODO: could save this to PEDOMFILE for lastUpdate's day?
|
||||||
stp_today = 1;
|
stp_today = 1;
|
||||||
}
|
}
|
||||||
if (stp_today === setting('goal')) {
|
if (stp_today === setting('goal')
|
||||||
|
&& !(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||||
let b = 3, buzz = () => {
|
let b = 3, buzz = () => {
|
||||||
if (b--) Bangle.buzz().then(() => setTimeout(buzz, 100))
|
if (b--) Bangle.buzz().then(() => setTimeout(buzz, 100))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ const APP_KEYS = [
|
||||||
'sortorder', 'readme', 'custom', 'interface', 'storage', 'data', 'allow_emulator',
|
'sortorder', 'readme', 'custom', 'interface', 'storage', 'data', 'allow_emulator',
|
||||||
'dependencies'
|
'dependencies'
|
||||||
];
|
];
|
||||||
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate'];
|
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite'];
|
||||||
const DATA_KEYS = ['name', 'wildcard', 'storageFile'];
|
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
|
||||||
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
|
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
|
||||||
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
|
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
|
||||||
|
|
||||||
|
|
|
||||||
2
core
|
|
@ -1 +1 @@
|
||||||
Subproject commit e65920a91f9f7178c9d8ed6551ac7d9af0a5d6e1
|
Subproject commit 7d04c488496c873f392c5a068f72a6c75df40f70
|
||||||