Merge remote-tracking branch 'upstream/master'
138
apps.json
|
|
@ -1,8 +1,8 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": "fwupdate",
|
"id": "fwupdate",
|
||||||
"name": "Firmware Update (BETA)",
|
"name": "Firmware Update",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "Uploads new Espruino firmwares to Bangle.js 2",
|
"description": "Uploads new Espruino firmwares to Bangle.js 2",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "RAM",
|
"type": "RAM",
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
{
|
{
|
||||||
"id": "boot",
|
"id": "boot",
|
||||||
"name": "Bootloader",
|
"name": "Bootloader",
|
||||||
"version": "0.37",
|
"version": "0.38",
|
||||||
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
||||||
"icon": "bootloader.png",
|
"icon": "bootloader.png",
|
||||||
"type": "bootloader",
|
"type": "bootloader",
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
{
|
{
|
||||||
"id": "messages",
|
"id": "messages",
|
||||||
"name": "Messages",
|
"name": "Messages",
|
||||||
"version": "0.09",
|
"version": "0.11",
|
||||||
"description": "App to display notifications from iOS and Gadgetbridge",
|
"description": "App to display notifications from iOS and Gadgetbridge",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
"name": "Android Integration",
|
"name": "Android Integration",
|
||||||
"shortName": "Android",
|
"shortName": "Android",
|
||||||
"version": "0.04",
|
"version": "0.04",
|
||||||
"description": "(BETA) App to display notifications from Gadgetbridge on Android. This will eventually replace the Gadgetbridge widget.",
|
"description": "Display notifications/music/etc from Gadgetbridge on Android. This replaces the old Gadgetbridge widget.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,system,messages,notifications",
|
"tags": "tool,system,messages,notifications",
|
||||||
"dependencies": {"messages":"app"},
|
"dependencies": {"messages":"app"},
|
||||||
|
|
@ -95,8 +95,8 @@
|
||||||
{
|
{
|
||||||
"id": "ios",
|
"id": "ios",
|
||||||
"name": "iOS Integration",
|
"name": "iOS Integration",
|
||||||
"version": "0.04",
|
"version": "0.06",
|
||||||
"description": "(BETA) App to display notifications from iOS devices",
|
"description": "Display notifications/music/etc from iOS devices",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,system,ios,apple,messages,notifications",
|
"tags": "tool,system,ios,apple,messages,notifications",
|
||||||
"dependencies": {"messages":"app"},
|
"dependencies": {"messages":"app"},
|
||||||
|
|
@ -146,7 +146,7 @@
|
||||||
{
|
{
|
||||||
"id": "setting",
|
"id": "setting",
|
||||||
"name": "Settings",
|
"name": "Settings",
|
||||||
"version": "0.35",
|
"version": "0.36",
|
||||||
"description": "A menu for setting up Bangle.js",
|
"description": "A menu for setting up Bangle.js",
|
||||||
"icon": "settings.png",
|
"icon": "settings.png",
|
||||||
"tags": "tool,system",
|
"tags": "tool,system",
|
||||||
|
|
@ -197,7 +197,7 @@
|
||||||
{
|
{
|
||||||
"id": "locale",
|
"id": "locale",
|
||||||
"name": "Languages",
|
"name": "Languages",
|
||||||
"version": "0.11",
|
"version": "0.13",
|
||||||
"description": "Translations for different countries",
|
"description": "Translations for different countries",
|
||||||
"icon": "locale.png",
|
"icon": "locale.png",
|
||||||
"type": "locale",
|
"type": "locale",
|
||||||
|
|
@ -283,7 +283,7 @@
|
||||||
"id": "gbridge",
|
"id": "gbridge",
|
||||||
"name": "Gadgetbridge",
|
"name": "Gadgetbridge",
|
||||||
"version": "0.24",
|
"version": "0.24",
|
||||||
"description": "The default notification handler for Gadgetbridge notifications from Android. This will eventually be replaced by the 'Android' app.",
|
"description": "(NOT RECOMMENDED) Handles Gadgetbridge notifications from Android. This is now replaced by the 'Android' app.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
"tags": "tool,system,android,widget",
|
"tags": "tool,system,android,widget",
|
||||||
|
|
@ -727,7 +727,7 @@
|
||||||
{
|
{
|
||||||
"id": "gpsrec",
|
"id": "gpsrec",
|
||||||
"name": "GPS Recorder",
|
"name": "GPS Recorder",
|
||||||
"version": "0.26",
|
"version": "0.27",
|
||||||
"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",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,outdoors,gps,widget",
|
"tags": "tool,outdoors,gps,widget",
|
||||||
|
|
@ -824,7 +824,7 @@
|
||||||
{
|
{
|
||||||
"id": "weather",
|
"id": "weather",
|
||||||
"name": "Weather",
|
"name": "Weather",
|
||||||
"version": "0.11",
|
"version": "0.12",
|
||||||
"description": "Show Gadgetbridge weather report",
|
"description": "Show Gadgetbridge weather report",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
|
|
@ -1127,7 +1127,7 @@
|
||||||
{
|
{
|
||||||
"id": "qrcode",
|
"id": "qrcode",
|
||||||
"name": "Custom QR Code",
|
"name": "Custom QR Code",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Use this to upload a customised QR code to Bangle.js",
|
"description": "Use this to upload a customised QR code to Bangle.js",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "qrcode",
|
"tags": "qrcode",
|
||||||
|
|
@ -1755,7 +1755,7 @@
|
||||||
"id": "cliock",
|
"id": "cliock",
|
||||||
"name": "Commandline-Clock",
|
"name": "Commandline-Clock",
|
||||||
"shortName": "CLI-Clock",
|
"shortName": "CLI-Clock",
|
||||||
"version": "0.14",
|
"version": "0.15",
|
||||||
"description": "Simple CLI-Styled Clock",
|
"description": "Simple CLI-Styled Clock",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot_cli.png"}],
|
"screenshots": [{"url":"screenshot_cli.png"}],
|
||||||
|
|
@ -1937,6 +1937,19 @@
|
||||||
{"name":"widmp.wid.js","url":"widget.js"}
|
{"name":"widmp.wid.js","url":"widget.js"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "widmpsh",
|
||||||
|
"name": "Moon Phase Widget Southern Hemisphere",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "Display the current moon phase in blueish for the southern hemisphere in eight phases",
|
||||||
|
"icon": "widget.png",
|
||||||
|
"type": "widget",
|
||||||
|
"tags": "widget,tools",
|
||||||
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"widmpsh.wid.js","url":"widget.js"}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "minionclk",
|
"id": "minionclk",
|
||||||
"name": "Minion clock",
|
"name": "Minion clock",
|
||||||
|
|
@ -2086,12 +2099,12 @@
|
||||||
"id": "numerals",
|
"id": "numerals",
|
||||||
"name": "Numerals Clock",
|
"name": "Numerals Clock",
|
||||||
"shortName": "Numerals Clock",
|
"shortName": "Numerals Clock",
|
||||||
"version": "0.09",
|
"version": "0.10",
|
||||||
"description": "A simple big numerals clock",
|
"description": "A simple big numerals clock",
|
||||||
"icon": "numerals.png",
|
"icon": "numerals.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "numerals,clock",
|
"tags": "numerals,clock",
|
||||||
"supports": ["BANGLEJS"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"screenshots": [{"url":"bangle1-numerals-screenshot.png"}],
|
"screenshots": [{"url":"bangle1-numerals-screenshot.png"}],
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
@ -2393,7 +2406,7 @@
|
||||||
{
|
{
|
||||||
"id": "calendar",
|
"id": "calendar",
|
||||||
"name": "Calendar",
|
"name": "Calendar",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Simple calendar",
|
"description": "Simple calendar",
|
||||||
"icon": "calendar.png",
|
"icon": "calendar.png",
|
||||||
"screenshots": [{"url":"screenshot_calendar.png"}],
|
"screenshots": [{"url":"screenshot_calendar.png"}],
|
||||||
|
|
@ -2403,8 +2416,10 @@
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"calendar.app.js","url":"calendar.js"},
|
{"name":"calendar.app.js","url":"calendar.js"},
|
||||||
|
{"name":"calendar.settings.js","url":"settings.js"},
|
||||||
{"name":"calendar.img","url":"calendar-icon.js","evaluate":true}
|
{"name":"calendar.img","url":"calendar-icon.js","evaluate":true}
|
||||||
]
|
],
|
||||||
|
"data": [{"name":"calendar.json"}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "hidjoystick",
|
"id": "hidjoystick",
|
||||||
|
|
@ -2642,12 +2657,12 @@
|
||||||
"id": "widviz",
|
"id": "widviz",
|
||||||
"name": "Widget Visibility Widget",
|
"name": "Widget Visibility Widget",
|
||||||
"shortName": "Viz Widget",
|
"shortName": "Viz Widget",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Swipe left to hide top bar widgets, swipe right to redisplay.",
|
"description": "Swipe left to hide top bar widgets, swipe right to redisplay.",
|
||||||
"icon": "eye.png",
|
"icon": "eye.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
"tags": "widget",
|
"tags": "widget",
|
||||||
"supports": ["BANGLEJS"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"widviz.wid.js","url":"widget.js"}
|
{"name":"widviz.wid.js","url":"widget.js"}
|
||||||
]
|
]
|
||||||
|
|
@ -3780,7 +3795,7 @@
|
||||||
"id": "gbmusic",
|
"id": "gbmusic",
|
||||||
"name": "Gadgetbridge Music Controls",
|
"name": "Gadgetbridge Music Controls",
|
||||||
"shortName": "Music Controls",
|
"shortName": "Music Controls",
|
||||||
"version": "0.07",
|
"version": "0.08",
|
||||||
"description": "Control the music on your Gadgetbridge-connected phone",
|
"description": "Control the music on your Gadgetbridge-connected phone",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"screenshots": [{"url":"screenshot_v1.png"},{"url":"screenshot_v2.png"}],
|
"screenshots": [{"url":"screenshot_v1.png"},{"url":"screenshot_v2.png"}],
|
||||||
|
|
@ -4080,7 +4095,7 @@
|
||||||
{"name":"carcrazy.img","url":"app-icon.js","evaluate":true},
|
{"name":"carcrazy.img","url":"app-icon.js","evaluate":true},
|
||||||
{"name":"carcrazy.settings.js","url":"settings.js"}
|
{"name":"carcrazy.settings.js","url":"settings.js"}
|
||||||
],
|
],
|
||||||
"data": [{"name":"app.json"}]
|
"data": [{"name":"CarCrazy.csv"}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "shortcuts",
|
"id": "shortcuts",
|
||||||
|
|
@ -4387,7 +4402,7 @@
|
||||||
"id": "emojuino",
|
"id": "emojuino",
|
||||||
"name": "Emojuino",
|
"name": "Emojuino",
|
||||||
"shortName": "Emojuino",
|
"shortName": "Emojuino",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Emojis & Espruino: broadcast Unicode emojis via Bluetooth Low Energy.",
|
"description": "Emojis & Espruino: broadcast Unicode emojis via Bluetooth Low Energy.",
|
||||||
"icon": "emojuino.png",
|
"icon": "emojuino.png",
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
|
|
@ -4409,7 +4424,7 @@
|
||||||
"id": "cliclockJS2Enhanced",
|
"id": "cliclockJS2Enhanced",
|
||||||
"name": "Commandline-Clock JS2 Enhanced",
|
"name": "Commandline-Clock JS2 Enhanced",
|
||||||
"shortName": "CLI-Clock JS2",
|
"shortName": "CLI-Clock JS2",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Simple CLI-Styled Clock with enhancements. Modes that are hard to use and unneded are removed (BPM, battery info, memory ect) credit to hughbarney for the original code and design. Also added HID media controlls, just swipe on the clock face to controll the media! Gadgetbride support coming soon(hopefully) Thanks to t0m1o1 for media controls!",
|
"description": "Simple CLI-Styled Clock with enhancements. Modes that are hard to use and unneded are removed (BPM, battery info, memory ect) credit to hughbarney for the original code and design. Also added HID media controlls, just swipe on the clock face to controll the media! Gadgetbride support coming soon(hopefully) Thanks to t0m1o1 for media controls!",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screengrab.png"}],
|
"screenshots": [{"url":"screengrab.png"}],
|
||||||
|
|
@ -4522,7 +4537,7 @@
|
||||||
{"name":"schoolCalendar.img","url":"app-icon.js","evaluate":true}
|
{"name":"schoolCalendar.img","url":"app-icon.js","evaluate":true}
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
{"name":"app.json"}
|
{"name":"calendarItems.csv"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ "id": "timecal",
|
{ "id": "timecal",
|
||||||
|
|
@ -4575,7 +4590,7 @@
|
||||||
"shortName":"93 Dub",
|
"shortName":"93 Dub",
|
||||||
"icon": "93dub.png",
|
"icon": "93dub.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
"version":"0.04",
|
"version":"0.05",
|
||||||
"description": "Fan recreation of orviwan's 91 Dub app for the Pebble smartwatch. Uses assets from his 91-Dub-v2.0 repo",
|
"description": "Fan recreation of orviwan's 91 Dub app for the Pebble smartwatch. Uses assets from his 91-Dub-v2.0 repo",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
@ -4593,9 +4608,10 @@
|
||||||
"version":"0.01",
|
"version":"0.01",
|
||||||
"description": "Simple app to power off your Bangle.js",
|
"description": "Simple app to power off your Bangle.js",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "poweroff, shutdown",
|
"tags": "tool, poweroff, shutdown",
|
||||||
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"poweroff.app.js","url":"app.js"},
|
{"name":"poweroff.app.js","url":"app.js"},
|
||||||
{"name":"poweroff.img","url":"app-icon.js","evaluate":true}
|
{"name":"poweroff.img","url":"app-icon.js","evaluate":true}
|
||||||
|
|
@ -4605,9 +4621,17 @@
|
||||||
"id": "sensible",
|
"id": "sensible",
|
||||||
"name": "SensiBLE",
|
"name": "SensiBLE",
|
||||||
"shortName": "SensiBLE",
|
"shortName": "SensiBLE",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Collect, display and advertise real-time sensor data.",
|
"description": "Collect, display and advertise real-time sensor data.",
|
||||||
"icon": "sensible.png",
|
"icon": "sensible.png",
|
||||||
|
"screenshots": [
|
||||||
|
{ "url": "screenshot-top.png" },
|
||||||
|
{ "url": "screenshot-acc.png" },
|
||||||
|
{ "url": "screenshot-bar.png" },
|
||||||
|
{ "url": "screenshot-gps.png" },
|
||||||
|
{ "url": "screenshot-hrm.png" },
|
||||||
|
{ "url": "screenshot-mag.png" }
|
||||||
|
],
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "tool,sensors",
|
"tags": "tool,sensors",
|
||||||
"supports" : [ "BANGLEJS2" ],
|
"supports" : [ "BANGLEJS2" ],
|
||||||
|
|
@ -4670,7 +4694,7 @@
|
||||||
"id": "pebble",
|
"id": "pebble",
|
||||||
"name": "Pebble Clock",
|
"name": "Pebble Clock",
|
||||||
"shortName": "Pebble",
|
"shortName": "Pebble",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "A pebble style clock to keep the rebellion going",
|
"description": "A pebble style clock to keep the rebellion going",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "pebble.png",
|
"icon": "pebble.png",
|
||||||
|
|
@ -4722,7 +4746,7 @@
|
||||||
{
|
{
|
||||||
"id": "weatherClock",
|
"id": "weatherClock",
|
||||||
"name": "Weather Clock",
|
"name": "Weather Clock",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "A clock which displays current weather conditions (requires Gadgetbridge and Weather apps).",
|
"description": "A clock which displays current weather conditions (requires Gadgetbridge and Weather apps).",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screens/screen1.png"}],
|
"screenshots": [{"url":"screens/screen1.png"}],
|
||||||
|
|
@ -4781,5 +4805,57 @@
|
||||||
{"name": "flow.app.js", "url": "app.js" },
|
{"name": "flow.app.js", "url": "app.js" },
|
||||||
{"name": "flow.img", "url": "app-icon.js","evaluate": true }
|
{"name": "flow.img", "url": "app-icon.js","evaluate": true }
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
{ "id": "scribble",
|
||||||
|
"name": "Scribble",
|
||||||
|
"shortName":"Scribble",
|
||||||
|
"version":"0.01",
|
||||||
|
"type": "app",
|
||||||
|
"description": "A keyboard on your wrist! Swipe right for space, left for delete.",
|
||||||
|
"icon": "app.png",
|
||||||
|
"allow_emulator": true,
|
||||||
|
"tags": "tools, keyboard, text, scribble",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"scribble.app.js","url":"app.js"},
|
||||||
|
{"name":"scribble.img","url":"app-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"screenshots":[
|
||||||
|
{ "url":"screenshot.png" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ptlaunch",
|
||||||
|
"name": "Pattern Launcher",
|
||||||
|
"shortName": "Pattern Launcher",
|
||||||
|
"version": "0.02",
|
||||||
|
"description": "Directly launch apps from the clock screen with custom patterns.",
|
||||||
|
"icon": "app.png",
|
||||||
|
"tags": "tools",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{ "name": "ptlaunch.app.js", "url": "app.js" },
|
||||||
|
{ "name": "ptlaunch.boot.js", "url": "boot.js" },
|
||||||
|
{ "name": "ptlaunch.img", "url": "app-icon.js", "evaluate": true }
|
||||||
|
],
|
||||||
|
"data": [{"name":"ptlaunch.patterns.json"}]
|
||||||
|
},
|
||||||
|
{ "id": "clicompleteclk",
|
||||||
|
"name": "CLI complete clock",
|
||||||
|
"shortName":"CLI cmplt clock",
|
||||||
|
"version":"0.02",
|
||||||
|
"description": "Command line styled clock with lots of information",
|
||||||
|
"icon": "app.png",
|
||||||
|
"allow_emulator": true,
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock,cli,command,bash,shell,weather,hrt",
|
||||||
|
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"clicompleteclk.app.js","url":"app.js"},
|
||||||
|
{"name":"clicompleteclk.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,4 @@
|
||||||
0.02: DiscoMinotaur's adjustments (removed battery and adjusted spacing)
|
0.02: DiscoMinotaur's adjustments (removed battery and adjusted spacing)
|
||||||
0.03: Code style cleanup
|
0.03: Code style cleanup
|
||||||
0.04: Set 00:00 to 12:00 for 12 hour time
|
0.04: Set 00:00 to 12:00 for 12 hour time
|
||||||
|
0.05: Display time, even on Thursday
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
Uses many portions from Espruino documentation, example watchfaces, and the waveclk app. It also sourced from Jon Barlow's 91 Dub v2.0 source code and resources and adapted for Bangle.js 2's screen. Time, date and the battery display works. It is not pixel perfect to the original.
|
Uses many portions from Espruino documentation, example watchfaces, and the waveclk app. It also sourced from Jon Barlow's 91 Dub v2.0 source code and resources and adapted for Bangle.js 2's screen. Time, date and the battery display works. It is not pixel perfect to the original.
|
||||||
|
|
||||||
Contributors:
|
Contributors:
|
||||||
Leer10
|
* Leer10
|
||||||
Orviwan (original watchface and assets)
|
* Orviwan (original watchface and assets)
|
||||||
Gordon Williams (Bangle.js, watchapps for reference code and documentation)
|
* Gordon Williams (Bangle.js, watchapps for reference code and documentation)
|
||||||
DiscoMinotaur (adjustments)
|
* DiscoMinotaur (adjustments)
|
||||||
Ray Holder (minor 12 hour time rendering adjustment)
|
* Ray Holder (minor 12 hour time rendering adjustment, fix Thursdays)
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ function draw(){
|
||||||
if (w == 1) {imgW = imgMon;}
|
if (w == 1) {imgW = imgMon;}
|
||||||
if (w == 2) {imgW = imgTue;}
|
if (w == 2) {imgW = imgTue;}
|
||||||
if (w == 3) {imgW = imgWed;}
|
if (w == 3) {imgW = imgWed;}
|
||||||
if (w == 4) {imgW = imgThr;}
|
if (w == 4) {imgW = imgThu;}
|
||||||
if (w == 5) {imgW = imgFri;}
|
if (w == 5) {imgW = imgFri;}
|
||||||
if (w == 6) {imgW = imgSat;}
|
if (w == 6) {imgW = imgSat;}
|
||||||
g.drawImage(imgW, 85, 63);
|
g.drawImage(imgW, 85, 63);
|
||||||
|
|
|
||||||
|
|
@ -41,3 +41,4 @@
|
||||||
Don't set beep vibration up on Bangle.js 2 (built in)
|
Don't set beep vibration up on Bangle.js 2 (built in)
|
||||||
0.36: Add comments to .boot0 to make debugging a bit easier
|
0.36: Add comments to .boot0 to make debugging a bit easier
|
||||||
0.37: Remove Quiet Mode settings: now handled by Quiet Mode Schedule app
|
0.37: Remove Quiet Mode settings: now handled by Quiet Mode Schedule app
|
||||||
|
0.38: Option to log to file if settings.log==2
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,14 @@ if (s.ble!==false) {
|
||||||
boot += `bleServiceOptions.hid=Bangle.HID;\n`;
|
boot += `bleServiceOptions.hid=Bangle.HID;\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
|
if (s.log==2) { // logging to file
|
||||||
if (s.log) boot += `Terminal.setConsole(true);\n`; // if showing debug, force REPL onto terminal
|
boot += `_DBGLOG=require("Storage").open("log.txt","a");
|
||||||
|
`;
|
||||||
|
} if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
|
||||||
|
if (s.log==2) boot += `_DBGLOG=require("Storage").open("log.txt","a");
|
||||||
|
LoopbackB.on('data',function(d) {_DBGLOG.write(d);Terminal.write(d);});
|
||||||
|
LoopbackA.setConsole(true);\n`;
|
||||||
|
else if (s.log) boot += `Terminal.setConsole(true);\n`; // if showing debug, force REPL onto terminal
|
||||||
else boot += `E.setConsole(null,{force:true});\n`; // on new (2v05+) firmware we have E.setConsole which allows a 'null' console
|
else boot += `E.setConsole(null,{force:true});\n`; // on new (2v05+) firmware we have E.setConsole which allows a 'null' console
|
||||||
/* If not programmable add our own handler for Bluetooth data
|
/* If not programmable add our own handler for Bluetooth data
|
||||||
to allow Gadgetbridge commands to be received*/
|
to allow Gadgetbridge commands to be received*/
|
||||||
|
|
@ -41,7 +47,10 @@ Bluetooth.on('line',function(l) {
|
||||||
try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {}
|
try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {}
|
||||||
});\n`;
|
});\n`;
|
||||||
} else {
|
} else {
|
||||||
if (s.log) boot += `if (!NRF.getSecurityStatus().connected) Terminal.setConsole();\n`; // if showing debug, put REPL on terminal (until connection)
|
if (s.log==2) boot += `_DBGLOG=require("Storage").open("log.txt","a");
|
||||||
|
LoopbackB.on('data',function(d) {_DBGLOG.write(d);Terminal.write(d);});
|
||||||
|
if (!NRF.getSecurityStatus().connected) LoopbackA.setConsole();\n`;
|
||||||
|
else if (s.log) boot += `if (!NRF.getSecurityStatus().connected) Terminal.setConsole();\n`; // if showing debug, put REPL on terminal (until connection)
|
||||||
else boot += `Bluetooth.setConsole(true);\n`; // else if no debug, force REPL to Bluetooth
|
else boot += `Bluetooth.setConsole(true);\n`; // else if no debug, force REPL to Bluetooth
|
||||||
}
|
}
|
||||||
// we just reset, so BLE should be on.
|
// we just reset, so BLE should be on.
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: Basic calendar
|
0.01: Basic calendar
|
||||||
0.02: Make Bangle 2 compatible
|
0.02: Make Bangle 2 compatible
|
||||||
|
0.03: Add setting to start week on Sunday
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,8 @@ Basic calendar
|
||||||
|
|
||||||
- Use `BTN4` (left screen tap) to go to the previous month
|
- Use `BTN4` (left screen tap) to go to the previous month
|
||||||
- Use `BTN5` (right screen tap) to go to the next month
|
- Use `BTN5` (right screen tap) to go to the next month
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
- Starts on Sunday: whether the calendar should start on Sunday (default is Monday).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ const gray2 = "#888888";
|
||||||
const gray3 = "#bbbbbb";
|
const gray3 = "#bbbbbb";
|
||||||
const red = "#d41706";
|
const red = "#d41706";
|
||||||
|
|
||||||
|
let settings = require('Storage').readJSON("calendar.json", true) || {};
|
||||||
|
if (settings.startOnSun === undefined)
|
||||||
|
settings.startOnSun = false;
|
||||||
|
|
||||||
function drawCalendar(date) {
|
function drawCalendar(date) {
|
||||||
g.setBgColor(color4);
|
g.setBgColor(color4);
|
||||||
g.clearRect(0, 0, maxX, maxY);
|
g.clearRect(0, 0, maxX, maxY);
|
||||||
|
|
@ -61,13 +65,18 @@ function drawCalendar(date) {
|
||||||
);
|
);
|
||||||
|
|
||||||
g.setFont("6x8", fontSize);
|
g.setFont("6x8", fontSize);
|
||||||
const dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
|
let dowLbls;
|
||||||
|
if (settings.startOnSun) {
|
||||||
|
dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
||||||
|
} else {
|
||||||
|
dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
|
||||||
|
}
|
||||||
dowLbls.forEach((lbl, i) => {
|
dowLbls.forEach((lbl, i) => {
|
||||||
g.drawString(lbl, i * colW + colW / 2, headerH + rowH / 2);
|
g.drawString(lbl, i * colW + colW / 2, headerH + rowH / 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
date.setDate(1);
|
date.setDate(1);
|
||||||
const dow = date.getDay();
|
const dow = date.getDay() + (settings.startOnSun ? 1 : 0);
|
||||||
const dowNorm = dow === 0 ? 7 : dow;
|
const dowNorm = dow === 0 ? 7 : dow;
|
||||||
|
|
||||||
const monthMaxDayMap = {
|
const monthMaxDayMap = {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
(function(back) {
|
||||||
|
var FILE = "calendar.json";
|
||||||
|
var settings = require('Storage').readJSON(FILE, true) || {};
|
||||||
|
if (settings.startOnSun === undefined)
|
||||||
|
settings.startOnSun = true;
|
||||||
|
|
||||||
|
function writeSettings() {
|
||||||
|
require('Storage').writeJSON(FILE, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu({
|
||||||
|
"" : { "title" : "Calendar" },
|
||||||
|
"< Back" : () => back(),
|
||||||
|
'Start on Sunday': {
|
||||||
|
value: settings.startOnSun,
|
||||||
|
format: v => v?"Yes":"No",
|
||||||
|
onchange: v => {
|
||||||
|
settings.startOnSun = v;
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: Submitted to App Loader
|
0.01: Submitted to App Loader
|
||||||
0.02: Removed unneded code, added HID controlls thanks to t0m1o1 for his code :p
|
0.02: Removed unneded code, added HID controlls thanks to t0m1o1 for his code :p
|
||||||
|
0.03: Load widgets after Bangle.setUI to ensure widgets know if they're on a clock or not (fix #970)
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ if (next) {
|
||||||
setTimeout(drawApp, 1000);
|
setTimeout(drawApp, 1000);
|
||||||
Bangle.setLocked(true);
|
Bangle.setLocked(true);
|
||||||
}, BTN1, { edge:"falling",repeat:true,debounce:50});
|
}, BTN1, { edge:"falling",repeat:true,debounce:50});
|
||||||
Bangle.on('drag', function(e) {
|
Bangle.on('drag', function(e) {
|
||||||
if(!e.b){
|
if(!e.b){
|
||||||
console.log(lasty);
|
console.log(lasty);
|
||||||
console.log(lastx);
|
console.log(lastx);
|
||||||
|
|
@ -91,7 +91,7 @@ if (next) {
|
||||||
lasty = lasty + e.dy;
|
lasty = lasty + e.dy;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -144,14 +144,15 @@ function writeLine(str,line){
|
||||||
}
|
}
|
||||||
|
|
||||||
g.clear();
|
g.clear();
|
||||||
Bangle.loadWidgets();
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
drawAll();
|
|
||||||
Bangle.on('lcdPower',function(on) {
|
Bangle.on('lcdPower',function(on) {
|
||||||
if (on) drawAll();
|
if (on) drawAll();
|
||||||
});
|
});
|
||||||
var click = setInterval(updateTime, 1000);
|
var click = setInterval(updateTime, 1000);
|
||||||
// Show launcher when button pressed
|
// Show launcher when button pressed
|
||||||
Bangle.setUI("clockupdown", btn=>{
|
Bangle.setUI("clockupdown", btn=>{
|
||||||
drawAll();
|
drawAll(); // why do we redraw here??
|
||||||
});
|
});
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
drawAll();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: New clock!
|
||||||
|
0.02: Load steps from Health Tracking app (if installed)
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Command line complete clock
|
||||||
|
|
||||||
|
Command line styled clock with lots of information:
|
||||||
|
|
||||||
|
It can show the following (depending on availability) information:
|
||||||
|
* Time
|
||||||
|
* Day of week
|
||||||
|
* Date
|
||||||
|
* Weather conditions and temperature (requires app [Weather](https://banglejs.com/apps/#weather))
|
||||||
|
* Steps (requires app [Health Tracking](https://banglejs.com/apps/#health%20tracking) or a step widget)
|
||||||
|
* Heart rate (when screen is on and unlocked)
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
* Make time font bigger
|
||||||
|
* Show progress of steps (if any goal is set)
|
||||||
|
* Show trend of HRM out of history data
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
Marco ([myxor](https://github.com/myxor))
|
||||||
|
|
||||||
|
## Icon
|
||||||
|
Icon taken from [materialdesignicons](https://materialdesignicons.com) under Apache License 2.0
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwgI/8/4ACAqYv/F/PwAqgA6A=="))
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
const storage = require('Storage');
|
||||||
|
const locale = require("locale");
|
||||||
|
|
||||||
|
const font = "12x20";
|
||||||
|
const fontsize = 1;
|
||||||
|
const fontheight = 19;
|
||||||
|
|
||||||
|
const marginTop = 10;
|
||||||
|
const marginLeftTopic = 3; // margin of topics
|
||||||
|
const marginLeftData = 68; // margin of data values
|
||||||
|
|
||||||
|
const topicColor = g.theme.dark ? "#fff" : "#000";
|
||||||
|
const textColor = g.theme.dark ? "#0f0" : "#080";
|
||||||
|
|
||||||
|
let hrtValue;
|
||||||
|
let hrtValueIsOld = false;
|
||||||
|
let localTempValue;
|
||||||
|
let weatherTempString;
|
||||||
|
let lastHeartRateRowIndex;
|
||||||
|
|
||||||
|
// timeout used to update every minute
|
||||||
|
var drawTimeout;
|
||||||
|
// schedule a draw for the next minute
|
||||||
|
function queueDraw() {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function() {
|
||||||
|
drawTimeout = undefined;
|
||||||
|
drawAll(false);
|
||||||
|
}, 60000 - (Date.now() % 60000));
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawAll(drawInfoToo){
|
||||||
|
let now = new Date();
|
||||||
|
updateTime(now);
|
||||||
|
if (drawInfoToo) {
|
||||||
|
drawInfo(now);
|
||||||
|
}
|
||||||
|
queueDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTime(now){
|
||||||
|
if (!Bangle.isLCDOn()) return;
|
||||||
|
writeLineTopic("TIME", 1);
|
||||||
|
writeLine(locale.time(now,1),1);
|
||||||
|
if(now.getMinutes() == 0)
|
||||||
|
drawInfo(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInfo(now) {
|
||||||
|
if (now == undefined)
|
||||||
|
now = new Date();
|
||||||
|
|
||||||
|
let i = 2;
|
||||||
|
|
||||||
|
writeLineTopic("DOWK", i);
|
||||||
|
writeLine(locale.dow(now),i);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
writeLineTopic("DATE", i);
|
||||||
|
writeLine(locale.date(now,1),i);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
writeLineTopic("BAT", i);
|
||||||
|
const b = E.getBattery();
|
||||||
|
writeLine(b + "%", i); // TODO make bars
|
||||||
|
i++;
|
||||||
|
*/
|
||||||
|
|
||||||
|
// weather
|
||||||
|
const weatherJson = getWeather();
|
||||||
|
if(weatherJson && weatherJson.weather){
|
||||||
|
const currentWeather = weatherJson.weather;
|
||||||
|
|
||||||
|
const weatherTempValue = locale.temp(currentWeather.temp-273.15);
|
||||||
|
weatherTempString = weatherTempValue;
|
||||||
|
writeLineTopic("WTHR", i);
|
||||||
|
writeLine(currentWeather.txt,i);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
writeLineTopic("TEMP", i);
|
||||||
|
writeLine(weatherTempValue,i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// steps
|
||||||
|
const steps = getSteps();
|
||||||
|
if (steps != undefined) {
|
||||||
|
writeLineTopic("STEP", i);
|
||||||
|
writeLine(steps, i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawHeartRate(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawHeartRate(i) {
|
||||||
|
if (i == undefined)
|
||||||
|
i = lastHeartRateRowIndex;
|
||||||
|
writeLineTopic("HRTM", i);
|
||||||
|
if (hrtValue != undefined) {
|
||||||
|
if (!hrtValueIsOld)
|
||||||
|
writeLine(hrtValue,i);
|
||||||
|
else
|
||||||
|
writeLine(hrtValue,i, topicColor);
|
||||||
|
}
|
||||||
|
lastHeartRateRowIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function writeLineTopic(str, line) {
|
||||||
|
var y = marginTop+line*fontheight;
|
||||||
|
g.setFont(font,fontsize);
|
||||||
|
g.setColor(topicColor).setFontAlign(-1,-1);
|
||||||
|
|
||||||
|
g.clearRect(0,y,g.getWidth(),y+fontheight-1);
|
||||||
|
g.drawString("[" + str + "]",marginLeftTopic,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeLine(str,line,pColor){
|
||||||
|
if (pColor == undefined)
|
||||||
|
pColor = textColor;
|
||||||
|
var y = marginTop+line*fontheight;
|
||||||
|
g.setFont(font,fontsize);
|
||||||
|
g.setColor(pColor).setFontAlign(-1,-1);
|
||||||
|
g.drawString(str,marginLeftData,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getSteps() {
|
||||||
|
var steps = 0;
|
||||||
|
let health;
|
||||||
|
try {
|
||||||
|
health = require("health");
|
||||||
|
} catch (e) {
|
||||||
|
// Module health not found
|
||||||
|
}
|
||||||
|
if (health != undefined) {
|
||||||
|
health.readDay(new Date(), h=>steps+=h.steps);
|
||||||
|
} else if (WIDGETS.wpedom !== undefined) {
|
||||||
|
return WIDGETS.wpedom.getSteps();
|
||||||
|
} else if (WIDGETS.activepedom !== undefined) {
|
||||||
|
return WIDGETS.activepedom.getSteps();
|
||||||
|
}
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWeather() {
|
||||||
|
let jsonWeather = storage.readJSON('weather.json');
|
||||||
|
return jsonWeather;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EVENTS:
|
||||||
|
|
||||||
|
// turn on HRM when the LCD is unlocked
|
||||||
|
Bangle.on('lock', function(isLocked) {
|
||||||
|
if (!isLocked) {
|
||||||
|
Bangle.setHRMPower(1,"clicompleteclk");
|
||||||
|
if (hrtValue == undefined)
|
||||||
|
hrtValue = "...";
|
||||||
|
else
|
||||||
|
hrtValueIsOld = true;
|
||||||
|
} else {
|
||||||
|
hrtValueIsOld = true;
|
||||||
|
Bangle.setHRMPower(0,"clicompleteclk");
|
||||||
|
}
|
||||||
|
drawHeartRate();
|
||||||
|
});
|
||||||
|
|
||||||
|
Bangle.on('lcdPower',function(on) {
|
||||||
|
if (on) {
|
||||||
|
drawAll(true);
|
||||||
|
} else {
|
||||||
|
hrtValueIsOld = true;
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Bangle.on('HRM', function(hrm) {
|
||||||
|
//if(hrm.confidence > 90){
|
||||||
|
hrtValueIsOld = false;
|
||||||
|
hrtValue = hrm.bpm;
|
||||||
|
if (Bangle.isLCDOn())
|
||||||
|
drawHeartRate();
|
||||||
|
//} else {
|
||||||
|
// hrtValue = undefined;
|
||||||
|
//}
|
||||||
|
});
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
drawAll(true);
|
||||||
|
After Width: | Height: | Size: 258 B |
|
|
@ -7,3 +7,4 @@
|
||||||
0.13: Use setUI, work with smaller screens and themes
|
0.13: Use setUI, work with smaller screens and themes
|
||||||
0.14: Fix BTN1 (fix #853)
|
0.14: Fix BTN1 (fix #853)
|
||||||
Add light/dark theme support
|
Add light/dark theme support
|
||||||
|
0.15: Load widgets after Bangle.setUI to ensure widgets know if they're on a clock or not (fix #970)
|
||||||
|
|
|
||||||
|
|
@ -183,9 +183,6 @@ Bangle.on('HRM', function(hrm) {
|
||||||
});
|
});
|
||||||
|
|
||||||
g.clear();
|
g.clear();
|
||||||
Bangle.loadWidgets();
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
drawAll();
|
|
||||||
Bangle.on('lcdPower',function(on) {
|
Bangle.on('lcdPower',function(on) {
|
||||||
if (on) drawAll();
|
if (on) drawAll();
|
||||||
});
|
});
|
||||||
|
|
@ -195,4 +192,7 @@ Bangle.setUI("clockupdown", btn=>{
|
||||||
if (btn<0) changeInfoMode();
|
if (btn<0) changeInfoMode();
|
||||||
if (btn>0) changeFunctionMode();
|
if (btn>0) changeFunctionMode();
|
||||||
drawAll();
|
drawAll();
|
||||||
});
|
});
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
drawAll();
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Upgraded text to images, added welcome screen and subtitles.
|
0.02: Upgraded text to images, added welcome screen and subtitles.
|
||||||
|
0.03: Advertise app name as Espruino manufacturer data when idle.
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ const CYCLE_BUZZ_MILLISECONDS = 50;
|
||||||
const WELCOME_MESSAGE = 'Emojuino:\r\n\r\n< Swipe >\r\nto select\r\n\r\nTap\r\nto transmit';
|
const WELCOME_MESSAGE = 'Emojuino:\r\n\r\n< Swipe >\r\nto select\r\n\r\nTap\r\nto transmit';
|
||||||
|
|
||||||
// Non-user-configurable constants
|
// Non-user-configurable constants
|
||||||
|
const APP_ID = 'emojuino';
|
||||||
const IMAGE_INDEX = 0;
|
const IMAGE_INDEX = 0;
|
||||||
const CODE_POINT_INDEX = 1;
|
const CODE_POINT_INDEX = 1;
|
||||||
const EMOJI_PX = 96;
|
const EMOJI_PX = 96;
|
||||||
|
|
@ -40,12 +41,11 @@ const EMOJI_Y = (g.getHeight() - EMOJI_PX) / 2;
|
||||||
const TX_X = 68;
|
const TX_X = 68;
|
||||||
const TX_Y = 12;
|
const TX_Y = 12;
|
||||||
const FONT_SIZE = 24;
|
const FONT_SIZE = 24;
|
||||||
const BTN_WATCH_OPTIONS = { repeat: true, debounce: 20, edge: "falling" };
|
const ESPRUINO_COMPANY_CODE = 0x0590;
|
||||||
const UNICODE_CODE_POINT_ELIDED_UUID = [ 0x49, 0x6f, 0x49, 0x44, 0x55,
|
const UNICODE_CODE_POINT_ELIDED_UUID = [ 0x49, 0x6f, 0x49, 0x44, 0x55,
|
||||||
0x54, 0x46, 0x2d, 0x33, 0x32 ];
|
0x54, 0x46, 0x2d, 0x33, 0x32 ];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
let emojiIndex = 0;
|
let emojiIndex = 0;
|
||||||
let isToggleOn = false;
|
let isToggleOn = false;
|
||||||
|
|
@ -100,9 +100,22 @@ function transmitEmoji(image, codePoint, duration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transmit the app name under the Espruino company code to facilitate discovery
|
||||||
|
function transmitAppName() {
|
||||||
|
let options = {
|
||||||
|
showName: false,
|
||||||
|
manufacturer: ESPRUINO_COMPANY_CODE,
|
||||||
|
manufacturerData: JSON.stringify({ name: APP_ID }),
|
||||||
|
interval: 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
NRF.setAdvertising({}, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Terminate the emoji transmission
|
// Terminate the emoji transmission
|
||||||
function terminateEmoji(displayIntervalId) {
|
function terminateEmoji(displayIntervalId) {
|
||||||
NRF.setAdvertising({ });
|
transmitAppName();
|
||||||
isTransmitting = false;
|
isTransmitting = false;
|
||||||
clearInterval(displayIntervalId);
|
clearInterval(displayIntervalId);
|
||||||
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX], false);
|
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX], false);
|
||||||
|
|
@ -169,3 +182,4 @@ g.setFontAlign(0, 0);
|
||||||
g.drawString(WELCOME_MESSAGE, g.getWidth() / 2, g.getHeight() / 2);
|
g.drawString(WELCOME_MESSAGE, g.getWidth() / 2, g.getHeight() / 2);
|
||||||
Bangle.on('touch', handleTouch);
|
Bangle.on('touch', handleTouch);
|
||||||
Bangle.on('drag', handleDrag);
|
Bangle.on('drag', handleDrag);
|
||||||
|
transmitAppName();
|
||||||
|
|
|
||||||
|
|
@ -1 +1,4 @@
|
||||||
0.01: Initial version
|
0.01: Initial version
|
||||||
|
0.02: Add support for ZIPs
|
||||||
|
Find and download ZIPs direct from the Espruino website
|
||||||
|
Take 'beta' tag off
|
||||||
|
|
|
||||||
|
|
@ -3,29 +3,44 @@
|
||||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p><b>THIS IS CURRENTLY BETA - PLEASE USE THE NORMAL FIRMWARE UPDATE
|
|
||||||
INSTRUCTIONS FOR <a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">BANGLE.JS</a> 1 AND <a href="https://www.espruino.com/Bangle.js2#firmware-updates" target="_blank">BANGLE.JS 2</a></b></p>
|
|
||||||
<div id="fw-unknown">
|
<div id="fw-unknown">
|
||||||
<p>Firmware updates using the App Loader are only possible on
|
<p><b>Firmware updates using the App Loader are only possible on
|
||||||
Bangle.js 2. For firmware updates on Bangle.js 1 please
|
Bangle.js 2. For firmware updates on Bangle.js 1 please
|
||||||
<a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">see the Bangle.js 1 instructions</a></p>
|
<a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">see the Bangle.js 1 instructions</a></b></p>
|
||||||
</div>
|
</div>
|
||||||
|
<p>Your current firmware version is <span id="fw-version" style="font-weight:bold">unknown</span></p>
|
||||||
<div id="fw-ok" style="display:none">
|
<div id="fw-ok" style="display:none">
|
||||||
<p>Please upload a hex file here. This file should be the <code>.app_hex</code>
|
<div id="latest-firmware" style="display:none">
|
||||||
|
<p>The currently available Espruino firmware releases are:</p>
|
||||||
|
<ul id="latest-firmware-list">
|
||||||
|
</ul>
|
||||||
|
<p>To update, click the link and then click the 'Upload' button that appears.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>Or you can upload a hex or zip file here. This file should be an <code>.app_hex</code>
|
||||||
file, *not* the normal <code>.hex</code> (as that contains the bootloader as well).</p>
|
file, *not* the normal <code>.hex</code> (as that contains the bootloader as well).</p>
|
||||||
|
|
||||||
<input class="form-input" type="file" id="fileLoader" accept=".hex,.app_hex"/><br>
|
<input class="form-input" type="file" id="fileLoader" accept=".hex,.app_hex,.zip"/><br>
|
||||||
<p><button id="upload" class="btn btn-primary">Upload</button></p>
|
<p><button id="upload" class="btn btn-primary" style="display:none">Upload</button></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p>Firmware updates via this tool work differently to the NRF Connect method mentioned on
|
||||||
|
<a href="https://www.espruino.com/Bangle.js2#firmware-updates">the Bangle.js page</a>. Firmware
|
||||||
|
is uploaded to a file on the Bangle. Once complete the Bangle reboots and the bootloader copies
|
||||||
|
the new firmware into internal Storage.</p>
|
||||||
|
|
||||||
<pre id="log"></pre>
|
<pre id="log"></pre>
|
||||||
|
|
||||||
<script src="../../core/lib/customize.js"></script>
|
<script src="../../core/lib/customize.js"></script>
|
||||||
|
<script src="../../core/lib/espruinotools.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var hex;
|
var hex;
|
||||||
var hexJS; // JS to upload hex
|
var hexJS; // JS to upload hex
|
||||||
var HEADER_LEN = 16; // size of app flash header
|
var HEADER_LEN = 16; // size of app flash header
|
||||||
|
var APP_START = 0x26000;
|
||||||
|
var APP_MAX_LENGTH = 0xda000; // from linker file - the max size the app can be, for sanity check!
|
||||||
var MAX_ADDRESS = 0x1000000; // discount anything in hex file above this
|
var MAX_ADDRESS = 0x1000000; // discount anything in hex file above this
|
||||||
var VERSION = 0x12345678; // VERSION! Use this to test firmware in JS land
|
var VERSION = 0x12345678; // VERSION! Use this to test firmware in JS land
|
||||||
var DEBUG = false;
|
var DEBUG = false;
|
||||||
|
|
@ -37,6 +52,8 @@ function log(t) {
|
||||||
|
|
||||||
function onInit(device) {
|
function onInit(device) {
|
||||||
console.log(device);
|
console.log(device);
|
||||||
|
if (device && device.version)
|
||||||
|
document.getElementById("fw-version").innerText = device.version;
|
||||||
if (device && device.id=="BANGLEJS2") {
|
if (device && device.id=="BANGLEJS2") {
|
||||||
document.getElementById("fw-unknown").style = "display:none";
|
document.getElementById("fw-unknown").style = "display:none";
|
||||||
document.getElementById("fw-ok").style = "";
|
document.getElementById("fw-ok").style = "";
|
||||||
|
|
@ -44,7 +61,7 @@ function onInit(device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkForFileOnServer() {
|
function checkForFileOnServer() {
|
||||||
/*function getURL(url, callback) {
|
function getURL(url, callback) {
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.onload = callback;
|
xhr.onload = callback;
|
||||||
baseURL = url;
|
baseURL = url;
|
||||||
|
|
@ -55,6 +72,7 @@ function checkForFileOnServer() {
|
||||||
|
|
||||||
function getFilesFromURL(url, regex, callback) {
|
function getFilesFromURL(url, regex, callback) {
|
||||||
getURL(url, function() {
|
getURL(url, function() {
|
||||||
|
console.log(this.responseXML)
|
||||||
var files = [];
|
var files = [];
|
||||||
var elements = this.responseXML.getElementsByTagName("a");
|
var elements = this.responseXML.getElementsByTagName("a");
|
||||||
for (var i=0;i<elements.length;i++) {
|
for (var i=0;i<elements.length;i++) {
|
||||||
|
|
@ -67,35 +85,78 @@ function checkForFileOnServer() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var regex = new RegExp("_bangle2");
|
var regex = new RegExp("_banglejs2.*zip$");
|
||||||
|
|
||||||
|
var domFirmwareList = document.getElementById("latest-firmware-list");
|
||||||
|
var domFirmware = document.getElementById("latest-firmware");
|
||||||
|
console.log("Checking server...");
|
||||||
|
|
||||||
var domFirmware = document.getElementById("latest-firmware");
|
|
||||||
getFilesFromURL("https://www.espruino.com/binaries/", regex, function(releaseFiles) {
|
getFilesFromURL("https://www.espruino.com/binaries/", regex, function(releaseFiles) {
|
||||||
releaseFiles.sort().reverse().forEach(function(f) {
|
releaseFiles.sort().reverse().forEach(function(f) {
|
||||||
var name = f.substr(f.substr(0,f.length-1).lastIndexOf('/')+1);
|
var name = f.substr(f.substr(0,f.length-1).lastIndexOf('/')+1);
|
||||||
domFirmware.innerHTML += 'Release: <a href="'+f+'">'+name+'</a><br/>';
|
console.log("Found "+name);
|
||||||
|
domFirmwareList.innerHTML += '<li>Release: <a href="'+f+'" class="fw-link">'+name+'</a></li>';
|
||||||
|
domFirmware.style = "";
|
||||||
});
|
});
|
||||||
getFilesFromURL("https://www.espruino.com/binaries/travis/master/",regex, function(travisFiles) {
|
getFilesFromURL("https://www.espruino.com/binaries/travis/master/",regex, function(travisFiles) {
|
||||||
travisFiles.forEach(function(f) {
|
travisFiles.forEach(function(f) {
|
||||||
var name = f.substr(f.lastIndexOf('/')+1);
|
var name = f.substr(f.lastIndexOf('/')+1);
|
||||||
domFirmware.innerHTML += 'Cutting Edge build: <a href="'+f+'">'+name+'</a><br/>';
|
console.log("Found "+name);
|
||||||
|
domFirmwareList.innerHTML += '<li>Cutting Edge build: <a href="'+f+'" class="fw-link">'+name+'</a></li>';
|
||||||
|
domFirmware.style = "";
|
||||||
});
|
});
|
||||||
document.getElementById("checking-server").style = "display:none";
|
console.log("Finished check for firmware files...");
|
||||||
document.getElementById("main-ui").style = "";
|
var fwlinks = document.querySelectorAll(".fw-link");
|
||||||
|
for (var i=0;i<fwlinks.length;i++)
|
||||||
|
fwlinks[i].addEventListener("click", e => {
|
||||||
|
e.preventDefault();
|
||||||
|
var url = e.target.href;
|
||||||
|
downloadZipFile(url).then(info=>{
|
||||||
|
document.getElementById("upload").style = ""; // show upload
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});*/
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadFile() {
|
function downloadZipFile(url) {
|
||||||
/*response = await fetch(APP_HEX_PATH+"readlink.php?link="+APP_HEX_FILE, {
|
return new Promise((resolve,reject) => {
|
||||||
method: 'GET',
|
Espruino.Core.Utils.getBinaryURL(url, (err, binary) => {
|
||||||
cache: 'no-cache',
|
if (err) return reject("Unable to download "+url);
|
||||||
});
|
resolve(binary);
|
||||||
if (response.ok) {
|
});
|
||||||
blob = await response.blob();
|
}).then(convertZipFile);
|
||||||
data = await blob.text();
|
}
|
||||||
document.getElementById("latest-firmware").innerHTML="(<b>"+data.toString()+"</b>)";
|
|
||||||
}*/
|
function convertZipFile(binary) {
|
||||||
|
var info = {};
|
||||||
|
Promise.resolve(binary).then(binary => {
|
||||||
|
info.binary = binary;
|
||||||
|
return JSZip.loadAsync(binary)
|
||||||
|
}).then(function(zipFile) {
|
||||||
|
info.zipFile = zipFile;
|
||||||
|
return info.zipFile.file("manifest.json").async("string");
|
||||||
|
}).then(function(content) {
|
||||||
|
info.manifest = JSON.parse(content).manifest;
|
||||||
|
}).then(function(content) {
|
||||||
|
console.log(info.manifest);
|
||||||
|
return info.zipFile.file(info.manifest.application.dat_file).async("arraybuffer");
|
||||||
|
}).then(function(content) {
|
||||||
|
info.dat_file = content;
|
||||||
|
}).then(function(content) {
|
||||||
|
console.log(info.manifest);
|
||||||
|
return info.zipFile.file(info.manifest.application.bin_file).async("arraybuffer");
|
||||||
|
}).then(function(content) {
|
||||||
|
info.bin_file = content;
|
||||||
|
if (info.bin_file.byteLength > APP_MAX_LENGTH) throw new Error("Firmware file is too big!");
|
||||||
|
info.storageContents = new Uint8Array(info.bin_file.byteLength + HEADER_LEN)
|
||||||
|
info.storageContents.set(new Uint8Array(info.bin_file), HEADER_LEN);
|
||||||
|
createJS_app(info.storageContents, APP_START, APP_START+info.bin_file.byteLength);
|
||||||
|
log("Download complete");
|
||||||
|
console.log("Download complete",info);
|
||||||
|
document.getElementById("upload").style = ""; // show upload
|
||||||
|
return info;
|
||||||
|
}).catch(err => log("ERROR:" + err));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFileSelect(event) {
|
function handleFileSelect(event) {
|
||||||
|
|
@ -103,13 +164,24 @@ function handleFileSelect(event) {
|
||||||
log("More than one file selected!");
|
log("More than one file selected!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var file = event.target.files[0];
|
||||||
|
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
reader.onload = function(event) {
|
if (file.name.endsWith(".hex") || file.name.endsWith(".app_hex")) {
|
||||||
hex = event.target.result.split("\n");
|
reader.onload = function(event) {
|
||||||
document.getElementById("upload").style = ""; // show upload
|
hex = event.target.result.split("\n");
|
||||||
fileLoaded();
|
document.getElementById("upload").style = ""; // show upload
|
||||||
};
|
fileLoaded();
|
||||||
reader.readAsText(event.target.files[0]);
|
};
|
||||||
|
reader.readAsText(event.target.files[0]);
|
||||||
|
} else if (file.name.endsWith(".zip")) {
|
||||||
|
reader.onload = function(event) {
|
||||||
|
convertZipFile(event.target.result);
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(event.target.files[0]);
|
||||||
|
} else {
|
||||||
|
log("Unknown file extension for "+file.name);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -174,14 +246,16 @@ function btoa(input) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// To upload the app, we write to external flash
|
/* To upload the app, we write to external flash,
|
||||||
function createJS_app(binary, bin32, startAddress, endAddress, HEADER_LEN) {
|
binary = Uint8Array of data to flash. Should include HEADER_LEN header, then bytes to flash */
|
||||||
|
function createJS_app(binary, startAddress, endAddress) {
|
||||||
/* typedef struct {
|
/* typedef struct {
|
||||||
uint32_t address;
|
uint32_t address;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t CRC;
|
uint32_t CRC;
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
} FlashHeader; */
|
} FlashHeader; */
|
||||||
|
var bin32 = new Uint32Array(binary.buffer);
|
||||||
bin32[0] = startAddress;
|
bin32[0] = startAddress;
|
||||||
bin32[1] = endAddress - startAddress;
|
bin32[1] = endAddress - startAddress;
|
||||||
bin32[2] = CRC32(new Uint8Array(binary.buffer, HEADER_LEN));
|
bin32[2] = CRC32(new Uint8Array(binary.buffer, HEADER_LEN));
|
||||||
|
|
@ -189,7 +263,8 @@ function createJS_app(binary, bin32, startAddress, endAddress, HEADER_LEN) {
|
||||||
console.log("CRC 0x"+bin32[2].toString(16));
|
console.log("CRC 0x"+bin32[2].toString(16));
|
||||||
hexJS = "";//`\x10if (E.CRC32(E.memoryArea(${startAddress},${endAddress-startAddress}))==${bin32[2]}) { print("FIRMWARE UP TO DATE!"); load();}\n`;
|
hexJS = "";//`\x10if (E.CRC32(E.memoryArea(${startAddress},${endAddress-startAddress}))==${bin32[2]}) { print("FIRMWARE UP TO DATE!"); load();}\n`;
|
||||||
hexJS += '\x10var s = require("Storage");\n';
|
hexJS += '\x10var s = require("Storage");\n';
|
||||||
var CHUNKSIZE = 1024;
|
hexJS += '\x10s.erase(".firmware");\n';
|
||||||
|
var CHUNKSIZE = 2048;
|
||||||
for (var i=0;i<binary.length;i+=CHUNKSIZE) {
|
for (var i=0;i<binary.length;i+=CHUNKSIZE) {
|
||||||
var l = binary.length-i;
|
var l = binary.length-i;
|
||||||
if (l>CHUNKSIZE) l=CHUNKSIZE;
|
if (l>CHUNKSIZE) l=CHUNKSIZE;
|
||||||
|
|
@ -243,10 +318,8 @@ function fileLoaded() {
|
||||||
});
|
});
|
||||||
console.log(`// Data from 0x${startAddress.toString(16)} to 0x${endAddress.toString(16)} (${endAddress-startAddress} bytes)`);
|
console.log(`// Data from 0x${startAddress.toString(16)} to 0x${endAddress.toString(16)} (${endAddress-startAddress} bytes)`);
|
||||||
// Work out data
|
// Work out data
|
||||||
var HEADER_LEN = 16;
|
|
||||||
var binary = new Uint8Array(HEADER_LEN + endAddress-startAddress);
|
var binary = new Uint8Array(HEADER_LEN + endAddress-startAddress);
|
||||||
binary.fill(0); // actually seems to assume a block is filled with 0 if not complete
|
binary.fill(0); // actually seems to assume a block is filled with 0 if not complete
|
||||||
var bin32 = new Uint32Array(binary.buffer);
|
|
||||||
parseLines(function(addr, data) {
|
parseLines(function(addr, data) {
|
||||||
if (addr>MAX_ADDRESS) return; // ignore data out of range
|
if (addr>MAX_ADDRESS) return; // ignore data out of range
|
||||||
var binAddr = HEADER_LEN + addr - startAddress;
|
var binAddr = HEADER_LEN + addr - startAddress;
|
||||||
|
|
@ -260,7 +333,7 @@ function fileLoaded() {
|
||||||
createJS_bootloader(new Uint8Array(binary.buffer, HEADER_LEN), startAddress, endAddress);
|
createJS_bootloader(new Uint8Array(binary.buffer, HEADER_LEN), startAddress, endAddress);
|
||||||
} else {
|
} else {
|
||||||
console.log("App - Writing to external flash");
|
console.log("App - Writing to external flash");
|
||||||
createJS_app(binary, bin32, startAddress, endAddress);
|
createJS_app(binary, startAddress, endAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -279,7 +352,7 @@ function handleUpload() {
|
||||||
|
|
||||||
document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false);
|
document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false);
|
||||||
document.getElementById("upload").addEventListener("click", handleUpload);
|
document.getElementById("upload").addEventListener("click", handleUpload);
|
||||||
checkForFileOnServer();
|
setTimeout(checkForFileOnServer, 10);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,4 @@
|
||||||
0.05: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker
|
0.05: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker
|
||||||
0.06: Bangle.js 2 support
|
0.06: Bangle.js 2 support
|
||||||
0.07: Fix "previous" button image
|
0.07: Fix "previous" button image
|
||||||
|
0.08: Fix scrolling title background color
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ function rScroller(l) {
|
||||||
y = l.y+l.h/2;
|
y = l.y+l.h/2;
|
||||||
l.offset = l.offset%w;
|
l.offset = l.offset%w;
|
||||||
g.setClipRect(l.x, l.y, l.x+l.w-1, l.y+l.h-1)
|
g.setClipRect(l.x, l.y, l.x+l.w-1, l.y+l.h-1)
|
||||||
.setColor(l.col)
|
.setColor(l.col).setBgColor(l.bgCol) // need to set colors: iScroll calls this function outside Layout
|
||||||
.setFontAlign(-1, 0) // left center
|
.setFontAlign(-1, 0) // left center
|
||||||
.clearRect(l.x, l.y, l.x+l.w-1, l.y+l.h-1)
|
.clearRect(l.x, l.y, l.x+l.w-1, l.y+l.h-1)
|
||||||
.drawString(l.label, l.x-l.offset+40, y)
|
.drawString(l.label, l.x-l.offset+40, y)
|
||||||
|
|
|
||||||
|
|
@ -28,3 +28,4 @@
|
||||||
0.24: Better support for Bangle.js 2, avoid widget area for Graphs, smooth graphs more
|
0.24: Better support for Bangle.js 2, avoid widget area for Graphs, smooth graphs more
|
||||||
0.25: Fix issue where if Bangle.js 2 got a GPS fix but no reported time, errors could be caused by the widget (fix #935)
|
0.25: Fix issue where if Bangle.js 2 got a GPS fix but no reported time, errors could be caused by the widget (fix #935)
|
||||||
0.26: Multiple bugfixes
|
0.26: Multiple bugfixes
|
||||||
|
0.27: Map drawing with light theme (fix #1023)
|
||||||
|
|
|
||||||
|
|
@ -197,15 +197,14 @@ function plotTrack(info) {
|
||||||
g.setColor(1,0.5,0.5);
|
g.setColor(1,0.5,0.5);
|
||||||
g.setFont("Vector",16);
|
g.setFont("Vector",16);
|
||||||
g.drawString("Track"+info.fn.toString()+" - Loading",10,220);
|
g.drawString("Track"+info.fn.toString()+" - Loading",10,220);
|
||||||
g.setColor(0,0,0);
|
g.setColor(g.theme.bg);
|
||||||
g.fillRect(0,220,239,239);
|
g.fillRect(0,220,239,239);
|
||||||
if (!info.qOSTM) {
|
if (!info.qOSTM) {
|
||||||
g.setColor(1, 0, 0);
|
g.setColor(1, 0, 0);
|
||||||
g.fillRect(9,80,11,120);
|
g.fillRect(9,80,11,120);
|
||||||
g.fillPoly([9,60,19,80,0,80]);
|
g.fillPoly([9,60,19,80,0,80]);
|
||||||
g.setColor(1,1,1);
|
g.setColor(g.theme.fg);
|
||||||
g.drawString("N",2,40);
|
g.drawString("N",2,40);
|
||||||
g.setColor(1,1,1);
|
|
||||||
} else {
|
} else {
|
||||||
osm.lat = info.lat;
|
osm.lat = info.lat;
|
||||||
osm.lon = info.lon;
|
osm.lon = info.lon;
|
||||||
|
|
@ -228,7 +227,7 @@ function plotTrack(info) {
|
||||||
g.setColor(0,1,0);
|
g.setColor(0,1,0);
|
||||||
g.fillCircle(mp.x,mp.y,5);
|
g.fillCircle(mp.x,mp.y,5);
|
||||||
if (info.qOSTM) g.setColor(1,0,0.55);
|
if (info.qOSTM) g.setColor(1,0,0.55);
|
||||||
else g.setColor(1,1,1);
|
else g.setColor(g.theme.fg);
|
||||||
l = f.readLine(f);
|
l = f.readLine(f);
|
||||||
while(l!==undefined) {
|
while(l!==undefined) {
|
||||||
c = l.split(",");
|
c = l.split(",");
|
||||||
|
|
@ -248,11 +247,11 @@ function plotTrack(info) {
|
||||||
g.setColor(1,0,0);
|
g.setColor(1,0,0);
|
||||||
g.fillCircle(ox,oy,5);
|
g.fillCircle(ox,oy,5);
|
||||||
if (info.qOSTM) g.setColor(0, 0, 0);
|
if (info.qOSTM) g.setColor(0, 0, 0);
|
||||||
else g.setColor(1,1,1);
|
else g.setColor(g.theme.fg);
|
||||||
g.drawString(require("locale").distance(dist),g.getWidth() / 2, g.getHeight() - 20);
|
g.drawString(require("locale").distance(dist),g.getWidth() / 2, g.getHeight() - 20);
|
||||||
g.setFont("6x8",2);
|
g.setFont("6x8",2);
|
||||||
g.setFontAlign(0,0,3);
|
g.setFontAlign(0,0,3);
|
||||||
g.drawString("Back",g.getWidth() - 10, g.getHeight() - 40);
|
g.drawString("Back",g.getWidth() - 10, g.getHeight()/2);
|
||||||
setWatch(function() {
|
setWatch(function() {
|
||||||
viewTrack(info.fn, info);
|
viewTrack(info.fn, info);
|
||||||
}, global.BTN3||BTN1);
|
}, global.BTN3||BTN1);
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,6 @@
|
||||||
0.02: Remove messages on disconnect
|
0.02: Remove messages on disconnect
|
||||||
0.03: Handling of message actions (ok/clear)
|
0.03: Handling of message actions (ok/clear)
|
||||||
0.04: Added common bundleId's
|
0.04: Added common bundleId's
|
||||||
|
0.05: Added more bundleId's (app-id's which can be used to
|
||||||
|
determine a friendly app name in the notifications)
|
||||||
|
0.06: Fix (not) popupping up old messages
|
||||||
|
|
@ -26,6 +26,13 @@ E.on('ANCS',msg=>{
|
||||||
function ancsHandler() {
|
function ancsHandler() {
|
||||||
var msg = Bangle.ancsMessageQueue[0];
|
var msg = Bangle.ancsMessageQueue[0];
|
||||||
NRF.ancsGetNotificationInfo( msg.uid ).then( info => {
|
NRF.ancsGetNotificationInfo( msg.uid ).then( info => {
|
||||||
|
|
||||||
|
if(msg.preExisting === true){
|
||||||
|
info.new = false;
|
||||||
|
} else {
|
||||||
|
info.new = true;
|
||||||
|
}
|
||||||
|
|
||||||
E.emit("notify", Object.assign(msg, info));
|
E.emit("notify", Object.assign(msg, info));
|
||||||
Bangle.ancsMessageQueue.shift();
|
Bangle.ancsMessageQueue.shift();
|
||||||
if (Bangle.ancsMessageQueue.length)
|
if (Bangle.ancsMessageQueue.length)
|
||||||
|
|
@ -49,30 +56,47 @@ E.on('notify',msg=>{
|
||||||
"message" : string,
|
"message" : string,
|
||||||
"messageSize" : string,
|
"messageSize" : string,
|
||||||
"date" : string,
|
"date" : string,
|
||||||
|
"new" : boolean,
|
||||||
"posAction" : string,
|
"posAction" : string,
|
||||||
"negAction" : string,
|
"negAction" : string,
|
||||||
"name" : string,
|
"name" : string,
|
||||||
*/
|
*/
|
||||||
var appNames = {
|
var appNames = {
|
||||||
"nl.ah.Appie": "Albert Heijn",
|
"com.apple.facetime": "FaceTime",
|
||||||
"com.apple.mobilecal": "Calendar",
|
"com.apple.mobilecal": "Calendar",
|
||||||
"com.apple.mobilemail": "Mail",
|
"com.apple.mobilemail": "Mail",
|
||||||
|
"com.apple.MobileSMS": "SMS Message",
|
||||||
|
"com.apple.Passbook": "iOS Wallet",
|
||||||
"com.apple.reminders": "Reminders",
|
"com.apple.reminders": "Reminders",
|
||||||
"com.apple.shortcuts": "Shortcuts",
|
"com.apple.shortcuts": "Shortcuts",
|
||||||
"com.atebits.Tweetie2": "Twitter",
|
"com.atebits.Tweetie2": "Twitter",
|
||||||
"com.burbn.instagram" : "Instagram",
|
"com.burbn.instagram" : "Instagram",
|
||||||
"com.facebook.Facebook": "Facebook",
|
"com.facebook.Facebook": "Facebook",
|
||||||
"com.facebook.Messenger": "FB Messenger",
|
"com.facebook.Messenger": "FB Messenger",
|
||||||
|
"com.google.Chromecast" : "Google Home",
|
||||||
"com.google.Gmail" : "GMail",
|
"com.google.Gmail" : "GMail",
|
||||||
"com.google.hangouts" : "Hangouts",
|
"com.google.hangouts" : "Hangouts",
|
||||||
"com.google.ios.youtube" : "YouTube",
|
"com.google.ios.youtube" : "YouTube",
|
||||||
|
"com.hammerandchisel.discord" : "Discord",
|
||||||
|
"com.ifttt.ifttt" : "IFTTT",
|
||||||
"com.jumbo.app" : "Jumbo",
|
"com.jumbo.app" : "Jumbo",
|
||||||
|
"com.linkedin.LinkedIn" : "LinkedIn",
|
||||||
|
"com.nestlabs.jasper.release" : "Nest",
|
||||||
"com.netflix.Netflix" : "Netflix",
|
"com.netflix.Netflix" : "Netflix",
|
||||||
|
"com.reddit.Reddit" : "Reddit",
|
||||||
"com.skype.skype": "Skype",
|
"com.skype.skype": "Skype",
|
||||||
"com.skype.SkypeForiPad": "Skype",
|
"com.skype.SkypeForiPad": "Skype",
|
||||||
"com.spotify.client": "Spotify",
|
"com.spotify.client": "Spotify",
|
||||||
"net.whatsapp.WhatsApp": "WhatsApp",
|
"com.tinyspeck.chatlyio": "Slack",
|
||||||
|
"com.toyopagroup.picaboo": "Snapchat",
|
||||||
|
"com.ubercab.UberClient": "Uber",
|
||||||
|
"com.ubercab.UberEats": "UberEats",
|
||||||
"com.wordfeud.free": "WordFeud",
|
"com.wordfeud.free": "WordFeud",
|
||||||
|
"com.zhiliaoapp.musically": "TikTok",
|
||||||
|
"net.whatsapp.WhatsApp": "WhatsApp",
|
||||||
|
"nl.ah.Appie": "Albert Heijn",
|
||||||
|
"nl.postnl.TrackNTrace": "PostNL",
|
||||||
|
"ph.telegra.Telegraph": "Telegram",
|
||||||
|
|
||||||
// could also use NRF.ancsGetAppInfo(msg.appId) here
|
// could also use NRF.ancsGetAppInfo(msg.appId) here
|
||||||
};
|
};
|
||||||
|
|
@ -85,6 +109,7 @@ E.on('notify',msg=>{
|
||||||
t : msg.event,
|
t : msg.event,
|
||||||
id : msg.uid,
|
id : msg.uid,
|
||||||
src : appNames[msg.appId] || msg.appId,
|
src : appNames[msg.appId] || msg.appId,
|
||||||
|
new : msg.new,
|
||||||
title : msg.title&&E.decodeUTF8(msg.title, unicodeRemap, replacer),
|
title : msg.title&&E.decodeUTF8(msg.title, unicodeRemap, replacer),
|
||||||
subject : msg.subtitle&&E.decodeUTF8(msg.subtitle, unicodeRemap, replacer),
|
subject : msg.subtitle&&E.decodeUTF8(msg.subtitle, unicodeRemap, replacer),
|
||||||
body : msg.message&&E.decodeUTF8(msg.message, unicodeRemap, replacer)
|
body : msg.message&&E.decodeUTF8(msg.message, unicodeRemap, replacer)
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,5 @@
|
||||||
0.09: Added New Zealand en_NZ
|
0.09: Added New Zealand en_NZ
|
||||||
0.10: Apply 12hour setting to time
|
0.10: Apply 12hour setting to time
|
||||||
0.11: Added translations for nl_NL and changes one formatting
|
0.11: Added translations for nl_NL and changes one formatting
|
||||||
|
0.12: Fixed nl_NL formatting, because the full months won't fit on the Bangle.js2's screen
|
||||||
|
0.13: Now use shorter de_DE date format to more closely match other languages for size
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ var locales = {
|
||||||
temperature: "°C",
|
temperature: "°C",
|
||||||
ampm: { 0: "", 1: "" },
|
ampm: { 0: "", 1: "" },
|
||||||
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
||||||
datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%Y" }, // Sonntag, 1. März 2020 // 01.01.20
|
datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mär 2020 // 01.03.20
|
||||||
abmonth: "Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez",
|
abmonth: "Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez",
|
||||||
month: "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
month: "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||||
|
|
@ -184,13 +184,30 @@ var locales = {
|
||||||
temperature: "°C",
|
temperature: "°C",
|
||||||
ampm: { 0: "", 1: "" },
|
ampm: { 0: "", 1: "" },
|
||||||
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
||||||
datePattern: { 0: "%B %d %Y", 1: "%d.%m.%y" }, // zondag 1 maart 2020 // 01.01.20
|
datePattern: { 0: "%d %b %Y", 1: "%d-%m-%Y" }, // 28 feb 2020 // 28-02-2020
|
||||||
abday: "zo,ma,di,wo,do,vr,za",
|
abday: "zo,ma,di,wo,do,vr,za",
|
||||||
day: "zondag,maandag,dinsdag,woensdag,donderdag,vrijdag,zaterdag",
|
day: "zondag,maandag,dinsdag,woensdag,donderdag,vrijdag,zaterdag",
|
||||||
abmonth: "jan,feb,mrt,apr,mei,jun,jul,aug,sep,okt,nov,dec",
|
abmonth: "jan,feb,mrt,apr,mei,jun,jul,aug,sep,okt,nov,dec",
|
||||||
month: "januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december",
|
month: "januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december",
|
||||||
trans: { yes: "ja", Yes: "Ja", no: "nee", No: "Nee", ok: "ok", on: "aan", off: "uit", "< Back": "< Terug" }
|
trans: { yes: "ja", Yes: "Ja", no: "nee", No: "Nee", ok: "ok", on: "aan", off: "uit", "< Back": "< Terug" }
|
||||||
},
|
},
|
||||||
|
"en_NL": { // English date units with Dutch number, currency and navigation units.
|
||||||
|
lang: "en_NL",
|
||||||
|
decimal_point: ",",
|
||||||
|
thousands_sep: ".",
|
||||||
|
currency_symbol: "€",
|
||||||
|
int_curr_symbol: "EUR",
|
||||||
|
speed: "km/h",
|
||||||
|
distance: { 0: "m", 1: "km" },
|
||||||
|
temperature: "°C",
|
||||||
|
ampm: { 0: "am", 1: "pm" },
|
||||||
|
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||||
|
datePattern: { 0: "%b %d %Y", 1: "%d/%m/%Y" }, // Feb 28 2020" // "01/03/2020"(short)
|
||||||
|
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
|
||||||
|
month: "January,February,March,April,May,June,July,August,September,October,November,December",
|
||||||
|
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
|
||||||
|
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
|
||||||
|
},
|
||||||
"en_CA": {
|
"en_CA": {
|
||||||
lang: "en_CA",
|
lang: "en_CA",
|
||||||
decimal_point: ".",
|
decimal_point: ".",
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,5 @@
|
||||||
buzz on new message (fix #999)
|
buzz on new message (fix #999)
|
||||||
0.09: Message now disappears after 60s if no action taken and clock loads (fix 922)
|
0.09: Message now disappears after 60s if no action taken and clock loads (fix 922)
|
||||||
Fix phone icon (#1014)
|
Fix phone icon (#1014)
|
||||||
|
0.10: Respect the 'new' attribute if it was set from iOS integrations
|
||||||
|
0.11: Open app when touching the widget (Bangle.js 2 only)
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Push a new message onto messages queue, event is:
|
/* Push a new message onto messages queue, event is:
|
||||||
{t:"add",id:int, src,title,subject,body,sender,tel, important:bool} // add new
|
{t:"add",id:int, src,title,subject,body,sender,tel, important:bool, new:bool}
|
||||||
{t:"add",id:int, id:"music", state, artist, track, etc} // add new
|
{t:"add",id:int, id:"music", state, artist, track, etc} // add new
|
||||||
{t:"remove-",id:int} // remove
|
{t:"remove-",id:int} // remove
|
||||||
{t:"modify",id:int, title:string} // modified
|
{t:"modify",id:int, title:string} // modified
|
||||||
|
|
@ -16,7 +16,11 @@ exports.pushMessage = function(event) {
|
||||||
if (mIdx>=0) messages.splice(mIdx, 1); // remove item
|
if (mIdx>=0) messages.splice(mIdx, 1); // remove item
|
||||||
mIdx=-1;
|
mIdx=-1;
|
||||||
} else { // add/modify
|
} else { // add/modify
|
||||||
if (event.t=="add") event.new=true; // new message
|
if (event.t=="add"){
|
||||||
|
if(event.new === undefined ) { // If 'new' has not been set yet, set it
|
||||||
|
event.new=true; // Assume it should be new
|
||||||
|
}
|
||||||
|
}
|
||||||
if (mIdx<0) {
|
if (mIdx<0) {
|
||||||
mIdx=0;
|
mIdx=0;
|
||||||
messages.unshift(event); // add new messages to the beginning
|
messages.unshift(event); // add new messages to the beginning
|
||||||
|
|
@ -27,7 +31,11 @@ exports.pushMessage = function(event) {
|
||||||
// if in app, process immediately
|
// if in app, process immediately
|
||||||
if (inApp) return onMessagesModified(mIdx<0 ? {id:event.id} : messages[mIdx]);
|
if (inApp) return onMessagesModified(mIdx<0 ? {id:event.id} : messages[mIdx]);
|
||||||
// ok, saved now - we only care if it's new
|
// ok, saved now - we only care if it's new
|
||||||
if (event.t!="add") return;
|
if (event.t!="add") {
|
||||||
|
return;
|
||||||
|
} else if(event.new == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// otherwise load messages/show widget
|
// otherwise load messages/show widget
|
||||||
var loadMessages = Bangle.CLOCK || event.important;
|
var loadMessages = Bangle.CLOCK || event.important;
|
||||||
// first, buzz
|
// first, buzz
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
WIDGETS["messages"]={area:"tl",width:0,draw:function() {
|
WIDGETS["messages"]={area:"tl",width:0,draw:function() {
|
||||||
|
Bangle.removeListener('touch', this.touch);
|
||||||
if (!this.width) return;
|
if (!this.width) return;
|
||||||
var c = (Date.now()-this.t)/1000;
|
var c = (Date.now()-this.t)/1000;
|
||||||
g.reset().setBgColor((c&1) ? "#0f0" : "#030").setColor((c&1) ? "#000" : "#fff");
|
g.reset().setBgColor((c&1) ? "#0f0" : "#030").setColor((c&1) ? "#000" : "#fff");
|
||||||
|
|
@ -12,6 +13,7 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
|
||||||
WIDGETS["messages"].buzz(); // buzz every 4 seconds
|
WIDGETS["messages"].buzz(); // buzz every 4 seconds
|
||||||
}
|
}
|
||||||
setTimeout(()=>WIDGETS["messages"].draw(), 1000);
|
setTimeout(()=>WIDGETS["messages"].draw(), 1000);
|
||||||
|
if (process.env.HWVERSION>1) Bangle.on('touch', this.touch);
|
||||||
},show:function(quiet) {
|
},show:function(quiet) {
|
||||||
WIDGETS["messages"].t=Date.now(); // first time
|
WIDGETS["messages"].t=Date.now(); // first time
|
||||||
WIDGETS["messages"].l=Date.now()-10000; // last buzz
|
WIDGETS["messages"].l=Date.now()-10000; // last buzz
|
||||||
|
|
@ -33,6 +35,10 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
|
||||||
if (c=="-") Bangle.buzz(500).then(()=>setTimeout(b,100));
|
if (c=="-") Bangle.buzz(500).then(()=>setTimeout(b,100));
|
||||||
}
|
}
|
||||||
b();
|
b();
|
||||||
|
},touch:function(b,c) {
|
||||||
|
var w=WIDGETS["messages"];
|
||||||
|
if (!w||!w.width||c.x<w.x||c.x>w.x+w.width||c.y<w.y||c.y>w.y+23) return;
|
||||||
|
load("messages.app.js");
|
||||||
}};
|
}};
|
||||||
/* We might have returned here if we were in the Messages app for a
|
/* We might have returned here if we were in the Messages app for a
|
||||||
message but then the watch was never viewed. In that case we don't
|
message but then the watch was never viewed. In that case we don't
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@
|
||||||
0.07: Add date on touch and some improvements (see settings and readme)
|
0.07: Add date on touch and some improvements (see settings and readme)
|
||||||
0.08: Add new draw styles, tidy up draw functionality
|
0.08: Add new draw styles, tidy up draw functionality
|
||||||
0.09: Tweak for faster rendering
|
0.09: Tweak for faster rendering
|
||||||
|
0.10: Enhance for use with Bangle2, insert new draw mode 'thickfill'
|
||||||
|
|
@ -7,14 +7,20 @@ Settings can be accessed through the app/widget settings menu of the Bangle.js
|
||||||
|
|
||||||
### Color:
|
### Color:
|
||||||
* rnd - shows numerals in different color combinations every time the watches wakes
|
* rnd - shows numerals in different color combinations every time the watches wakes
|
||||||
* r/g - red/green
|
* r/g - red/green (Bangle1/Bangle2)
|
||||||
* y/w - yellow/white
|
* y/w - yellow/white (Bangle1 only)
|
||||||
* o/c - orange/cyan
|
* o/c - orange/cyan (Bangle1 only)
|
||||||
* b/y - blue/yellow'ish
|
* b/y - blue/yellow'ish (Bangle1 only)
|
||||||
|
* r/g - red/green (Bangle2 only)
|
||||||
|
* g/b - green/blue (Bangle2 only)
|
||||||
|
* r/c - red/cyan (Bangle2 only)
|
||||||
|
* m/g - magenta/green (Bangle2 only)
|
||||||
|
|
||||||
### Draw mode
|
### Draw mode
|
||||||
* fill - fill numerals
|
* fill - fill numerals
|
||||||
* frame - only shows outline of numerals
|
* frame - only shows outline of numerals
|
||||||
|
* framefill - frame with lighter color fill
|
||||||
|
* thickfill - thick frame in theme foreground color
|
||||||
|
|
||||||
### Menu button
|
### Menu button
|
||||||
* choose button to start launcher menu with
|
* choose button to start launcher menu with
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* + see README.md for details
|
* + see README.md for details
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var numerals = {
|
var numerals = {
|
||||||
0:[[9,1,82,1,90,9,90,92,82,100,9,100,1,92,1,9],[30,25,61,25,69,33,69,67,61,75,30,75,22,67,22,33]],
|
0:[[9,1,82,1,90,9,90,92,82,100,9,100,1,92,1,9],[30,25,61,25,69,33,69,67,61,75,30,75,22,67,22,33]],
|
||||||
1:[[50,1,82,1,90,9,90,92,82,100,73,100,65,92,65,27,50,27,42,19,42,9]],
|
1:[[50,1,82,1,90,9,90,92,82,100,73,100,65,92,65,27,50,27,42,19,42,9]],
|
||||||
2:[[9,1,82,1,90,9,90,53,82,61,21,61,21,74,82,74,90,82,90,92,82,100,9,100,1,92,1,48,9,40,70,40,70,27,9,27,1,19,1,9]],
|
2:[[9,1,82,1,90,9,90,53,82,61,21,61,21,74,82,74,90,82,90,92,82,100,9,100,1,92,1,48,9,40,70,40,70,27,9,27,1,19,1,9]],
|
||||||
|
|
@ -19,8 +19,8 @@ var numerals = {
|
||||||
9:[[9,1,82,1,90,9,90,92,82,100,9,100,1,92,1,82,9,74,69,74,69,61,9,61,1,53,1,9],[22,27,69,27,69,41,22,41]],
|
9:[[9,1,82,1,90,9,90,92,82,100,9,100,1,92,1,82,9,74,69,74,69,61,9,61,1,53,1,9],[22,27,69,27,69,41,22,41]],
|
||||||
};
|
};
|
||||||
var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false;
|
var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false;
|
||||||
var _hCol = ["#ff5555","#ffff00","#FF9901","#2F00FF"];
|
var _hCol = [];
|
||||||
var _mCol = ["#55ff55","#ffffff","#00EFEF","#FFBF00"];
|
var _mCol = [];
|
||||||
var _rCol = 0;
|
var _rCol = 0;
|
||||||
var scale = g.getWidth()/240;
|
var scale = g.getWidth()/240;
|
||||||
var interval = 0;
|
var interval = 0;
|
||||||
|
|
@ -42,15 +42,23 @@ var drawFuncs = {
|
||||||
},
|
},
|
||||||
thickframe : function(poly,isHole){
|
thickframe : function(poly,isHole){
|
||||||
g.drawPoly(poly,true);
|
g.drawPoly(poly,true);
|
||||||
g.drawPoly(translate(1,0,poly),true);
|
g.drawPoly(translate(1,0,poly,1),true);
|
||||||
g.drawPoly(translate(1,1,poly),true);
|
g.drawPoly(translate(1,1,poly,1),true);
|
||||||
g.drawPoly(translate(0,1,poly),true);
|
g.drawPoly(translate(0,1,poly,1),true);
|
||||||
|
},
|
||||||
|
thickfill : function(poly,isHole){
|
||||||
|
if (isHole) g.setColor(g.theme.bg);
|
||||||
|
g.fillPoly(poly,true);
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.drawPoly(translate(1,0,poly,1),true);
|
||||||
|
g.drawPoly(translate(1,1,poly,1),true);
|
||||||
|
g.drawPoly(translate(0,1,poly,1),true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function translate(tx, ty, p){
|
function translate(tx, ty, p, ascale){
|
||||||
//return p.map((x, i)=> x+((i&1)?ty:tx));
|
//return p.map((x, i)=> x+((i&1)?ty:tx));
|
||||||
return g.transformVertices(p, {x:tx,y:ty,scale:scale});
|
return g.transformVertices(p, {x:tx,y:ty,scale:ascale==undefined?scale:ascale});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -99,6 +107,18 @@ function setUpdateInt(set){
|
||||||
if (set) interval=setInterval(draw, REFRESH_RATE);
|
if (set) interval=setInterval(draw, REFRESH_RATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setUp(){
|
||||||
|
if (process.env.HWVERSION==1){
|
||||||
|
_hCol = ["#ff5555","#ffff00","#FF9901","#2F00FF"];
|
||||||
|
_mCol = ["#55ff55","#ffffff","#00EFEF","#FFBF00"];
|
||||||
|
} else {
|
||||||
|
_hCol = ["#ff0000","#00ff00","#ff0000","#ff00ff"];
|
||||||
|
_mCol = ["#00ff00","#0000ff","#00ffff","#00ff00"];
|
||||||
|
}
|
||||||
|
if (settings.color==0) _rCol = Math.floor(Math.random()*_hCol.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUp();
|
||||||
g.clear(1);
|
g.clear(1);
|
||||||
// Show launcher when button pressed
|
// Show launcher when button pressed
|
||||||
Bangle.setUI("clock");
|
Bangle.setUI("clock");
|
||||||
|
|
@ -111,11 +131,12 @@ if (settings.showDate) {
|
||||||
}
|
}
|
||||||
Bangle.on('lcdPower', function(on){
|
Bangle.on('lcdPower', function(on){
|
||||||
if (on){
|
if (on){
|
||||||
if (settings.color==0) _rCol = Math.floor(Math.random()*_hCol.length);
|
setUp();
|
||||||
draw();
|
draw();
|
||||||
setUpdateInt(1);
|
setUpdateInt(1);
|
||||||
} else setUpdateInt(0);
|
} else setUpdateInt(0);
|
||||||
});
|
});
|
||||||
|
Bangle.on('lock', () => setUp());
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
}
|
}
|
||||||
let numeralsSettings = storage.readJSON('numerals.json',1);
|
let numeralsSettings = storage.readJSON('numerals.json',1);
|
||||||
if (!numeralsSettings) resetSettings();
|
if (!numeralsSettings) resetSettings();
|
||||||
let dm = ["fill","frame","framefill","thickframe"];
|
let dm = ["fill","frame","framefill","thickframe","thickfill"];
|
||||||
let col = ["rnd","r/g","y/w","o/c","b/y"];
|
let col = process.env.HWVERSION==1?["rnd","r/g","y/w","o/c","b/y"]:["rnd","r/g","g/b","r/c","m/g"];
|
||||||
let btn = [[24,"BTN1"],[22,"BTN2"],[23,"BTN3"],[11,"BTN4"],[16,"BTN5"]];
|
let btn = [[24,"BTN1"],[22,"BTN2"],[23,"BTN3"],[11,"BTN4"],[16,"BTN5"]];
|
||||||
var menu={
|
var menu={
|
||||||
"" : { "title":"Numerals"},
|
"" : { "title":"Numerals"},
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: first release
|
0.01: first release
|
||||||
0.02: included deployment of pebble.settings.js in apps.json
|
0.02: included deployment of pebble.settings.js in apps.json
|
||||||
0.03: Changed time+calendar font to LECO1976Regular, changed to slanting boot
|
0.03: Changed time+calendar font to LECO1976Regular, changed to slanting boot
|
||||||
|
0.04: Fix widget hiding code (fix #1046)
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ function draw() {
|
||||||
// turn the warning on once we have dipped below 30%
|
// turn the warning on once we have dipped below 30%
|
||||||
if (E.getBattery() < 30)
|
if (E.getBattery() < 30)
|
||||||
batteryWarning = true;
|
batteryWarning = true;
|
||||||
|
|
||||||
// turn the warning off once we have dipped above 40%
|
// turn the warning off once we have dipped above 40%
|
||||||
if (E.getBattery() > 40)
|
if (E.getBattery() > 40)
|
||||||
batteryWarning = false;
|
batteryWarning = false;
|
||||||
|
|
@ -57,7 +57,7 @@ function draw() {
|
||||||
g.setFontAlign(0, -1);
|
g.setFontAlign(0, -1);
|
||||||
g.drawString(da[0].toUpperCase(), w/4, ha); // day of week
|
g.drawString(da[0].toUpperCase(), w/4, ha); // day of week
|
||||||
g.drawString(getSteps(), 3*w/4, ha);
|
g.drawString(getSteps(), 3*w/4, ha);
|
||||||
|
|
||||||
// time
|
// time
|
||||||
// white on red for battery warning
|
// white on red for battery warning
|
||||||
g.setColor(!batteryWarning ? g.theme.bg : '#f00');
|
g.setColor(!batteryWarning ? g.theme.bg : '#f00');
|
||||||
|
|
@ -71,7 +71,7 @@ function draw() {
|
||||||
// contrast bar
|
// contrast bar
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.fillRect(0, h3, w, h3 + t);
|
g.fillRect(0, h3, w, h3 + t);
|
||||||
|
|
||||||
// the bottom
|
// the bottom
|
||||||
g.setColor(settings.bg);
|
g.setColor(settings.bg);
|
||||||
g.fillRect(0, h3 + t, w, h);
|
g.fillRect(0, h3 + t, w, h);
|
||||||
|
|
@ -111,9 +111,10 @@ g.clear();
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
/*
|
/*
|
||||||
* we are not drawing the widgets as we are taking over the whole screen
|
* we are not drawing the widgets as we are taking over the whole screen
|
||||||
* so we will blank out the draw() functions of each widget
|
* so we will blank out the draw() functions of each widget and change the
|
||||||
|
* area to the top bar doesn't get cleared.
|
||||||
*/
|
*/
|
||||||
for (let wd of WIDGETS) {wd.draw=()=>{};}
|
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||||
loadSettings();
|
loadSettings();
|
||||||
setInterval(draw, 15000); // refresh every 15s
|
setInterval(draw, 15000); // refresh every 15s
|
||||||
draw();
|
draw();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: Initial creation of the pattern launch app
|
||||||
|
0.02: Turn on lcd when launching an app if the lock screen was disabled in the settings
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
# Pattern Launcher
|
||||||
|
|
||||||
|
Directly launch apps from the clock screen with custom patterns.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Create patterns and link them to apps in the Pattern Launcher app.
|
||||||
|
|
||||||
|
Then launch the linked apps directly from the clock screen by simply drawing the desired pattern.
|
||||||
|
|
||||||
|
## Screenshots and detailed steps
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
From the main menu you can:
|
||||||
|
- Add a new pattern and link it to an app (first entry)
|
||||||
|
- To create a new pattern first select "Add Pattern"
|
||||||
|
- Now draw any pattern you like, this will later launch the linked app from the clock screen
|
||||||
|
- If you don't like the pattern, simply re-draw it. The previous pattern will be discarded.
|
||||||
|
- If you are happy with the pattern tap on screen or press the button to continue
|
||||||
|
- Now select the app you want to launch with the pattern.
|
||||||
|
- Note, you can bind multiple patterns to the same app.
|
||||||
|
- Remove linked patterns (second entry)
|
||||||
|
- To remove a pattern first select "Remove Pattern"
|
||||||
|
- You will now see a list of apps that have patterns linked to them
|
||||||
|
- Simply select the app that you want to unlink. This will remove the saved pattern, but not the app itself!
|
||||||
|
- Note, that you can not actually preview the patterns. This makes removing patterns that are linked to the same app annoying. sorry!
|
||||||
|
- Disable the lock screen on the clock screen from the settings (third entry)
|
||||||
|
- To launch the app from the pattern on the clock screen the watch must be unlocked.
|
||||||
|
- If this annoys you, you can disable the lock on the clock screen from the setting here
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
1) Nothing happens when I draw on the clock screen!
|
||||||
|
|
||||||
|
Please double-check if you actually have a pattern linked to an app.
|
||||||
|
|
||||||
|
2) I have a pattern linked to an app and still nothing happens when I draw on the clock screen!
|
||||||
|
|
||||||
|
Make sure the watch is unlocked before you start drawing. If this bothers you, you can permanently disable the watch-lock from within the Pattern Launcher app (via the Settings).
|
||||||
|
|
||||||
|
3) I have done all that and still nothing happens!
|
||||||
|
|
||||||
|
Please note that drawing on the clock screen will not visually show the pattern you drew. It will start the app as soon as the pattern was recognized - this might take 1 or 2 seconds! If still nothing happens, that might be a bug, sorry!
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwkE//ziEAiM//4ACmMAgMvA4fziIIBCAUgEAUCBwXxFIYYDCAvyHIgPCGwIgFCA0wAYIRCh49BLQoXB+AHEgYUBgQaCE4JGEHAZGDAAMBAQMjC4UBEw0Aj5PFDIchC4Q/BC5CtIgIXUGwIXDI6EBiCaCCYJ3RIganTa5AnEgbXIewwPGn4ICCA8hgESAoQABmUQgI2CCBQA/AAvzbIRuD/8xNwMTCBTfDPwbYEPAaPDf4LnFB4T/EEAS/Fj7vFZ4LvBgMiFIQXBCAwmEE4Q3BiUikTnHJAQFEJ4XwgERHgI/CJ4oAIC4QYBiYXDCxgXDgUzLQQXIGwpHDLoRHJgJmFO4arCO5MCK4QACh6nCJ4poDCAbGFe4QnEY4IgGG4oOCc4ofCbAj3C/8hiMSAoQYCiMRMQQQKAH4AGkMAJwsyiEBL4wQER4Z+DR5AQFX4ooCX44QGVobvOgMREAUQBwg3B+IXFc4cTmYUBgIXFgImCAAkf/59BkERIgMBBwo/BC5AkDCgwXOAAIMGI5xFBBgR3SJYinXa5A4EfAQQHewoABJAgfCCA/zFAMRn4OC/8xIAIWDCAJGBgIQBA=="))
|
||||||
|
|
@ -0,0 +1,416 @@
|
||||||
|
var storage = require("Storage");
|
||||||
|
|
||||||
|
var DEBUG = false;
|
||||||
|
var log = (message) => {
|
||||||
|
if (DEBUG) {
|
||||||
|
console.log(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var CIRCLE_RADIUS = 25;
|
||||||
|
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
||||||
|
|
||||||
|
var CIRCLES = [
|
||||||
|
{ x: 25, y: 25, i: 0 },
|
||||||
|
{ x: 87, y: 25, i: 1 },
|
||||||
|
{ x: 150, y: 25, i: 2 },
|
||||||
|
{ x: 25, y: 87, i: 3 },
|
||||||
|
{ x: 87, y: 87, i: 4 },
|
||||||
|
{ x: 150, y: 87, i: 5 },
|
||||||
|
{ x: 25, y: 150, i: 6 },
|
||||||
|
{ x: 87, y: 150, i: 7 },
|
||||||
|
{ x: 150, y: 150, i: 8 },
|
||||||
|
];
|
||||||
|
|
||||||
|
var showMainMenu = () => {
|
||||||
|
log("loading patterns");
|
||||||
|
var storedPatterns = storage.readJSON("ptlaunch.patterns.json", 1) || {};
|
||||||
|
|
||||||
|
var mainmenu = {
|
||||||
|
"": {
|
||||||
|
title: "Pattern Launcher",
|
||||||
|
},
|
||||||
|
"< Back": () => {
|
||||||
|
log("cancel");
|
||||||
|
load();
|
||||||
|
},
|
||||||
|
"Add Pattern": () => {
|
||||||
|
log("creating pattern");
|
||||||
|
createPattern().then((pattern) => {
|
||||||
|
log("got pattern");
|
||||||
|
log(pattern);
|
||||||
|
log(pattern.length);
|
||||||
|
|
||||||
|
var confirmPromise = new Promise((resolve) => resolve(true));
|
||||||
|
|
||||||
|
if (storedPatterns[pattern]) {
|
||||||
|
log("pattern already exists. show confirmation prompt");
|
||||||
|
confirmPromise = E.showPrompt("Pattern already exists\nOverwrite?", {
|
||||||
|
title: "Confirm",
|
||||||
|
buttons: { Yes: true, No: false },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmPromise.then((confirm) => {
|
||||||
|
log("confirmPromise resolved: " + confirm);
|
||||||
|
if (!confirm) {
|
||||||
|
showMainMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log("selecting app");
|
||||||
|
getSelectedApp().then((app) => {
|
||||||
|
E.showMessage("Saving...");
|
||||||
|
log("got app");
|
||||||
|
log("saving pattern");
|
||||||
|
|
||||||
|
storedPatterns[pattern] = {
|
||||||
|
app: { name: app.name, src: app.src },
|
||||||
|
};
|
||||||
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
|
showMainMenu();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"Remove Pattern": () => {
|
||||||
|
log("selecting pattern through app");
|
||||||
|
getStoredPatternViaApp(storedPatterns).then((pattern) => {
|
||||||
|
E.showMessage("Deleting...");
|
||||||
|
delete storedPatterns[pattern];
|
||||||
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
|
showMainMenu();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Settings: () => {
|
||||||
|
var settings = storedPatterns["settings"] || {};
|
||||||
|
|
||||||
|
var settingsmenu = {
|
||||||
|
"": {
|
||||||
|
title: "Pattern Settings",
|
||||||
|
},
|
||||||
|
"< Back": () => {
|
||||||
|
log("cancel");
|
||||||
|
load();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (settings.lockDisabled) {
|
||||||
|
settingsmenu["Enable lock"] = () => {
|
||||||
|
settings.lockDisabled = false;
|
||||||
|
storedPatterns["settings"] = settings;
|
||||||
|
Bangle.setOptions({ lockTimeout: 1000 * 30 });
|
||||||
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
settingsmenu["Disable lock"] = () => {
|
||||||
|
settings.lockDisabled = true;
|
||||||
|
storedPatterns["settings"] = settings;
|
||||||
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
|
Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 });
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu(settingsmenu);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
E.showMenu(mainmenu);
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawCircle = (circle) => {
|
||||||
|
g.fillCircle(circle.x, circle.y, CIRCLE_RADIUS);
|
||||||
|
};
|
||||||
|
|
||||||
|
var positions = [];
|
||||||
|
var createPattern = () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
E.showMenu();
|
||||||
|
g.clear();
|
||||||
|
g.setColor(0, 0, 0);
|
||||||
|
CIRCLES.forEach((circle) => drawCircle(circle));
|
||||||
|
|
||||||
|
var pattern = [];
|
||||||
|
|
||||||
|
var isFinished = false;
|
||||||
|
var finishHandler = () => {
|
||||||
|
if (pattern.length === 0 || isFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log("Pattern is finished.");
|
||||||
|
isFinished = true;
|
||||||
|
Bangle.removeListener("drag", dragHandler);
|
||||||
|
Bangle.removeListener("tap", finishHandler);
|
||||||
|
resolve(pattern.join(""));
|
||||||
|
};
|
||||||
|
setWatch(() => finishHandler(), BTN);
|
||||||
|
setTimeout(() => Bangle.on("tap", finishHandler), 250);
|
||||||
|
|
||||||
|
var dragHandler = (position) => {
|
||||||
|
positions.push(position);
|
||||||
|
|
||||||
|
debounce().then(() => {
|
||||||
|
if (isFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
E.showMessage("Calculating...");
|
||||||
|
var t0 = Date.now();
|
||||||
|
|
||||||
|
log(positions.length);
|
||||||
|
|
||||||
|
var circlesClone = cloneCirclesArray();
|
||||||
|
pattern = [];
|
||||||
|
|
||||||
|
var step = Math.floor(positions.length / 100) + 1;
|
||||||
|
|
||||||
|
var p, a, b, circle;
|
||||||
|
|
||||||
|
for (var i = 0; i < positions.length; i += step) {
|
||||||
|
p = positions[i];
|
||||||
|
|
||||||
|
circle = circlesClone[0];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[1];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[2];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[3];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(3, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[4];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(4, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[5];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(5, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[6];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(6, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
circle = circlesClone[7];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(7, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[8];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(8, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var tx = Date.now();
|
||||||
|
log(tx - t0);
|
||||||
|
positions = [];
|
||||||
|
var t1 = Date.now();
|
||||||
|
log(t1 - t0);
|
||||||
|
|
||||||
|
log("pattern:");
|
||||||
|
log(pattern);
|
||||||
|
|
||||||
|
log("redrawing");
|
||||||
|
g.clear();
|
||||||
|
g.setColor(0, 0, 0);
|
||||||
|
CIRCLES.forEach((circle) => drawCircle(circle));
|
||||||
|
|
||||||
|
g.setColor(1, 1, 1);
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
g.setFont("6x8", 4);
|
||||||
|
pattern.forEach((circleIndex, patternIndex) => {
|
||||||
|
var circle = CIRCLES[circleIndex];
|
||||||
|
g.drawString(patternIndex + 1, circle.x, circle.y);
|
||||||
|
});
|
||||||
|
var t2 = Date.now();
|
||||||
|
log(t2 - t0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Bangle.on("drag", dragHandler);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var getAppList = () => {
|
||||||
|
var appList = storage
|
||||||
|
.list(/\.info$/)
|
||||||
|
.map((appInfoFileName) => {
|
||||||
|
var appInfo = storage.readJSON(appInfoFileName, 1);
|
||||||
|
return (
|
||||||
|
appInfo && {
|
||||||
|
name: appInfo.name,
|
||||||
|
// type: appInfo.type,
|
||||||
|
// icon: appInfo.icon,
|
||||||
|
sortorder: appInfo.sortorder,
|
||||||
|
src: appInfo.src,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter((app) => app && !!app.src);
|
||||||
|
appList.sort((a, b) => {
|
||||||
|
var n = (0 | a.sortorder) - (0 | b.sortorder);
|
||||||
|
if (n) return n; // do sortorder first
|
||||||
|
if (a.name < b.name) return -1;
|
||||||
|
if (a.name > b.name) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return appList;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getSelectedApp = () => {
|
||||||
|
E.showMessage("Loading apps...");
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
var selectAppMenu = {
|
||||||
|
"": {
|
||||||
|
title: "Select App",
|
||||||
|
},
|
||||||
|
"< Cancel": () => {
|
||||||
|
log("cancel");
|
||||||
|
showMainMenu();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var appList = getAppList();
|
||||||
|
appList.forEach((app) => {
|
||||||
|
selectAppMenu[app.name] = () => {
|
||||||
|
log("app selected");
|
||||||
|
log(app);
|
||||||
|
resolve(app);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
E.showMenu(selectAppMenu);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var getStoredPatternViaApp = (storedPatterns) => {
|
||||||
|
E.showMessage("Loading patterns...");
|
||||||
|
log("getStoredPatternViaApp");
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
var selectPatternMenu = {
|
||||||
|
"": {
|
||||||
|
title: "Select App",
|
||||||
|
},
|
||||||
|
"< Cancel": () => {
|
||||||
|
log("cancel");
|
||||||
|
showMainMenu();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
log(storedPatterns);
|
||||||
|
var patterns = Object.keys(storedPatterns);
|
||||||
|
log(patterns);
|
||||||
|
|
||||||
|
patterns.forEach((pattern) => {
|
||||||
|
if (pattern) {
|
||||||
|
if (storedPatterns[pattern]) {
|
||||||
|
var app = storedPatterns[pattern].app;
|
||||||
|
if (!!app && !!app.name) {
|
||||||
|
var appName = app.name;
|
||||||
|
var i = 0;
|
||||||
|
while (appName in selectPatternMenu[app.name]) {
|
||||||
|
appName = app.name + i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
selectPatternMenu[appName] = () => {
|
||||||
|
log("pattern via app selected");
|
||||||
|
log(pattern);
|
||||||
|
log(app);
|
||||||
|
resolve(pattern);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
E.showMenu(selectPatternMenu);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
showMainMenu();
|
||||||
|
|
||||||
|
//////
|
||||||
|
// lib functions
|
||||||
|
//////
|
||||||
|
|
||||||
|
var debounceTimeoutId;
|
||||||
|
var debounce = (delay) => {
|
||||||
|
if (debounceTimeoutId) {
|
||||||
|
clearTimeout(debounceTimeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
debounceTimeoutId = setTimeout(() => {
|
||||||
|
debounceTimeoutId = undefined;
|
||||||
|
resolve();
|
||||||
|
}, delay || 500);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var cloneCirclesArray = () => {
|
||||||
|
var circlesClone = Array(CIRCLES.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < CIRCLES.length; i++) {
|
||||||
|
circlesClone[i] = CIRCLES[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return circlesClone;
|
||||||
|
};
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,202 @@
|
||||||
|
var DEBUG = true;
|
||||||
|
var log = (message) => {
|
||||||
|
if (DEBUG) {
|
||||||
|
console.log(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var CIRCLE_RADIUS = 25;
|
||||||
|
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
||||||
|
|
||||||
|
var CIRCLES = [
|
||||||
|
{ x: 25, y: 25, i: 0 },
|
||||||
|
{ x: 87, y: 25, i: 1 },
|
||||||
|
{ x: 150, y: 25, i: 2 },
|
||||||
|
{ x: 25, y: 87, i: 3 },
|
||||||
|
{ x: 87, y: 87, i: 4 },
|
||||||
|
{ x: 150, y: 87, i: 5 },
|
||||||
|
{ x: 25, y: 150, i: 6 },
|
||||||
|
{ x: 87, y: 150, i: 7 },
|
||||||
|
{ x: 150, y: 150, i: 8 },
|
||||||
|
];
|
||||||
|
|
||||||
|
var storedPatterns;
|
||||||
|
var positions = [];
|
||||||
|
var dragHandler = (position) => {
|
||||||
|
positions.push(position);
|
||||||
|
|
||||||
|
debounce().then(() => {
|
||||||
|
log(positions.length);
|
||||||
|
|
||||||
|
var circlesClone = cloneCirclesArray();
|
||||||
|
var pattern = [];
|
||||||
|
|
||||||
|
var step = Math.floor(positions.length / 100) + 1;
|
||||||
|
|
||||||
|
var p, a, b, circle;
|
||||||
|
|
||||||
|
for (var i = 0; i < positions.length; i += step) {
|
||||||
|
p = positions[i];
|
||||||
|
|
||||||
|
circle = circlesClone[0];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[1];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[2];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[3];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(3, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[4];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(4, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[5];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(5, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[6];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(6, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
circle = circlesClone[7];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(7, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circle = circlesClone[8];
|
||||||
|
if (circle) {
|
||||||
|
a = p.x - circle.x;
|
||||||
|
b = p.y - circle.y;
|
||||||
|
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||||
|
pattern.push(circle.i);
|
||||||
|
circlesClone.splice(8, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
positions = [];
|
||||||
|
|
||||||
|
pattern = pattern.join("");
|
||||||
|
|
||||||
|
if (pattern) {
|
||||||
|
if (storedPatterns[pattern]) {
|
||||||
|
var app = storedPatterns[pattern].app;
|
||||||
|
if (!!app && !!app.src) {
|
||||||
|
if (storedPatterns.settings) {
|
||||||
|
if (storedPatterns.settings.lockDisabled) {
|
||||||
|
Bangle.setLCDPower(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bangle.removeListener("drag", dragHandler);
|
||||||
|
load(app.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var debounceTimeoutId;
|
||||||
|
var debounce = (delay) => {
|
||||||
|
if (debounceTimeoutId) {
|
||||||
|
clearTimeout(debounceTimeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
debounceTimeoutId = setTimeout(() => {
|
||||||
|
debounceTimeoutId = undefined;
|
||||||
|
resolve();
|
||||||
|
}, delay || 500);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var cloneCirclesArray = () => {
|
||||||
|
var circlesClone = Array(CIRCLES.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < CIRCLES.length; i++) {
|
||||||
|
circlesClone[i] = CIRCLES[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return circlesClone;
|
||||||
|
};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var sui = Bangle.setUI;
|
||||||
|
Bangle.setUI = function (mode, cb) {
|
||||||
|
sui(mode, cb);
|
||||||
|
if (!mode) {
|
||||||
|
Bangle.removeListener("drag", dragHandler);
|
||||||
|
storedPatterns = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mode.startsWith("clock")) {
|
||||||
|
storedPatterns = {};
|
||||||
|
Bangle.removeListener("drag", dragHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var storage = require("Storage");
|
||||||
|
storedPatterns = storage.readJSON("ptlaunch.patterns.json", 1) || {};
|
||||||
|
if (Object.keys(storedPatterns).length > 0) {
|
||||||
|
Bangle.on("drag", dragHandler);
|
||||||
|
if (storedPatterns.settings) {
|
||||||
|
if (storedPatterns.settings.lockDisabled) {
|
||||||
|
Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Add posibillity to generate Wifi code.
|
0.02: Add posibillity to generate Wifi code.
|
||||||
|
0.03: Forces integer scaling and adds more configuration (error correction, description, display)
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,11 @@
|
||||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<input type="radio" id="useURL" name="mode" checked/>
|
<input type="radio" id="useTEXT" name="mode" checked/>
|
||||||
<label for="useURL">Use URL:</label>
|
<label for="useTEXT">Use text (for example an URL):</label>
|
||||||
<input type="text" id="url" class="form-input" value="http://espruino.com">
|
<input type="text" id="text" class="form-input" value="www.espruino.com">
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<input type="radio" id="useWIFI" name="mode"/>
|
<input type="radio" id="useWIFI" name="mode"/>
|
||||||
<label for="useWIFI">Use Wifi Credentials:</label>
|
<label for="useWIFI">Use Wifi Credentials:</label>
|
||||||
<input type="text" id="ssid" class="form-input" value="">
|
<input type="text" id="ssid" class="form-input" value="">
|
||||||
|
|
@ -25,30 +26,54 @@
|
||||||
<input type="checkbox" id="hidden" name="hidden"/>
|
<input type="checkbox" id="hidden" name="hidden"/>
|
||||||
<label for="hidden">Wifi is hidden</label>
|
<label for="hidden">Wifi is hidden</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<p id="errors" style="color:Tomato;"></p>
|
||||||
<p>Try your QR Code: <div id="qrcode"></div></p>
|
<p>Try your QR Code: <div id="qrcode"></div></p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<p>Additional options:</p>
|
||||||
|
<input type="checkbox" id="preventIntegerScaling" name="preventIntegerScaling"/>
|
||||||
|
<label for="preventIntegerScaling">Prevent integer scaling</label></br>
|
||||||
|
<input type="checkbox" id="boostBacklight" name="boostBacklight"/>
|
||||||
|
<label for="boostBacklight">Set backlight to max. while QR is shown</label></br>
|
||||||
|
<input type="checkbox" id="stayOn" name="stayOn"/>
|
||||||
|
<label for="stayOn">Do not lock or dim while showing QR</label></br>
|
||||||
|
<input type="checkbox" id="hideDescription" name="hideDescription"/>
|
||||||
|
<label for="hideDescription">Hide Description</label></br>
|
||||||
|
<label for="description">Replace default description:</label>
|
||||||
|
<input type="text" id="description" class="form-input" value="">
|
||||||
|
<label for="correction">Error correction level:</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<select name="correction" id="correction" class="form-control">
|
||||||
|
<option value="1">L - Low - 7%</option>
|
||||||
|
<option value="0">M - Medium - 15%</option>
|
||||||
|
<option value="3">Q - Quartile - 25%</option>
|
||||||
|
<option value="2">H - High - 30%</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||||
|
|
||||||
|
|
||||||
<script src="../../core/lib/customize.js"></script>
|
<script src="../../core/lib/customize.js"></script>
|
||||||
<script src="../../core/lib/qrcode.min.js"></script><!-- https://davidshimjs.github.io/qrcodejs/ -->
|
<script src="../../core/lib/qrcode.min.js"></script><!-- https://davidshimjs.github.io/qrcodejs/ -->
|
||||||
<script src="../../core/lib/heatshrink.js"></script>
|
<script src="../../core/lib/heatshrink.js"></script>
|
||||||
<script src="../../core/lib/imageconverter.js"></script>
|
<script src="../../core/lib/imageconverter.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var targetWidth = 200;
|
var targetSize = 200;
|
||||||
var targetHeight = 200;
|
|
||||||
|
|
||||||
function onInit(device) {
|
function onInit(device) {
|
||||||
if (device && device.info && device.info.g) {
|
if (device && device.info && device.info.g) {
|
||||||
targetWidth = device.info.g.width - 20;
|
border = 4;
|
||||||
targetHeight = device.info.g.height - 20;
|
targetSize = Math.min(device.info.g.width - border, device.info.g.height - border);
|
||||||
}
|
}
|
||||||
qrcode = new QRCode("qrcode", {
|
qrcode = new QRCode("qrcode", {
|
||||||
text: document.getElementById("url").value,
|
text: document.getElementById("text").value,
|
||||||
width: targetWidth,
|
|
||||||
height: targetHeight,
|
|
||||||
colorDark : "#000000",
|
colorDark : "#000000",
|
||||||
colorLight : "#ffffff",
|
colorLight : "#ffffff",
|
||||||
});
|
});
|
||||||
|
refreshQRCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
//https://github.com/evgeni/qifi/blob/gh-pages/index.html#L168
|
//https://github.com/evgeni/qifi/blob/gh-pages/index.html#L168
|
||||||
|
|
@ -75,40 +100,82 @@
|
||||||
return qrstring;
|
return qrstring;
|
||||||
}
|
}
|
||||||
function refreshQRCode(){
|
function refreshQRCode(){
|
||||||
|
document.getElementById("errors").innerText="";
|
||||||
qrcode.clear(); // clear the code.
|
qrcode.clear(); // clear the code.
|
||||||
|
var qrText = "";
|
||||||
if(document.getElementById("useWIFI").checked){
|
if(document.getElementById("useWIFI").checked){
|
||||||
const ssid = document.getElementById("ssid").value;
|
const ssid = document.getElementById("ssid").value;
|
||||||
const password = document.getElementById("password").value;
|
const password = document.getElementById("password").value;
|
||||||
const encryption = document.getElementById("encryption").value;
|
const encryption = document.getElementById("encryption").value;
|
||||||
const hidden = document.getElementById("hidden").checked;
|
const hidden = document.getElementById("hidden").checked;
|
||||||
const wifiString = generateWifiString(ssid, password, hidden, encryption);
|
const wifiString = generateWifiString(ssid, password, hidden, encryption);
|
||||||
qrcode.makeCode(wifiString);
|
qrText= wifiString;
|
||||||
}else{
|
} else {
|
||||||
qrcode.makeCode(document.getElementById("url").value);
|
qrText = document.getElementById("text").value;
|
||||||
|
}
|
||||||
|
qrcode._htOption.text = qrText;
|
||||||
|
qrcode._htOption.correctLevel = parseInt(document.getElementById("correction").value);
|
||||||
|
try {
|
||||||
|
qrcode.makeCode(qrText);
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById("errors").innerText="Error: QR could not be created.";
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
var finalSizeQr=targetSize;
|
||||||
|
var finalSizeCanvas=targetSize;
|
||||||
|
|
||||||
|
var integerScale = Math.max(Math.floor(targetSize / (qrcode._oQRCode.moduleCount + 1)),1);
|
||||||
|
if (integerScale == 1) document.getElementById("errors").innerText = "Warning, QR will probably be too small to properly scan. Try less data or less error correction.";
|
||||||
|
|
||||||
|
if (!document.getElementById("preventIntegerScaling").checked){
|
||||||
|
|
||||||
|
finalSizeQr = integerScale * (qrcode._oQRCode.moduleCount + 1);
|
||||||
|
finalSizeCanvas = finalSizeQr - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qrcode._htOption.width = finalSizeQr;
|
||||||
|
qrcode._htOption.height = finalSizeQr;
|
||||||
|
|
||||||
|
document.getElementsByTagName("canvas")[0].width = finalSizeCanvas;
|
||||||
|
document.getElementsByTagName("canvas")[0].height = finalSizeCanvas;
|
||||||
|
try {
|
||||||
|
qrcode.makeCode(qrText);
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById("errors").innerText="Error: QR could not be created.";
|
||||||
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var qrcode;
|
var qrcode;
|
||||||
|
|
||||||
document.getElementById("url").addEventListener("change", refreshQRCode);
|
|
||||||
document.getElementById("ssid").addEventListener("change",refreshQRCode);
|
document.getElementById("ssid").addEventListener("change",refreshQRCode);
|
||||||
|
document.getElementById("text").addEventListener("change",refreshQRCode);
|
||||||
document.getElementById("password").addEventListener("change",refreshQRCode);
|
document.getElementById("password").addEventListener("change",refreshQRCode);
|
||||||
document.getElementById("encryption").addEventListener("change",refreshQRCode);
|
document.getElementById("encryption").addEventListener("change",refreshQRCode);
|
||||||
document.getElementById("hidden").addEventListener("change",refreshQRCode);
|
document.getElementById("hidden").addEventListener("change",refreshQRCode);
|
||||||
document.getElementById("useURL").addEventListener("change",refreshQRCode);
|
document.getElementById("useTEXT").addEventListener("change",refreshQRCode);
|
||||||
document.getElementById("useWIFI").addEventListener("change",refreshQRCode);
|
document.getElementById("useWIFI").addEventListener("change",refreshQRCode);
|
||||||
|
document.getElementById("preventIntegerScaling").addEventListener("change",refreshQRCode);
|
||||||
|
document.getElementById("correction").addEventListener("change",refreshQRCode);
|
||||||
document.getElementById("upload").addEventListener("click", function() {
|
document.getElementById("upload").addEventListener("click", function() {
|
||||||
var content = document.getElementById("url").value;
|
var content = document.getElementById("text").value;
|
||||||
if(document.getElementById("useWIFI").checked){
|
if(document.getElementById("useWIFI").checked){
|
||||||
content = document.getElementById("ssid").value
|
content = document.getElementById("ssid").value
|
||||||
}
|
}
|
||||||
|
if(!(document.getElementById("description").value === "")){
|
||||||
|
content = document.getElementById("description").value;
|
||||||
|
}
|
||||||
var img = imageconverter.canvastoString(document.getElementsByTagName("canvas")[0],{mode:"1bit",output:"string",compression:true});
|
var img = imageconverter.canvastoString(document.getElementsByTagName("canvas")[0],{mode:"1bit",output:"string",compression:true});
|
||||||
var app = `var img = ${img};
|
var app = `var img = ${img};
|
||||||
var content = ${JSON.stringify(content)};
|
${document.getElementById("boostBacklight").checked ? 'Bangle.setLCDBrightness(1);' : ''}
|
||||||
|
${document.getElementById("stayOn").checked ? 'Bangle.setLCDTimeout(0);' : ''}
|
||||||
|
${document.getElementById("hideDescription").checked ? '' : `var content = ${JSON.stringify(content)};`}
|
||||||
g.clear(1).setColor(1,1,1).setBgColor(0,0,0);
|
g.clear(1).setColor(1,1,1).setBgColor(0,0,0);
|
||||||
g.fillRect(0,0,g.getWidth()-1,g.getHeight()-1);
|
g.fillRect(0,0,g.getWidth()-1,g.getHeight()-1);
|
||||||
g.drawImage(img,(g.getWidth()-img[0])/2,(g.getHeight()-img[1])/2);
|
g.drawImage(img,(g.getWidth()-img[0])/2,(g.getHeight()-img[1])/2);
|
||||||
g.setFontAlign(0,0).setFont("6x8").setColor(0,0,0);
|
${ document.getElementById("hideDescription").checked ? '' : `g.setFontAlign(0,0).setFont("6x8").setColor(0,0,0);
|
||||||
g.drawString(content,g.getWidth()/2,g.getHeight()-(g.getHeight()-img[1])/4));
|
g.drawString(content,g.getWidth()/2,g.getHeight()-(g.getHeight()-img[1])/4));
|
||||||
|
`}
|
||||||
g.setColor(1,1,1);
|
g.setColor(1,1,1);
|
||||||
`;
|
`;
|
||||||
sendCustomizedApp({
|
sendCustomizedApp({
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: Initial release
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Scribble
|
||||||
|
|
||||||
|
A tree-based keyboard, inspired by Tertiary Text on Pebble.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Tap a button to select text.
|
||||||
|
Swipe left to right for enter space.
|
||||||
|
Swipe right to left to delete.
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
|
||||||
|
enricorov
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
{ "id": "scribble",
|
||||||
|
"name": "Scribble",
|
||||||
|
"shortName":"Scribble",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "A keyboard on your wrist!",
|
||||||
|
"icon": "app.png",
|
||||||
|
"tags": "keyboard, text, scribble",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"scribble.app.js","url":"app.js"},
|
||||||
|
{"name":"scribble.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwMB/4A2/IFE+IFE+YFE84FE44FE54SEz/jAocfDAk/54EC/1/x4FC/l/z4FDCQJGD/wFD+IYBIwYSBIwf4IwhfEIwuPIwkPIwMAj//g/P/gFCkOP/AEB/8wj5+Dn0/Aoc8n/4JAU4v/8gYFBaYWAJ4MHAoPwEgMPOgUfLogJCBYQFE+AFD8BHB/EAAAV/AoYyCB4IKBc6QA=="))
|
||||||
|
|
@ -0,0 +1,469 @@
|
||||||
|
const black = "#000000";
|
||||||
|
const white = "#ffffff";
|
||||||
|
const gray1 = "#444444";
|
||||||
|
const gray2 = "#888888";
|
||||||
|
const gray3 = "#bbbbbb";
|
||||||
|
|
||||||
|
const red = "#FF0000";
|
||||||
|
const green = "#00FF00";
|
||||||
|
const blue = "#0000FF";
|
||||||
|
|
||||||
|
const transp = -1;
|
||||||
|
const abc = "abcdefghijklmnopqrstuvwxyz1234567890";
|
||||||
|
// const abc_up = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||||
|
const uppercase = 1;
|
||||||
|
var last_layer = false; // set to true at the last layer of the tree
|
||||||
|
let chunk_size = 6;
|
||||||
|
|
||||||
|
const font_height = 2;
|
||||||
|
const global_font = "Dennis8";
|
||||||
|
require("FontDennis8").add(Graphics);
|
||||||
|
|
||||||
|
const editable_buf = "Scribble";
|
||||||
|
|
||||||
|
const left = 3;
|
||||||
|
const _screen_mid = g.getWidth() / 2;
|
||||||
|
const right = 176 - 4;
|
||||||
|
|
||||||
|
const box_size = {
|
||||||
|
w: _screen_mid - 6,
|
||||||
|
h: 46,
|
||||||
|
};
|
||||||
|
|
||||||
|
const spacing = 4;
|
||||||
|
const border = 4;
|
||||||
|
const top_start = 25;
|
||||||
|
|
||||||
|
const pos_y = [
|
||||||
|
top_start,
|
||||||
|
top_start + (box_size.h + spacing),
|
||||||
|
top_start + (box_size.h + spacing) * 2,
|
||||||
|
];
|
||||||
|
|
||||||
|
// list of points to render
|
||||||
|
const points = {
|
||||||
|
"3x2": [{ x: left, y: pos_y[0] },
|
||||||
|
{ x: left, y: pos_y[1] },
|
||||||
|
{ x: left, y: pos_y[2] },
|
||||||
|
{ x: _screen_mid + 2, y: pos_y[0] },
|
||||||
|
{ x: _screen_mid + 2, y: pos_y[1] },
|
||||||
|
{ x: _screen_mid + 2, y: pos_y[2] },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
g.theme = {
|
||||||
|
fg: white,
|
||||||
|
bg: black,
|
||||||
|
fg2: white,
|
||||||
|
bg2: black,
|
||||||
|
fgH: black,
|
||||||
|
bgH: red,
|
||||||
|
dark: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const maxX = g.getWidth();
|
||||||
|
const maxY = g.getHeight();
|
||||||
|
const fontSize = g.getWidth() > 200 ? 2 : 1;
|
||||||
|
const rowN = 7;
|
||||||
|
const colN = 7;
|
||||||
|
const headerH = maxY / 7;
|
||||||
|
const rowH = (maxY - headerH) / rowN;
|
||||||
|
const colW = maxX / colN;
|
||||||
|
|
||||||
|
function getRndInteger(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Window {
|
||||||
|
constructor(label, bgCol) {
|
||||||
|
this.label = "win_"
|
||||||
|
this.label += (typeof label !== "undefined") ? label : "Unset";
|
||||||
|
console.log(`Constructing Window ${this.label}, args: ${arguments}`)
|
||||||
|
|
||||||
|
this.bgCol = bgCol;
|
||||||
|
this.layers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
push(layer) {
|
||||||
|
layer.label=`${this.layers.length}_${layer.label}`;
|
||||||
|
this.layers.push(layer);
|
||||||
|
}
|
||||||
|
pop() {
|
||||||
|
this.layers.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
top_layer() {
|
||||||
|
return this.layers[this.layers.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if (this.bgCol !== transp) {
|
||||||
|
console.log(`${this.label}: filling bg in ${this.bgCol}`);
|
||||||
|
g.setColor(this.bgCol);
|
||||||
|
g.fillRect(0, 0, g.getWidth(), g.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
this.layers.forEach((lyr) => {
|
||||||
|
// console.log(`Rendering Layer ${i} ${lyr.label}`)
|
||||||
|
i++;
|
||||||
|
lyr.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Layer {
|
||||||
|
constructor(label) {
|
||||||
|
|
||||||
|
this.label = "lyr_"
|
||||||
|
|
||||||
|
this.label += (typeof label !== "undefined") ? label : "Unset";
|
||||||
|
console.log(`Constructing Layer ${this.label}, args: ${arguments}`)
|
||||||
|
this.items = [];
|
||||||
|
// console.log(`bg is ${bg} type ${typeof bg}`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
push(button) {
|
||||||
|
this.items.push(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLabel(label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseTaps(xy) {
|
||||||
|
this.items.forEach(item => {
|
||||||
|
// // print(item)
|
||||||
|
if (item.was_tapped(xy)) {
|
||||||
|
// pass parent layer to the tapped button
|
||||||
|
item.callback(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
this.items.forEach((item) => {
|
||||||
|
|
||||||
|
item.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BTN_layer extends Layer {
|
||||||
|
|
||||||
|
constructor(label, layout) {
|
||||||
|
super();
|
||||||
|
Layer.call(this, label)
|
||||||
|
|
||||||
|
this.alphabet = (uppercase) ? abc.toUpperCase() : abc;
|
||||||
|
console.log(`Constructing BTN_Layer ${this.label}, layout ${this.layout}`)
|
||||||
|
|
||||||
|
if (layout in points) {
|
||||||
|
|
||||||
|
this.create_layout(layout);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw `Invalid layout passed ->[${layout}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// // print(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
Layer.prototype.render.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
create_layout(layout) {
|
||||||
|
|
||||||
|
console.log(`Creating layout ${layout}`);
|
||||||
|
|
||||||
|
let start_p = 0;
|
||||||
|
|
||||||
|
this.items = this.push_buttons(points[layout], this.alphabet, start_p, chunk_size)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
push_buttons(points, in_string, start_p) {
|
||||||
|
|
||||||
|
items = [];
|
||||||
|
spacer = "" // char interposed b/w the two halves of text per button
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
substr = `${in_string.substring(
|
||||||
|
start_p,
|
||||||
|
start_p + chunk_size / 2
|
||||||
|
)}${spacer}${in_string.substring(start_p + chunk_size / 2, start_p + chunk_size)}`;
|
||||||
|
|
||||||
|
btn_label =
|
||||||
|
uppercase === 1
|
||||||
|
? substr.toUpperCase()
|
||||||
|
: substr;
|
||||||
|
|
||||||
|
start_p += chunk_size;
|
||||||
|
|
||||||
|
items.push(
|
||||||
|
new Button(
|
||||||
|
i, // ID of button
|
||||||
|
points[i].x, // left
|
||||||
|
points[i].y, // top
|
||||||
|
btn_label, // text to render in the button
|
||||||
|
box_size.w, // width
|
||||||
|
box_size.h, // height
|
||||||
|
g.theme.bg, // box bg
|
||||||
|
white, // box fill
|
||||||
|
black // text col
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_labels(in_string, start_p, chk_size) {
|
||||||
|
// print(`Updating labels | in_string ${in_string} start_p ${start_p} chk_size ${chk_size}`);
|
||||||
|
in_string.replace('\n', ''); // remove newlines just in case
|
||||||
|
|
||||||
|
spacer = "" // char interposed b/w the two halves of text per button
|
||||||
|
|
||||||
|
for (let i = 0; i < this.items.length; i++) {
|
||||||
|
|
||||||
|
item = this.items[i];
|
||||||
|
substr = (chk_size < 3)
|
||||||
|
? in_string.substring(start_p + chk_size * i, start_p + (chk_size * (i + 1)))
|
||||||
|
: `${in_string.substring(
|
||||||
|
start_p + chk_size * i,
|
||||||
|
start_p + chk_size * i + chk_size / 2
|
||||||
|
)}${spacer}${in_string.substring(start_p + chk_size * i + chk_size / 2, start_p + chk_size * i + chk_size)}`;
|
||||||
|
// // print(`(chk_size > 3): ${(chk_size > 3)}`)
|
||||||
|
// print(`Label ${i} -> ${substr}`);
|
||||||
|
item.setLabel(substr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
zoom_in(id) {
|
||||||
|
let start_p = id * chunk_size;
|
||||||
|
// print(`Zooming in | start_p ${start_p}`)
|
||||||
|
if (chunk_size % this.items.length !== 0) {
|
||||||
|
throw `Chunk size [${chunk_size}] does not fit #btns [${this.items.length}]`
|
||||||
|
}
|
||||||
|
subchunk_size = chunk_size / this.items.length;
|
||||||
|
|
||||||
|
substr = this.alphabet.substring(start_p, start_p + chunk_size);
|
||||||
|
// print(`substr ${substr}`);
|
||||||
|
// print(`subchunk_size ${subchunk_size}`);
|
||||||
|
this.update_labels(substr, 0, subchunk_size);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Button {
|
||||||
|
constructor(id, x, y, text, w, h, col, bgCol, txtCol, font) {
|
||||||
|
this.id = id;
|
||||||
|
this.label = `btn_${this.id}`;
|
||||||
|
|
||||||
|
this.text = text;
|
||||||
|
this.x1 = x;
|
||||||
|
this.y1 = y;
|
||||||
|
this.w = w;
|
||||||
|
this.h = h;
|
||||||
|
this.col = typeof col !== "undefined" ? col : black;
|
||||||
|
this.bgCol = typeof bgCol !== "undefined" ? bgCol : gray2;
|
||||||
|
this.txtCol = typeof txtCol !== "undefined" ? txtCol : black;
|
||||||
|
// this.font = font;
|
||||||
|
|
||||||
|
this.x2 = this.x1 + this.w;
|
||||||
|
this.y2 = this.y1 + this.h;
|
||||||
|
this.center = {
|
||||||
|
x: (this.x1 + this.x2) / 2,
|
||||||
|
y: (this.y1 + this.y2) / 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
console.log(`Constructed button `)
|
||||||
|
// // print(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// console.log(
|
||||||
|
// `Button ${this.text} -> P1: (${this.x1}, ${this.y1}) | P2: (${this.x2}, ${this.y2})`
|
||||||
|
// );
|
||||||
|
|
||||||
|
g.setColor(this.bgCol);
|
||||||
|
g.fillRect(this.x1, this.y1, this.x2, this.y2);
|
||||||
|
g.setColor(this.col);
|
||||||
|
g.drawRect(this.x1, this.y1, this.x2, this.y2);
|
||||||
|
g.setColor(this.txtCol);
|
||||||
|
|
||||||
|
g.setFontAlign(0, 0).setFont(global_font, font_height);
|
||||||
|
g.drawString(this.text, this.center.x, this.center.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// short tap callback func
|
||||||
|
callback(parent_layer) {
|
||||||
|
// print(`Tapped button ${this.id}`);
|
||||||
|
|
||||||
|
// this.highlight(); // TODO set up highlighting
|
||||||
|
if (last_layer) {
|
||||||
|
l_text.items[0].text += this.text;
|
||||||
|
// print(`Updated buffer to ${l_text.items[0].text}`)
|
||||||
|
parent_layer.update_labels(parent_layer.alphabet, 0, chunk_size);
|
||||||
|
last_layer = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parent_layer.zoom_in(this.id);
|
||||||
|
last_layer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
was_tapped(xy) {
|
||||||
|
var x = xy.x;
|
||||||
|
var y = xy.y;
|
||||||
|
|
||||||
|
if ((x > this.x1 && x < this.x2) && (y > this.y1 && y < this.y2)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLabel(lbl) {
|
||||||
|
// // print(`Button ${this.id}, updating label ${this.text} with ${lbl}`);
|
||||||
|
this.text = lbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLabel(lbl) {
|
||||||
|
|
||||||
|
return this.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight() {
|
||||||
|
|
||||||
|
g.setColor(g.theme.bgH);
|
||||||
|
g.fillRect(this.x1, this.y1, this.x2, this.y2);
|
||||||
|
g.setColor(g.theme.fgH);
|
||||||
|
g.drawRect(this.x1, this.y1, this.x2, this.y2);
|
||||||
|
g.setColor(this.fg);
|
||||||
|
|
||||||
|
g.setFontAlign(0, 0).setFont(global_font, font_height);
|
||||||
|
g.drawString(this.text, this.center.x, this.center.y);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextBox {
|
||||||
|
|
||||||
|
constructor(x, y, text, col) {
|
||||||
|
|
||||||
|
// x and y are the center points
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.text = (typeof text !== undefined) ? text : "Default";
|
||||||
|
this.col = (typeof col !== undefined) ? col : red;
|
||||||
|
|
||||||
|
// console.log(`Constr TextBox ${this.text} -> Center: (${this.x}, ${this.y}) | Col ${this.col}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// console.log(`Rendering TextBox`)
|
||||||
|
|
||||||
|
var align_center = (0, 1);
|
||||||
|
var align_right = (0, 0);
|
||||||
|
alignment = (g.stringWidth(this.text) < g.getWidth()) ? align_center : align_right;
|
||||||
|
// coords = (g.stringWidth(this.text) < g.getWidth()- 20) ? {x:this.x, y:this.y} : {x:g.getWidth()-border, y:this.y}
|
||||||
|
coords = { x: this.x, y: this.y };
|
||||||
|
g.setColor(this.col);
|
||||||
|
g.setFontAlign(0, 0).setFont(global_font, font_height);
|
||||||
|
g.drawString(this.text, coords.x, coords.y);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Screen refresh *************************************/
|
||||||
|
|
||||||
|
function draw(obj) {
|
||||||
|
console.log("draw()");
|
||||||
|
obj.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
let tickTimer;
|
||||||
|
|
||||||
|
function clearTickTimer() {
|
||||||
|
if (tickTimer) {
|
||||||
|
clearTimeout(tickTimer);
|
||||||
|
tickTimer = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueNextTick() {
|
||||||
|
clearTickTimer();
|
||||||
|
tickTimer = setTimeout(tick, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tick() {
|
||||||
|
console.log("tick");
|
||||||
|
draw(window);
|
||||||
|
// queueNextTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init **********************************************/
|
||||||
|
|
||||||
|
var window = new Window("abc", red);
|
||||||
|
|
||||||
|
var l_btns = new BTN_layer("btns", "3x2");
|
||||||
|
|
||||||
|
var l_text = new Layer("text"); // black
|
||||||
|
|
||||||
|
var box = new TextBox(
|
||||||
|
_screen_mid,
|
||||||
|
12,
|
||||||
|
editable_buf,
|
||||||
|
white
|
||||||
|
);
|
||||||
|
|
||||||
|
l_text.push(box);
|
||||||
|
|
||||||
|
window.push(l_text);
|
||||||
|
window.push(l_btns);
|
||||||
|
|
||||||
|
// Set up callbacks for touches
|
||||||
|
|
||||||
|
Bangle.on('touch', function (button, xy) {
|
||||||
|
|
||||||
|
window.top_layer().parseTaps(xy);
|
||||||
|
window.render();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Bangle.on('swipe', function (direction) {
|
||||||
|
|
||||||
|
console.log(`Swipe dir ${direction}`);
|
||||||
|
|
||||||
|
if (direction === -1) { // left
|
||||||
|
|
||||||
|
l_text.items[0].text = l_text.items[0].text.slice(0, -1);
|
||||||
|
|
||||||
|
} else if (direction == 1) { // right
|
||||||
|
|
||||||
|
l_text.items[0].text += ' ';
|
||||||
|
|
||||||
|
}
|
||||||
|
window.render();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear the screen once, at startup
|
||||||
|
g.clear();
|
||||||
|
|
||||||
|
// Start ticking
|
||||||
|
tick();
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Corrected variable initialisation
|
0.02: Corrected variable initialisation
|
||||||
|
0.03: Advertise app name, added screenshots
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
// Non-user-configurable constants
|
// Non-user-configurable constants
|
||||||
const APP_ID = 'sensible';
|
const APP_ID = 'sensible';
|
||||||
|
const ESPRUINO_COMPANY_CODE = 0x0590;
|
||||||
|
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
|
|
@ -90,6 +91,19 @@ let magMenu = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Transmit the app name under the Espruino company code to facilitate discovery
|
||||||
|
function transmitAppName() {
|
||||||
|
let options = {
|
||||||
|
showName: false,
|
||||||
|
manufacturer: ESPRUINO_COMPANY_CODE,
|
||||||
|
manufacturerData: JSON.stringify({ name: APP_ID }),
|
||||||
|
interval: 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
NRF.setAdvertising({}, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update acceleration
|
// Update acceleration
|
||||||
Bangle.on('accel', function(newAcc) {
|
Bangle.on('accel', function(newAcc) {
|
||||||
acc = newAcc;
|
acc = newAcc;
|
||||||
|
|
@ -155,6 +169,7 @@ Bangle.on('mag', function(newMag) {
|
||||||
|
|
||||||
// On start: enable sensors and display main menu
|
// On start: enable sensors and display main menu
|
||||||
g.clear();
|
g.clear();
|
||||||
|
transmitAppName();
|
||||||
Bangle.setBarometerPower(isBarEnabled, APP_ID);
|
Bangle.setBarometerPower(isBarEnabled, APP_ID);
|
||||||
Bangle.setGPSPower(isGpsEnabled, APP_ID);
|
Bangle.setGPSPower(isGpsEnabled, APP_ID);
|
||||||
Bangle.setHRMPower(isHrmEnabled, APP_ID);
|
Bangle.setHRMPower(isHrmEnabled, APP_ID);
|
||||||
|
|
|
||||||
|
|
@ -38,3 +38,4 @@
|
||||||
0.33: Really fix 'beep' menu on Bangle.js 2 this time
|
0.33: Really fix 'beep' menu on Bangle.js 2 this time
|
||||||
0.34: Remove Quiet Mode LCD settings: now handled by Quiet Mode Schedule app
|
0.34: Remove Quiet Mode LCD settings: now handled by Quiet Mode Schedule app
|
||||||
0.35: Change App/Widget settings to 'App Settings' so it fits on Bangle screen
|
0.35: Change App/Widget settings to 'App Settings' so it fits on Bangle screen
|
||||||
|
0.36: Added 'Utils' menu with helpful utilities for restoring Bangle.js
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,26 @@
|
||||||
|
|
||||||
This is Bangle.js's settings menu
|
This is Bangle.js's settings menu
|
||||||
|
|
||||||
* **Make Connectable** regardless of the current Bluetooth settings, makes Bangle.js so you can connect to it (while the window is up)
|
|
||||||
* **App/Widget Settings** settings specific to installed applications
|
* **App/Widget Settings** settings specific to installed applications
|
||||||
* **BLE** Bluetooth Settings menu - see below.
|
* **BLE** Bluetooth Settings menu - see below.
|
||||||
* **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
|
* **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.
|
|
||||||
* **NOTE:** on some platforms enabling HID can cause you problems when trying to connect to Bangle.js to upload apps.
|
|
||||||
* **Set Time** Configure the current time - Note that this can be done much more easily by choosing 'Set Time' from the App Loader
|
* **Set Time** Configure the current time - Note that this can be done much more easily by choosing 'Set Time' from the App Loader
|
||||||
* **LCD** Configure settings about the screen. How long it stays on, how bright it is, and when it turns on - see below.
|
* **LCD** Configure settings about the screen. How long it stays on, how bright it is, and when it turns on - see below.
|
||||||
* **Theme** Adjust the colour scheme
|
* **Theme** Adjust the colour scheme
|
||||||
* **Reset Settings** Reset the settings to defaults
|
* **Utils** Utilities - including resetting settings (see below)
|
||||||
* **Turn Off** Turn Bangle.js off
|
* **Turn Off** Turn Bangle.js off
|
||||||
|
|
||||||
## BLE - Bluetooth Settings
|
## BLE - Bluetooth Settings
|
||||||
|
|
||||||
|
* **Make Connectable** regardless of the current Bluetooth settings, makes Bangle.js so you can connect to it (while the window is up)
|
||||||
* **BLE** is Bluetooth LE enabled and the watch connectable?
|
* **BLE** is Bluetooth LE enabled and the watch connectable?
|
||||||
* **Programmable** if BLE is on, can the watch be connected to in order to program/upload apps? As long as your watch firmware is up to date, Gadgetbridge will work even with `Programmable` set to `Off`.
|
* **Programmable** if BLE is on, can the watch be connected to in order to program/upload apps? As long as your watch firmware is up to date, Gadgetbridge will work even with `Programmable` set to `Off`.
|
||||||
|
* **HID** When Bluetooth is enabled, Bangle.js can appear as a Bluetooth Keyboard/Joystick/etc to send keypresses to a connected device.
|
||||||
|
* **NOTE:** on some platforms enabling HID can cause you problems when trying to connect to Bangle.js to upload apps.
|
||||||
* **Passkey BETA** allows you to set a passkey that is required to connect and pair to Bangle.js. **Note:** This is Beta and you will almost certainly encounter issues connecting with Web Bluetooth using this option.
|
* **Passkey BETA** allows you to set a passkey that is required to connect and pair to Bangle.js. **Note:** This is Beta and you will almost certainly encounter issues connecting with Web Bluetooth using this option.
|
||||||
* **Whitelist** allows you to specify only specific devices that you will let connect to your Bangle.js. Simply choose the menu item, then `Add Device`, and then connect to Bangle.js with the device you want to add. If you are already connected you will have to disconnect first. Changes will take effect when you exit the `Settings` app.
|
* **Whitelist** allows you to specify only specific devices that you will let connect to your Bangle.js. Simply choose the menu item, then `Add Device`, and then connect to Bangle.js with the device you want to add. If you are already connected you will have to disconnect first. Changes will take effect when you exit the `Settings` app.
|
||||||
* **NOTE:** iOS devices and newer Android devices often implement Address Randomisation and change their Bluetooth address every so often. If you device's address changes, you will be unable to connect until you update the whitelist again.
|
* **NOTE:** iOS devices and newer Android devices often implement Address Randomisation and change their Bluetooth address every so often. If you device's address changes, you will be unable to connect until you update the whitelist again.
|
||||||
|
|
@ -44,4 +43,16 @@ The exact effects depend on the app. In general the watch will not wake up by i
|
||||||
- Off: Normal operation
|
- Off: Normal operation
|
||||||
- Alarms: Stops notifications, but "alarm" apps will still work
|
- Alarms: Stops notifications, but "alarm" apps will still work
|
||||||
- Silent: Blocks even alarms
|
- Silent: Blocks even alarms
|
||||||
|
|
||||||
|
## Utils
|
||||||
|
|
||||||
|
|
||||||
|
* **Debug Info** should debug info be shown on the watch's screen or not?
|
||||||
|
* `Hide` (default) do not show debug information
|
||||||
|
* `Show` Show on the Bangle's screen (when not connected to Bluetooth or `Programmable:off`)
|
||||||
|
* `Log` Show on the Bangle's screen **and** write to a file called `log.txt` on Storage (when not connected to Bluetooth or `Programmable:off`). Warning - this file is appended to so may grow to be large if this is left enabled.
|
||||||
|
* **Compact Storage** Removes deleted/old files from Storage - this will speed up your Bangle.js
|
||||||
|
* **Rewrite Settings** Should not normally be required, but if `.boot0` has been deleted/corrupted (and so no settings are being loaded) this will fix it.
|
||||||
|
* **Flatten Battery** Turns on all devices and draws as much power as possible, attempting to flatten the Bangle.js battery. This can still take 5+ hours.
|
||||||
|
* **Reset Settings** Reset the settings (as set in this app) to defaults. Does not reset settings for other apps.
|
||||||
|
* **Factory Reset** (not available on Bangle.js 1) - wipe **everything** and return to a factory state
|
||||||
|
|
|
||||||
|
|
@ -95,17 +95,8 @@ function showMainMenu() {
|
||||||
const mainmenu = {
|
const mainmenu = {
|
||||||
'': { 'title': 'Settings' },
|
'': { 'title': 'Settings' },
|
||||||
'< Back': ()=>load(),
|
'< Back': ()=>load(),
|
||||||
'Make Connectable': ()=>makeConnectable(),
|
|
||||||
'App Settings': ()=>showAppSettingsMenu(),
|
'App Settings': ()=>showAppSettingsMenu(),
|
||||||
'BLE': ()=>showBLEMenu(),
|
'BLE': ()=>showBLEMenu(),
|
||||||
'Debug Info': {
|
|
||||||
value: settings.log,
|
|
||||||
format: v => v ? "Show" : "Hide",
|
|
||||||
onchange: () => {
|
|
||||||
settings.log = !settings.log;
|
|
||||||
updateSettings();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Beep': beepMenuItem,
|
'Beep': beepMenuItem,
|
||||||
'Vibration': {
|
'Vibration': {
|
||||||
value: settings.vibrate,
|
value: settings.vibrate,
|
||||||
|
|
@ -134,7 +125,7 @@ function showMainMenu() {
|
||||||
'Set Time': ()=>showSetTimeMenu(),
|
'Set Time': ()=>showSetTimeMenu(),
|
||||||
'LCD': ()=>showLCDMenu(),
|
'LCD': ()=>showLCDMenu(),
|
||||||
'Theme': ()=>showThemeMenu(),
|
'Theme': ()=>showThemeMenu(),
|
||||||
'Reset Settings': ()=>showResetMenu(),
|
'Utils': ()=>showUtilMenu(),
|
||||||
'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() },
|
'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -146,6 +137,7 @@ function showBLEMenu() {
|
||||||
var hidN = ["Off", "Kbrd & Media", "Kbrd","Joystick"];
|
var hidN = ["Off", "Kbrd & Media", "Kbrd","Joystick"];
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
'< Back': ()=>showMainMenu(),
|
'< Back': ()=>showMainMenu(),
|
||||||
|
'Make Connectable': ()=>makeConnectable(),
|
||||||
'BLE': {
|
'BLE': {
|
||||||
value: settings.ble,
|
value: settings.ble,
|
||||||
format: boolFormat,
|
format: boolFormat,
|
||||||
|
|
@ -476,21 +468,63 @@ function showLocaleMenu() {
|
||||||
return E.showMenu(localemenu);
|
return E.showMenu(localemenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showResetMenu() {
|
function showUtilMenu() {
|
||||||
const resetmenu = {
|
var menu = {
|
||||||
'': { 'title': 'Reset' },
|
'': { 'title': 'Utilities' },
|
||||||
'< Back': ()=>showMainMenu(),
|
'< Back': ()=>showMainMenu(),
|
||||||
|
'Debug Info': {
|
||||||
|
value: E.clip(0|settings.log,0,2),
|
||||||
|
format: v => ["Hide","Show","Log"][E.clip(0|v,0,2)],
|
||||||
|
onchange: v => {
|
||||||
|
settings.log = v;
|
||||||
|
updateSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Compact Storage': () => {
|
||||||
|
E.showMessage("Compacting...\nTakes approx\n1 minute",{title:"Storage"});
|
||||||
|
require("Storage").compact();
|
||||||
|
showUtilMenu();
|
||||||
|
},
|
||||||
|
'Rewrite Settings': () => {
|
||||||
|
require("Storage").write(".boot0","eval(require('Storage').read('bootupdate.js'));");
|
||||||
|
load("setting.app.js");
|
||||||
|
},
|
||||||
|
'Flatten Battery': () => {
|
||||||
|
E.showMessage('Flattening battery - this can take hours.\nLong-press button to cancel.');
|
||||||
|
Bangle.setLCDTimeout(0);
|
||||||
|
Bangle.setLCDPower(1);
|
||||||
|
if (Bangle.setGPSPower) Bangle.setGPSPower(1,"flat");
|
||||||
|
if (Bangle.setHRMPower) Bangle.setHRMPower(1,"flat");
|
||||||
|
if (Bangle.setCompassPower) Bangle.setCompassPower(1,"flat");
|
||||||
|
if (Bangle.setBarometerPower) Bangle.setBarometerPower(1,"flat");
|
||||||
|
if (Bangle.setHRMPower) Bangle.setGPSPower(1,"flat");
|
||||||
|
setInterval(function() {
|
||||||
|
var i=1000;while (i--);
|
||||||
|
}, 1);
|
||||||
|
},
|
||||||
'Reset Settings': () => {
|
'Reset Settings': () => {
|
||||||
E.showPrompt('Reset Settings?').then((v) => {
|
E.showPrompt('Reset to Defaults?',{title:"Settings"}).then((v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
E.showMessage('Resetting');
|
E.showMessage('Resetting');
|
||||||
resetSettings();
|
resetSettings();
|
||||||
}
|
setTimeout(showMainMenu, 50);
|
||||||
setTimeout(showMainMenu, 50);
|
} else showUtilMenu();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return E.showMenu(resetmenu);
|
if (Bangle.factoryReset) {
|
||||||
|
menu['Factory Reset'] = ()=>{
|
||||||
|
E.showPrompt('This will remove everything!',{title:"Factory Reset"}).then((v) => {
|
||||||
|
if (v) {
|
||||||
|
E.showMessage();
|
||||||
|
Terminal.setConsole();
|
||||||
|
Bangle.factoryReset();
|
||||||
|
} else showUtilMenu();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeConnectable() {
|
function makeConnectable() {
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@
|
||||||
0.09: Fix crash when weather.json is absent.
|
0.09: Fix crash when weather.json is absent.
|
||||||
0.10: Use new Layout library
|
0.10: Use new Layout library
|
||||||
0.11: Bangle.js 2 support
|
0.11: Bangle.js 2 support
|
||||||
|
0.12: Allow hiding the widget
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ You can view the full report through the app:
|
||||||
1. Install [Gadgetbridge for Android](https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/) on your phone.
|
1. Install [Gadgetbridge for Android](https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/) on your phone.
|
||||||
2. Set up [Gadgetbridge weather reporting](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Weather).
|
2. Set up [Gadgetbridge weather reporting](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Weather).
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
* Expiration timespan can be set after which the local weather data is considered as invalid
|
||||||
|
* Widget can be hidden
|
||||||
|
|
||||||
## Controls
|
## Controls
|
||||||
|
|
||||||
BTN2: opens the launcher
|
BTN2: opens the launcher (Bangle.js 1)
|
||||||
|
BTN: opens the launcher (Bangle.js 2)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,14 @@
|
||||||
},
|
},
|
||||||
onchange: x => save('expiry', x),
|
onchange: x => save('expiry', x),
|
||||||
},
|
},
|
||||||
|
'Hide Widget': {
|
||||||
|
value: "hide" in settings ? settings.hide : false,
|
||||||
|
format: () => (settings.hide ? 'Yes' : 'No'),
|
||||||
|
onchange: () => {
|
||||||
|
settings.hide = !settings.hide
|
||||||
|
save('hide', settings.hide);
|
||||||
|
},
|
||||||
|
},
|
||||||
'< Back': back,
|
'< Back': back,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,25 @@
|
||||||
(() => {
|
(() => {
|
||||||
const weather = require('weather');
|
const weather = require('weather');
|
||||||
|
|
||||||
var dirty = false;
|
var dirty = false;
|
||||||
|
|
||||||
|
let settings;
|
||||||
|
|
||||||
|
function loadSettings() {
|
||||||
|
settings = require('Storage').readJSON('weather.json', 1) || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setting(key) {
|
||||||
|
if (!settings) { loadSettings(); }
|
||||||
|
const DEFAULTS = {
|
||||||
|
'expiry': 2*3600000,
|
||||||
|
'hide': false
|
||||||
|
};
|
||||||
|
return (key in settings) ? settings[key] : DEFAULTS[key];
|
||||||
|
}
|
||||||
|
|
||||||
weather.on("update", w => {
|
weather.on("update", w => {
|
||||||
|
if (setting('hide')) return;
|
||||||
if (w) {
|
if (w) {
|
||||||
if (!WIDGETS["weather"].width) {
|
if (!WIDGETS["weather"].width) {
|
||||||
WIDGETS["weather"].width = 20;
|
WIDGETS["weather"].width = 20;
|
||||||
|
|
@ -21,7 +37,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
Bangle.on('lcdPower', on => {
|
Bangle.on('lcdPower', on => {
|
||||||
if (on && dirty) {
|
if (on && dirty && !setting('hide')) {
|
||||||
WIDGETS["weather"].draw();
|
WIDGETS["weather"].draw();
|
||||||
dirty = false;
|
dirty = false;
|
||||||
}
|
}
|
||||||
|
|
@ -29,8 +45,9 @@
|
||||||
|
|
||||||
WIDGETS["weather"] = {
|
WIDGETS["weather"] = {
|
||||||
area: "tl",
|
area: "tl",
|
||||||
width: weather.get() ? 20 : 0,
|
width: weather.get() && !setting('hide') ? 20 : 0,
|
||||||
draw: function() {
|
draw: function() {
|
||||||
|
if (setting('hide')) return;
|
||||||
const w = weather.get();
|
const w = weather.get();
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
g.reset();
|
g.reset();
|
||||||
|
|
@ -47,5 +64,9 @@
|
||||||
g.drawString(t, this.x+10, this.y+24);
|
g.drawString(t, this.x+10, this.y+24);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
reload:function() {
|
||||||
|
loadSettings();
|
||||||
|
WIDGETS["weather"].redraw();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Minor layout format tweak so it uses less memory and draws ok on Bangle.js 1 (#1012)
|
0.02: Minor layout format tweak so it uses less memory and draws ok on Bangle.js 1 (#1012)
|
||||||
|
0.03: Minor layout extra spaces.
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ function draw() {
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
clockLayout.time.label = locale.time(date, 1);
|
clockLayout.time.label = locale.time(date, 1);
|
||||||
clockLayout.date.label = locale.date(date, 1).toUpperCase();
|
clockLayout.date.label = locale.date(date, 1).toUpperCase();
|
||||||
clockLayout.dow.label = locale.dow(date, 1).toUpperCase();
|
clockLayout.dow.label = locale.dow(date, 1).toUpperCase() + " ";
|
||||||
var weatherJson = getWeather();
|
var weatherJson = getWeather();
|
||||||
if(weatherJson && weatherJson.weather){
|
if(weatherJson && weatherJson.weather){
|
||||||
var currentWeather = weatherJson.weather;
|
var currentWeather = weatherJson.weather;
|
||||||
|
|
@ -110,7 +110,7 @@ function draw() {
|
||||||
clockLayout.tempUnit.label = temp[2];
|
clockLayout.tempUnit.label = temp[2];
|
||||||
clockLayout.weatherIcon.src = chooseIcon(currentWeather.txt);
|
clockLayout.weatherIcon.src = chooseIcon(currentWeather.txt);
|
||||||
const wind = locale.speed(currentWeather.wind).match(/^(\D*\d*)(.*)$/);
|
const wind = locale.speed(currentWeather.wind).match(/^(\D*\d*)(.*)$/);
|
||||||
clockLayout.wind.label = wind[1] + " ";
|
clockLayout.wind.label = wind[1] + " ".repeat(wind[2].length-1);
|
||||||
clockLayout.windUnit.label = wind[2] + " " + (currentWeather.wrose||'').toUpperCase();
|
clockLayout.windUnit.label = wind[2] + " " + (currentWeather.wrose||'').toUpperCase();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: Copied from widmp and flipped the phase directions!
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
WIDGETS["widmoonsh"] = { area: "tr", width: 24, draw: function() {
|
||||||
|
const MC = 29.5305882, NM = 694039.09;
|
||||||
|
var r = 11, mx = this.x + 12; my = this.y + 12;
|
||||||
|
|
||||||
|
function moonPhase(d) {
|
||||||
|
var tmp, month = d.getMonth(), year = d.getFullYear(), day = d.getDate();
|
||||||
|
if (month < 3) {year--; month += 12;}
|
||||||
|
tmp = ((365.25 * year + 30.6 * ++month + day - NM) / MC);
|
||||||
|
return Math.round(((tmp - (tmp | 0)) * 7)+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const BLACK = g.theme.bg, MOON = 0x41f;
|
||||||
|
var moon = {
|
||||||
|
0: () => { g.reset().setColor(BLACK).fillRect(mx - r, my - r, mx + r, my + r);},
|
||||||
|
1: () => { moon[0](); g.setColor(MOON).drawCircle(mx, my, r);},
|
||||||
|
2: () => { moon[3](); g.setColor(BLACK).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);},
|
||||||
|
3: () => { moon[0](); g.setColor(MOON).fillCircle(mx, my, r).setColor(BLACK).fillRect(mx, my - r, mx + r, my + r);},
|
||||||
|
4: () => { moon[3](); g.setColor(MOON).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);},
|
||||||
|
5: () => { moon[0](); g.setColor(MOON).fillCircle(mx, my, r);},
|
||||||
|
6: () => { moon[7](); g.setColor(MOON).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);},
|
||||||
|
7: () => { moon[0](); g.setColor(MOON).fillCircle(mx, my, r).setColor(BLACK).fillRect(mx - r, my - r, mx, my + r);},
|
||||||
|
8: () => { moon[7](); g.setColor(BLACK).fillEllipse(mx - r / 2, my - r, mx + r / 2, my + r);}
|
||||||
|
};
|
||||||
|
moon[moonPhase(Date())]();
|
||||||
|
} };
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -1,3 +1,3 @@
|
||||||
0.01: New Widget
|
0.01: New Widget
|
||||||
0.02: swipe left,right update
|
0.02: swipe left,right update
|
||||||
|
0.03: Fix widget visibility code to the top bar isn't cleared by drawWidgets
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,21 @@
|
||||||
if (!Bangle.isLCDOn() || saved) return;
|
if (!Bangle.isLCDOn() || saved) return;
|
||||||
saved = [];
|
saved = [];
|
||||||
for (var wd of WIDGETS) {
|
for (var wd of WIDGETS) {
|
||||||
saved.push(wd.draw);
|
saved.push({d:wd.draw,a:wd.area});
|
||||||
wd.draw=()=>{};
|
wd.draw=()=>{};
|
||||||
|
wd.area="";
|
||||||
}
|
}
|
||||||
g.setColor(0,0,0);
|
g.setColor(0,0,0);
|
||||||
g.fillRect(0,0,239,23);
|
g.fillRect(0,0,g.getWidth(),23);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reveal(){
|
function reveal(){
|
||||||
if (!Bangle.isLCDOn() || !saved) return;
|
if (!Bangle.isLCDOn() || !saved) return;
|
||||||
for (var wd of WIDGETS) wd.draw = saved.shift();
|
for (var wd of WIDGETS) {
|
||||||
|
var o = saved.shift();
|
||||||
|
wd.draw = o.d;
|
||||||
|
wd.area = o.a;
|
||||||
|
}
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
saved=null;
|
saved=null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -207,10 +207,10 @@ apps.forEach((app,appIdx) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// prefer "appid.json" over "appid.settings.json" (TODO: change to ERROR once all apps comply?)
|
// prefer "appid.json" over "appid.settings.json" (TODO: change to ERROR once all apps comply?)
|
||||||
if (dataNames.includes(app.id+".settings.json") && !dataNames.includes(app.id+".json"))
|
/* if (dataNames.includes(app.id+".settings.json") && !dataNames.includes(app.id+".json"))
|
||||||
WARN(`App ${app.id} uses data file ${app.id+'.settings.json'} instead of ${app.id+'.json'}`)
|
WARN(`App ${app.id} uses data file ${app.id+'.settings.json'} instead of ${app.id+'.json'}`)
|
||||||
else if (dataNames.includes(app.id+".settings.json"))
|
else if (dataNames.includes(app.id+".settings.json"))
|
||||||
WARN(`App ${app.id} uses data file ${app.id+'.settings.json'}`)
|
WARN(`App ${app.id} uses data file ${app.id+'.settings.json'}`)*/
|
||||||
// settings files should be listed under data, not storage (TODO: change to ERROR once all apps comply?)
|
// settings files should be listed under data, not storage (TODO: change to ERROR once all apps comply?)
|
||||||
if (fileNames.includes(app.id+".settings.json"))
|
if (fileNames.includes(app.id+".settings.json"))
|
||||||
WARN(`App ${app.id} uses storage file ${app.id+'.settings.json'} instead of data file`)
|
WARN(`App ${app.id} uses storage file ${app.id+'.settings.json'} instead of data file`)
|
||||||
|
|
|
||||||