Merge branch 'master' into master

pull/1159/head
ColtJLaCh 2021-12-21 15:42:38 -05:00 committed by GitHub
commit d533b225f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
222 changed files with 6805 additions and 1120 deletions

321
apps.json
View File

@ -3,7 +3,7 @@
"id": "fwupdate", "id": "fwupdate",
"name": "Firmware Update", "name": "Firmware Update",
"version": "0.02", "version": "0.02",
"description": "Uploads new Espruino firmwares to Bangle.js 2", "description": "[BETA] Uploads new Espruino firmwares to Bangle.js 2. For now, please use the instructions under https://www.espruino.com/Bangle.js2#firmware-updates",
"icon": "app.png", "icon": "app.png",
"type": "RAM", "type": "RAM",
"tags": "tools,system", "tags": "tools,system",
@ -11,12 +11,12 @@
"custom": "custom.html", "custom": "custom.html",
"customConnect": true, "customConnect": true,
"storage": [], "storage": [],
"sortorder": -20 "sortorder": 20
}, },
{ {
"id": "boot", "id": "boot",
"name": "Bootloader", "name": "Bootloader",
"version": "0.38", "version": "0.39",
"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",
@ -33,10 +33,11 @@
"id": "hebrew_calendar", "id": "hebrew_calendar",
"name": "Hebrew Calendar", "name": "Hebrew Calendar",
"shortName": "HebCal", "shortName": "HebCal",
"version": "0.03", "version": "0.04",
"description": "lists the date according to the hebrew calendar", "description": "lists the date according to the hebrew calendar",
"icon": "app.png", "icon": "app.png",
"tags": "", "allow_emulator": false,
"tags": "tool,locale",
"supports": [ "supports": [
"BANGLEJS", "BANGLEJS",
"BANGLEJS2" "BANGLEJS2"
@ -47,6 +48,10 @@
"name": "hebrew_calendar.app.js", "name": "hebrew_calendar.app.js",
"url": "app.js" "url": "app.js"
}, },
{
"name": "hebrewDate",
"url": "hebrewDate.js"
},
{ {
"name": "hebrew_calendar.img", "name": "hebrew_calendar.img",
"url": "app-icon.js", "url": "app-icon.js",
@ -54,10 +59,25 @@
} }
] ]
}, },
{ "id": "golfscore",
"name": "Golf Score",
"shortName":"golfscore",
"version":"0.02",
"description": "keeps track of strokes during a golf game",
"icon": "app.png",
"tags": "outdoors",
"allow_emulator": true,
"supports" : ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"golfscore.app.js","url":"app.js"},
{"name":"golfscore.img","url":"app-icon.js","evaluate":true}
]
},
{ {
"id": "messages", "id": "messages",
"name": "Messages", "name": "Messages",
"version": "0.11", "version": "0.14",
"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",
@ -72,14 +92,15 @@
{"name":"messages","url":"lib.js"} {"name":"messages","url":"lib.js"}
], ],
"data": [{"name":"messages.json"},{"name":"messages.settings.json"}], "data": [{"name":"messages.json"},{"name":"messages.settings.json"}],
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot-notify.gif"}],
"sortorder": -9 "sortorder": -9
}, },
{ {
"id": "android", "id": "android",
"name": "Android Integration", "name": "Android Integration",
"shortName": "Android", "shortName": "Android",
"version": "0.04", "version": "0.05",
"description": "Display notifications/music/etc from Gadgetbridge on Android. This replaces the old Gadgetbridge widget.", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system,messages,notifications", "tags": "tool,system,messages,notifications",
"dependencies": {"messages":"app"}, "dependencies": {"messages":"app"},
@ -95,7 +116,7 @@
{ {
"id": "ios", "id": "ios",
"name": "iOS Integration", "name": "iOS Integration",
"version": "0.06", "version": "0.07",
"description": "Display notifications/music/etc 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",
@ -111,7 +132,7 @@
{ {
"id": "health", "id": "health",
"name": "Health Tracking", "name": "Health Tracking",
"version": "0.08", "version": "0.09",
"description": "Logs health data and provides an app to view it (requires firmware 2v10.100 or later)", "description": "Logs health data and provides an app to view it (requires firmware 2v10.100 or later)",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system,health", "tags": "tool,system,health",
@ -146,7 +167,7 @@
{ {
"id": "setting", "id": "setting",
"name": "Settings", "name": "Settings",
"version": "0.36", "version": "0.38",
"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 +218,7 @@
{ {
"id": "locale", "id": "locale",
"name": "Languages", "name": "Languages",
"version": "0.13", "version": "0.14",
"description": "Translations for different countries", "description": "Translations for different countries",
"icon": "locale.png", "icon": "locale.png",
"type": "locale", "type": "locale",
@ -282,8 +303,8 @@
{ {
"id": "gbridge", "id": "gbridge",
"name": "Gadgetbridge", "name": "Gadgetbridge",
"version": "0.24", "version": "0.25",
"description": "(NOT RECOMMENDED) Handles Gadgetbridge notifications from Android. This is now replaced by the 'Android' app.", "description": "(NOT RECOMMENDED) Displays Gadgetbridge notifications from Android. Please use the 'Android' Bangle.js app instead.",
"icon": "app.png", "icon": "app.png",
"type": "widget", "type": "widget",
"tags": "tool,system,android,widget", "tags": "tool,system,android,widget",
@ -533,12 +554,12 @@
{ {
"id": "impwclock", "id": "impwclock",
"name": "Imprecise Word Clock", "name": "Imprecise Word Clock",
"version": "0.03", "version": "0.04",
"description": "Imprecise word clock for vacations, weekends, and those who never need accurate time.", "description": "Imprecise word clock for vacations, weekends, and those who never need accurate time.",
"icon": "clock-impword.png", "icon": "clock-impword.png",
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"screenshots": [{"url":"bangle1-impercise-word-clock-screenshot.png"}], "screenshots": [{"url":"bangle1-impercise-word-clock-screenshot.png"}],
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
@ -824,7 +845,7 @@
{ {
"id": "weather", "id": "weather",
"name": "Weather", "name": "Weather",
"version": "0.12", "version": "0.13",
"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 +1148,7 @@
{ {
"id": "qrcode", "id": "qrcode",
"name": "Custom QR Code", "name": "Custom QR Code",
"version": "0.03", "version": "0.04",
"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",
@ -1569,7 +1590,7 @@
{ {
"id": "widpedom", "id": "widpedom",
"name": "Pedometer widget", "name": "Pedometer widget",
"version": "0.19", "version": "0.20",
"description": "Daily pedometer widget", "description": "Daily pedometer widget",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
@ -1678,14 +1699,15 @@
"id": "rtorch", "id": "rtorch",
"name": "Red Torch", "name": "Red Torch",
"shortName": "RedTorch", "shortName": "RedTorch",
"version": "0.01", "version": "0.02",
"description": "Turns screen RED to help you see in the dark without breaking your night vision. Select from the launcher or press BTN3,BTN1,BTN3,BTN1 quickly to start when in any app that shows widgets", "description": "Turns screen RED to help you see in the dark without breaking your night vision. Select from the launcher or on Bangle 1 press BTN3,BTN1,BTN3,BTN1 quickly to start when in any app that shows widgets",
"icon": "app.png", "icon": "app.png",
"tags": "tool,torch", "tags": "tool,torch",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"allow_emulator": true,
"storage": [ "storage": [
{"name":"rtorch.app.js","url":"app.js"}, {"name":"rtorch.app.js","url":"app.js"},
{"name":"rtorch.wid.js","url":"widget.js"}, {"name":"rtorch.wid.js","url":"widget.js", "supports": ["BANGLEJS"]},
{"name":"rtorch.img","url":"app-icon.js","evaluate":true} {"name":"rtorch.img","url":"app-icon.js","evaluate":true}
] ]
}, },
@ -2147,14 +2169,15 @@
{ "id": "snek", { "id": "snek",
"name": "The snek game", "name": "The snek game",
"shortName":"Snek", "shortName":"Snek",
"version": "0.01", "version": "0.02",
"description": "A snek game where you control a snek to eat all the apples!", "description": "A snek game where you control a snek to eat all the apples!",
"icon": "snek-icon.js", "screenshots": [{"url":"screenshot_snek.png"}],
"icon": "snek.png",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"tags": "game,fun", "tags": "game,fun",
"storage": [ "storage": [
{"name":"snek.app.js","url":"snek.js"}, {"name":"snek.app.js","url":"snek.js"},
{"name":"snek.img","url":"snek-icon.js","evaluate":true} {"name":"snek.img","url":"snek.icon.js","evaluate":true}
] ]
}, },
{ {
@ -2406,7 +2429,7 @@
{ {
"id": "calendar", "id": "calendar",
"name": "Calendar", "name": "Calendar",
"version": "0.03", "version": "0.04",
"description": "Simple calendar", "description": "Simple calendar",
"icon": "calendar.png", "icon": "calendar.png",
"screenshots": [{"url":"screenshot_calendar.png"}], "screenshots": [{"url":"screenshot_calendar.png"}],
@ -3274,7 +3297,7 @@
{ {
"id": "dtlaunch", "id": "dtlaunch",
"name": "Desktop Launcher", "name": "Desktop Launcher",
"version": "0.05", "version": "0.07",
"description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.", "description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.",
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}], "screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
"icon": "icon.png", "icon": "icon.png",
@ -3285,8 +3308,11 @@
"storage": [ "storage": [
{"name":"dtlaunch.app.js","url":"app-b1.js", "supports": ["BANGLEJS"]}, {"name":"dtlaunch.app.js","url":"app-b1.js", "supports": ["BANGLEJS"]},
{"name":"dtlaunch.app.js","url":"app-b2.js", "supports": ["BANGLEJS2"]}, {"name":"dtlaunch.app.js","url":"app-b2.js", "supports": ["BANGLEJS2"]},
{"name":"dtlaunch.settings.js","url":"settings-b1.js", "supports": ["BANGLEJS"]},
{"name":"dtlaunch.settings.js","url":"settings-b2.js", "supports": ["BANGLEJS2"]},
{"name":"dtlaunch.img","url":"app-icon.js","evaluate":true} {"name":"dtlaunch.img","url":"app-icon.js","evaluate":true}
] ],
"data": [{"name":"dtlaunch.json"}]
}, },
{ {
"id": "HRV", "id": "HRV",
@ -3452,8 +3478,8 @@
{ {
"id": "speedalt2", "id": "speedalt2",
"name": "GPS Adventure Sports II", "name": "GPS Adventure Sports II",
"shortName": "GPS Adv Sport II", "shortName":"GPS Adv Sport II",
"version": "0.07", "version":"1.10",
"description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",
@ -3870,7 +3896,7 @@
"id": "qmsched", "id": "qmsched",
"name": "Quiet Mode Schedule and Widget", "name": "Quiet Mode Schedule and Widget",
"shortName": "Quiet Mode", "shortName": "Quiet Mode",
"version": "0.04", "version": "0.06",
"description": "Automatically turn Quiet Mode on or off at set times, and change LCD options while Quiet Mode is active.", "description": "Automatically turn Quiet Mode on or off at set times, and change LCD options while Quiet Mode is active.",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screenshot_b1_main.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_lcd.png"}, "screenshots": [{"url":"screenshot_b1_main.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_lcd.png"},
@ -3988,11 +4014,12 @@
{ {
"id": "thermom", "id": "thermom",
"name": "Thermometer", "name": "Thermometer",
"version": "0.03", "version": "0.05",
"description": "Displays the current temperature in degree Celsius, updated every 20 seconds", "description": "Displays the current temperature in degree Celsius/Fahrenheit (depending on locale), updates every 10 seconds with average of last 5 readings.",
"icon": "app.png", "icon": "app.png",
"tags": "tool", "tags": "tool",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS", "BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"}],
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
{"name":"thermom.app.js","url":"app.js"}, {"name":"thermom.app.js","url":"app.js"},
@ -4054,7 +4081,7 @@
"id": "thermomF", "id": "thermomF",
"name": "Fahrenheit Temp", "name": "Fahrenheit Temp",
"version": "0.01", "version": "0.01",
"description": "A modification of the Thermometer App to display temprature in Fahrenheit", "description": "[NOT RECOMMENDED] A modification of the Thermometer App to display temprature in Fahrenheit. Please use the 'Thermometer App' and install 'Languages' to get the temperature in the correct format for your locale.",
"icon": "thermf.png", "icon": "thermf.png",
"tags": "tool", "tags": "tool",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS"],
@ -4117,14 +4144,17 @@
{ {
"id": "vectorclock", "id": "vectorclock",
"name": "Vector Clock", "name": "Vector Clock",
"version": "0.02", "version": "0.03",
"description": "A digital clock that uses the built-in vector font.", "description": "A digital clock that uses the built-in vector font.",
"icon": "app.png", "icon": "app.png",
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS", "BANGLEJS2"],
"allow_emulator": true, "allow_emulator": true,
"screenshots": [{"url":"bangle1-vector-clock-screenshot.png"}], "screenshots": [
{"url":"bangle2-vector-clock-screenshot.png"},
{"url":"bangle1-vector-clock-screenshot.png"}
],
"storage": [ "storage": [
{"name":"vectorclock.app.js","url":"app.js"}, {"name":"vectorclock.app.js","url":"app.js"},
{"name":"vectorclock.img","url":"app-icon.js","evaluate":true} {"name":"vectorclock.img","url":"app-icon.js","evaluate":true}
@ -4180,10 +4210,10 @@
"id": "pastel", "id": "pastel",
"name": "Pastel Clock", "name": "Pastel Clock",
"shortName": "Pastel", "shortName": "Pastel",
"version": "0.08", "version": "0.09",
"description": "A Configurable clock with custom fonts and background. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times", "description": "A Configurable clock with custom fonts and background. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times",
"icon": "pastel.png", "icon": "pastel.png",
"dependencies": {"mylocation":"app"}, "dependencies": {"mylocation":"app", "widpedom":"app"},
"screenshots": [{"url":"screenshot_pastel.png"}], "screenshots": [{"url":"screenshot_pastel.png"}],
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
@ -4442,9 +4472,9 @@
"name": "A Battery Widget (with percentage)", "name": "A Battery Widget (with percentage)",
"shortName":"A Battery Widget", "shortName":"A Battery Widget",
"icon": "widget.png", "icon": "widget.png",
"version":"1.01", "version":"1.02",
"type": "widget", "type": "widget",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"description": "Simple and slim battery widget with charge status and percentage", "description": "Simple and slim battery widget with charge status and percentage",
"tags": "widget,battery", "tags": "widget,battery",
@ -4457,7 +4487,7 @@
"name": "LCARS Clock", "name": "LCARS Clock",
"shortName":"LCARS", "shortName":"LCARS",
"icon": "lcars.png", "icon": "lcars.png",
"version":"0.06", "version":"0.07",
"readme": "README.md", "readme": "README.md",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"description": "Library Computer Access Retrieval System (LCARS) clock.", "description": "Library Computer Access Retrieval System (LCARS) clock.",
@ -4466,7 +4496,8 @@
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"screenshot.png"}],
"storage": [ "storage": [
{"name":"lcars.app.js","url":"lcars.app.js"}, {"name":"lcars.app.js","url":"lcars.app.js"},
{"name":"lcars.img","url":"lcars.icon.js","evaluate":true} {"name":"lcars.img","url":"lcars.icon.js","evaluate":true},
{"name":"lcars.settings.js","url":"lcars.settings.js"}
] ]
}, },
{ "id": "binwatch", { "id": "binwatch",
@ -4621,7 +4652,7 @@
"id": "sensible", "id": "sensible",
"name": "SensiBLE", "name": "SensiBLE",
"shortName": "SensiBLE", "shortName": "SensiBLE",
"version": "0.03", "version": "0.04",
"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": [ "screenshots": [
@ -4694,8 +4725,9 @@
"id": "pebble", "id": "pebble",
"name": "Pebble Clock", "name": "Pebble Clock",
"shortName": "Pebble", "shortName": "Pebble",
"version": "0.04", "version": "0.06",
"description": "A pebble style clock to keep the rebellion going", "description": "A pebble style clock to keep the rebellion going",
"dependencies": {"widpedom":"app"},
"readme": "README.md", "readme": "README.md",
"icon": "pebble.png", "icon": "pebble.png",
"screenshots": [{"url":"pebble_screenshot.png"}], "screenshots": [{"url":"pebble_screenshot.png"}],
@ -4711,7 +4743,7 @@
{ "id": "pooqroman", { "id": "pooqroman",
"name": "pooq Roman watch face", "name": "pooq Roman watch face",
"shortName":"pooq Roman", "shortName":"pooq Roman",
"version":"0.0.0", "version":"0.03",
"description": "A classic watch face with a certain dynamicity. Most amusing in 24h mode. Slide up to show more hands, down for less(!). By design does not support standard widgets, sorry!", "description": "A classic watch face with a certain dynamicity. Most amusing in 24h mode. Slide up to show more hands, down for less(!). By design does not support standard widgets, sorry!",
"icon": "app.png", "icon": "app.png",
"type": "clock", "type": "clock",
@ -4746,7 +4778,7 @@
{ {
"id": "weatherClock", "id": "weatherClock",
"name": "Weather Clock", "name": "Weather Clock",
"version": "0.03", "version": "0.04",
"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"}],
@ -4829,9 +4861,10 @@
"id": "ptlaunch", "id": "ptlaunch",
"name": "Pattern Launcher", "name": "Pattern Launcher",
"shortName": "Pattern Launcher", "shortName": "Pattern Launcher",
"version": "0.02", "version": "0.11",
"description": "Directly launch apps from the clock screen with custom patterns.", "description": "Directly launch apps from the clock screen with custom patterns.",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"manage_patterns_light.png"}],
"tags": "tools", "tags": "tools",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
@ -4870,6 +4903,194 @@
"storage": [ "storage": [
{"name":"slimehunt.app.js","url":"app.js"}, {"name":"slimehunt.app.js","url":"app.js"},
{"name":"slimehunt.img","url":"app-icon.js","evaluate":true} {"name":"slimehunt.img","url":"app-icon.js","evaluate":true}
]
},
{
"id": "rebble",
"name": "Rebble Clock",
"shortName": "Rebble",
"version": "0.03",
"description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion",
"readme": "README.md",
"icon": "rebble.png",
"dependencies": {"mylocation":"app", "widpedom":"app"},
"screenshots": [{"url":"screenshot_rebble.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"rebble.app.js","url":"rebble.app.js"},
{"name":"rebble.settings.js","url":"rebble.settings.js"},
{"name":"rebble.img","url":"rebble.icon.js","evaluate":true}
]
},
{ "id": "snaky",
"name": "Snaky",
"shortName":"Snaky",
"version":"0.01",
"description": "The classic snake game. Eat apples and don't bite your tail. Control the snake with the touch screen.",
"tags": "game,fun",
"icon": "snaky.png",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"snaky.app.js","url":"snaky.js"},
{"name":"snaky.img","url":"snaky-icon.js","evaluate":true}
]
},
{
"id": "clicompleteclk",
"name": "CLI complete clock",
"shortName":"CLI cmplt clock",
"version":"0.03",
"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},
{"name":"clicompleteclk.settings.js","url":"settings.js"}
],
"data": [{"name":"clicompleteclk.json"}]
},
{
"id":"awairmonitor",
"name":"Awair Monitor",
"icon": "app.png",
"allow_emulator": true,
"version":"0.01",
"description": "Displays the level of CO2, VOC, PM 2.5, Humidity and Temperature, from your Awair device.",
"tags": "tool,health",
"readme":"README.md",
"supports":["BANGLEJS2"],
"storage": [
{"name":"awairmonitor.app.js","url":"app.js"},
{"name":"awairmonitor.img","url":"app-icon.js","evaluate":true}
]
},
{ "id": "pooqround",
"name": "pooq Round watch face",
"shortName":"pooq Round",
"version":"0.01",
"description": "A 24 hour analogue watchface with high legibility and a novel style.",
"icon": "app.png",
"type": "clock",
"tags": "clock",
"supports" : ["BANGLEJS2"],
"allow_emulator":true,
"readme": "README.md",
"storage": [
{"name":"pooqround.app.js","url":"app.js"},
{"name":"pooqround.img","url":"app-icon.js","evaluate":true}
],
"data": [
{"name":"pooqround.json"}
]
},
{
"id": "coretemp",
"name": "Core Temp Display",
"version": "0.01",
"description": "Display CoreTemp device sensor data",
"icon": "coretemp.png",
"type": "app",
"tags": "health",
"readme": "README.md",
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"coretemp.boot.js","url":"boot.js"},
{"name":"coretemp.app.js","url":"coretemp.js"},
{"name":"coretemp.img","url":"coretemp-icon.js","evaluate":true}
]
},
{
"id": "showimg",
"name": "simple image viewer",
"shortName":"showImage",
"version":"0.2",
"description": "Displays the image in \"showimg.user.img\". The file has to be uploaded via the espruino IDE. Returns to watch face after 60s or button push. I use it to display my vaccination certificate.",
"icon": "app.png",
"tags": "tool",
"supports" : ["BANGLEJS2"],
"storage": [
{"name":"showimg.app.js","url":"app.js"},
{"name":"showimg.img","url":"app-icon.js","evaluate":true}
]
},
{
"id": "lapcounter",
"name": "Lap Counter",
"version": "0.01",
"description": "Click button to count laps. Shows count and total time snapshot (like a stopwatch, but laid back).",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"type": "app",
"tags": "tool,outdoors",
"readme":"README.md",
"supports": ["BANGLEJS", "BANGLEJS2"],
"allow_emulator": true,
"storage": [
{"name":"lapcounter.app.js","url":"app.js"},
{"name":"lapcounter.img","url":"app-icon.js","evaluate":true}
]
},
{
"id": "pebbled",
"name": "Pebble Clock with distance",
"shortName": "Pebble + distance",
"version": "0.1",
"description": "Fork of Pebble Clock with distance in KM. Both step count and the distance are on the main screen. Default step length = 0.75m (can be changed in settings).",
"readme": "README.md",
"icon": "pebbled.png",
"screenshots": [{"url":"pebble_screenshot.png"}],
"type": "clock",
"tags": "clock,distance",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"pebbled.app.js","url":"pebbled.app.js"},
{"name":"pebbled.settings.js","url":"pebbled.settings.js"},
{"name":"pebbled.img","url":"pebbled.icon.js","evaluate":true}
]
},
{ "id": "circlesclock",
"name": "Circles clock",
"shortName":"Circles clock",
"version":"0.02",
"description": "A clock with circles for different data at the bottom in a probably familiar style",
"icon": "app.png",
"dependencies": {"widpedom":"app"},
"type": "clock",
"tags": "clock",
"supports" : ["BANGLEJS2"],
"allow_emulator":true,
"readme": "README.md",
"storage": [
{"name":"circlesclock.app.js","url":"app.js"},
{"name":"circlesclock.img","url":"app-icon.js","evaluate":true},
{"name":"circlesclock.settings.js","url":"settings.js"}
],
"data": [
{"name":"circlesclock.json"}
]
},
{
"id": "ltherm",
"name": "Localized Thermometer",
"shortName": "Thermometer",
"version": "0.01",
"description": "Displays the current temperature in localized units.",
"icon": "thermf.png",
"tags": "tool",
"supports": ["BANGLEJS2"],
"allow_emulator": true,
"readme": "README.md",
"storage": [
{"name":"ltherm.app.js","url":"app.js"},
{"name":"ltherm.img","url":"icon.js","evaluate":true}
] ]
} }
] ]

View File

@ -21,8 +21,8 @@ function showAlarm(alarm) {
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
E.showPrompt(msg,{ E.showPrompt(msg,{
title:alarm.timer ? "TIMER!" : "ALARM!", title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!",
buttons : {"Sleep":true,"Ok":false} // default is sleep so it'll come back in 10 mins buttons : {/*LANG*/"Sleep":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins
}).then(function(sleep) { }).then(function(sleep) {
buzzCount = 0; buzzCount = 0;
if (sleep) { if (sleep) {

View File

@ -33,16 +33,16 @@ function getCurrentHr() {
function showMainMenu() { function showMainMenu() {
const menu = { const menu = {
'': { 'title': 'Alarm/Timer' }, '': { 'title': 'Alarm/Timer' },
'< Back' : ()=>{load();}, /*LANG*/'< Back' : ()=>{load();},
'New Alarm': ()=>editAlarm(-1), /*LANG*/'New Alarm': ()=>editAlarm(-1),
'New Timer': ()=>editTimer(-1) /*LANG*/'New Timer': ()=>editTimer(-1)
}; };
alarms.forEach((alarm,idx)=>{ alarms.forEach((alarm,idx)=>{
if (alarm.timer) { if (alarm.timer) {
txt = "TIMER "+(alarm.on?"on ":"off ")+formatMins(alarm.timer); txt = /*LANG*/"TIMER "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatMins(alarm.timer);
} else { } else {
txt = "ALARM "+(alarm.on?"on ":"off ")+formatTime(alarm.hr); txt = /*LANG*/"ALARM "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatTime(alarm.hr);
if (alarm.rp) txt += " (repeat)"; if (alarm.rp) txt += /*LANG*/" (repeat)";
} }
menu[txt] = function() { menu[txt] = function() {
if (alarm.timer) editTimer(idx); if (alarm.timer) editTimer(idx);
@ -70,27 +70,27 @@ function editAlarm(alarmIndex) {
as = a.as; as = a.as;
} }
const menu = { const menu = {
'': { 'title': 'Alarm' }, '': { 'title': /*LANG*/'Alarm' },
'< Back' : showMainMenu, /*LANG*/'< Back' : showMainMenu,
'Hours': { /*LANG*/'Hours': {
value: hrs, value: hrs,
onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this' onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this'
}, },
'Minutes': { /*LANG*/'Minutes': {
value: mins, value: mins,
onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this' onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this'
}, },
'Enabled': { /*LANG*/'Enabled': {
value: en, value: en,
format: v=>v?"On":"Off", format: v=>v?"On":"Off",
onchange: v=>en=v onchange: v=>en=v
}, },
'Repeat': { /*LANG*/'Repeat': {
value: en, value: en,
format: v=>v?"Yes":"No", format: v=>v?"Yes":"No",
onchange: v=>repeat=v onchange: v=>repeat=v
}, },
'Auto snooze': { /*LANG*/'Auto snooze': {
value: as, value: as,
format: v=>v?"Yes":"No", format: v=>v?"Yes":"No",
onchange: v=>as=v onchange: v=>as=v
@ -108,14 +108,14 @@ function editAlarm(alarmIndex) {
last : day, rp : repeat, as: as last : day, rp : repeat, as: as
}; };
} }
menu["> Save"] = function() { menu[/*LANG*/"> Save"] = function() {
if (newAlarm) alarms.push(getAlarm()); if (newAlarm) alarms.push(getAlarm());
else alarms[alarmIndex] = getAlarm(); else alarms[alarmIndex] = getAlarm();
require("Storage").write("alarm.json",JSON.stringify(alarms)); require("Storage").write("alarm.json",JSON.stringify(alarms));
showMainMenu(); showMainMenu();
}; };
if (!newAlarm) { if (!newAlarm) {
menu["> Delete"] = function() { menu[/*LANG*/"> Delete"] = function() {
alarms.splice(alarmIndex,1); alarms.splice(alarmIndex,1);
require("Storage").write("alarm.json",JSON.stringify(alarms)); require("Storage").write("alarm.json",JSON.stringify(alarms));
showMainMenu(); showMainMenu();
@ -136,18 +136,18 @@ function editTimer(alarmIndex) {
en = a.on; en = a.on;
} }
const menu = { const menu = {
'': { 'title': 'Timer' }, '': { 'title': /*LANG*/'Timer' },
'Hours': { /*LANG*/'Hours': {
value: hrs, value: hrs,
onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this' onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this'
}, },
'Minutes': { /*LANG*/'Minutes': {
value: mins, value: mins,
onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this' onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this'
}, },
'Enabled': { /*LANG*/'Enabled': {
value: en, value: en,
format: v=>v?"On":"Off", format: v=>v?/*LANG*/"On":/*LANG*/"Off",
onchange: v=>en=v onchange: v=>en=v
} }
}; };

View File

@ -7,7 +7,7 @@
active = active.sort((a,b)=>(a.hr-b.hr)+(a.last-b.last)*24); active = active.sort((a,b)=>(a.hr-b.hr)+(a.last-b.last)*24);
var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
if (!require('Storage').read("alarm.js")) { if (!require('Storage').read("alarm.js")) {
console.log("No alarm app!"); console.log(/*LANG*/"No alarm app!");
require('Storage').write('alarm.json',"[]"); require('Storage').write('alarm.json',"[]");
} else { } else {
var t = 3600000*(active[0].hr-hr); var t = 3600000*(active[0].hr-hr);

View File

@ -3,3 +3,4 @@
Fix music control Fix music control
0.03: Handling of message actions (ok/clear) 0.03: Handling of message actions (ok/clear)
0.04: Android icon now goes to settings page with 'find phone' 0.04: Android icon now goes to settings page with 'find phone'
0.05: Fix handling of message actions

View File

@ -65,7 +65,7 @@
// Message response // Message response
Bangle.messageResponse = (msg,response) => { Bangle.messageResponse = (msg,response) => {
if (msg.id=="call") return gbSend({ t: "call", n:response?"ACCEPT":"REJECT" }); if (msg.id=="call") return gbSend({ t: "call", n:response?"ACCEPT":"REJECT" });
if (isFinite(msg.id)) return gbSend({ t: "notify", n:response?"OPEN":"DISMISS" }); if (isFinite(msg.id)) return gbSend({ t: "notify", n:response?"OPEN":"DISMISS", id: msg.id });
// error/warn here? // error/warn here?
}; };
})(); })();

View File

@ -1,4 +1,4 @@
0.04: Fix tapping at very bottom of list, exit on inactivity
0.03: Add "Calculating" placeholder, update JSON save format
0.02: Fix JSON save format
0.01: First release 0.01: First release
0.02: Fix JSON save format
0.03: Add "Calculating" placeholder, update JSON save format
0.04: Fix tapping at very bottom of list, exit on inactivity

View File

@ -0,0 +1 @@
0.01: Beta version for Bangle 2 paired with Chrome (2021/12/11)

View File

@ -0,0 +1,22 @@
# Awair Monitor
Displays the level of CO2, VOC, PM 2.5, Humidity and Temperature, from your Awair device.
* What you need:
* A BangleJS 2
* An Awair device [with local API enabled](https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature)
* The web app [awair_to_bangle.html](awair_to_bangle.html) that will retrive the data from your Awair device and sent it to your BangleJS 2 through Chrome's Bluetooth LE connection
* How to get started
* Open awair_to_bangle.html with a text/code editor and input the IP address of your Awair on top (const awair_ip_1 = "192.168.xx.xx")
* Launch the Awair Monitor app on your BangleJS
* Open awair_to_bangle.html on Chrome and click "Connect BangleJS" - it connects to your watch the same way as the Bangle app store
* Once connected to the watch with the app running, the watch app is updated once per second
![](screenshot.png)
![](awair-monitor-photo.jpg)
## Creator
[@alainsaas](https://github.com/alainsaas)
Contributions are welcome, send me your Pull Requests!

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgP/AD38g4FD8EAAoeAgE/AoUD/EfAgP+AYMPDgQPBw4FB/F///DAoPwAQPjAQPBAQPxDgJVCAoP4gYaCCwIcBAoM/8P8h0HjEP8f4h0Gp0H4/44lj5+H4/54lzj/jx/5/lyDgIFDh/xAoQRBAoXsuY8Bx4jCAoeEkYFB447CAoRxBOAPxM4RmC8IFD4ZZD/8H/DHDh/+AoaSBUAIABCoYATVwS2Ct4FE84REXQQLCk4RJAo0XGxY="))

98
apps/awairmonitor/app.js Normal file
View File

@ -0,0 +1,98 @@
Graphics.prototype.setFontMichroma36 = function() {
g.setFontCustom(atob("AAAAAAAAAAAAAAAAeAAAAAeAAAAAeAAAAAeAAAAAAAAAAAAAAAAAAAAAAAGAAAAA+AAAAD+AAAAP+AAAA/8AAAD/wAAAf/AAAB/4AAAH/gAAAf+AAAB/4AAAH/gAAAf+AAAAfwAAAAfAAAAAcAAAAAAAAAAAAAAAAAAAAAAAA///AAD///wAH///4AP///8APwAD+APAAAeAeAAAeAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAeAPAAAeAPwAD+AP///8AH///4AD///wAA///AAAAAAAAAAAAAAAAAAAAAEAAAAAOAAAAAfAAAAA+AAAAB8AAAAD8AAAAH4AAAAPwAAAAPgAAAAfAAAAAf///+Af///+Af///+Af///+AAAAAAAAAAAAAAAAAAAAAAAAAA/Af+AD/A/+AH/B/+AP/D/+APwD4eAPADweAfADweAeADweAeADweAeADweAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAPgeAeAPAeAeAPAeAeAPAeAeAPAeAfAPAeAPw/AeAP/+AeAH/+AeAD/8AeAB/wAOAAAAAAAAAAAAAAAAAAAAAAAAAB8APgAD8AP4AH8AP8AP8AP8APgAB+AfAAAeAeAAAeAeAAAPAeAAAPAeAAAPAeAAAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAeAfAeAeAPx/h+AP///+AH///8AD///4AB/h/gAAAAAAAAAAAAAAAAAAAAAAeAAAAA/AAAAA/AAAAB/AAAAD/AAAAH/AAAAPvAAAAPPAAAAfPAAAA+PAAAB8PAAAD4PAAADwPAAAHwPAAAPgPAAAfAPAAA+APAAA8APAAB8APAAD4APAAHwAPAAPgAPAAPAAPAAfAAPAAf///+Af///+Af///+Af///+AAAAPAAAAAPAAAAAPAAAAAPAAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAf/8PgAf/8P4Af/8P8Af/8P8AeB4A+AeB4AeAeDwAeAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAfAeDwAeAeD4A+AeD+D+AeB//8AeB//4AeA//4AAAP/AAAAAAAAAAAAAAAAAAAAAAAAAAA///AAD///wAH///4AH///8AP4fB+APAeAeAfA8AeAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAfA8APAPA+AeAPgeAeAP8fh+AH8f/8AD8P/8AA8H/4AAAB/gAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAeAAAAAeAAAAAeAAAAAeAAAAAeAAACAeAAAGAeAAAOAeAAAeAeAAA+AeAAD+AeAAH8AeAAP4AeAAfwAeAA/gAeAB/AAeAD+AAeAP4AAeAfwAAeA/gAAeB/AAAeD+AAAeH8AAAefwAAAe/gAAAf/AAAAf+AAAAf8AAAAf4AAAAfgAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAMAAB+B/wAD/j/4AH/3/8AP///+AP//A+AfB+AeAeA+AeAeA+APAeA+APAeA+APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA+APAeA+APAeA+APAeA+AOAeA+AeAPh/A+AP///+AP/3/8AH/3/8AB/D/wAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAD/4HAAH/8HwAP/+H4AP5/H8AfAfA8AeAPAeAeAPAeAeAPAeAeAHgfAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHAPAeAPAOAeAPAeAPAPAeAPwfB+AP///8AH///4AD///wAA///AAAAAAAAAAAAAAAAAAAAAAAAAAAB8DwAAB8HwAAB8HwAAB8DwAAAAAAAAAAAAA"), 46, atob("CBIkESMjJCMjIyMjCA=="), 36+(1<<8)+(1<<16));
};
var drawTimeout;
function queueNextDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 1000 - (Date.now() % 1000));
}
var locale = require("locale");
var bt_current_co2 = 0;
var bt_current_voc = 0;
var bt_current_pm25 = 0;
var bt_current_humi = 0;
var bt_current_temp = 0;
var bt_last_update = 0;
var last_update = 0;
var bt_co2_history = new Array(10).fill(0);
var bt_voc_history = new Array(10).fill(0);
var bt_pm25_history = new Array(10).fill(0);
var bt_humi_history = new Array(10).fill(0);
var bt_temp_history = new Array(10).fill(0);
var internal_last_update = -1;
function draw() {
g.reset().clearRect(0,24,g.getWidth(),g.getHeight());
var date = new Date();
g.setFontAlign(0,0);
g.setFont("Michroma36").drawString(locale.time(date,1), g.getWidth()/2, 56);
g.setFont("6x8");
g.drawString(locale.date(new Date(),1), g.getWidth()/2, 80);
g.setFont("6x8");
g.drawString("CO2", 20, 100);
g.drawString("VOC", 55, 100);
g.drawString("PM25", 90, 100);
g.drawString("Humi", 125, 100);
g.drawString("Temp", 160, 100);
g.setFont("HaxorNarrow7x17");
g.drawString(""+bt_current_co2, 18, 110);
g.drawString(""+bt_current_voc, 53, 110);
g.drawString(""+bt_current_pm25, 88, 110);
g.drawString(""+bt_current_humi, 123, 110);
g.drawString(""+bt_current_temp, 158, 110);
if (last_update != bt_last_update) {
last_update = bt_last_update;
internal_last_update = last_update;
if (last_update % 10 == 0) {
bt_co2_history.shift(); bt_co2_history.push(bt_current_co2);
bt_voc_history.shift(); bt_voc_history.push(bt_current_voc);
bt_pm25_history.shift(); bt_pm25_history.push(bt_current_pm25);
bt_humi_history.shift(); bt_humi_history.push(bt_current_humi);
bt_temp_history.shift(); bt_temp_history.push(bt_current_temp);
}
}
if (internal_last_update == -1) {
g.drawString("Waiting for connection", 88, 164);
} else if (internal_last_update > last_update + 5) {
g.drawString("Trying to reconnect since " + (internal_last_update - last_update), 88, 164);
}
for (i = 0; i < 10; i++) {
// max height = 32
g.drawLine(10+i*2, 150-(Math.min(Math.max(bt_co2_history[i],400), 1200)-400)/25, 10+i*2, 150);
g.drawLine(45+i*2, 150-(Math.min(Math.max(bt_voc_history[i],0), 1440)-0)/45, 45+i*2, 150);
g.drawLine(80+i*2, 150-(Math.min(Math.max(bt_pm25_history[i],0), 32)-0)/1, 80+i*2, 150);
g.drawLine(115+i*2, 150-(Math.min(Math.max(bt_humi_history[i],20), 60)-20)/1.25, 115+i*2, 150);
g.drawLine(150+i*2, 150-(Math.min(Math.max(bt_temp_history[i],19), 27)-19)*4, 150+i*2, 150);
// target humidity level
g.setColor("#00F").drawLine(115, 150-(40-20)/1.25, 115+18, 150-(40-20)/1.25);
g.reset();
}
if (internal_last_update != -1) { internal_last_update++; }
queueNextDraw();
}
// init
require("FontHaxorNarrow7x17").add(Graphics);
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
draw();

BIN
apps/awairmonitor/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

View File

@ -0,0 +1,195 @@
<!DOCTYPE HTML>
<html>
<head>
<script src="https://puck-js.com/puck.js"></script>
<script type="text/javascript">
// Don't forget to enable the Local API on your Awair before using this
// https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature
const awair_ip_1 = "192.168.2.2"; // <- INPUT YOUR AWAIR IP ADDRESS HERE
const awair_name_1 = "Awair";
var bt_connection;
var is_connected = false;
var reconnect_counter = 5;
var reconnect_attempt_counter = 1;
window.onload = function() {
var chart_co2;
var chart_voc;
var chart_pm;
var chart_temperature;
var chart_humidity;
var dataPoints_1 = [];
var posx = 0;
$.getJSON("http://"+awair_ip_1+"/air-data/latest", function(data) {
$.each(data, function(key, value){
if (dataPoints_1[key] === undefined) { dataPoints_1[key] = []; }
if (key === "temp" || key === "humid") { dataPoints_1[key].push({x: posx, y: parseFloat(value)}); }
else { dataPoints_1[key].push({x: posx, y: parseInt(value)}); }
});
posx++;
chart_co2 = new CanvasJS.Chart("chartContainer_co2",{
title:{ text:"CO2", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ minimum: 0, labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.co2 }]
});
chart_voc = new CanvasJS.Chart("chartContainer_voc",{
title:{ text:"VOC", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ minimum: 0, labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.voc }]
});
chart_pm = new CanvasJS.Chart("chartContainer_pm",{
title:{ text:"PM", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ minimum: 0, labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.pm25 }]
});
chart_humidity = new CanvasJS.Chart("chartContainer_humidity",{
title:{ text:"Humidity", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.humid }]
});
chart_temperature = new CanvasJS.Chart("chartContainer_temperature",{
title:{ text:"Temperature", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
axisY:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.temp }]
});
chart_co2.set("backgroundColor", "#1A202C");
chart_voc.set("backgroundColor", "#1A202C");
chart_pm.set("backgroundColor", "#1A202C");
chart_humidity.set("backgroundColor", "#1A202C");
chart_temperature.set("backgroundColor", "#1A202C");
updateChart();
});
function updateChart() {
$.getJSON("http://"+awair_ip_1+"/air-data/latest", function(data) {
$.each(data, function(key, value){
if (dataPoints_1[key] === undefined) { dataPoints_1[key] = []; }
if (key === "temp" || key === "humid") { dataPoints_1[key].push({x: posx, y: parseFloat(value)}); }
else { dataPoints_1[key].push({x: posx, y: parseInt(value)}); }
});
posx++;
chart_co2.render();
chart_voc.render();
chart_pm.render();
chart_temperature.render();
chart_humidity.render();
chart_co2.title.set("text", "CO2 level (ppm)");
chart_voc.title.set("text", "VOC level (ppb)");
chart_pm.title.set("text", "PM2.5 level (ug/m³)");
chart_humidity.title.set("text", "Humidity level (%)");
chart_temperature.title.set("text", "Temperature level (°C)");
let current_co2 = dataPoints_1['co2'][dataPoints_1['co2'].length-1].y;
let current_voc = dataPoints_1['voc'][dataPoints_1['voc'].length-1].y;
let current_pm25 = dataPoints_1['pm25'][dataPoints_1['pm25'].length-1].y;
let current_humi = dataPoints_1['humid'][dataPoints_1['humid'].length-1].y;
let current_temp = dataPoints_1['temp'][dataPoints_1['temp'].length-1].y;
let last_update = dataPoints_1['temp'].length-1;
if (is_connected && bt_connection.isOpen) {
bt_connection.write('\x10bt_current_co2='+current_co2+';bt_current_voc='+current_voc+';bt_current_pm25='+current_pm25+';bt_current_humi='+current_humi+';bt_current_temp='+current_temp+';bt_last_update='+last_update+';\n');
console.log("Sent data through Bluetooth");
} else if (is_connected && !bt_connection.isOpen) {
console.log("Disconnected - Next attempt to reconnect in " + reconnect_counter);
reconnect_counter--;
if (reconnect_counter <= 0) {
reconnect_counter = 10 * reconnect_attempt_counter;
reconnect_attempt_counter++;
console.log("Trying to reconnect");
bt_connection.reconnect(function(c) {
console.log("Reconnect callback");
if (!c) {
console.log("Couldn't reconnect");
return;
}
bt_connection = c;
is_connected = true;
reconnect_attempt_counter = 1;
});
}
}
setTimeout(function(){updateChart()}, 1000);
});
}
}
function connectBT() {
console.log("Connect BT");
Puck.connect(function(c) {
console.log("Connect callback");
if (!c) {
console.log("Couldn't connect");
return;
}
bt_connection = c;
is_connected = true;
reconnect_attempt_counter = 1;
});
}
function disconnectBT() {
if (is_connected && bt_connection) {
bt_connection.close();
is_connected = false;
console.log("Closed Bluetooth connection");
}
}
</script>
<script type="text/javascript" src="https://canvasjs.com/assets/script/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
</head>
<body style="background-color:#1A202C;">
<p style="color: #F7FAFC">
<b>How to use</b>
<br/><br/>
Step 1: Enable the Local API on your Awair: https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature
<br/><br/>
Step 2: Modify this HTML file to input the IP address of your Awair on top (const awair_ip_1 = "192.168.xx.xx")
<br/><br/>
Step 3: Launch the Awair Monitor app on your BangleJS
<br/><br/>
Step 4: Click "Connect BangleJS"
<br/><br/>
Step 5: Optionally, open the web inspector's console (Right click > Inspector > Console) to read the bluetooth logs
</p>
<center>
<button onclick="connectBT();">Connect BangleJS</button>
<button onclick="disconnectBT();">Disconnect BangleJS</button>
</center>
<br/><br/>
<div id="chartContainer_co2" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
<div id="chartContainer_voc" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
<div id="chartContainer_pm" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
<div id="chartContainer_humidity" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
<div id="chartContainer_temperature" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAMGAAAAAAYDAAAAAAwBgAAAABgAwAAAABAAQAAAABAAQAAAABAAQAAAABAAQAAAABAAQAAAABgAwAAAAAwBgAAAAAYDAAAAAAMGAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAP+AAAAAAf/AAAAAA//gAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAA//gAAAAAf/AAAAAAP+AAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")) require("heatshrink").decompress(atob("mEwgIurg/wAocMjAFDjEMIAkGAodggYFDoBLEAq4jFF4o7FI4pTFOLsP/AFDj/8Aoc//wFDv//As4vFHYpHFOLoAPA=="))

View File

@ -42,3 +42,4 @@
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 0.38: Option to log to file if settings.log==2
0.39: Fix passkey support (fix https://github.com/espruino/Espruino/issues/2035)

View File

@ -88,7 +88,7 @@ if (global.save) boot += `global.save = function() { throw new Error("You can't
// Apply any settings-specific stuff // Apply any settings-specific stuff
if (s.options) boot+=`Bangle.setOptions(${E.toJS(s.options)});\n`; if (s.options) boot+=`Bangle.setOptions(${E.toJS(s.options)});\n`;
if (s.brightness && s.brightness!=1) boot+=`Bangle.setLCDBrightness(${s.brightness});\n`; if (s.brightness && s.brightness!=1) boot+=`Bangle.setLCDBrightness(${s.brightness});\n`;
if (s.passkey!==undefined && s.passkey.length==6) boot+=`NRF.setSecurity({passkey:${s.passkey}, mitm:1, display:1});\n`; if (s.passkey!==undefined && s.passkey.length==6) boot+=`NRF.setSecurity({passkey:${E.toJS(s.passkey.toString())}, mitm:1, display:1});\n`;
if (s.whitelist) boot+=`NRF.on('connect', function(addr) { if (!(require('Storage').readJSON('setting.json',1)||{}).whitelist.includes(addr)) NRF.disconnect(); });\n`; if (s.whitelist) boot+=`NRF.on('connect', function(addr) { if (!(require('Storage').readJSON('setting.json',1)||{}).whitelist.includes(addr)) NRF.disconnect(); });\n`;
// Pre-2v10 firmwares without a theme/setUI // Pre-2v10 firmwares without a theme/setUI
delete g.theme; // deleting stops us getting confused by our own decl. builtins can't be deleted delete g.theme; // deleting stops us getting confused by our own decl. builtins can't be deleted

View File

@ -1,3 +1,4 @@
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 0.03: Add setting to start week on Sunday
0.04: Add setting to switch color schemes. On Bangle 2 non-dithering colors will be used by default. Use localized names for months and days of the week (Language app needed).

View File

@ -9,5 +9,6 @@ Basic calendar
## Settings ## Settings
- Starts on Sunday: whether the calendar should start on Sunday (default is Monday). - Starts Sunday: whether the calendar should start on Sunday (default is Monday).
- B2 Colors: use non-dithering colors (default, recommended for Bangle 2) or the original color scheme.

View File

@ -1,5 +1 @@
require("heatshrink").decompress( require("heatshrink").decompress(atob("mEwwcCpMkyQC3wAIFgIRJn8JAoeQ/gRYwB0Bn57F/gCBHAgfCn8EDgdI/kSAoIR8oBkFgAFCCIysKCPM//4AKZAgR3/0Aj+Ag/ggP4gF/CPpr/Nf5r/NfYRhw4RL8IRDyEAABUJCIYC/AVI="))
atob(
"mEwxH+AH4A/ADuIUCARRDhgePCKIv13YAEDoYJFAA4RJFyQvcGBYRGy4dDy4uLCJgv/DoOBDgOBF5oRLF6IeBDgIvNCJYvQDwQuNCJovRADov/F9OsAEgv/F/4vhwIACAqYv/F/4vnd94vvX/4v/F/7vvF96//F/4v/d94v/F/4wsFxQwjFxgA/AH4A/AH4AZA=="
)
)

View File

@ -1,6 +1,6 @@
const maxX = g.getWidth(); const maxX = g.getWidth();
const maxY = g.getHeight(); const maxY = g.getHeight();
const fontSize = g.getWidth()>200?2:1; const fontSize = g.getWidth() > 200 ? 2 : 1;
const rowN = 7; const rowN = 7;
const colN = 7; const colN = 7;
const headerH = maxY / 7; const headerH = maxY / 7;
@ -10,26 +10,109 @@ const color1 = "#035AA6";
const color2 = "#4192D9"; const color2 = "#4192D9";
const color3 = "#026873"; const color3 = "#026873";
const color4 = "#038C8C"; const color4 = "#038C8C";
const color5 = "#03A696"; const gray1 = "#bbbbbb";
const black = "#000000"; const black = "#000000";
const white = "#ffffff"; const white = "#ffffff";
const gray1 = "#444444";
const gray2 = "#888888";
const gray3 = "#bbbbbb";
const red = "#d41706"; const red = "#d41706";
const blue = "#0000ff";
const yellow = "#ffff00";
let settings = require('Storage').readJSON("calendar.json", true) || {}; let settings = require('Storage').readJSON("calendar.json", true) || {};
if (settings.startOnSun === undefined) if (settings.startOnSun === undefined)
settings.startOnSun = false; settings.startOnSun = false;
if (settings.ndColors === undefined)
if (process.env.HWVERSION == 2) {
settings.ndColors = true;
} else {
settings.ndColors = false;
}
if (settings.ndColors === true) {
let bgColor = white;
let bgColorMonth = blue;
let bgColorDow = black;
let bgColorWeekend = yellow;
let fgOtherMonth = blue;
let fgSameMonth = black;
} else {
let bgColor = color4;
let bgColorMonth = color1;
let bgColorDow = color2;
let bgColorWeekend = color3;
let fgOtherMonth = gray1;
let fgSameMonth = white;
}
function getDowLbls(locale) {
let dowLbls;
//TODO: Find some clever way to generate this programmatically from locale lib
switch (locale) {
case "de_AT":
case "de_CH":
case "de_DE":
if (settings.startOnSun) {
dowLbls = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"];
} else {
dowLbls = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
}
break;
case "nl_NL":
if (settings.startOnSun) {
dowLbls = ["zo", "ma", "di", "wo", "do", "vr", "za"];
} else {
dowLbls = ["ma", "di", "wo", "do", "vr", "za", "zo"];
}
break;
case "fr_BE":
case "fr_CH":
case "fr_FR":
if (settings.startOnSun) {
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
} else {
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
}
break;
case "sv_SE":
if (settings.startOnSun) {
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
} else {
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
}
break;
case "it_CH":
case "it_IT":
if (settings.startOnSun) {
dowLbls = ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"];
} else {
dowLbls = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"];
}
break;
case "oc_FR":
if (settings.startOnSun) {
dowLbls = ["dg", "dl", "dm", "dc", "dj", "dv", "ds"];
} else {
dowLbls = ["dl", "dm", "dc", "dj", "dv", "ds", "dg"];
}
break;
default:
if (settings.startOnSun) {
dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
} else {
dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
}
break;
}
return dowLbls;
}
function drawCalendar(date) { function drawCalendar(date) {
g.setBgColor(color4); g.setBgColor(bgColor);
g.clearRect(0, 0, maxX, maxY); g.clearRect(0, 0, maxX, maxY);
g.setBgColor(color1); g.setBgColor(bgColorMonth);
g.clearRect(0, 0, maxX, headerH); g.clearRect(0, 0, maxX, headerH);
g.setBgColor(color2); g.setBgColor(bgColorDow);
g.clearRect(0, headerH, maxX, headerH + rowH); g.clearRect(0, headerH, maxX, headerH + rowH);
g.setBgColor(color3); g.setBgColor(bgColorWeekend);
g.clearRect(colW * 5, headerH + rowH, maxX, maxY); g.clearRect(colW * 5, headerH + rowH, maxX, maxY);
for (let y = headerH; y < maxY; y += rowH) { for (let y = headerH; y < maxY; y += rowH) {
g.drawLine(0, y, maxX, y); g.drawLine(0, y, maxX, y);
@ -40,24 +123,11 @@ function drawCalendar(date) {
const month = date.getMonth(); const month = date.getMonth();
const year = date.getFullYear(); const year = date.getFullYear();
const monthMap = { const localeMonth = require('locale').month(date);
0: "January",
1: "February",
2: "March",
3: "April",
4: "May",
5: "June",
6: "July",
7: "August",
8: "September",
9: "October",
10: "November",
11: "December"
};
g.setFontAlign(0, 0); g.setFontAlign(0, 0);
g.setFont("6x8", fontSize); g.setFont("6x8", fontSize);
g.setColor(white); g.setColor(white);
g.drawString(`${monthMap[month]} ${year}`, maxX / 2, headerH / 2); g.drawString(`${localeMonth} ${year}`, maxX / 2, headerH / 2);
g.drawPoly([10, headerH / 2, 20, 10, 20, headerH - 10], true); g.drawPoly([10, headerH / 2, 20, 10, 20, headerH - 10], true);
g.drawPoly( g.drawPoly(
[maxX - 10, headerH / 2, maxX - 20, 10, maxX - 20, headerH - 10], [maxX - 10, headerH / 2, maxX - 20, 10, maxX - 20, headerH - 10],
@ -65,12 +135,7 @@ function drawCalendar(date) {
); );
g.setFont("6x8", fontSize); g.setFont("6x8", fontSize);
let dowLbls; let dowLbls = getDowLbls(require('locale').name);
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);
}); });
@ -120,14 +185,19 @@ function drawCalendar(date) {
today.year === year && today.month === month && today.day === day - 50; today.year === year && today.month === month && today.day === day - 50;
if (isToday) { if (isToday) {
g.setColor(red); g.setColor(red);
let x1 = x * colW;
let y1 = y * rowH + headerH + rowH;
let x2 = x * colW + colW;
let y2 = y * rowH + headerH + rowH + rowH;
g.drawRect(x1, y1, x2, y2);
g.drawRect( g.drawRect(
x * colW, x1 + 1,
y * rowH + headerH + rowH, y1 + 1,
x * colW + colW - 1, x2 - 1,
y * rowH + headerH + rowH + rowH y2 - 1
); );
} }
g.setColor(day < 50 ? gray3 : white); g.setColor(day < 50 ? fgOtherMonth : fgSameMonth);
g.drawString( g.drawString(
(day > 50 ? day - 50 : day).toString(), (day > 50 ? day - 50 : day).toString(),
x * colW + colW / 2, x * colW + colW / 2,
@ -145,10 +215,10 @@ const today = {
}; };
drawCalendar(date); drawCalendar(date);
clearWatch(); clearWatch();
Bangle.on("touch",area=>{ Bangle.on("touch", area => {
const month = date.getMonth(); const month = date.getMonth();
let prevMonth; let prevMonth;
if (area==1) { if (area == 1) {
let prevMonth = month > 0 ? month - 1 : 11; let prevMonth = month > 0 ? month - 1 : 11;
if (prevMonth === 11) date.setFullYear(date.getFullYear() - 1); if (prevMonth === 11) date.setFullYear(date.getFullYear() - 1);
date.setMonth(prevMonth); date.setMonth(prevMonth);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 B

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,24 +1,38 @@
(function(back) { (function (back) {
var FILE = "calendar.json"; var FILE = "calendar.json";
var settings = require('Storage').readJSON(FILE, true) || {}; var settings = require('Storage').readJSON(FILE, true) || {};
if (settings.startOnSun === undefined) if (settings.startOnSun === undefined)
settings.startOnSun = true; settings.startOnSun = false;
if (settings.ndColors === undefined)
if (process.env.HWVERSION == 2) {
settings.ndColors = true;
} else {
settings.ndColors = false;
}
function writeSettings() { function writeSettings() {
require('Storage').writeJSON(FILE, settings); require('Storage').writeJSON(FILE, settings);
} }
E.showMenu({ E.showMenu({
"" : { "title" : "Calendar" }, "": { "title": "Calendar" },
"< Back" : () => back(), "< Back": () => back(),
'Start on Sunday': { 'Start Sunday': {
value: settings.startOnSun, value: settings.startOnSun,
format: v => v?"Yes":"No", format: v => v ? "Yes" : "No",
onchange: v => { onchange: v => {
settings.startOnSun = v; settings.startOnSun = v;
writeSettings(); writeSettings();
} }
}, },
'B2 Colors': {
value: settings.ndColors,
format: v => v ? "Yes" : "No",
onchange: v => {
settings.ndColors = v;
writeSettings();
}
},
}); });
}) })

View File

@ -0,0 +1,2 @@
0.01: New clock
0.02: Fix icon & add battery warn functionality

View File

@ -0,0 +1,21 @@
# Circles clock
A clock with circles for different data at the bottom in a probably familiar style
It shows besides time, date and day of week the following information:
* Steps (requires [pedometer widget](https://banglejs.com/apps/#pedometer))
* Heart rate (when screen is on and unlocked)
* Battery (including charging and battery low)
## Screenshot
![Screenshot](screenshot.png)
## TODO
* Show weather information
## Creator
Marco ([myxor](https://github.com/myxor))
## Icons
Icons taken from [materialdesignicons](https://materialdesignicons.com) under Apache License 2.0

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwcCIf4ALv///gFCv0Agf+CJP/wAODAwPAEpAjCCIX8h4RMj/+g/8gP4CA4LBDoP/GpkH8EP4/8LIIRMAQIOCCJU/CgQOBEwMPI5ARCR4YRJgP/gB3CI5Z0CCIiABfHRfEj+BAoN+n4FBLIkP/8chwRBx5cC//8v4REhytDgYRCv//8fxEYwRFgfxA4I1FRgI1D+JHE/7FINZzCBAAc4CRU4/kB44FCjgRKLQRlBPQ4RHgYCB/jpBABB6BPoKzBCJYAGuD/vAB1JkgLJm3bAgUCpMnwDdCPwIFChu27dgAoMSCIP+FAQRB+AFBtoRBtgFByQCBRIIoBAocDtonBAQWQdgXAgVIAocDEAUNwEEyEHBYUSoE//gRCsI7BxvACIILDCIcBCIYFCCJ3/wIRCIIYRBI4h6CAoJrDLJYRDDwJ9LAoKhBoMDUIcEgFwUIQREgUBaAcIkhPCAAQzBAAUBdIhhDAAMGCIkAkAFEdAQAFA=="))

235
apps/circlesclock/app.js Normal file
View File

@ -0,0 +1,235 @@
const locale = require("locale");
const heatshrink = require("heatshrink");
const shoesIcon = heatshrink.decompress(atob("h0OwYJGgmAAgUBkgECgVJB4cSoAUDyEBkARDpADBhMAyQRBgVAkgmDhIUDAAuQAgY1DAAYA="));
const heartIcon = heatshrink.decompress(atob("h0OwYOLkmQhMkgACByVJgESpIFBpEEBAIFBCgIFCCgsABwcAgQOCAAMSpAwDyBNM"));
const powerIcon = heatshrink.decompress(atob("h0OwYQNsAED7AEDmwEDtu2AgUbtuABwXbBIUN23AAoYOCgEDFIgODABI"));
const powerIconGreen = heatshrink.decompress(atob("h0OwYQNkAEDpAEDiQEDkmSAgUJkmABwVJBIUEyVAAoYOCgEBFIgODABI"));
const powerIconRed = heatshrink.decompress(atob("h0OwYQNoAEDyAEDkgEDpIFDiVJBweSAgUJkmAAoYZDgQpEBwYAJA"));
const SETTINGS_FILE = "circlesclock.json";
let settings;
function loadSettings() {
settings = require("Storage").readJSON(SETTINGS_FILE, 1) || {
'maxHR': 200,
'stepGoal': 10000,
'batteryWarn': 30
};
}
const colorFg = '#fff';
const colorBg = '#000';
const colorGrey = '#808080';
const colorRed = '#ff0000';
const colorGreen = '#00ff00';
let hrtValue;
const h = g.getHeight();
const w = g.getWidth();
const hOffset = 30;
const h1 = Math.round(1 * h / 5 - hOffset);
const h2 = Math.round(3 * h / 5 - hOffset);
const h3 = Math.round(8 * h / 8 - hOffset);
const w1 = Math.round(w / 6);
const w2 = Math.round(3 * w / 6);
const w3 = Math.round(5 * w / 6);
const radiusOuter = 22;
const radiusInner = 16;
function draw() {
g.reset();
g.setColor(colorBg);
g.fillRect(0, 0, w, h);
// time
g.setFont("Vector:50");
g.setFontAlign(-1, -1);
g.setColor(colorFg);
g.drawString(locale.time(new Date(), 1), w / 10, h1 + 8);
// date & dow
g.setFont("Vector:20");
g.setFontAlign(-1, 0);
g.drawString(locale.date(new Date()), w / 10, h2);
g.drawString(locale.dow(new Date()), w / 10, h2 + 22);
// Steps circle
drawSteps();
// Heart circle
drawHeartRate();
// Battery circle
drawBattery();
}
function drawSteps() {
const steps = getSteps();
const blue = '#0000ff';
g.setColor(colorGrey);
g.fillCircle(w1, h3, radiusOuter);
const stepGoal = settings.stepGoal;
if (stepGoal > 0) {
let percent = steps / stepGoal;
if (stepGoal < steps) percent = 1;
drawGauge(w1, h3, percent, blue);
}
g.setColor(colorBg);
g.fillCircle(w1, h3, radiusInner);
g.fillPoly([w1, h3, w1 - 15, h3 + radiusOuter + 5, w1 + 15, h3 + radiusOuter + 5]);
g.setFont("Vector:12");
g.setFontAlign(0, 0);
g.setColor(colorFg);
g.drawString(shortValue(steps), w1 + 2, h3);
g.drawImage(shoesIcon, w1 - 6, h3 + radiusOuter - 6);
}
function drawHeartRate() {
g.setColor(colorGrey);
g.fillCircle(w2, h3, radiusOuter);
if (hrtValue != undefined) {
const percent = hrtValue / settings.maxHR;
drawGauge(w2, h3, percent, colorRed);
}
g.setColor(colorBg);
g.fillCircle(w2, h3, radiusInner);
g.fillPoly([w2, h3, w2 - 15, h3 + radiusOuter + 5, w2 + 15, h3 + radiusOuter + 5]);
g.setFont("Vector:12");
g.setFontAlign(0, 0);
g.setColor(colorFg);
g.drawString(hrtValue != undefined ? hrtValue : "-", w2, h3);
g.drawImage(heartIcon, w2 - 6, h3 + radiusOuter - 6);
}
function drawBattery() {
const battery = E.getBattery();
const yellow = '#ffff00';
g.setColor(colorGrey);
g.fillCircle(w3, h3, radiusOuter);
if (battery > 0) {
const percent = battery / 100;
drawGauge(w3, h3, percent, yellow);
}
g.setColor(colorBg);
g.fillCircle(w3, h3, radiusInner);
g.fillPoly([w3, h3, w3 - 15, h3 + radiusOuter + 5, w3 + 15, h3 + radiusOuter + 5]);
g.setFont("Vector:12");
g.setFontAlign(0, 0);
let icon = powerIcon;
let color = colorFg;
if (Bangle.isCharging()) {
color = colorGreen;
icon = powerIconGreen;
}
else {
if (settings.batteryWarn != undefined && battery <= settings.batteryWarn) {
color = colorRed;
icon = powerIconRed;
}
}
g.setColor(color);
g.drawString(battery + '%', w3, h3);
g.drawImage(icon, w3 - 6, h3 + radiusOuter - 6);
}
function radians(a) {
return a * Math.PI / 180;
}
function drawGauge(cx, cy, percent, color) {
let offset = 30;
let end = 300;
var i = 0;
var r = radiusInner + 3;
if (percent > 1) percent = 1;
var startrot = -offset;
var endrot = startrot - ((end - offset) * percent);
g.setColor(color);
// draw gauge
for (i = startrot; i > endrot; i -= 4) {
x = cx + r * Math.sin(radians(i));
y = cy + r * Math.cos(radians(i));
g.fillCircle(x, y, 4);
}
}
function shortValue(v) {
if (isNaN(v)) return '-';
if (v <= 999) return v;
if (v >= 1000 && v < 10000) {
v = Math.floor(v / 100) * 100;
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
}
if (v >= 10000) {
v = Math.floor(v / 1000) * 1000;
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
}
}
function getSteps() {
if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom.getSteps();
}
return 0;
}
Bangle.on('lock', function(isLocked) {
if (!isLocked) {
Bangle.setHRMPower(1, "watch");
} else {
Bangle.setHRMPower(0, "watch");
}
drawHeartRate();
drawSteps();
});
Bangle.on('HRM', function(hrm) {
//if(hrm.confidence > 90){
hrtValue = hrm.bpm;
if (Bangle.isLCDOn())
drawHeartRate();
//} else {
// hrtValue = undefined;
//}
});
g.clear();
Bangle.loadWidgets();
/*
* 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 and change the
* area to the top bar doesn't get cleared.
*/
for (let wd of WIDGETS) {
wd.draw = () => {};
wd.area = "";
}
loadSettings();
setInterval(draw, 60000);
draw();
Bangle.setUI("clock");

BIN
apps/circlesclock/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,43 @@
(function(back) {
const SETTINGS_FILE = "circlesclock.json";
const storage = require('Storage');
let settings = storage.readJSON(SETTINGS_FILE, 1) || {};
function save(key, value) {
settings[key] = value;
storage.write(SETTINGS_FILE, settings);
}
E.showMenu({
'': { 'title': 'circlesclock' },
'max heartrate': {
value: "maxHR" in settings ? settings.maxHR : 200,
min: 20,
max : 250,
step: 10,
format: x => {
return x;
},
onchange: x => save('maxHR', x),
},
'step goal': {
value: "stepGoal" in settings ? settings.stepGoal : 10000,
min: 2000,
max : 50000,
step: 2000,
format: x => {
return x;
},
onchange: x => save('stepGoal', x),
},
'battery warn lvl': {
value: "batteryWarn" in settings ? settings.batteryWarn : 30,
min: 10,
max : 100,
step: 10,
format: x => {
return x + '%';
},
onchange: x => save('batteryWarn', x),
},
'< Back': back,
});
});

View File

@ -1,2 +1,3 @@
0.01: New clock! 0.01: New clock!
0.02: Load steps from Health Tracking app (if installed) 0.02: Load steps from Health Tracking app (if installed)
0.03: ...

View File

@ -3,12 +3,14 @@
Command line styled clock with lots of information: Command line styled clock with lots of information:
It can show the following (depending on availability) information: It can show the following (depending on availability) information:
* Time * Time data:
* Day of week * Time
* Date * Day of week
* Weather conditions and temperature (requires app [Weather](https://banglejs.com/apps/#weather)) * Date
* Steps (requires app [Health Tracking](https://banglejs.com/apps/#health%20tracking) or a step widget) * Additional information (can be toggled via settings):
* Heart rate (when screen is on and unlocked) * 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 ## TODO
* Make time font bigger * Make time font bigger

View File

@ -1,31 +1,60 @@
const storage = require('Storage'); const storage = require('Storage');
const locale = require("locale"); const locale = require("locale");
const font = "12x20"; const font12 = g.getFonts().includes("12x20");
const fontsize = 1; const font = font12 ? "12x20" : "6x8";
const fontsize = font12 ? 1: 2;
const fontheight = 19; const fontheight = 19;
const marginTop = 10; const marginTop = 5;
const marginLeftTopic = 3; // margin of topics const marginLeftTopic = 3; // margin of topics
const marginLeftData = 68; // margin of data values const marginLeftData = font12 ? 64 : 75; // margin of data values
const topicColor = g.theme.dark ? "#fff" : "#000"; const topicColor = g.theme.dark ? "#fff" : "#000";
const textColor = g.theme.dark ? "#0f0" : "#080"; const textColor = g.theme.dark ? "#0f0" : "#080";
const textColorRed = g.theme.dark ? "#FF0000" : "#FF0000";
let hrtValue; let hrtValue;
let hrtValueIsOld = false; let hrtValueIsOld = false;
let localTempValue; let localTempValue;
let weatherTempString; let weatherTempString;
let lastHeartRateRowIndex; let lastHeartRateRowIndex;
let lastStepsRowIndex;
let i = 2;
let settings;
function loadSettings() {
settings = storage.readJSON('clicompleteclk.json', 1) || {};
}
function setting(key) {
if (!settings) { loadSettings(); }
const DEFAULTS = {
'battery': true,
'batteryLvl': 30,
'weather': true,
'steps': true,
'heartrate': true
};
return (key in settings) ? settings[key] : DEFAULTS[key];
}
let showBattery = setting('battery');
let batteryWarnLevel = setting('batteryLvl');
let showWeather = setting('weather');
let showSteps = setting('steps');
let showHeartRate = setting('heartrate');
// timeout used to update every minute
var drawTimeout; var drawTimeout;
// schedule a draw for the next minute
function queueDraw() { function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() { drawTimeout = setTimeout(function() {
drawTimeout = undefined; drawTimeout = undefined;
drawAll(false); drawAll(true);
}, 60000 - (Date.now() % 60000)); }, 60000 - (Date.now() % 60000));
} }
@ -42,15 +71,13 @@ function updateTime(now){
if (!Bangle.isLCDOn()) return; if (!Bangle.isLCDOn()) return;
writeLineTopic("TIME", 1); writeLineTopic("TIME", 1);
writeLine(locale.time(now,1),1); writeLine(locale.time(now,1),1);
if(now.getMinutes() == 0)
drawInfo(now);
} }
function drawInfo(now) { function drawInfo(now) {
if (now == undefined) if (now == undefined)
now = new Date(); now = new Date();
let i = 2; i = 2;
writeLineTopic("DOWK", i); writeLineTopic("DOWK", i);
writeLine(locale.dow(now),i); writeLine(locale.dow(now),i);
@ -60,14 +87,28 @@ function drawInfo(now) {
writeLine(locale.date(now,1),i); writeLine(locale.date(now,1),i);
i++; i++;
/* if (showBattery) {
writeLineTopic("BAT", i); writeLineTopic("BATT", i);
const b = E.getBattery(); const b = E.getBattery();
writeLine(b + "%", i); // TODO make bars writeLine(b + "%", i, b < batteryWarnLevel ? textColorRed : textColor);
i++; i++;
*/ }
// weather if (showWeather) {
drawWeather();
}
if (showSteps) {
drawSteps(i);
i++;
}
if (showHeartRate) {
drawHeartRate(i);
}
}
function drawWeather() {
const weatherJson = getWeather(); const weatherJson = getWeather();
if(weatherJson && weatherJson.weather){ if(weatherJson && weatherJson.weather){
const currentWeather = weatherJson.weather; const currentWeather = weatherJson.weather;
@ -82,19 +123,22 @@ function drawInfo(now) {
writeLine(weatherTempValue,i); writeLine(weatherTempValue,i);
i++; i++;
} }
}
// steps function drawSteps(i) {
if (!showSteps) return;
if (i == undefined)
i = lastStepsRowIndex;
const steps = getSteps(); const steps = getSteps();
if (steps != undefined) { if (steps != undefined) {
writeLineTopic("STEP", i); writeLineTopic("STEP", i);
writeLine(steps, i); writeLine(steps, i);
i++;
} }
lastStepsRowIndex = i;
drawHeartRate(i);
} }
function drawHeartRate(i) { function drawHeartRate(i) {
if (!showHeartRate) return;
if (i == undefined) if (i == undefined)
i = lastHeartRateRowIndex; i = lastHeartRateRowIndex;
writeLineTopic("HRTM", i); writeLineTopic("HRTM", i);
@ -155,15 +199,21 @@ function getWeather() {
// turn on HRM when the LCD is unlocked // turn on HRM when the LCD is unlocked
Bangle.on('lock', function(isLocked) { Bangle.on('lock', function(isLocked) {
if (!isLocked) { if (!isLocked) {
Bangle.setHRMPower(1,"clicompleteclk"); if (showHeartRate) {
if (hrtValue == undefined) Bangle.setHRMPower(1,"clicompleteclk");
hrtValue = "..."; if (hrtValue == undefined)
else hrtValue = "...";
hrtValueIsOld = true; else
hrtValueIsOld = true;
}
} else { } else {
hrtValueIsOld = true; if (showHeartRate) {
Bangle.setHRMPower(0,"clicompleteclk"); hrtValueIsOld = true;
Bangle.setHRMPower(0,"clicompleteclk");
}
} }
// Update steps and heart rate
drawSteps();
drawHeartRate(); drawHeartRate();
}); });
@ -171,25 +221,30 @@ Bangle.on('lcdPower',function(on) {
if (on) { if (on) {
drawAll(true); drawAll(true);
} else { } else {
hrtValueIsOld = true; if (showHeartRate) {
hrtValueIsOld = true;
}
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined; drawTimeout = undefined;
} }
}); });
Bangle.on('HRM', function(hrm) { if (showHeartRate) {
//if(hrm.confidence > 90){ Bangle.on('HRM', function(hrm) {
hrtValueIsOld = false; //if(hrm.confidence > 90){
hrtValue = hrm.bpm; hrtValueIsOld = false;
if (Bangle.isLCDOn()) hrtValue = hrm.bpm;
drawHeartRate(); if (Bangle.isLCDOn())
//} else { drawHeartRate();
// hrtValue = undefined; //} else {
//} // hrtValue = undefined;
}); //}
});
}
g.clear(); g.clear();
Bangle.setUI("clock"); Bangle.setUI("clock");
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
loadSettings();
drawAll(true); drawAll(true);

View File

@ -0,0 +1,54 @@
(function(back) {
const storage = require('Storage');
let settings = storage.readJSON('clicompleteclk.json', 1) || {};
function save(key, value) {
settings[key] = value;
storage.write('clicompleteclk.json', settings);
}
E.showMenu({
'': { 'title': 'CLI complete clk' },
'Show battery': {
value: "battery" in settings ? settings.battery : false,
format: () => (settings.battery ? 'Yes' : 'No'),
onchange: () => {
settings.battery = !settings.battery;
save('battery', settings.battery);
},
},
'Battery warn': {
value: "batteryLvl" in settings ? settings.batteryLvl : 30,
min: 0,
max : 100,
step: 10,
format: x => {
return x + "%";
},
onchange: x => save('batteryLvl', x),
},
'Show weather': {
value: "weather" in settings ? settings.weather : false,
format: () => (settings.weather ? 'Yes' : 'No'),
onchange: () => {
settings.weather = !settings.weather;
save('weather', settings.weather);
},
},
'Show steps': {
value: "steps" in settings ? settings.steps : false,
format: () => (settings.steps ? 'Yes' : 'No'),
onchange: () => {
settings.steps = !settings.steps;
save('steps', settings.steps);
},
},
'Show heartrate': {
value: "heartrate" in settings ? settings.heartrate : false,
format: () => (settings.heartrate ? 'Yes' : 'No'),
onchange: () => {
settings.heartrate = !settings.heartrate;
save('heartrate', settings.heartrate);
},
},
'< Back': back,
});
});

1
apps/coretemp/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New app

20
apps/coretemp/README.md Normal file
View File

@ -0,0 +1,20 @@
# CoreTemp display
Basic bare-bones example of connecting to a bluetooth [CoreTemp](https://corebodytemp.com/) device and displaying the current body core temperature readings.
## Usage
On startup connects to a CoreTemp device (1809/2A1C) and emits a "Core, temp" value for each reading.
The app simply displays these readings on screen.
## TODO
* Integrate with other tracking/sports apps to log data.
* Add device selection
* Provide enable/disable option
* Check status, add Retry/reconnect
* Also provide skin temp reading
## Creator
Ivor Hewitt

23
apps/coretemp/boot.js Normal file
View File

@ -0,0 +1,23 @@
(function() {
var gatt;
//Would it be better to scan by uuid rather than name?
NRF.requestDevice({ timeout: 20000, filters: [{ name: 'CORE [a]' }] }).then(function(device) {
return device.gatt.connect();
}).then(function(g) {
gatt = g;
return gatt.getPrimaryService("1809");
}).then(function(service) {
return service.getCharacteristic("2A1C");
}).then(function(characteristic) {
characteristic.on('characteristicvaluechanged', function(event) {
var dv = event.target.value;
var core = (dv.buffer[2]*256+dv.buffer[1])/100;
Bangle.emit('Core',{
temp:core
});
});
return characteristic.startNotifications();
}).then(function() {
});
})();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4UA///k0DxUFgsDCY8KwAfJlQLHhWglWq1WgBIcCA4QCB1WoComq0+iBYWqCwl//4OBAAQxChWlv/2BYIlCBYUqv9VvQLBwA9BBYWlqtV/QLBGoRIBgQLBr9aBYQ2BBYMKroLBtQLCgALClIKC1AXG1NVuoFBF4sC09V+woCBAJHCgWXq9oPQZrDgWdq9gBZG9rqgCTwSbCgVVqysDBYkK6tWYoa/DkEJ6vaaIgWBaAILCbQhUCBYXoc4wNBBZWqBfBtB1ALKKZILCR4J3FToQLBU4KPEWoQLNZYILIa4NVcYReEcYOnqtaDAbvDgALBcg4EBlNVqtqDoOgd4YoBBYNWytWCwQdCgQLBAAVaBYkA0oLDuwLFkv1BgZGDAAMJuoKCroWEGAOnDAVftShGr////1tDdG14LB+wiEAAdqHAjTHBYgA=="))

19
apps/coretemp/coretemp.js Normal file
View File

@ -0,0 +1,19 @@
Bangle.setLCDPower(1);
Bangle.setLCDTimeout(0);
var btm = g.getHeight()-1;
function onCore(c) {
var px = g.getWidth()/2;
g.setFontAlign(0,0);
g.clearRect(0,24,g.getWidth(),80);
var str = c.temp + "C";
g.setFontVector(40).drawString(str,px,45);
}
Bangle.on('Core', onCore);
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
g.reset().setFont("6x8",2).setFontAlign(0,0);
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);

BIN
apps/coretemp/coretemp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("/wA/AH4A/AH4A/ACmsAEQuMlcAAD0rGBQKBFr4ADGBOsqwvjqwvJRsCRFF/8Gg4ADEZYQEgwvWg8+AAgwKCJgvQDgoABF5IRMF5xEBJpBhGCJwvNDQM4AYMNAAQaBnCAFCJ4vNIwQeBAAkxQAwGCmIRFFwIRDF64dDgwGBgwRNF/4v/F/4v/F/4v/F/4dJmIdECIkxF7MHFwUHhoACg4eCAYIACCJ4vNDQIgCAAgICKwoROF5yAEAAgtFCKAvQJpAAICJgvQgEGg4ADFxIwCAAcGBYovRADov6qwvjqwvJ1gvjEoIvHGASRgRoIuJGAYAhFxQA/AH4A/AH4A/ABQ")) require("heatshrink").decompress(atob("mEw4UA///A4N551ulxL/ACkK1QAG0ALBlNVAA1oBYOlBY9aBYO1BY9eBYOVBY9WBbf/+oIBr//BYlX//9BYN///VC599qtX6oBBqt9BYYRBr/1AIIdBBf4L/BY6bLZcb7MBau1BY9eBYOlBY9aBYMpBY9oBYMK1QAG0ALBAH4ASA"))

View File

@ -3,3 +3,5 @@
0.03: cycle thru pages 0.03: cycle thru pages
0.04: reset to clock after 2 mins of inactivity 0.04: reset to clock after 2 mins of inactivity
0.05: add Bangle 2 version 0.05: add Bangle 2 version
0.06: Adds settings page (hide clocks or launchers)
0.07: Adds setting for directly launching app on touch for Bangle 2

View File

@ -2,6 +2,11 @@
* *
*/ */
var settings = Object.assign({
showClocks: true,
showLaunchers: true,
}, require('Storage').readJSON("dtlaunch.json", true) || {});
function wdog(handle,timeout){ function wdog(handle,timeout){
if(handle !== undefined){ if(handle !== undefined){
wdog.handle = handle; wdog.handle = handle;
@ -17,7 +22,13 @@ function wdog(handle,timeout){
wdog(load,120000) wdog(load,120000)
var s = require("Storage"); var s = require("Storage");
var apps = s.list(/\.info$/).map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="clock" || !app.type)); var apps = s.list(/\.info$/).map(app=>{
var a=s.readJSON(app,1);
return a && {
name:a.name, type:a.type, icon:a.icon, sortorder:a.sortorder, src:a.src
};}).filter(
app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || (app.type=="launch" && settings.showLaunchers) || !app.type));
apps.sort((a,b)=>{ apps.sort((a,b)=>{
var n=(0|a.sortorder)-(0|b.sortorder); var n=(0|a.sortorder)-(0|b.sortorder);
if (n) return n; // do sortorder first if (n) return n; // do sortorder first

View File

@ -2,8 +2,20 @@
* *
*/ */
var settings = Object.assign({
showClocks: true,
showLaunchers: true,
direct: false,
}, require('Storage').readJSON("dtlaunch.json", true) || {});
var s = require("Storage"); var s = require("Storage");
var apps = s.list(/\.info$/).map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="clock" || !app.type)); var apps = s.list(/\.info$/).map(app=>{
var a=s.readJSON(app,1);
return a && {
name:a.name, type:a.type, icon:a.icon, sortorder:a.sortorder, src:a.src
};}).filter(
app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || (app.type=="launch" && settings.showLaunchers) || !app.type));
apps.sort((a,b)=>{ apps.sort((a,b)=>{
var n=(0|a.sortorder)-(0|b.sortorder); var n=(0|a.sortorder)-(0|b.sortorder);
if (n) return n; // do sortorder first if (n) return n; // do sortorder first
@ -28,7 +40,7 @@ const YOFF = 30;
function draw_icon(p,n,selected) { function draw_icon(p,n,selected) {
var x = (n%2)*72+XOFF; var x = (n%2)*72+XOFF;
var y = n>1?72+YOFF:YOFF; var y = n>1?72+YOFF:YOFF;
(selected?g.setColor(g.theme.fgH):g.setColor(g.theme.bg)).fillRect(x+10,y+2,x+60,y+52); (selected?g.setColor(g.theme.fgH):g.setColor(g.theme.bg)).fillRect(x+11,y+3,x+60,y+52);
g.clearRect(x+12,y+4,x+59,y+51); g.clearRect(x+12,y+4,x+59,y+51);
g.setColor(g.theme.fg); g.setColor(g.theme.fg);
try{g.drawImage(apps[p*4+n].icon,x+12,y+4);} catch(e){} try{g.drawImage(apps[p*4+n].icon,x+12,y+4);} catch(e){}
@ -52,7 +64,7 @@ function drawPage(p){
} }
for (var i=0;i<4;i++) { for (var i=0;i<4;i++) {
if (!apps[p*4+i]) return i; if (!apps[p*4+i]) return i;
draw_icon(p,i,selected==i); draw_icon(p,i,selected==i && !settings.direct);
} }
g.flip(); g.flip();
} }
@ -81,9 +93,9 @@ Bangle.on("touch",(_,p)=>{
for (i=0;i<4;i++){ for (i=0;i<4;i++){
if((page*4+i)<Napps){ if((page*4+i)<Napps){
if (isTouched(p,i)) { if (isTouched(p,i)) {
draw_icon(page,i,true); draw_icon(page,i,true && !settings.direct);
if (selected>=0) { if (selected>=0 || settings.direct) {
if (selected!=i){ if (selected!=i && !settings.direct){
draw_icon(page,selected,false); draw_icon(page,selected,false);
} else { } else {
load(apps[page*4+i].src); load(apps[page*4+i].src);

View File

@ -0,0 +1,33 @@
(function(back) {
var FILE = "dtlaunch.json";
var settings = Object.assign({
showClocks: true,
showLaunchers: true
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
E.showMenu({
"" : { "title" : "Desktop launcher" },
"< Back" : () => back(),
'Show clocks': {
value: settings.showClocks,
format: v => v?"On":"Off",
onchange: v => {
settings.showClocks = v;
writeSettings();
}
},
'Show launchers': {
value: settings.showLaunchers,
format: v => v?"On":"Off",
onchange: v => {
settings.showLaunchers = v;
writeSettings();
}
}
});
})

View File

@ -0,0 +1,42 @@
(function(back) {
var FILE = "dtlaunch.json";
var settings = Object.assign({
showClocks: true,
showLaunchers: true,
direct: false
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
E.showMenu({
"" : { "title" : "Desktop launcher" },
"< Back" : () => back(),
'Show clocks': {
value: settings.showClocks,
format: v => v?"On":"Off",
onchange: v => {
settings.showClocks = v;
writeSettings();
}
},
'Show launchers': {
value: settings.showLaunchers,
format: v => v?"On":"Off",
onchange: v => {
settings.showLaunchers = v;
writeSettings();
}
},
'Direct launch': {
value: settings.direct,
format: v => v?"On":"Off",
onchange: v => {
settings.direct = v;
writeSettings();
}
}
});
})

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,8 @@
<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>. For usage on Bangle.js 2 you'll likely need to have an updated bootloader.</p>
<div id="fw-unknown"> <div id="fw-unknown">
<p><b>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

View File

@ -24,3 +24,5 @@
0.22: Respect Quiet Mode 0.22: Respect Quiet Mode
0.23: Allow notification dismiss to remove from phone too 0.23: Allow notification dismiss to remove from phone too
0.24: tag HRM power requests to allow this to work alongside other widgets/apps (fix #799) 0.24: tag HRM power requests to allow this to work alongside other widgets/apps (fix #799)
0.25: workaround call notification
Fix inflated step number

View File

@ -184,7 +184,7 @@
case "call": case "call":
var note = { size: 55, title: event.name, id: "call", var note = { size: 55, title: event.name, id: "call",
body: event.number, icon:require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw="))} body: event.number, icon:require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw="))}
if (event.cmd === "incoming") { if (event.cmd === "incoming" || event.cmd === "") {
require("notify").show(note); require("notify").show(note);
if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) { if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
Bangle.buzz(); Bangle.buzz();
@ -262,7 +262,7 @@
// Send a summary of activity to Gadgetbridge // Send a summary of activity to Gadgetbridge
function sendActivity(hrm) { function sendActivity(hrm) {
var steps = currentSteps - lastSentSteps; var steps = currentSteps - lastSentSteps;
lastSentSteps = 0; lastSentSteps = currentSteps;
gbSend({ t: "act", stp: steps, hrm:hrm }); gbSend({ t: "act", stp: steps, hrm:hrm });
} }

2
apps/golfscore/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New App!
0.02: multiple player score support

37
apps/golfscore/README.md Normal file
View File

@ -0,0 +1,37 @@
# Golf Score
Lets you keep track of strokes during a game of Golf.
![](mainmenu.png)
![](setupmenu.png)
![](scorecard.png)
![](holemenu.png)
## Usage
1. Open the app,
1. scroll to setup
2. set the number of holes (18 by default, but can be configured)
3. set the number of players (4 by default, but can be 1-20)
4. click back
5. scroll to a hole (hole 1)
6. scroll to a player and set the number of strokes they took (repeat as needed)
7. click next hole and repeat #6 and #7 as needed; or click back
8. at any time, check the score card for a sum total of all the strokes for each player
## Features
Track strokes for multiple players (1-20)
Set number of holes on course
## Controls
N/A
## Requests
Michael Salaverry (github.com/barakplasma)
## Creator
Michael Salaverry

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwIEBgOABQcD4AFDg1wAokYDokOAokDDwkBDwkADwn4nAFD/geDgP8gYFEDwn8gFgDocA+AFCkE/A4IABg//Aoc//4RDn/+Goc/8AFJj4FLEQYFGh4FLIAYFGg4FKh5sBApEfnhTEAok+Aol8vihEAon4AocB+F4ZQYFF8AFDg/AAocPAouAKYcfXQQFHjzEEhjvDA"))

113
apps/golfscore/app.js Normal file
View File

@ -0,0 +1,113 @@
// @ts-check
// @ts-ignore
const menu = require("graphical_menu");
/**
* @type {{showMenu: (config) => void}}
*/
let E;
/**
* @type {{clear: () => void}}
*/
let g;
let holes_count = 18;
let player_count = 4;
/**
* @type {number[][]}
*/
let course = new Array(holes_count).map(() => new Array(player_count).fill(0));
const main_menu = {
"": {
"title": "-- Golf --"
},
"Setup": function () { E.showMenu(setup_menu); },
"Score Card": function () {
calculate_score();
E.showMenu(score_card);
},
};
function calculate_score() {
let scores = course.reduce((acc, hole) => {
hole.forEach((stroke_count, player) => {
acc[player] = acc[player]+stroke_count;
});
return acc;
}, new Array(player_count).fill(0));
score_card = {
"": {
"title": "score card"
},
"< Back": function () { E.showMenu(main_menu); },
};
for (let player = 0; player < player_count; player++) {
score_card["Player - " + (player + 1)] = {
value: scores[player]
};
}
}
let score_card = {};
const setup_menu = {
"": {
"title": "-- Golf Setup --"
},
"Holes": {
value: holes_count,
min: 1, max: 20, step: 1, wrap: true,
onchange: v => { holes_count = v; add_holes(); }
},
"Players": {
value: player_count,
min: 1, max: 10, step: 1, wrap: true,
onchange: v => { player_count = v; }
},
"< Back": function () { E.showMenu(main_menu); },
};
function inc_hole(i, player) { return function (v) { course[i][player] = v; }; }
function add_holes() {
for (let j = 0; j < 20; j++) {
delete main_menu["Hole - " + (j + 1)];
}
for (let i = 0; i < holes_count; i++) {
course[i] = new Array(player_count).fill(0);
main_menu["Hole - " + (i + 1)] = goto_hole_menu(i);
}
E.showMenu(main_menu);
}
function goto_hole_menu(i) {
return function () {
E.showMenu(hole_menu(i));
};
}
function hole_menu(i) {
let menu = {
"": {
"title": `-- Hole ${i + 1}--`
},
"Next hole": goto_hole_menu(i + 1),
"< Back": function () { E.showMenu(main_menu); },
};
for (let player = 0; player < player_count; player++) {
menu[`player - ${player + 1}`] = {
value: course[i][player],
min: 1, max: 20, step: 1, wrap: true,
onchange: inc_hole(i, player)
};
}
return menu;
}
// @ts-ignore
g.clear();
add_holes();

BIN
apps/golfscore/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
apps/golfscore/holemenu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
apps/golfscore/mainmenu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -7,3 +7,4 @@
0.06: Fix daily health summary for movement (a line got deleted!) 0.06: Fix daily health summary for movement (a line got deleted!)
0.07: Added coloured bar charts 0.07: Added coloured bar charts
0.08: Suppress bleed through of E.showMenu's when displaying bar charts 0.08: Suppress bleed through of E.showMenu's when displaying bar charts
0.09: Fix file naming so months are 1-based (not 0) (fix #1119)

View File

@ -27,7 +27,7 @@ Bangle.on("health", health => {
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN; const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
function getRecordFN(d) { function getRecordFN(d) {
return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw"; return "health-"+d.getFullYear()+"-"+(d.getMonth()+1)+".raw";
} }
function getRecordIdx(d) { function getRecordIdx(d) {
return (DB_RECORDS_PER_DAY*(d.getDate()-1)) + return (DB_RECORDS_PER_DAY*(d.getDate()-1)) +

View File

@ -6,7 +6,7 @@ const DB_HEADER_LEN = 8;
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN; const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
function getRecordFN(d) { function getRecordFN(d) {
return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw"; return "health-"+d.getFullYear()+"-"+(d.getMonth()+1)+".raw";
} }
function getRecordIdx(d) { function getRecordIdx(d) {
return (DB_RECORDS_PER_DAY*(d.getDate()-1)) + return (DB_RECORDS_PER_DAY*(d.getDate()-1)) +

View File

@ -1,3 +1,4 @@
0.01: New App! 0.01: New App!
0.02: using TS and rollup to bundle 0.02: using TS and rollup to bundle
0.03: bug fixes and support bangle 1 0.03: bug fixes and support bangle 1
0.04: removing TS

View File

@ -1,5 +1,5 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2021 Michael Salaverry
Copyright (c) 2016-20 Ionică Bizău <bizauionica@gmail.com> (https://ionicabizau.net) Copyright (c) 2016-20 Ionică Bizău <bizauionica@gmail.com> (https://ionicabizau.net)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy

File diff suppressed because one or more lines are too long

View File

@ -1,17 +1,26 @@
!function(){"use strict"; g.clear();
/*!
* This script was taked from this page and ported to Node.js by Ionic Bizu let now = new Date();
* http://www.shamash.org/help/javadate.shtml
* let today = require('hebrewDate').hebrewDate(now);
* This script was adapted from C sources written by
* Scott E. Lee, which contain the following copyright notice: var mainmenu = {
* "": {
* Copyright 1993-1995, Scott E. Lee, all rights reserved. "title": "Hebrew Date"
* Permission granted to use, copy, modify, distribute and sell so long as },
* the above copyright and this permission statement are retained in all greg: {
* copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. // @ts-ignore
* value: require('locale').date(now, 1),
* Bill Hastings },
* RBI Software Systems date: {
* bhastings@rbi.com value: today.date,
*/var t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};var i=new function(t,i,e,o,r,n,h,a,s,f,u,l,v,c){this[0]=t,this[1]=i,this[2]=e,this[3]=o,this[4]=r,this[5]=n,this[6]=h,this[7]=a,this[8]=s,this[9]=f,this[10]=u,this[11]=l,this[12]=v,this[13]=c}("Tishri","Heshvan","Kislev","Tevet","Shevat","AdarI","AdarII","Nisan","Iyyar","Sivan","Tammuz","Av","Elul"),e=new function(t,i,e,o,r,n,h,a,s,f,u,l,v,c,y,d,m,M,b){this[0]=t,this[1]=i,this[2]=e,this[3]=o,this[4]=r,this[5]=n,this[6]=h,this[7]=a,this[8]=s,this[9]=f,this[10]=u,this[11]=l,this[12]=v,this[13]=c,this[14]=y,this[15]=d,this[16]=m,this[17]=M,this[18]=b}(12,12,13,12,12,13,12,13,12,12,13,12,12,13,12,12,13,12,13);g.clear();let o=new Date,r=function(o){var r,n,h=0,a=0,s=0,f=0,u=0,l=0,v=0;function c(t){var i,o,r,n;for(f=Math.floor((t+310)/6940),r=void 0,n=void 0,r=31524,n=(r+=45971*f)>>16,n+=2744*f,o=Math.floor(n/25920),r=(n-=25920*o)<<16|65535&r,i=Math.floor(r/25920),l=o<<16|i,v=r-=25920*i;l<t-6940+310;)f++,v+=179876755,l+=Math.floor(v/25920),v%=25920;for(u=0;u<18&&!(l>t-74);u++)v+=765433*e[u],l+=Math.floor(v/25920),v%=25920}function y(t,i,e){var o=i,r=o%7;return(e>=19440||!(2==t||5==t||7==t||10==t||13==t||16==t||18==t)&&2==r&&e>=9924||(3==t||6==t||8==t||11==t||14==t||17==t||0==t)&&1==r&&e>=16789)&&(o++,7==++r&&(r=0)),3!=r&&5!=r&&0!=r||o++,o}var d=o;return"object"===(void 0===d?"undefined":t(d))&&(r=o.getMonth()+1,n=o.getDate(),d=o.getFullYear()),function(t){var i,o=0,r=0,n=t-347997;if(c(n),n>=(o=y(u,l,v))){if(s=19*f+u+1,n<o+59)return void(n<o+30?(h=1,a=n-o+1):(h=2,a=n-o-29));v+=765433*e[u],l+=Math.floor(v/25920),r=y((u+1)%19,l,v%=25920)}else{if(s=19*f+u,n>=o-177)return void(n>o-30?(h=13,a=n-o+30):n>o-60?(h=12,a=n-o+60):n>o-89?(h=11,a=n-o+89):n>o-119?(h=10,a=n-o+119):n>o-148?(h=9,a=n-o+148):(h=8,a=n-o+178));if(13==e[(s-1)%19]){if(h=7,(a=n-o+207)>0)return;if(h--,(a+=30)>0)return;h--,a+=30}else{if(h=6,(a=n-o+207)>0)return;h--,a+=30}if(a>0)return;if(h--,(a+=29)>0)return;r=o,c(l-365),o=y(u,l,v)}if(l=n-o-29,355==(i=r-o)||385==i){if(l<=30)return h=2,void(a=l);l-=30}else{if(l<=29)return h=2,void(a=l);l-=29}h=3,a=l}(function(t,i,e){var o=0,r=0,n=void 0;return o=t<0?t+4801:t+4800,i>2?r=i-3:(r=i+9,o--),n=Math.floor(146097*Math.floor(o/100)/4),n+=Math.floor(o%100*1461/4),n+=Math.floor((153*r+2)/5),n+=e-32045}(d,r,n)),{year:s,month:h,date:a,month_name:i[h-1]}}(o);var n={"":{title:"Hebrew Date"},cal:{value:require("locale").date(o,1),onchange:()=>{}},date:{value:r.date,onchange:()=>{}},month:{value:r.month_name,onchange:()=>{}},year:{value:r.year,onchange:()=>{}}};E.showMenu(n)}(); },
month: {
value: today.month_name,
},
year: {
value: today.year,
}
};
// @ts-ignore
E.showMenu(mainmenu);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,6 +1,5 @@
/*! /*!
* This script was taked from this page and ported to Node.js by Ionic Bizu * This script was taked from this page http://www.shamash.org/help/javadate.shtml and ported to Node.js by Ionică Bizău in https://github.com/IonicaBizau/hebrew-date
* http://www.shamash.org/help/javadate.shtml
* *
* This script was adapted from C sources written by * This script was adapted from C sources written by
* Scott E. Lee, which contain the following copyright notice: * Scott E. Lee, which contain the following copyright notice:
@ -14,33 +13,11 @@
* RBI Software Systems * RBI Software Systems
* bhastings@rbi.com * bhastings@rbi.com
*/ */
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var GREG_SDN_OFFSET = 32045, DAYS_PER_5_MONTHS = 153, DAYS_PER_4_YEARS = 1461, DAYS_PER_400_YEARS = 146097;
var GREG_SDN_OFFSET = 32045, var HALAKIM_PER_HOUR = 1080, HALAKIM_PER_DAY = 25920, HALAKIM_PER_LUNAR_CYCLE = 29 * HALAKIM_PER_DAY + 13753, HALAKIM_PER_METONIC_CYCLE = HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7);
DAYS_PER_5_MONTHS = 153, var HEB_SDN_OFFSET = 347997, NEW_MOON_OF_CREATION = 31524, NOON = 18 * HALAKIM_PER_HOUR, AM3_11_20 = 9 * HALAKIM_PER_HOUR + 204, AM9_32_43 = 15 * HALAKIM_PER_HOUR + 589;
DAYS_PER_4_YEARS = 1461, var SUN = 0, MON = 1, TUES = 2, WED = 3, THUR = 4, FRI = 5, SAT = 6;
DAYS_PER_400_YEARS = 146097;
var HALAKIM_PER_HOUR = 1080,
HALAKIM_PER_DAY = 25920,
HALAKIM_PER_LUNAR_CYCLE = 29 * HALAKIM_PER_DAY + 13753,
HALAKIM_PER_METONIC_CYCLE = HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7);
var HEB_SDN_OFFSET = 347997,
NEW_MOON_OF_CREATION = 31524,
NOON = 18 * HALAKIM_PER_HOUR,
AM3_11_20 = 9 * HALAKIM_PER_HOUR + 204,
AM9_32_43 = 15 * HALAKIM_PER_HOUR + 589;
var SUN = 0,
MON = 1,
TUES = 2,
WED = 3,
THUR = 4,
FRI = 5,
SAT = 6;
function weekdayarr(d0, d1, d2, d3, d4, d5, d6) { function weekdayarr(d0, d1, d2, d3, d4, d5, d6) {
this[0] = d0; this[0] = d0;
this[1] = d1; this[1] = d1;
@ -50,7 +27,6 @@ function weekdayarr(d0, d1, d2, d3, d4, d5, d6) {
this[5] = d5; this[5] = d5;
this[6] = d6; this[6] = d6;
} }
function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) { function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) {
this[0] = m0; this[0] = m0;
this[1] = m1; this[1] = m1;
@ -65,8 +41,7 @@ function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) {
this[10] = m10; this[10] = m10;
this[11] = m11; this[11] = m11;
} }
function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13) {
function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13?: any) {
this[0] = m0; this[0] = m0;
this[1] = m1; this[1] = m1;
this[2] = m2; this[2] = m2;
@ -82,7 +57,6 @@ function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m
this[12] = m12; this[12] = m12;
this[13] = m13; this[13] = m13;
} }
function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18) { function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18) {
this[0] = m0; this[0] = m0;
this[1] = m1; this[1] = m1;
@ -104,12 +78,7 @@ function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12,
this[17] = m17; this[17] = m17;
this[18] = m18; this[18] = m18;
} }
var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"), gMonth = new gregmontharr("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"), hMonth = new hebrewmontharr("Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul"), mpy = new monthsperyeararr(12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13);
var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"),
gMonth = new gregmontharr("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"),
hMonth = new hebrewmontharr("Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul"),
mpy = new monthsperyeararr(12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13);
/** /**
* hebrewDate * hebrewDate
* Convert the Gregorian dates into Hebrew calendar dates. * Convert the Gregorian dates into Hebrew calendar dates.
@ -124,55 +93,36 @@ var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "S
* - `month_name`: The Hebrew month name. * - `month_name`: The Hebrew month name.
* - `date`: The Hebrew date. * - `date`: The Hebrew date.
*/ */
export const hebrewDate = function (inputDateOrYear: Date) { function hebrewDate(inputDateOrYear) {
var inputMonth, inputDate; var inputMonth, inputDate;
var hebrewMonth = 0, hebrewDate = 0, hebrewYear = 0, metonicCycle = 0, metonicYear = 0, moladDay = 0, moladHalakim = 0;
var hebrewMonth = 0,
hebrewDate = 0,
hebrewYear = 0,
metonicCycle = 0,
metonicYear = 0,
moladDay = 0,
moladHalakim = 0;
function GregorianToSdn(inputYear, inputMonth, inputDay) { function GregorianToSdn(inputYear, inputMonth, inputDay) {
var year = 0, month = 0, sdn = void 0;
var year = 0,
month = 0,
sdn = void 0;
// Make year a positive number // Make year a positive number
if (inputYear < 0) { if (inputYear < 0) {
year = inputYear + 4801; year = inputYear + 4801;
} else { }
else {
year = inputYear + 4800; year = inputYear + 4800;
} }
// Adjust the start of the year // Adjust the start of the year
if (inputMonth > 2) { if (inputMonth > 2) {
month = inputMonth - 3; month = inputMonth - 3;
} else { }
else {
month = inputMonth + 9; month = inputMonth + 9;
year--; year--;
} }
sdn = Math.floor(Math.floor(year / 100) * DAYS_PER_400_YEARS / 4); sdn = Math.floor(Math.floor(year / 100) * DAYS_PER_400_YEARS / 4);
sdn += Math.floor(year % 100 * DAYS_PER_4_YEARS / 4); sdn += Math.floor(year % 100 * DAYS_PER_4_YEARS / 4);
sdn += Math.floor((month * DAYS_PER_5_MONTHS + 2) / 5); sdn += Math.floor((month * DAYS_PER_5_MONTHS + 2) / 5);
sdn += inputDay - GREG_SDN_OFFSET; sdn += inputDay - GREG_SDN_OFFSET;
return sdn; return sdn;
} }
function SdnToHebrew(sdn) { function SdnToHebrew(sdn) {
var tishri1 = 0, var tishri1 = 0, tishri1After = 0, yearLength = 0, inputDay = sdn - HEB_SDN_OFFSET;
tishri1After = 0,
yearLength = 0,
inputDay = sdn - HEB_SDN_OFFSET;
FindTishriMolad(inputDay); FindTishriMolad(inputDay);
tishri1 = Tishri1(metonicYear, moladDay, moladHalakim); tishri1 = Tishri1(metonicYear, moladDay, moladHalakim);
if (inputDay >= tishri1) { if (inputDay >= tishri1) {
// It found Tishri 1 at the start of the year. // It found Tishri 1 at the start of the year.
hebrewYear = metonicCycle * 19 + metonicYear + 1; hebrewYear = metonicCycle * 19 + metonicYear + 1;
@ -180,7 +130,8 @@ export const hebrewDate = function (inputDateOrYear: Date) {
if (inputDay < tishri1 + 30) { if (inputDay < tishri1 + 30) {
hebrewMonth = 1; hebrewMonth = 1;
hebrewDate = inputDay - tishri1 + 1; hebrewDate = inputDay - tishri1 + 1;
} else { }
else {
hebrewMonth = 2; hebrewMonth = 2;
hebrewDate = inputDay - tishri1 - 29; hebrewDate = inputDay - tishri1 - 29;
} }
@ -191,7 +142,8 @@ export const hebrewDate = function (inputDateOrYear: Date) {
moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY);
moladHalakim = moladHalakim % HALAKIM_PER_DAY; moladHalakim = moladHalakim % HALAKIM_PER_DAY;
tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim); tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim);
} else { }
else {
// It found Tishri 1 at the end of the year. // It found Tishri 1 at the end of the year.
hebrewYear = metonicCycle * 19 + metonicYear; hebrewYear = metonicCycle * 19 + metonicYear;
if (inputDay >= tishri1 - 177) { if (inputDay >= tishri1 - 177) {
@ -199,44 +151,56 @@ export const hebrewDate = function (inputDateOrYear: Date) {
if (inputDay > tishri1 - 30) { if (inputDay > tishri1 - 30) {
hebrewMonth = 13; hebrewMonth = 13;
hebrewDate = inputDay - tishri1 + 30; hebrewDate = inputDay - tishri1 + 30;
} else if (inputDay > tishri1 - 60) { }
else if (inputDay > tishri1 - 60) {
hebrewMonth = 12; hebrewMonth = 12;
hebrewDate = inputDay - tishri1 + 60; hebrewDate = inputDay - tishri1 + 60;
} else if (inputDay > tishri1 - 89) { }
else if (inputDay > tishri1 - 89) {
hebrewMonth = 11; hebrewMonth = 11;
hebrewDate = inputDay - tishri1 + 89; hebrewDate = inputDay - tishri1 + 89;
} else if (inputDay > tishri1 - 119) { }
else if (inputDay > tishri1 - 119) {
hebrewMonth = 10; hebrewMonth = 10;
hebrewDate = inputDay - tishri1 + 119; hebrewDate = inputDay - tishri1 + 119;
} else if (inputDay > tishri1 - 148) { }
else if (inputDay > tishri1 - 148) {
hebrewMonth = 9; hebrewMonth = 9;
hebrewDate = inputDay - tishri1 + 148; hebrewDate = inputDay - tishri1 + 148;
} else { }
else {
hebrewMonth = 8; hebrewMonth = 8;
hebrewDate = inputDay - tishri1 + 178; hebrewDate = inputDay - tishri1 + 178;
} }
return; return;
} else { }
else {
if (mpy[(hebrewYear - 1) % 19] == 13) { if (mpy[(hebrewYear - 1) % 19] == 13) {
hebrewMonth = 7; hebrewMonth = 7;
hebrewDate = inputDay - tishri1 + 207; hebrewDate = inputDay - tishri1 + 207;
if (hebrewDate > 0) return; if (hebrewDate > 0)
return;
hebrewMonth--; hebrewMonth--;
hebrewDate += 30; hebrewDate += 30;
if (hebrewDate > 0) return; if (hebrewDate > 0)
hebrewMonth--; return;
hebrewDate += 30;
} else {
hebrewMonth = 6;
hebrewDate = inputDay - tishri1 + 207;
if (hebrewDate > 0) return;
hebrewMonth--; hebrewMonth--;
hebrewDate += 30; hebrewDate += 30;
} }
if (hebrewDate > 0) return; else {
hebrewMonth = 6;
hebrewDate = inputDay - tishri1 + 207;
if (hebrewDate > 0)
return;
hebrewMonth--;
hebrewDate += 30;
}
if (hebrewDate > 0)
return;
hebrewMonth--; hebrewMonth--;
hebrewDate += 29; hebrewDate += 29;
if (hebrewDate > 0) return; if (hebrewDate > 0)
return;
// We need the length of the year to figure this out,so find Tishri 1 of this year. // We need the length of the year to figure this out,so find Tishri 1 of this year.
tishri1After = tishri1; tishri1After = tishri1;
FindTishriMolad(moladDay - 365); FindTishriMolad(moladDay - 365);
@ -253,7 +217,8 @@ export const hebrewDate = function (inputDateOrYear: Date) {
return; return;
} }
moladDay -= 30; moladDay -= 30;
} else { }
else {
// Heshvan has 29 days // Heshvan has 29 days
if (moladDay <= 29) { if (moladDay <= 29) {
hebrewMonth = 2; hebrewMonth = 2;
@ -266,7 +231,6 @@ export const hebrewDate = function (inputDateOrYear: Date) {
hebrewMonth = 3; hebrewMonth = 3;
hebrewDate = moladDay; hebrewDate = moladDay;
} }
function FindTishriMolad(inputDay) { function FindTishriMolad(inputDay) {
// Estimate the metonic cycle number. Note that this may be an under // Estimate the metonic cycle number. Note that this may be an under
// estimate because there are 6939.6896 days in a metonic cycle not // estimate because there are 6939.6896 days in a metonic cycle not
@ -287,18 +251,15 @@ export const hebrewDate = function (inputDateOrYear: Date) {
} }
// Find the molad of Tishri closest to this date. // Find the molad of Tishri closest to this date.
for (metonicYear = 0; metonicYear < 18; metonicYear++) { for (metonicYear = 0; metonicYear < 18; metonicYear++) {
if (moladDay > inputDay - 74) break; if (moladDay > inputDay - 74)
break;
moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear]; moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear];
moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY);
moladHalakim = moladHalakim % HALAKIM_PER_DAY; moladHalakim = moladHalakim % HALAKIM_PER_DAY;
} }
} }
function MoladOfMetonicCycle() { function MoladOfMetonicCycle() {
var r1 = void 0, var r1 = void 0, r2 = void 0, d1 = void 0, d2 = void 0;
r2 = void 0,
d1 = void 0,
d2 = void 0;
// Start with the time of the first molad after creation. // Start with the time of the first molad after creation.
r1 = NEW_MOON_OF_CREATION; r1 = NEW_MOON_OF_CREATION;
// Calculate gMetonicCycle * HALAKIM_PER_METONIC_CYCLE. The upper 32 // Calculate gMetonicCycle * HALAKIM_PER_METONIC_CYCLE. The upper 32
@ -317,42 +278,34 @@ export const hebrewDate = function (inputDateOrYear: Date) {
moladDay = d2 << 16 | d1; moladDay = d2 << 16 | d1;
moladHalakim = r1; moladHalakim = r1;
} }
function Tishri1(metonicYear, moladDay, moladHalakim) { function Tishri1(metonicYear, moladDay, moladHalakim) {
var tishri1 = moladDay, var tishri1 = moladDay, dow = tishri1 % 7, leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7 || metonicYear == 10 || metonicYear == 13 || metonicYear == 16 || metonicYear == 18, lastWasLeapYear = metonicYear == 3 || metonicYear == 6 || metonicYear == 8 || metonicYear == 11 || metonicYear == 14 || metonicYear == 17 || metonicYear == 0;
dow = tishri1 % 7,
leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7 || metonicYear == 10 || metonicYear == 13 || metonicYear == 16 || metonicYear == 18,
lastWasLeapYear = metonicYear == 3 || metonicYear == 6 || metonicYear == 8 || metonicYear == 11 || metonicYear == 14 || metonicYear == 17 || metonicYear == 0;
// Apply rules 2,3 and 4 // Apply rules 2,3 and 4
if (moladHalakim >= NOON || !leapYear && dow == TUES && moladHalakim >= AM3_11_20 || lastWasLeapYear && dow == MON && moladHalakim >= AM9_32_43) { if (moladHalakim >= NOON || !leapYear && dow == TUES && moladHalakim >= AM3_11_20 || lastWasLeapYear && dow == MON && moladHalakim >= AM9_32_43) {
tishri1++; tishri1++;
dow++; dow++;
if (dow == 7) dow = 0; if (dow == 7)
dow = 0;
} }
// Apply rule 1 after the others because it can cause an additional delay of one day. // Apply rule 1 after the others because it can cause an additional delay of one day.
if (dow == WED || dow == FRI || dow == SUN) { if (dow == WED || dow == FRI || dow == SUN) {
tishri1++; tishri1++;
} }
return tishri1; return tishri1;
} }
var inputYear = inputDateOrYear;
var inputYear: Date | number = inputDateOrYear;
if ((typeof inputYear === "undefined" ? "undefined" : _typeof(inputYear)) === "object") { if ((typeof inputYear === "undefined" ? "undefined" : _typeof(inputYear)) === "object") {
inputMonth = inputDateOrYear.getMonth() + 1; inputMonth = inputDateOrYear.getMonth() + 1;
inputDate = inputDateOrYear.getDate(); inputDate = inputDateOrYear.getDate();
inputYear = inputDateOrYear.getFullYear(); inputYear = inputDateOrYear.getFullYear();
} }
SdnToHebrew(GregorianToSdn(inputYear, inputMonth, inputDate)); SdnToHebrew(GregorianToSdn(inputYear, inputMonth, inputDate));
return { return {
year: hebrewYear, year: hebrewYear,
month: hebrewMonth, month: hebrewMonth,
date: hebrewDate, date: hebrewDate,
month_name: hMonth[hebrewMonth - 1] month_name: hMonth[hebrewMonth - 1]
}; };
}; }
exports.hebrewDate = hebrewDate;

View File

@ -1,23 +0,0 @@
{
"name": "hebrew_calendar",
"version": "0.0.3",
"description": "Bangle.js app for seeing hebrew calendar",
"main": "app.js",
"types": "app.d.ts",
"scripts": {
"build": "rollup -c"
},
"author": {
"name": "Michael Salaverry",
"url": "https://github.com/barakplasma"
},
"license": "MIT",
"devDependencies": {
"@rollup/plugin-typescript": "^4.1.1",
"rollup": "^2.10.2",
"rollup-plugin-terser": "^5.3.0",
"terser": "^4.7.0",
"tslib": "^2.0.0",
"typescript": "^3.9.2"
}
}

View File

@ -1,15 +0,0 @@
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
export default {
input: './src/app.ts',
output: {
dir: '.',
format: 'iife',
name: 'hebrew_calendar'
},
plugins: [
typescript(),
terser(),
]
};

View File

@ -1,34 +0,0 @@
declare var Bangle: any;
declare var g: any;
declare var E: any;
declare var require: any;
g.clear();
let now = new Date();
import { hebrewDate } from "./hebrewDate";
let today = hebrewDate(now);
var mainmenu = {
"" : {
"title" : "Hebrew Date"
},
cal: {
value: require('locale').date(now,1),
onchange : () => {}
},
date: {
value : today.date,
onchange : () => {}
},
month: {
value : today.month_name,
onchange : () => {}
},
year: {
value : today.year,
onchange : () => {}
}
};
E.showMenu(mainmenu);

View File

@ -1,10 +0,0 @@
{
"compilerOptions": {
"module": "es2015",
"noImplicitAny": false,
"target": "es2015"
},
"include": [
"src"
]
}

View File

@ -1,3 +1,4 @@
0.01: New App! 0.01: New App!
0.02: Stopped watchface from flashing every interval 0.02: Stopped watchface from flashing every interval
0.03: Move to Bangle.setUI to launcher support 0.03: Move to Bangle.setUI to launcher support
0.04: Tweaks for compatibility with BangleJS2

View File

@ -1,4 +1,4 @@
# Imprecise Word Clock # Imprecise Word Clock
This clock tells time in very rough approximation, as in "Late morning" or "Early afternoon." Good for vacations and weekends. Press button 1 to see the time in accurate, digital form. But do you really need to know the exact time? This clock tells time in very rough approximation, as in "Late morning" or "Early afternoon." Good for vacations and weekends. Touch the screen to see the time in accurate, digital form. But do you really need to know the exact time?

View File

@ -2,7 +2,7 @@
A remix of word clock A remix of word clock
by Gordon Williams https://github.com/gfwilliams by Gordon Williams https://github.com/gfwilliams
- Changes the representation of time to be more general - Changes the representation of time to be more general
- Shows accurate digital time when button 1 is pressed - Toggles showing of accurate digital time when screen touched.
*/ */
/* jshint esversion: 6 */ /* jshint esversion: 6 */
@ -34,14 +34,16 @@ const timeOfDay = {
}; };
var big = g.getWidth()>200;
// offsets and increments // offsets and increments
const xs = 35; const xs = big ? 35 : 20;
const ys = 31; const ys = big ? 31 : 28;
const dy = 22; const dx = big ? 25 : 20;
const dx = 25; const dy = big ? 22 : 16;
// font size and color // font size and color
const fontSize = 3; // "6x8" const fontSize = big ? 3 : 2; // "6x8"
const passivColor = 0x3186 /*grey*/ ; const passivColor = 0x3186 /*grey*/ ;
const activeColorNight = 0xF800 /*red*/ ; const activeColorNight = 0xF800 /*red*/ ;
const activeColorDay = 0xFFFF /* white */; const activeColorDay = 0xFFFF /* white */;
@ -115,6 +117,8 @@ function drawWordClock() {
// check whether we need to redraw the watchface // check whether we need to redraw the watchface
if (hidx !== hidxPrev) { if (hidx !== hidxPrev) {
// Turn off showDigitalTime
showDigitalTime = false;
// draw allWords // draw allWords
var c; var c;
var y = ys; var y = ys;
@ -138,15 +142,14 @@ function drawWordClock() {
hidxPrev = hidx; hidxPrev = hidx;
} }
// Display digital time while button 1 is pressed // Display digital time when button is pressed or screen touched
g.clearRect(0, 215, 240, 240); g.clearRect(0, big ? 215 : 160, big ? 240 : 176, big ? 240 : 176);
if (showDigitalTime){ if (showDigitalTime){
g.setColor(activeColor); g.setColor(activeColor);
g.drawString(time, 120, 215); g.drawString(time, big ? 120 : 90, big ? 215 : 160);
} }
} }
Bangle.on('lcdPower', function(on) { Bangle.on('lcdPower', function(on) {
if (on) drawWordClock(); if (on) drawWordClock();
}); });
@ -157,17 +160,14 @@ Bangle.drawWidgets();
setInterval(drawWordClock, 1E4); setInterval(drawWordClock, 1E4);
drawWordClock(); drawWordClock();
// Show digital time while top button is pressed (if we have physical buttons)
if (global.BTN3) setWatch(function() {
showDigitalTime = BTN1.read();
drawWordClock();
}, BTN1, {repeat:true,edge:"both"});
// If LCD pressed (on Bangle.js 2) draw digital time // If LCD pressed, toggle drawing digital time
Bangle.on('drag',e=>{ Bangle.on('touch',e=>{
var pressed = e.b!=0; if (showDigitalTime){
if (pressed!=showDigitalTime) { showDigitalTime = false;
showDigitalTime = pressed; drawWordClock();
} else {
showDigitalTime = true;
drawWordClock(); drawWordClock();
} }
}); });

View File

@ -5,3 +5,6 @@
0.05: Added more bundleId's (app-id's which can be used to 0.05: Added more bundleId's (app-id's which can be used to
determine a friendly app name in the notifications) determine a friendly app name in the notifications)
0.06: Fix (not) popupping up old messages 0.06: Fix (not) popupping up old messages
0.07: Added more details from music (instead of Undefined)
Added more app identifiers

31
apps/ios/README.md Normal file
View File

@ -0,0 +1,31 @@
# iOS integration app
This is the iOS integration app for Bangle.js. This app allows you to receive
notifications from your iPhone. The Apple Notification Center Service (ANCS)
sends all the messages to your watch.
You can allow this if you connect your Bangle to your iPhone. It will be
prompted for immediatly after you connect the Bangle to the iPhone.
### Connecting your Bangle(2).js to your iPhone
The Bangle watches are Bluetooth Low Energy (BLE) devices. Sometimes they
will not be seen/detected by the Bluetooth scanner in your iPhone settings
menu.
To resolve this, you can download numerous apps who can actually scan
for BLE devices. There are great ones out there, free and paid.
We really like WebBLE, which we also recommend to load apps on your
watch with your iOS device, as Safari does not support WebBluetooth
for now. It's just a few bucks/pounds/euro's.
If you like to try a free app first, you can always use NRF Toolbox or
Bluetooth BLE Device Finder to find and connect your Bangle.
## Requests
Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=ios%20app
## Creator
Gordon Williams

View File

@ -65,14 +65,16 @@ E.on('notify',msg=>{
"com.apple.facetime": "FaceTime", "com.apple.facetime": "FaceTime",
"com.apple.mobilecal": "Calendar", "com.apple.mobilecal": "Calendar",
"com.apple.mobilemail": "Mail", "com.apple.mobilemail": "Mail",
"com.apple.mobilephone": "Phone",
"com.apple.MobileSMS": "SMS Message", "com.apple.MobileSMS": "SMS Message",
"com.apple.Passbook": "iOS Wallet", "com.apple.Passbook": "iOS Wallet",
"com.apple.podcasts": "Podcasts",
"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": "Messenger",
"com.google.Chromecast" : "Google Home", "com.google.Chromecast" : "Google Home",
"com.google.Gmail" : "GMail", "com.google.Gmail" : "GMail",
"com.google.hangouts" : "Hangouts", "com.google.hangouts" : "Hangouts",
@ -81,22 +83,26 @@ E.on('notify',msg=>{
"com.ifttt.ifttt" : "IFTTT", "com.ifttt.ifttt" : "IFTTT",
"com.jumbo.app" : "Jumbo", "com.jumbo.app" : "Jumbo",
"com.linkedin.LinkedIn" : "LinkedIn", "com.linkedin.LinkedIn" : "LinkedIn",
"com.microsoft.Office.Outlook" : "Outlook Mail",
"com.nestlabs.jasper.release" : "Nest", "com.nestlabs.jasper.release" : "Nest",
"com.netflix.Netflix" : "Netflix", "com.netflix.Netflix" : "Netflix",
"com.reddit.Reddit" : "Reddit", "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",
"com.strava.stravaride": "Strava",
"com.tinyspeck.chatlyio": "Slack", "com.tinyspeck.chatlyio": "Slack",
"com.toyopagroup.picaboo": "Snapchat", "com.toyopagroup.picaboo": "Snapchat",
"com.ubercab.UberClient": "Uber", "com.ubercab.UberClient": "Uber",
"com.ubercab.UberEats": "UberEats", "com.ubercab.UberEats": "UberEats",
"com.vilcsak.bitcoin2": "Coinbase",
"com.wordfeud.free": "WordFeud", "com.wordfeud.free": "WordFeud",
"com.zhiliaoapp.musically": "TikTok", "com.zhiliaoapp.musically": "TikTok",
"net.whatsapp.WhatsApp": "WhatsApp", "net.whatsapp.WhatsApp": "WhatsApp",
"nl.ah.Appie": "Albert Heijn", "nl.ah.Appie": "Albert Heijn",
"nl.postnl.TrackNTrace": "PostNL", "nl.postnl.TrackNTrace": "PostNL",
"ph.telegra.Telegraph": "Telegram", "ph.telegra.Telegraph": "Telegram",
"tv.twitch": "Twitch",
// could also use NRF.ancsGetAppInfo(msg.appId) here // could also use NRF.ancsGetAppInfo(msg.appId) here
}; };
@ -104,7 +110,7 @@ E.on('notify',msg=>{
'2019':"'" '2019':"'"
}; };
var replacer = ""; //(n)=>print('Unknown unicode '+n.toString(16)); var replacer = ""; //(n)=>print('Unknown unicode '+n.toString(16));
if (appNames[msg.appId]) msg.a //if (appNames[msg.appId]) msg.a
require("messages").pushMessage({ require("messages").pushMessage({
t : msg.event, t : msg.event,
id : msg.uid, id : msg.uid,
@ -122,9 +128,10 @@ E.on('AMS',a=>{
function push(m) { function push(m) {
var msg = { t : "modify", id : "music", title:"Music" }; var msg = { t : "modify", id : "music", title:"Music" };
if (a.id=="artist") msg.artist = m; if (a.id=="artist") msg.artist = m;
else if (a.id=="album") msg.artist = m; else if (a.id=="album") msg.album = m;
else if (a.id=="title") msg.tracl = m; else if (a.id=="title") msg.track = m;
else return; // duration? need to reformat else if (a.id=="duration") msg.dur = m;
else return;
require("messages").pushMessage(msg); require("messages").pushMessage(msg);
} }
if (a.truncated) NRF.amsGetMusicInfo(a.id).then(push) if (a.truncated) NRF.amsGetMusicInfo(a.id).then(push)

View File

@ -0,0 +1 @@
0.01: first release

19
apps/lapcounter/README.md Normal file
View File

@ -0,0 +1,19 @@
# Lap Counter
Click button to count laps (e.g. in a swimming pool).
Also shows total duration snapshot (like a stopwatch, but laid back).
![Screenshot](screenshot.png)
## Usage
* Click BTN1 to start counting. Counter becomes `0`, duration becomes `00:00.0`
* Each time you click BTN1, counter is incremented, and you see duration between first and last clicks.
## Features
Disables LCD timeout (so that you can be _sure_ what BTN1 would do).
## Creator
[Nimrod Kerrett](https://zzzen.com)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AAkQgEBAREAC6oABdZQXkI6wuKC5iPUFxoXIOpoX/C6QFCC6IsCC6ZEDC/4XcPooXOFgoXQIgwX/C7IUFC5wsIC5ouCC6hcJC5h1DF9YwBChCPOAH4A/AH4Ap"))

53
apps/lapcounter/app.js Normal file
View File

@ -0,0 +1,53 @@
const w = g.getWidth();
const h = g.getHeight();
const wid_h = 24;
let tStart;
let tNow;
let counter=-1;
const icon = require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AAkQgEBAREAC6oABdZQXkI6wuKC5iPUFxoXIOpoX/C6QFCC6IsCC6ZEDC/4XcPooXOFgoXQIgwX/C7IUFC5wsIC5ouCC6hcJC5h1DF9YwBChCPOAH4A/AH4Ap"));
function timeToText(t) { // Courtesy of stopwatch app
let hrs = Math.floor(t/3600000);
let mins = Math.floor(t/60000)%60;
let secs = Math.floor(t/1000)%60;
let tnth = Math.floor(t/100)%10;
let text;
if (hrs === 0)
text = ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2) + "." + tnth;
else
text = ("0"+hrs) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
//log_debug(text);
return text;
}
function doCounter() {
if (counter<0) {
tStart = Date.now();
tNow = tStart;
} else {
tNow = Date.now();
}
counter++;
let dT = tNow-tStart;
g.clearRect(0,wid_h,w,h-wid_h);
g.setFontAlign(0,0);
g.setFont("Vector",72);
g.drawString(counter,w/2,h/2);
g.setFont("Vector",24);
g.drawString(timeToText(dT),w/2,h/2+50);
}
setWatch(doCounter, BTN1, true);
g.clear(true);
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setLCDTimeout(0);
g.drawImage(icon,w/2-24,h/2-24);
g.setFontAlign(0,0);
require("Font8x12").add(Graphics);
g.setFont("8x12");
g.drawString("Click button to count.", w/2, h/2+22);

BIN
apps/lapcounter/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -3,4 +3,5 @@
0.03: New design with different icons if gps, hrm or compass is on. 0.03: New design with different icons if gps, hrm or compass is on.
0.04: Inluded LCARS Logo. 0.04: Inluded LCARS Logo.
0.05: Additional icons for (1) charging and (2) bat < 30%. 0.05: Additional icons for (1) charging and (2) bat < 30%.
0.06: Fix - Alarm disabled, if clock was closed 0.06: Fix - Alarm disabled, if clock was closed.
0.07: Added settings to adjust data that is shown for each row.

View File

@ -5,10 +5,9 @@ Note: To display the steps, its necessary to install
the [Pedometer widget](https://banglejs.com/apps/#pedometer%20widget). the [Pedometer widget](https://banglejs.com/apps/#pedometer%20widget).
## Features ## Features
* Shows the time * LCARS Style watch face
* Shows the date * Shows satate (charging, out of battery etc.)
* Shows the current battery level in % * SHows data that can be configured (steps, HRM, temperature etc.)
* Shows the number of daily steps
* Swipe left/right to activate an alarm * Swipe left/right to activate an alarm
## Icons ## Icons

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -1,8 +1,21 @@
const filename = "lcars.setting.json"; const SETTINGS_FILE = "lcars.setting.json";
const Storage = require("Storage"); const Storage = require("Storage");
let settings = Storage.readJSON(filename,1) || {
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
const storage = require('Storage')
let settings = {
alarm: -1, alarm: -1,
dataRow1: "Battery",
dataRow2: "Steps",
dataRow3: "Temp."
}; };
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
for (const key in saved_settings) {
settings[key] = saved_settings[key]
}
let hrmValue = 0;
/* /*
* Requirements and globals * Requirements and globals
@ -12,7 +25,7 @@ const locale = require('locale');
var backgroundImage = { var backgroundImage = {
width : 176, height : 151, bpp : 3, width : 176, height : 151, bpp : 3,
transparent : 2, transparent : 2,
buffer : require("heatshrink").decompress(atob("AAUEufPnnzATkAg4daIIXnz15ATvkwEDDrUAgPHQDyDghyAeQcNzJQ0cuPHATCDBDrUDJQ1AgAA3jjOF+BA4T4KDFyBB5Qf4ABQAaD9QAaD/QesH8CD/n/8Qf8//+AQfsB///GQ6D2h5BJQf6D7/yD8jl/IIIABjiD5n4/DAAWAQe8B//8QYfH//x4CD2HwMDQIf4AoP4Qesf/56BQYYFBuP/Qev//0AQYoKBn/gQecH/lwQwQADBYaDzGoZBHR4OAQehBKj5BBsuWrICDBAIAofYZBFBAZ6qIJJ6DQZBB3IAiDDgZBygJ6EIIn8IOqDKIIscuPHAQdwINkHIJEfIIPnz15AQeAINT+CHwcPAYI1BIIU8+fPAQbOqg56BQYcAgKD4IIv4RgSDCAQSD34AIC//wBYSDyO4P+IIoIB+E/8AFBQeL7B//HHYJKE+P/AoSDygF/QQJBF//4AoSDygEBQYgFBj/xZYaDzgE/PoIAE/wMDQeZBB/jICAAMcuAMDQevgQwR0CvyD3gP/BAxBEQek4A40OQe4ANQegAMQf6D/AAccQf8Ak6DFyCD/QfcDQYueIPMAuaDE+fBIPMOQYoCb8glB7dt2wCW2EAgKDFATkAg2atOmAS5eBhKDigyDZ2zHCjiD/AAMChEgwQCcQb4AiQb5BiQbscuPHATyDfyfPnnzATnwQbsBQD6DghKAeQcJoHiFBggCYQYVhdwQATgOmgVPNAnOECwAGQYIZXgM2dI1wIL2aoCDYibsF4CD/QcGYILGmyaDFwCD/QfaADQf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D4jCD/ADKDnILSD/Qf6DEHO6DJIP6D/Qf6D/QY8cuPHAQdAQfPz588AQeAQf8cuCD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6DqoCD5HO6DJIP6D/Qf6D/QY8cuPHAQdwE7sGzCDZ+fPngCDwBBe7aD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/QfcTQYvAQf6DgzVAQbECp6DE5yD5gCDFATqDCsOAIKtB00AhKDEATnwQYVt2wCXQwKDltOmAS6IC2aD82BBCQccaQbGAA==")) buffer : require("heatshrink").decompress(atob("AAdx48cATsAg4daIAX3799ATv2wEFDrUAgNHQDyDghaAeQcJKG86D4gRKGgAA4jxKFuBB5iaDF6BB5ZwyD6QAYCC4CD/Qf6Dzg/gQf8H/iD/n//wCD9gP///wQfpBKQf6D4h5BB/yD8jl/IIIABjiD5n4/DAAWAQe8B//8QYfHj//PAaDzHwICCAAP4gYCBQep6DIIYFBRgKD1j/+gB9BQYYKBn/gQen/+BBFQAUH/iDzGoZBHJoOAQeRBDj5BHj6PB0WKlACDJQIAofYZBFBAZBBAGMHPQZB8QYZAEIIcDIOiDI/hB3QZBBFjlx44CDuBBpg4DCIJEfIIPnz15AQeAQeH8gIDBGoJBCnnz54CDZ1UHPQMHIIUAIIKD3II6MBQYQCCQeI1B+BBC/BKCBASGCQeK5B/xBC4BKEn/gAoKDyj//45BFj/xZYSDzgF/IAP+JQrLCQecAgKDBF4cHQYKJDQecAn6EBAAiJEQeZBB/jICAAMcvwMDQevgQwR0CIIiDzgP/BA1/4CD3nAHGhyD3ABqD0ABiD/Qf4ADjiD/gEnQYuQQf6D7gaDFzxB5gFzQYnz4BB5hyDFATfkEoIdagEBQYoCcgEHDrReBhKDhwEBQbYABjiD/AH4A/AH4AGiFx48cATsAg4daIIWSpMkATuQEbkAgJfbQckJQDyDhZxQA1gRKFpBA4gEQQYtwIPMSQYtAIPKADQfqADAQRA5Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf4A/AH4A/AH4A/AFkcuPHAQdAIPOSpMkAQaD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf4A/AH4A/AH4A/AGUcuPHAQdwIPOSpMkAQaD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf4AciSDFoCD/QfcCQYtIIPMAQYoC6gEJQYgC6gEBQf7HCQf4ABiiD9"))
} }
var iconEarth = { var iconEarth = {
@ -95,6 +108,33 @@ function queueDraw() {
} }
function printData(key, y){
g.setFontAlign(-1,-1,0);
if(key == "Battery"){
var bat = E.getBattery();
g.drawString("BAT:", 30, y);
g.drawString(bat+ "%", 68, y);
} else if(key == "Steps"){
var steps = getSteps();
g.drawString("STEP:", 30, y);
g.drawString(steps, 68, y);
} else if(key == "Temp."){
var temperature = Math.floor(E.getTemperature());
g.drawString("TEMP:", 30, y);
g.drawString(temperature + "C", 69, y);
} else if(key == "HRM"){
g.drawString("HRM:", 30, y);
g.drawString(hrmValue, 69, y);
} else {
g.drawString("NOT FOUND", 30, y);
}
}
function draw(){ function draw(){
// First handle alarm to show this correctly afterwards // First handle alarm to show this correctly afterwards
@ -125,7 +165,7 @@ function draw(){
// Alarm within symbol // Alarm within symbol
g.setFontAlign(0,0,0); g.setFontAlign(0,0,0);
g.setFontAntonioSmall(); g.setFontAntonioSmall();
g.drawString(iconImg.text, 115+25, 102); g.drawString(iconImg.text, 115+25, 105);
if(isAlarmEnabled() > 0){ if(isAlarmEnabled() > 0){
g.drawString(getAlarmMinutes(), 115+25, 115+25); g.drawString(getAlarmMinutes(), 115+25, 115+25);
} }
@ -147,18 +187,9 @@ function draw(){
g.drawString(dayName, 100, 55); g.drawString(dayName, 100, 55);
// Draw battery // Draw battery
g.drawString("BAT:", 25, 98); printData(settings.dataRow1, 98);
g.drawString(bat+ "%", 62, 98); printData(settings.dataRow2, 121);
printData(settings.dataRow3, 144);
// Draw steps
var steps = getSteps();
g.drawString("STEP:", 25, 121);
g.drawString(steps, 62, 121);
// Temperature
g.setFontAlign(-1,-1,0);
g.drawString("TEMP:", 25, 144);
g.drawString(Math.floor(E.getTemperature()) + "C", 62, 144);
// Queue draw in one minute // Queue draw in one minute
queueDraw(); queueDraw();
@ -182,6 +213,12 @@ function stepsWidget() {
return undefined; return undefined;
} }
/*
* HRM Listener
*/
Bangle.on('HRM', function (hrm) {
hrmValue = hrm.bpm;
});
/* /*
* Handle alarm * Handle alarm
@ -220,7 +257,7 @@ function handleAlarm(){
// Update alarm state to disabled // Update alarm state to disabled
settings.alarm = -1; settings.alarm = -1;
Storage.writeJSON(filename, settings); Storage.writeJSON(SETTINGS_FILE, settings);
} }
@ -250,7 +287,7 @@ Bangle.on('swipe',function(dir) {
draw(); draw();
// Update alarm state // Update alarm state
Storage.writeJSON(filename, settings); Storage.writeJSON(SETTINGS_FILE, settings);
}); });

View File

@ -0,0 +1,54 @@
(function(back) {
const SETTINGS_FILE = "lcars.setting.json";
// initialize with default settings...
const storage = require('Storage')
let settings = {
alarm: -1,
dataRow1: "Battery",
dataRow2: "Steps",
dataRow3: "Temp."
};
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
for (const key in saved_settings) {
settings[key] = saved_settings[key]
}
function save() {
storage.write(SETTINGS_FILE, settings)
}
var data_options = ['Battery', 'Steps', 'Temp.', "HRM"];
E.showMenu({
'': { 'title': 'LCARS Clock' },
'< Back': back,
'Row 1': {
value: 0 | data_options.indexOf(settings.dataRow1),
min: 0, max: 3,
format: v => data_options[v],
onchange: v => {
settings.dataRow1 = data_options[v];
save();
},
},
'Row 2': {
value: 0 | data_options.indexOf(settings.dataRow2),
min: 0, max: 3,
format: v => data_options[v],
onchange: v => {
settings.dataRow2 = data_options[v];
save();
},
},
'Row 3': {
value: 0 | data_options.indexOf(settings.dataRow3),
min: 0, max: 3,
format: v => data_options[v],
onchange: v => {
settings.dataRow3 = data_options[v];
save();
},
}
});
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -13,3 +13,4 @@
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.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 0.13: Now use shorter de_DE date format to more closely match other languages for size
0.14: Added some first translations for Messages in nl_NL

View File

@ -37,11 +37,30 @@ const codePages = {
/* When it's not in the codepage, try and use /* When it's not in the codepage, try and use
these conversions */ these conversions */
const charFallbacks = { const charFallbacks = {
"ą":"a",
"ā":"a",
"č":"c", "č":"c",
"ř":"r", "ć":"c",
"ě":"e",
"ę":"e",
"ē":"e",
"ģ":"g",
"i":"ī",
"ķ":"k",
"ļ":"l",
"ł":"l",
"ń":"n",
"ņ":"n",
"ő":"o", "ő":"o",
"ě":"e" "ó":"o",
}; "ř":"r",
"ś":"s",
"š":"s",
"ū":"u",
"ż":"z",
"ź":"z",
"ž":"z",
};
/* /*
timePattern / datePattern: timePattern / datePattern:
@ -135,7 +154,8 @@ var locales = {
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",
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus", "< Back": "< Zurück" } trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
"< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" }
}, },
"en_US": { "en_US": {
lang: "en_US", lang: "en_US",
@ -189,7 +209,8 @@ var locales = {
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", "Delete": "Verwijderen", "Mark Unread": "Markeer als ongelezen" }
}, },
"en_NL": { // English date units with Dutch number, currency and navigation units. "en_NL": { // English date units with Dutch number, currency and navigation units.
lang: "en_NL", lang: "en_NL",
@ -307,13 +328,15 @@ var locales = {
speed: "kmh", speed: "kmh",
distance: { 0: "m", 1: "km" }, distance: { 0: "m", 1: "km" },
temperature: "°C", temperature: "°C",
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.03.20 datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%y" }, // Sonntag, 1. März 2020 // 01.03.20
abmonth: "Jän,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", abmonth: "Jän,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez",
month: "Jänner,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember", month: "Jänner,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",
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus" } trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
"< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" }
}, },
"en_IL": { "en_IL": {
lang: "en_IL", lang: "en_IL",
@ -349,7 +372,8 @@ var locales = {
month: "enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre", month: "enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre",
abday: "dom,lun,mar,mié,jue,vie,sáb", abday: "dom,lun,mar,mié,jue,vie,sáb",
day: "domingo,lunes,martes,miércoles,jueves,viernes,sábado", day: "domingo,lunes,martes,miércoles,jueves,viernes,sábado",
trans: { yes: "sí", Yes: "Sí", no: "no", No: "No", ok: "ok", on: "on", off: "off" } trans: { yes: "sí", Yes: "Sí", no: "no", No: "No", ok: "ok", on: "on", off: "off",
"< Back": "< Atrás", "Delete": "Borrar ", "Mark Unread": "Marcar como no leído" }
}, },
"fr_BE": { "fr_BE": {
lang: "fr_BE", lang: "fr_BE",
@ -603,6 +627,42 @@ var locales = {
day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado", day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado",
trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "ok", on: "on", off: "off" } trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "ok", on: "on", off: "off" }
}, },
"pl_PL": {
lang: "pl_PL",
decimal_point: ",",
thousands_sep: " ",
currency_symbol: "zł",
int_curr_symbol: "PLN",
speed: "kmh",
distance: { 0: "m", 1: "km" },
temperature: "°C",
ampm: { 0: "", 1: "" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2021 // 01.03.2021
abmonth: "Sty,Lut,Mar,Kwi,Maj,Cze,Lip,Sie,Wrz,Paź,Lis,Gru",
month: "Styczeń,Luty,Marzec,Kwiecień,Maj,Czerwiec,Lipiec,Sierpień,Wrzesień,Październik,Listopad,Grudzień",
abday: "Ndz,Pon,Wt,Śr,Czw,Pt,Sob",
day: "Niedziela,Poniedziałek,Wtorek,Środa,Czwartek,Piątek,Sobota",
trans: { yes: "tak", Yes: "Tak", no: "nie", No: "Nie", ok: "ok", on: "on", off: "off", "< Back": "< Wstecz" }
},
"lv_LV": { // Using charfallbacks
lang: "lv_LV",
decimal_point: ",",
thousands_sep: " ",
currency_symbol: "€",
int_curr_symbol: "EUR",
speed: "kmh",
distance: { 0: "m", 1: "km" },
temperature: "°C",
ampm: { 0: "", 1: "" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20
abmonth: "Jan,Feb,Mar,Apr,Mai,Jūn,Jūl,Aug,Sep,Okt,Nov,Dec",
month: "Janvāris,Februāris,Marts,Aprīlis,Maijs,Jūnijs,Jūlijs,Augusts,Septemberis,Oktobris,Novembris,Decembris",
abday: "Pr,Ot,Tr,Ce,Pk,Se,Sv",
day: "Pirmdiena,Otrdiena,Trešdiena,Ceturtdiena,Piektdiena,Sestdiena,Svētdiena",
trans: { yes: "jā", Yes: "Jā", no: "nē", No: "Nē", ok: "labi", on: "Ieslēgt", off: "Izslēgt", "< Back": "< Atpakaļ" }
},
/*, /*,
"he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399 "he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399
codePage : "ISO8859-8", codePage : "ISO8859-8",

3
apps/ltherm/README.md Normal file
View File

@ -0,0 +1,3 @@
# Thermometer
Localized Bangle.js 2 thermometer app. It also starts maintaining an average of the temperature to help lower the margin of error after 10 consecutive readings; due to the low quality die-thermometer.

25
apps/ltherm/app.js Normal file
View File

@ -0,0 +1,25 @@
function drawTemperature() {
g.reset(1).clearRect(0,24,g.getWidth(),g.getHeight());
g.setFont("6x8",2).setFontAlign(0,0);
var x = g.getWidth()/2;
var y = g.getHeight()/2 + 10;
g.drawString("Temp", x, y - 45);
g.setFontVector(70).setFontAlign(0,0);
var h = E.getTemperature();
if (avg.length < 10) {
avg[avg.length] = h;
} else {
avg.shift();
avg[avg.length] = h;
h = ((avg[0] + avg[1] + avg[2] + avg[3] + avg[4] + avg[5] + avg[6] + avg[7] + avg[8] + avg[9]) / 10);
}
var t = require('locale').temp(h);
g.drawString(t, x, y);
}
const avg = [];
setInterval(function() {
drawTemperature();
}, 2000);
E.showMessage("Loading...");
Bangle.loadWidgets();
Bangle.drawWidgets();

1
apps/ltherm/icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwhC/AH4AChGIxGAC6eIAQgARFgUIC9ReCAYJgSC7BHDF6gUBC6ovWI/5Hga/6P/ABsCkABDC/4XxkQXDkQuSAQwXPDQkAC6BBCkQDDC6MCmczFoIXQCQQXBDgQXP2EA2YXBncAhYXR3YXB3YXRCQWznYcCC6ICBAYYXPhYrBApAwPFyQqCIoYuRLwZgDAH4A/"))

BIN
apps/ltherm/thermf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -14,3 +14,10 @@
Fix phone icon (#1014) Fix phone icon (#1014)
0.10: Respect the 'new' attribute if it was set from iOS integrations 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) 0.11: Open app when touching the widget (Bangle.js 2 only)
0.12: Extra app-specific notification icons
New animated notifcationicon (instead of large blinking 'MESSAGES')
Added screenshots
0.13: Add /*LANG*/ comments for internationalisation
Add 'Delete All' option to message options
Now update correctly when 'require("messages").clearAll()' is called
0.14: Hide widget when all unread notifications are dismissed from phone

Some files were not shown because too many files have changed in this diff Show More