diff --git a/apps.json b/apps.json index c1f51f272..8b4e86d52 100644 --- a/apps.json +++ b/apps.json @@ -16,7 +16,7 @@ { "id": "boot", "name": "Bootloader", - "version": "0.38", + "version": "0.39", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "icon": "bootloader.png", "type": "bootloader", @@ -77,7 +77,7 @@ { "id": "messages", "name": "Messages", - "version": "0.12", + "version": "0.13", "description": "App to display notifications from iOS and Gadgetbridge", "icon": "app.png", "type": "app", @@ -100,7 +100,7 @@ "name": "Android Integration", "shortName": "Android", "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", "tags": "tool,system,messages,notifications", "dependencies": {"messages":"app"}, @@ -167,7 +167,7 @@ { "id": "setting", "name": "Settings", - "version": "0.36", + "version": "0.37", "description": "A menu for setting up Bangle.js", "icon": "settings.png", "tags": "tool,system", @@ -304,7 +304,7 @@ "id": "gbridge", "name": "Gadgetbridge", "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", "type": "widget", "tags": "tool,system,android,widget", @@ -1590,7 +1590,7 @@ { "id": "widpedom", "name": "Pedometer widget", - "version": "0.19", + "version": "0.20", "description": "Daily pedometer widget", "icon": "widget.png", "type": "widget", @@ -1699,14 +1699,15 @@ "id": "rtorch", "name": "Red Torch", "shortName": "RedTorch", - "version": "0.01", - "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", + "version": "0.02", + "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", "tags": "tool,torch", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS","BANGLEJS2"], + "allow_emulator": true, "storage": [ {"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} ] }, @@ -3477,8 +3478,8 @@ { "id": "speedalt2", "name": "GPS Adventure Sports II", - "shortName": "GPS Adv Sport II", - "version": "0.07", + "shortName":"GPS Adv Sport II", + "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.", "icon": "app.png", "type": "app", @@ -4208,10 +4209,10 @@ "id": "pastel", "name": "Pastel Clock", "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", "icon": "pastel.png", - "dependencies": {"mylocation":"app"}, + "dependencies": {"mylocation":"app", "widpedom":"app"}, "screenshots": [{"url":"screenshot_pastel.png"}], "type": "clock", "tags": "clock", @@ -4649,7 +4650,7 @@ "id": "sensible", "name": "SensiBLE", "shortName": "SensiBLE", - "version": "0.03", + "version": "0.04", "description": "Collect, display and advertise real-time sensor data.", "icon": "sensible.png", "screenshots": [ @@ -4722,8 +4723,9 @@ "id": "pebble", "name": "Pebble Clock", "shortName": "Pebble", - "version": "0.04", + "version": "0.06", "description": "A pebble style clock to keep the rebellion going", + "dependencies": {"widpedom":"app"}, "readme": "README.md", "icon": "pebble.png", "screenshots": [{"url":"pebble_screenshot.png"}], @@ -4857,10 +4859,10 @@ "id": "ptlaunch", "name": "Pattern Launcher", "shortName": "Pattern Launcher", - "version": "0.10", + "version": "0.11", "description": "Directly launch apps from the clock screen with custom patterns.", "icon": "app.png", - "screenshots": [{"url":"main_menu_add.png"}, {"url":"add_pattern.png"}, {"url":"select_app.png"}, {"url":"main_menu_manage.png"}, {"url":"manage_patterns.png"}], + "screenshots": [{"url":"manage_patterns_light.png"}], "tags": "tools", "supports": ["BANGLEJS2"], "readme": "README.md", @@ -4875,11 +4877,11 @@ "id": "rebble", "name": "Rebble Clock", "shortName": "Rebble", - "version": "0.02", + "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"}, + "dependencies": {"mylocation":"app", "widpedom":"app"}, "screenshots": [{"url":"screenshot_rebble.png"}], "type": "clock", "tags": "clock", @@ -4917,6 +4919,7 @@ "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"} ], @@ -4940,7 +4943,7 @@ { "id": "pooqround", "name": "pooq Round watch face", "shortName":"pooq Round", - "version":"0.00", + "version":"0.01", "description": "A 24 hour analogue watchface with high legibility and a novel style.", "icon": "app.png", "type": "clock", @@ -4971,5 +4974,57 @@ {"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 \"showimage.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": "circlesclock", + "name": "Circles clock", + "shortName":"Circles clock", + "version":"0.01", + "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"} + ] + } ] diff --git a/apps/alarm/alarm.js b/apps/alarm/alarm.js index bb5722106..a655dad1e 100644 --- a/apps/alarm/alarm.js +++ b/apps/alarm/alarm.js @@ -21,8 +21,8 @@ function showAlarm(alarm) { Bangle.loadWidgets(); Bangle.drawWidgets(); E.showPrompt(msg,{ - title:alarm.timer ? "TIMER!" : "ALARM!", - buttons : {"Sleep":true,"Ok":false} // default is sleep so it'll come back in 10 mins + title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!", + buttons : {/*LANG*/"Sleep":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins }).then(function(sleep) { buzzCount = 0; if (sleep) { diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 53c7154bc..17062d44a 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -33,16 +33,16 @@ function getCurrentHr() { function showMainMenu() { const menu = { '': { 'title': 'Alarm/Timer' }, - '< Back' : ()=>{load();}, - 'New Alarm': ()=>editAlarm(-1), - 'New Timer': ()=>editTimer(-1) + /*LANG*/'< Back' : ()=>{load();}, + /*LANG*/'New Alarm': ()=>editAlarm(-1), + /*LANG*/'New Timer': ()=>editTimer(-1) }; alarms.forEach((alarm,idx)=>{ 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 { - txt = "ALARM "+(alarm.on?"on ":"off ")+formatTime(alarm.hr); - if (alarm.rp) txt += " (repeat)"; + txt = /*LANG*/"ALARM "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatTime(alarm.hr); + if (alarm.rp) txt += /*LANG*/" (repeat)"; } menu[txt] = function() { if (alarm.timer) editTimer(idx); @@ -70,27 +70,27 @@ function editAlarm(alarmIndex) { as = a.as; } const menu = { - '': { 'title': 'Alarm' }, - '< Back' : showMainMenu, - 'Hours': { + '': { 'title': /*LANG*/'Alarm' }, + /*LANG*/'< Back' : showMainMenu, + /*LANG*/'Hours': { value: hrs, onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this' }, - 'Minutes': { + /*LANG*/'Minutes': { value: mins, onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this' }, - 'Enabled': { + /*LANG*/'Enabled': { value: en, format: v=>v?"On":"Off", onchange: v=>en=v }, - 'Repeat': { + /*LANG*/'Repeat': { value: en, format: v=>v?"Yes":"No", onchange: v=>repeat=v }, - 'Auto snooze': { + /*LANG*/'Auto snooze': { value: as, format: v=>v?"Yes":"No", onchange: v=>as=v @@ -108,14 +108,14 @@ function editAlarm(alarmIndex) { last : day, rp : repeat, as: as }; } - menu["> Save"] = function() { + menu[/*LANG*/"> Save"] = function() { if (newAlarm) alarms.push(getAlarm()); else alarms[alarmIndex] = getAlarm(); require("Storage").write("alarm.json",JSON.stringify(alarms)); showMainMenu(); }; if (!newAlarm) { - menu["> Delete"] = function() { + menu[/*LANG*/"> Delete"] = function() { alarms.splice(alarmIndex,1); require("Storage").write("alarm.json",JSON.stringify(alarms)); showMainMenu(); @@ -136,18 +136,18 @@ function editTimer(alarmIndex) { en = a.on; } const menu = { - '': { 'title': 'Timer' }, - 'Hours': { + '': { 'title': /*LANG*/'Timer' }, + /*LANG*/'Hours': { value: hrs, onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this' }, - 'Minutes': { + /*LANG*/'Minutes': { value: mins, onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this' }, - 'Enabled': { + /*LANG*/'Enabled': { value: en, - format: v=>v?"On":"Off", + format: v=>v?/*LANG*/"On":/*LANG*/"Off", onchange: v=>en=v } }; diff --git a/apps/alarm/boot.js b/apps/alarm/boot.js index 47dae5361..dffb3a37f 100644 --- a/apps/alarm/boot.js +++ b/apps/alarm/boot.js @@ -7,7 +7,7 @@ active = active.sort((a,b)=>(a.hr-b.hr)+(a.last-b.last)*24); var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); if (!require('Storage').read("alarm.js")) { - console.log("No alarm app!"); + console.log(/*LANG*/"No alarm app!"); require('Storage').write('alarm.json',"[]"); } else { var t = 3600000*(active[0].hr-hr); diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index b941a9937..5c929421b 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -42,3 +42,4 @@ 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.38: Option to log to file if settings.log==2 +0.39: Fix passkey support (fix https://github.com/espruino/Espruino/issues/2035) diff --git a/apps/boot/bootupdate.js b/apps/boot/bootupdate.js index 3001bb5c1..e338d9020 100644 --- a/apps/boot/bootupdate.js +++ b/apps/boot/bootupdate.js @@ -88,7 +88,7 @@ if (global.save) boot += `global.save = function() { throw new Error("You can't // Apply any settings-specific stuff 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.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`; // Pre-2v10 firmwares without a theme/setUI delete g.theme; // deleting stops us getting confused by our own decl. builtins can't be deleted diff --git a/apps/circlesclock/Changelog b/apps/circlesclock/Changelog new file mode 100644 index 000000000..af119ab59 --- /dev/null +++ b/apps/circlesclock/Changelog @@ -0,0 +1 @@ +0.01: New clock diff --git a/apps/circlesclock/README.md b/apps/circlesclock/README.md new file mode 100644 index 000000000..87edd5981 --- /dev/null +++ b/apps/circlesclock/README.md @@ -0,0 +1,19 @@ +# 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 + +## Screenshot + +![Screenshot](screenshot.png) + +## TODO +* Show weather information + + +## Creator +Marco ([myxor](https://github.com/myxor)) diff --git a/apps/circlesclock/app-icon.js b/apps/circlesclock/app-icon.js new file mode 100644 index 000000000..ad727251a --- /dev/null +++ b/apps/circlesclock/app-icon.js @@ -0,0 +1 @@ + require("heatshrink").decompress(atob("2GwwcCIf4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AF0D/4AHwAVKh4OHgIIB+BB9v4YC4BBzHAQOEj4ZEIOQUDBwcHDIv8IOJ6DBwc/IP5BHcBgAXgImMGowUC/wFBh5BlEwKqKfwhBF+AFHIOp9GZYJBjv5BLfwhBECghQBZYRBi8ALIWwXxIPq8CwJBwgYxBBhI4CQwRB0j///CPFIIwFFgE///wIMI7BIJJNC8BBIHYQFFIMI7DIJB9JX4TLBBYhBqAoZBGg4GBAAf8IEMAEoPAIJALBIPw1CBYJBGC4QAD8BAhGogLIfYRByGoQAGn//+BBIYtJBKHYRBJJoIAFR4gAcO4hBIAAzXCC4JZCh5B6R5AdIAC4jLIJZ9GRIhBgU5BBN/gSDg5B/IMYpGIP6VSC40/IMN/IKwFI+BBh8BBXHYSJBINMf//4IJi/CAAoLDADcDEQIIFIP5BSg5AF/jEfHAJB/HBBBQLgYACID5BbgF/IAXAIMAjIIKQIC+BAgAH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AOj///4ROgf+AgU//gMFh4dD//wBA+AIKosGCJBBCF4I1DJoQdDn4EB4AIEg5BXC5omBIK8BFJxBHwBZOg/8vwEBv4yBZYYdBI4P/wK/Bh/4BAosBIKgmDIJcAIIQCCAA44B/BBCBAnAILUDIgUBEwYADIIc/XgJBQFIRBWHwTpCXIP/8BBIBYP/TAzUBLIRBDBAIsEILIjBGoJ3GIJiMBIIyVDILJoDgf+gBBK4AOCAAcBTAJBFBARBZj5BBOQP/RIQAGIIQCBII1/HYRBEBARB0gf/4BBFBAZBZeQMHUIRBC/4gFIJYFCIIoOEIK0/HAMH/gsDAoZBGv/ATAIdEAoUB/4OJIKi/BHAQEBUgN/BAYABaIfgh4DBGQoMCMQQdBBAeBAYSPBIKbCCj6kCGoIQEIIh3BaIpBECIIdBILQA/AH4A/AH4A/AH4A/ABsf/4AB/0A/gXQgYUBIP5B/INQABn4DCIP5B/IIl+AYICBj/wn8fwAIBh/AAYMH8ZBBgfx/5HDDQRBi////BBF/44CBgMAgIDBBAIDBBAIUBRkRBFFgZBD//AIIXgIJF/BwPwIMuAAoJBE8EOAoUH8EP/B6Bg/8I4LRCBwJBk/gFB8BBEBYUfaIQ4BIISJCBAP4j+AIOC5BYoJBIgP4TwJBxBYP8IJP/DQJBov/A/7FFAoKDBXgJBBI4JBBJoRBpF4JBFgYHBPoX//0AAYJBD8BBpGoTFFv/4CgRBCj5BnADhWBIHyPBIP7REAHt+IH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AANJkmSAQOAFctt23bAQQUOHwQCCyAsQuPHjlx4ATOHwYCDN5kEIIuSIB/jx04AQXgCZkGII+wCpY+EAQOT44rMgKACAQlwCpc2II+2ChUJII2SNxsOQAYCEChUNHwwCC7AVJHwqDDNxYQBQY9x4AVJHw4CDChECII6DBNxUECAKDInAVIgZBLsAVHiQ+DkAICyJuLCYUnSQcBQwZBIjY7D2AICIIdsVxItBoAJENxUBKofgBQgUCBAo4GPQpKDwCuIkmQBQsHNxMJS4wADCgMcBI0GIIXYMQyMGVwskJgxuDBIzZDPA8OTYIgGmxBCc44LDIJBsHNwZBJbIpuDQYNwGpB3GaIpBRgbyIIJcAQYOOILUBVxTyJgRBCCpMHQYz7DeA4ABjZBJpArJeQKDFIIWQCpMAQYxBCtgUJgZBGhJBMeQQHEiRBMQYNx4AHDhpBXeBLyDUwhBCVxKDIIIVgCpRBBWAhBNQZRBLQZJBM26DLj/+g6DRgf/4AXBQYs4IJARC//wn/guBBC3CDHAwf8h/HeQwaCIIhWDwP4C4J9DQZIpE8F+NAPwWBBBGJoKDPHAcB/HgIIkDQZApCNYV+n8DEwUOnCDL/7FBgZWCQZzFBIIqDLFIRBBDQJBCQZqbCCgaDNgZBHQZcfIIn8BwSDNTYRQEQZuBYoyDLNYRBCHYaDNIIX/QaEcgJBGQZYpCIIMH8f+QZ7dCgY7DQZrFBC4IODQZYpC//wFgOOQZ8DCgMAHYaDMVoQXBDoiDKCIUfwE/C4aDNAA6DMABCDLABKDJoAVKQZIHEAA3jQZFgCpSDJIJRWGIJ6DJIJdx44GEQcwGEQasBIINIQaMCIIOQCpMHQY0BIINsQaJBNKwxBOQY5BNgeOnAIFIINJKxaDFgBBBySDLuAIFm3btrcJTAKDFIIcgKxSDFIIdAFZE4QYxBD2CYKQZJBIbQ5BNgKYBQZJBJQYPABAsEIIMkTQ5WIgEJbhUOQYIgGgxBB2w2GTBIABIIWQd46DIgKaKCgMcFY5BC7CYIQY8AiSxCKxCDHbgckBIsDCgPgCo8bIIPbTBCDIgRBIQYRWHbgjvHTA5NCIJCDCuAWIYojIEKxLcDYoyDCCpLFIWAWACpEJkgLCQwaDBKxLcCDIagBAoKYJAAMN2wMDhiDECpLzBIIK0BBAbvITQhBDRILyCCpc2IIdsQYYVLgi0DCBYAEhDfDZZAAHgwEDIIYAQIIMkCiJBSAAcDtuwIScBIKTFFIM0SIIOAIM8btoqRIIiXTyVIINDFUgBBBoArTtgUTACsEyQWUIKsBkAVTyArUsBBqAH4AiA==")) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js new file mode 100644 index 000000000..8474b7f4e --- /dev/null +++ b/apps/circlesclock/app.js @@ -0,0 +1,218 @@ +const locale = require("locale"); +const heatshrink = require("heatshrink"); + +var shoesIcon = heatshrink.decompress(atob("h0OwYJGgmAAgUBkgECgVJB4cSoAUDyEBkARDpADBhMAyQRBgVAkgmDhIUDAAuQAgY1DAAYA=")); +var heartIcon = heatshrink.decompress(atob("h0OwYOLkmQhMkgACByVJgESpIFBpEEBAIFBCgIFCCgsABwcAgQOCAAMSpAwDyBNM")); +var powerIcon = heatshrink.decompress(atob("h0OwYQNsAED7AEDmwEDtu2AgUbtuABwXbBIUN23AAoYOCgEDFIgODABI")); + +const SETTINGS_FILE = "circlesclock.json"; +let settings; + +function loadSettings() { + settings = require("Storage").readJSON(SETTINGS_FILE, 1) || { + 'maxHR': 200, + 'stepGoal': 10000 + }; +} + +const colorFg = '#fff'; +const colorBg = '#000'; +const colorGrey = '#808080'; + +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() { + const red = '#ff0000'; + g.setColor(colorGrey); + g.fillCircle(w2, h3, radiusOuter); + + if (hrtValue != undefined) { + const percent = hrtValue / settings.maxHR; + drawGauge(w2, h3, percent, red); + } + + 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 : 0, 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); + g.setColor(colorFg); + g.drawString(battery + '%', w3, h3); + + g.drawImage(powerIcon, 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"); diff --git a/apps/circlesclock/app.png b/apps/circlesclock/app.png new file mode 100644 index 000000000..94ff885fa Binary files /dev/null and b/apps/circlesclock/app.png differ diff --git a/apps/circlesclock/screenshot.png b/apps/circlesclock/screenshot.png new file mode 100644 index 000000000..94ff885fa Binary files /dev/null and b/apps/circlesclock/screenshot.png differ diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js new file mode 100644 index 000000000..2de278b47 --- /dev/null +++ b/apps/circlesclock/settings.js @@ -0,0 +1,33 @@ +(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), + }, + '< Back': back, + }); +}); diff --git a/apps/lapcounter/ChangeLog b/apps/lapcounter/ChangeLog new file mode 100644 index 000000000..9db0e26c5 --- /dev/null +++ b/apps/lapcounter/ChangeLog @@ -0,0 +1 @@ +0.01: first release diff --git a/apps/lapcounter/README.md b/apps/lapcounter/README.md new file mode 100644 index 000000000..8866955e4 --- /dev/null +++ b/apps/lapcounter/README.md @@ -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) diff --git a/apps/lapcounter/app-icon.js b/apps/lapcounter/app-icon.js new file mode 100644 index 000000000..a443b3a41 --- /dev/null +++ b/apps/lapcounter/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AAkQgEBAREAC6oABdZQXkI6wuKC5iPUFxoXIOpoX/C6QFCC6IsCC6ZEDC/4XcPooXOFgoXQIgwX/C7IUFC5wsIC5ouCC6hcJC5h1DF9YwBChCPOAH4A/AH4Ap")) diff --git a/apps/lapcounter/app.js b/apps/lapcounter/app.js new file mode 100644 index 000000000..215f6140a --- /dev/null +++ b/apps/lapcounter/app.js @@ -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); diff --git a/apps/lapcounter/app.png b/apps/lapcounter/app.png new file mode 100644 index 000000000..7d6ca8317 Binary files /dev/null and b/apps/lapcounter/app.png differ diff --git a/apps/lapcounter/screenshot.png b/apps/lapcounter/screenshot.png new file mode 100644 index 000000000..f3113d86e Binary files /dev/null and b/apps/lapcounter/screenshot.png differ diff --git a/apps/locale/locales.js b/apps/locale/locales.js index 1d659f161..2e3fa8713 100644 --- a/apps/locale/locales.js +++ b/apps/locale/locales.js @@ -37,20 +37,30 @@ const codePages = { /* When it's not in the codepage, try and use these conversions */ const charFallbacks = { + "ą":"a", + "ā":"a", "č":"c", - "ř":"r", - "ő":"o", + "ć":"c", "ě":"e", "ę":"e", - "ą":"a", + "ē":"e", + "ģ":"g", + "i":"ī", + "ķ":"k", + "ļ":"l", + "ł":"l", + "ń":"n", + "ņ":"n", + "ő":"o", "ó":"o", + "ř":"r", + "ś":"s", + "š":"s", + "ū":"u", "ż":"z", "ź":"z", - "ń":"n", - "ł":"l", - "ś":"s", - "ć":"c", -}; + "ž":"z", + }; /* timePattern / datePattern: @@ -631,6 +641,24 @@ var locales = { 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 codePage : "ISO8859-8", diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog index 6109daf7a..16d0010cc 100644 --- a/apps/messages/ChangeLog +++ b/apps/messages/ChangeLog @@ -17,4 +17,6 @@ 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 diff --git a/apps/messages/app.js b/apps/messages/app.js index 965c50b85..79009e77e 100644 --- a/apps/messages/app.js +++ b/apps/messages/app.js @@ -52,11 +52,11 @@ var MESSAGES = require("Storage").readJSON("messages.json",1)||[]; if (!Array.isArray(MESSAGES)) MESSAGES=[]; var onMessagesModified = function(msg) { // TODO: if new, show this new one - if (msg.new) { + if (msg && msg.new) { if (WIDGETS["messages"]) WIDGETS["messages"].buzz(); else Bangle.buzz(); } - showMessage(msg.id); + showMessage(msg&&msg.id); }; function saveMessages() { require("Storage").writeJSON("messages.json",MESSAGES) @@ -174,24 +174,33 @@ function showMusicMessage(msg) { } function showMessageSettings(msg) { - E.showMenu({"":{"title":"Message"}, + E.showMenu({"":{"title":/*LANG*/"Message"}, "< Back" : () => showMessage(msg.id), - "Delete" : () => { + /*LANG*/"Delete" : () => { MESSAGES = MESSAGES.filter(m=>m.id!=msg.id); saveMessages(); checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0}); }, - "Mark Unread" : () => { + /*LANG*/"Mark Unread" : () => { msg.new = true; saveMessages(); checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0}); }, + /*LANG*/"Delete all messages" : () => { + E.showPrompt(/*LANG*/"Are you sure?", {title:/*LANG*/"Delete All Messages"}).then(isYes => { + if (isYes) { + MESSAGES = []; + saveMessages(); + } + checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0}); + }); + }, }); } function showMessage(msgid) { var msg = MESSAGES.find(m=>m.id==msgid); - if (!msg) return checkMessages(); // go home if no message found + if (!msg) return checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0}); // go home if no message found if (msg.src=="Maps") { cancelReloadTimeout(); // don't auto-reload to clock now return showMapMessage(msg); @@ -228,7 +237,6 @@ function showMessage(msgid) { } if (msg.negative) { buttons.push({type:"btn", src:getNegImage(), cb:()=>{ - console.log("Response"); msg.new = false; saveMessages(); cancelReloadTimeout(); // don't auto-reload to clock now Bangle.messageResponse(msg,false); @@ -266,10 +274,10 @@ function checkMessages(options) { options=options||{}; // If no messages, just show 'no messages' and return if (!MESSAGES.length) { - if (!options.clockIfNoMsg) return E.showPrompt("No Messages",{ - title:"Messages", + if (!options.clockIfNoMsg) return E.showPrompt(/*LANG*/"No Messages",{ + title:/*LANG*/"Messages", img:require("heatshrink").decompress(atob("kkk4UBrkc/4AC/tEqtACQkBqtUDg0VqAIGgoZFDYQIIM1sD1QAD4AIBhnqA4WrmAIBhc6BAWs8AIBhXOBAWz0AIC2YIC5wID1gkB1c6BAYFBEQPqBAYXBEQOqBAnDAIQaEnkAngaEEAPDFgo+IKA5iIOhCGIAFb7RqAIGgtUBA0VqobFgNVA")), - buttons : {"Ok":1} + buttons : {/*LANG*/"Ok":1} }).then(() => { load() }); return load(); } @@ -297,7 +305,7 @@ function checkMessages(options) { var x = r.x+2, title = msg.title, body = msg.body; var img = getMessageImage(msg); if (msg.id=="music") { - title = msg.artist || "Music"; + title = msg.artist || /*LANG*/"Music"; body = msg.track; } if (img) { diff --git a/apps/pastel/ChangeLog b/apps/pastel/ChangeLog index 2ede0e161..afeb305c5 100644 --- a/apps/pastel/ChangeLog +++ b/apps/pastel/ChangeLog @@ -6,3 +6,4 @@ 0.06: Converted fonts to font modules 0.07: Added info line that cycles on BTN1/BTN3 (or vitual buttons on a bangle 2) 0.08: Added dependancy on MyLocation +0.09: Added dependancy on Pedometer Widget diff --git a/apps/pebble/ChangeLog b/apps/pebble/ChangeLog index b3d37f841..d92be5e9c 100644 --- a/apps/pebble/ChangeLog +++ b/apps/pebble/ChangeLog @@ -3,3 +3,4 @@ 0.03: Changed time+calendar font to LECO1976Regular, changed to slanting boot 0.04: Fix widget hiding code (fix #1046) 0.05: Fix typo in settings - Purple +0.06: Added dependancy on Pedometer Widget diff --git a/apps/pooqround/ChangeLog b/apps/pooqround/ChangeLog index 12876f71a..8eb91cf97 100644 --- a/apps/pooqround/ChangeLog +++ b/apps/pooqround/ChangeLog @@ -1 +1,2 @@ 0.00: Initial check-in. +0.01: Add tap-to-decorate feature. Bugfixes. diff --git a/apps/pooqround/README.md b/apps/pooqround/README.md index 3c651ed67..d413fd88e 100644 --- a/apps/pooqround/README.md +++ b/apps/pooqround/README.md @@ -10,16 +10,18 @@ Either you'll like that, or you won't. ## Options -Because sometimes I don't want to burn what I'm cooking and others I'm lazy and just want to know if it's afternoon yet, -you can alter the number of ‘hands’ on the display. When the watch is unlocked, slide up to add dots representing the minute and second, -or down to remove the distraction. There's also a setting that displays the second hand, but only if the watch is perfectly face-to-the-sky, -in case you want the ability to check the _exact_ time, hands free, without the impact on battery life this usually entails. - Although we generally obey the system-wide theming, you can long press on the display for a menu of additional options specific to the face. We don't observe the system 12/24 setting, since it the design of the face is equally good in either interpretation. -By default, there is a backlight that comes on when you twist your wrist. This, of course, somewhat increases power draw and could be -annoying in an intentionally dark environment, so there is an option to disable it. +If you like an uncluttered display style, you can still bring up the day, date and minute hand transiently with a tap on the watchface (when unlocked). + +Similarly, because sometimes I don't want to burn what I'm cooking and others I'm lazy and just want to know if it's afternoon yet, +you can quickly alter the number of ‘hands’ on the display. When the watch is unlocked, slide up to add dots representing the minute and second, +or down to remove the distraction. There's also a setting that displays the second hand, but only if the watch is perfectly face-to-the-sky, +in case you want the ability to check the _exact_ time, hands free, without the impact on battery life this usually entails. + +In some versions of the Bangle.js firmware, the backlight doesn't come on automatically when you twist your wrist. There's currently a workaround +for this integrated into the watchface; you can disable it in the menu, if you prefer. ## Limitations diff --git a/apps/pooqround/app.js b/apps/pooqround/app.js index 29fae6ee6..22cf5ff79 100644 --- a/apps/pooqround/app.js +++ b/apps/pooqround/app.js @@ -83,7 +83,6 @@ class Options { Bangle.removeListener('drag', this.reactivator); this.emit('done'); } - g.clear(true); E.showMenu(m); } @@ -309,7 +308,7 @@ class Round { buffer: this.c.buffer, transparent: 0 }; this.options = new RoundOptions(); - this.timescales = [1000, 0, 60000, 900000]; + this.timescales = [1000, [1000, 60000], 60000, 900000]; this.state = {}; // Precomputed polygons for the border areas. this.tl = [0, 0, 58, 0, 0, 58]; @@ -323,13 +322,15 @@ class Round { this.r = this.xc - this.minR; } - reset() {this.state = {}; this.g.clear(true);} + reset(clear) {this.state = {}; clear && this.g.clear(true);} doIcons(which) { this.state[which] = null; this.render(new Date()); // Not quite right, I think. } + enhanceUntil(t) {this.enhance = t;} + pie(f, a0, a1, invert) { if (!invert) return this.pie(f, a1, a0 + 1, true); let t0 = Math.tan(a0 * 2 * Math.PI), t1 = Math.tan(a1 * 2 * Math.PI); @@ -369,17 +370,18 @@ class Round { const g = this.g; const b = this.b, bI = this.bI; const c = this.c, cI = this.cI; + const e = d < this.enhance; const state = this.state; const options = this.options; const cal = options.calendric; const res = options.resolution; - const dow = (cal == 1 || cal > 2) && d.getDay(); + const dow = (e || cal == 1 || cal > 2) && d.getDay(); const ts = res < 2 && d.getSeconds(); - const tm = res < 3 && d.getMinutes() + ts / 60; + const tm = (e || res < 3) && d.getMinutes() + ts / 60; const th = d.getHours() + d.getMinutes() / 60; - const dd = cal > 1 && d.getDate(); - const dm = cal > 3 && d.getMonth(); - const dy = cal > 4 && d.getFullYear(); + const dd = (e || cal > 1) && d.getDate(); + const dm = (e || cal > 3) && d.getMonth(); + const dy = (e || cal > 4) && d.getFullYear(); const xc = this.xc, yc = this.yc, r = this.r; const dlr = xc * 3/4, dlw = 8, dlhw = 4; @@ -475,7 +477,6 @@ class Clock { this.timescales = face.timescales; this.options = face.options; this.rates = {}; - this.faceUp = null; this.options.on('done', () => this.start()); @@ -485,7 +486,6 @@ class Clock { lock: () => {face.doIcons('locked'); this.active();}, faceUp: up => { this.conservative = !up; - this.faceUp = up; this.active(); }, twist: _ => this.options.autolight && Bangle.setLCDPower(true), @@ -504,9 +504,15 @@ class Clock { this.options.resolution++; this.rates.clock = this.timescales[this.options.resolution]; this.active(); - } else if (this.yX - this.yN < 20 && Date.now() - this.t0 > 500) { - this.stop(); - this.options.interact(); + } else if (this.yX - this.yN < 20) { + const now = new Date(); + if (now - this.t0 < 250) { + face.enhanceUntil(now + 30000); + face.render(now); + } else if (now - this.t0 > 500) { + this.stop(); + this.options.interact(); + } } this.t0 = null; } @@ -520,7 +526,7 @@ class Clock { redraw(rate) { const now = this.updated = new Date(); - if (this.refresh) this.face.reset(); + if (this.refresh) this.face.reset(true); this.refresh = false; rate = this.face.render(now, rate); if (rate !== this.rates.face) { @@ -535,7 +541,7 @@ class Clock { this.exception && clearTimeout(this.exception); this.interval && clearInterval(this.interval); this.timeout = this.exception = this.interval = this.rate = null; - this.face.reset(); // Cancel any ongoing background rendering + this.face.reset(false); // Cancel any ongoing background rendering return this; } diff --git a/apps/ptlaunch/ChangeLog b/apps/ptlaunch/ChangeLog index de38d715a..23031cff3 100644 --- a/apps/ptlaunch/ChangeLog +++ b/apps/ptlaunch/ChangeLog @@ -1,4 +1,5 @@ 0.01: Initial creation of the pattern launch app 0.02: Turn on lcd when launching an app if the lock screen was disabled in the settings 0.03: Make tap to confirm new pattern more reliable. Also allow for easier creation of single circle patterns. -0.10: Improve the management of existing patterns: Draw the linked pattern on the left hand side of the app name within a scroller, similar to the default launcher. Slighlty clean up the code to make it less horrible. \ No newline at end of file +0.10: Improve the management of existing patterns: Draw the linked pattern on the left hand side of the app name within a scroller, similar to the default launcher. Slighlty clean up the code to make it less horrible. +0.11: Respect theme colors. Fix: Do not pollute global space with internal variables ans functions in boot.js \ No newline at end of file diff --git a/apps/ptlaunch/README.md b/apps/ptlaunch/README.md index 8d61afece..7cc39e3d6 100644 --- a/apps/ptlaunch/README.md +++ b/apps/ptlaunch/README.md @@ -10,14 +10,21 @@ Then launch the linked apps directly from the clock screen by simply drawing the ## Add Pattern Screenshots -![](main_menu_add.png) -![](add_pattern.png) -![](select_app.png) +![](main_menu_add_light.png) +![](add_pattern_light.png) +![](select_app_light.png) + +![](main_menu_add_dark.png) +![](add_pattern_dark.png) +![](select_app_dark.png) ## Manage Pattern Screenshots -![](main_menu_manage.png) -![](manage_patterns.png) +![](main_menu_manage_light.png) +![](manage_patterns_light.png) + +![](main_menu_manage_dark.png) +![](manage_patterns_dark.png) ## Detailed Steps diff --git a/apps/ptlaunch/add_pattern.png b/apps/ptlaunch/add_pattern.png deleted file mode 100644 index c7cc38e82..000000000 Binary files a/apps/ptlaunch/add_pattern.png and /dev/null differ diff --git a/apps/ptlaunch/add_pattern_dark.png b/apps/ptlaunch/add_pattern_dark.png new file mode 100644 index 000000000..04dfdecd6 Binary files /dev/null and b/apps/ptlaunch/add_pattern_dark.png differ diff --git a/apps/ptlaunch/add_pattern_light.png b/apps/ptlaunch/add_pattern_light.png new file mode 100644 index 000000000..47549b43e Binary files /dev/null and b/apps/ptlaunch/add_pattern_light.png differ diff --git a/apps/ptlaunch/app.js b/apps/ptlaunch/app.js index b5a3bf610..062cc3c62 100644 --- a/apps/ptlaunch/app.js +++ b/apps/ptlaunch/app.js @@ -119,8 +119,7 @@ var recognizeAndDrawPattern = () => { return new Promise((resolve) => { E.showMenu(); g.clear(); - g.setColor(0, 0, 0); - CIRCLES.forEach((circle) => drawCircle(circle)); + drawCirclesWithPattern([]); var pattern = []; @@ -369,7 +368,6 @@ var drawAppWithPattern = (i, r, storedPatterns) => { offset: { x: 1, y: 3 + r.y }, }); - g.setColor(0, 0, 0); if (!storedPattern.wrappedAppName) { storedPattern.wrappedAppName = g .wrapString(app.name, g.getWidth() - 64) @@ -490,7 +488,7 @@ var drawCircle = (circle, drawBuffer, scale) => { log("drawing circle"); log({ x: x, y: y, r: r }); - drawBuffer.fillCircle(x, y, r); + drawBuffer.drawCircle(x, y, r); }; var cachedCirclesDrawings = {}; @@ -535,17 +533,15 @@ var drawCirclesWithPattern = (pattern, options) => { { msb: true } ); - drawBuffer.setColor(1); CIRCLES.forEach((circle) => drawCircle(circle, drawBuffer, scale)); - drawBuffer.setColor(0); drawBuffer.setFontAlign(0, 0); - drawBuffer.setFont("6x8", 4 * scale); + drawBuffer.setFont("Vector", 40 * scale); pattern.forEach((circleIndex, patternIndex) => { var circle = CIRCLES[circleIndex]; drawBuffer.drawString( patternIndex + 1, - circle.x * scale, + (circle.x + (scale === 1 ? 1 : 5)) * scale, circle.y * scale ); }); diff --git a/apps/ptlaunch/boot.js b/apps/ptlaunch/boot.js index a23607768..6fbd3ca41 100644 --- a/apps/ptlaunch/boot.js +++ b/apps/ptlaunch/boot.js @@ -1,167 +1,167 @@ -var DEBUG = true; -var log = (message) => { - if (DEBUG) { - console.log(JSON.stringify(message)); - } -}; - -var storedPatterns; -var positions = []; -var dragHandler = (position) => { - positions.push(position); - - debounce().then(() => { - log(positions.length); - - var CIRCLE_RADIUS = 25; - var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS; - - var circles = [ - { x: 25, y: 25, i: 0 }, - { x: 87, y: 25, i: 1 }, - { x: 150, y: 25, i: 2 }, - { x: 25, y: 87, i: 3 }, - { x: 87, y: 87, i: 4 }, - { x: 150, y: 87, i: 5 }, - { x: 25, y: 150, i: 6 }, - { x: 87, y: 150, i: 7 }, - { x: 150, y: 150, i: 8 }, - ]; - var pattern = []; - - var step = Math.floor(positions.length / 100) + 1; - - var p, a, b, circle; - - for (var i = 0; i < positions.length; i += step) { - p = positions[i]; - - circle = circles[0]; - if (circle) { - a = p.x - circle.x; - b = p.y - circle.y; - if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { - pattern.push(circle.i); - circles.splice(0, 1); - } - } - - circle = circles[1]; - if (circle) { - a = p.x - circle.x; - b = p.y - circle.y; - if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { - pattern.push(circle.i); - circles.splice(1, 1); - } - } - - circle = circles[2]; - if (circle) { - a = p.x - circle.x; - b = p.y - circle.y; - if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { - pattern.push(circle.i); - circles.splice(2, 1); - } - } - - circle = circles[3]; - if (circle) { - a = p.x - circle.x; - b = p.y - circle.y; - if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { - pattern.push(circle.i); - circles.splice(3, 1); - } - } - - circle = circles[4]; - if (circle) { - a = p.x - circle.x; - b = p.y - circle.y; - if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { - pattern.push(circle.i); - circles.splice(4, 1); - } - } - - circle = circles[5]; - if (circle) { - a = p.x - circle.x; - b = p.y - circle.y; - if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { - pattern.push(circle.i); - circles.splice(5, 1); - } - } - - circle = circles[6]; - if (circle) { - a = p.x - circle.x; - b = p.y - circle.y; - if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { - pattern.push(circle.i); - circles.splice(6, 1); - } - } - circle = circles[7]; - if (circle) { - a = p.x - circle.x; - b = p.y - circle.y; - if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { - pattern.push(circle.i); - circles.splice(7, 1); - } - } - - circle = circles[8]; - if (circle) { - a = p.x - circle.x; - b = p.y - circle.y; - if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { - pattern.push(circle.i); - circles.splice(8, 1); - } - } - } - positions = []; - - pattern = pattern.join(""); - - if (pattern) { - if (storedPatterns[pattern]) { - var app = storedPatterns[pattern].app; - if (!!app && !!app.src) { - if (storedPatterns.settings) { - if (storedPatterns.settings.lockDisabled) { - Bangle.setLCDPower(true); - } - } - - Bangle.removeListener("drag", dragHandler); - load(app.src); - } - } - } - }); -}; - -var debounceTimeoutId; -var debounce = (delay) => { - if (debounceTimeoutId) { - clearTimeout(debounceTimeoutId); - } - - return new Promise((resolve) => { - debounceTimeoutId = setTimeout(() => { - debounceTimeoutId = undefined; - resolve(); - }, delay || 500); - }); -}; - (function () { + var DEBUG = false; + var log = (message) => { + if (DEBUG) { + console.log(JSON.stringify(message)); + } + }; + + var storedPatterns; + var positions = []; + var dragHandler = (position) => { + positions.push(position); + + debounce().then(() => { + log(positions.length); + + var CIRCLE_RADIUS = 25; + var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS; + + var circles = [ + { x: 25, y: 25, i: 0 }, + { x: 87, y: 25, i: 1 }, + { x: 150, y: 25, i: 2 }, + { x: 25, y: 87, i: 3 }, + { x: 87, y: 87, i: 4 }, + { x: 150, y: 87, i: 5 }, + { x: 25, y: 150, i: 6 }, + { x: 87, y: 150, i: 7 }, + { x: 150, y: 150, i: 8 }, + ]; + var pattern = []; + + var step = Math.floor(positions.length / 100) + 1; + + var p, a, b, circle; + + for (var i = 0; i < positions.length; i += step) { + p = positions[i]; + + circle = circles[0]; + if (circle) { + a = p.x - circle.x; + b = p.y - circle.y; + if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { + pattern.push(circle.i); + circles.splice(0, 1); + } + } + + circle = circles[1]; + if (circle) { + a = p.x - circle.x; + b = p.y - circle.y; + if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { + pattern.push(circle.i); + circles.splice(1, 1); + } + } + + circle = circles[2]; + if (circle) { + a = p.x - circle.x; + b = p.y - circle.y; + if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { + pattern.push(circle.i); + circles.splice(2, 1); + } + } + + circle = circles[3]; + if (circle) { + a = p.x - circle.x; + b = p.y - circle.y; + if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { + pattern.push(circle.i); + circles.splice(3, 1); + } + } + + circle = circles[4]; + if (circle) { + a = p.x - circle.x; + b = p.y - circle.y; + if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { + pattern.push(circle.i); + circles.splice(4, 1); + } + } + + circle = circles[5]; + if (circle) { + a = p.x - circle.x; + b = p.y - circle.y; + if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { + pattern.push(circle.i); + circles.splice(5, 1); + } + } + + circle = circles[6]; + if (circle) { + a = p.x - circle.x; + b = p.y - circle.y; + if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { + pattern.push(circle.i); + circles.splice(6, 1); + } + } + circle = circles[7]; + if (circle) { + a = p.x - circle.x; + b = p.y - circle.y; + if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { + pattern.push(circle.i); + circles.splice(7, 1); + } + } + + circle = circles[8]; + if (circle) { + a = p.x - circle.x; + b = p.y - circle.y; + if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { + pattern.push(circle.i); + circles.splice(8, 1); + } + } + } + positions = []; + + pattern = pattern.join(""); + + if (pattern) { + if (storedPatterns[pattern]) { + var app = storedPatterns[pattern].app; + if (!!app && !!app.src) { + if (storedPatterns.settings) { + if (storedPatterns.settings.lockDisabled) { + Bangle.setLCDPower(true); + } + } + + Bangle.removeListener("drag", dragHandler); + load(app.src); + } + } + } + }); + }; + + var debounceTimeoutId; + var debounce = (delay) => { + if (debounceTimeoutId) { + clearTimeout(debounceTimeoutId); + } + + return new Promise((resolve) => { + debounceTimeoutId = setTimeout(() => { + debounceTimeoutId = undefined; + resolve(); + }, delay || 500); + }); + }; + var sui = Bangle.setUI; Bangle.setUI = function (mode, cb) { sui(mode, cb); diff --git a/apps/ptlaunch/main_menu_add_dark.png b/apps/ptlaunch/main_menu_add_dark.png new file mode 100644 index 000000000..4c1564eca Binary files /dev/null and b/apps/ptlaunch/main_menu_add_dark.png differ diff --git a/apps/ptlaunch/main_menu_add.png b/apps/ptlaunch/main_menu_add_light.png similarity index 100% rename from apps/ptlaunch/main_menu_add.png rename to apps/ptlaunch/main_menu_add_light.png diff --git a/apps/ptlaunch/main_menu_manage_dark.png b/apps/ptlaunch/main_menu_manage_dark.png new file mode 100644 index 000000000..e630db46d Binary files /dev/null and b/apps/ptlaunch/main_menu_manage_dark.png differ diff --git a/apps/ptlaunch/main_menu_manage.png b/apps/ptlaunch/main_menu_manage_light.png similarity index 100% rename from apps/ptlaunch/main_menu_manage.png rename to apps/ptlaunch/main_menu_manage_light.png diff --git a/apps/ptlaunch/manage_patterns.png b/apps/ptlaunch/manage_patterns.png deleted file mode 100644 index 82b10ad43..000000000 Binary files a/apps/ptlaunch/manage_patterns.png and /dev/null differ diff --git a/apps/ptlaunch/manage_patterns_dark.png b/apps/ptlaunch/manage_patterns_dark.png new file mode 100644 index 000000000..698a19222 Binary files /dev/null and b/apps/ptlaunch/manage_patterns_dark.png differ diff --git a/apps/ptlaunch/manage_patterns_light.png b/apps/ptlaunch/manage_patterns_light.png new file mode 100644 index 000000000..5e4b27131 Binary files /dev/null and b/apps/ptlaunch/manage_patterns_light.png differ diff --git a/apps/ptlaunch/select_app_dark.png b/apps/ptlaunch/select_app_dark.png new file mode 100644 index 000000000..422f2b5e2 Binary files /dev/null and b/apps/ptlaunch/select_app_dark.png differ diff --git a/apps/ptlaunch/select_app.png b/apps/ptlaunch/select_app_light.png similarity index 100% rename from apps/ptlaunch/select_app.png rename to apps/ptlaunch/select_app_light.png diff --git a/apps/rebble/ChangeLog b/apps/rebble/ChangeLog index 3e7094eab..16e65d4f9 100644 --- a/apps/rebble/ChangeLog +++ b/apps/rebble/ChangeLog @@ -1,2 +1,3 @@ 0.01: First release -0.02: Fix dependancies, fix type to Purple +0.02: Fix typo to Purple +0.03: Added dependancy on Pedometer Widget diff --git a/apps/rtorch/ChangeLog b/apps/rtorch/ChangeLog index 06f10fe08..13cbb6e72 100644 --- a/apps/rtorch/ChangeLog +++ b/apps/rtorch/ChangeLog @@ -1 +1,2 @@ 0.01: Cloning torch and making it red :D +0.02: Modify for setUI and Bangle 2 diff --git a/apps/rtorch/app.js b/apps/rtorch/app.js index 4f6b1d6f7..03a50ee10 100644 --- a/apps/rtorch/app.js +++ b/apps/rtorch/app.js @@ -2,21 +2,38 @@ Bangle.setLCDPower(1); Bangle.setLCDTimeout(0); g.reset(); c = 1; + function setColor(delta){ c+=delta; c = Math.max(c,0); c = Math.min(c,2); if (c<1){ g.setColor(c,0,0); + Bangle.setLCDBrightness(c >= 0.1 ? c : 0.1); }else{ g.setColor(1,c-1,c-1); + Bangle.setLCDBrightness(1); } g.fillRect(0,0,g.getWidth(),g.getHeight()); } -setColor(0) -// BTN1 light up toward white -// BTN3 light down to red -// BTN2 to reset -setWatch(()=>setColor(0.1), BTN1, { repeat:true, edge:"rising", debounce: 50 }); -setWatch(()=>load(), BTN2); -setWatch(()=>setColor(-0.1), BTN3, { repeat:true, edge:"rising", debounce: 50 }); + +function updownHandler(direction){ + if (direction == undefined){ + c=1; + setColor(0); + } else { + setColor(-direction * 0.1); + } +} + +setColor(0); + +// Bangle 1: +// BTN1: light up toward white +// BTN3: light down to red +// BTN2: reset +// Bangle 2: +// Swipe up: light up toward white +// Swipe down: light down to red +// BTN1: reset +Bangle.setUI("updown", updownHandler); diff --git a/apps/sensible/ChangeLog b/apps/sensible/ChangeLog index baa93f297..c50431f51 100644 --- a/apps/sensible/ChangeLog +++ b/apps/sensible/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Corrected variable initialisation 0.03: Advertise app name, added screenshots +0.04: Advertise bar, GPS, HRM and mag services diff --git a/apps/sensible/README.md b/apps/sensible/README.md index f79b61aea..fcff3b0f9 100644 --- a/apps/sensible/README.md +++ b/apps/sensible/README.md @@ -17,7 +17,7 @@ Currently implements: - Heart Rate Monitor - Magnetometer -in the menu display but NOT YET in Bluetooth Low Energy advertising (which will be implemented in a subsequent version). +in the menu display, and broadcasts all sensor data readings _except_ acceleration in Bluetooth Low Energy advertising packets as GATT characteristic services. ## Controls diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index 45852adab..3da39998e 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -7,6 +7,9 @@ // Non-user-configurable constants const APP_ID = 'sensible'; const ESPRUINO_COMPANY_CODE = 0x0590; +const APP_ADVERTISING_DATA = [ 0x12, 0xff, 0x90, 0x05, 0x7b, 0x6e, 0x61, 0x6d, + 0x65, 0x3a, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x62, + 0x6c, 0x65, 0x7d ]; // Global variables @@ -20,6 +23,12 @@ let isBarEnabled = true; let isGpsEnabled = true; let isHrmEnabled = true; let isMagEnabled = true; +let isNewAccData = false; +let isNewBarData = false; +let isNewGpsData = false; +let isNewHrmData = false; +let isNewMagData = false; + // Menus @@ -91,22 +100,121 @@ let magMenu = { }; -// Transmit the app name under the Espruino company code to facilitate discovery -function transmitAppName() { - let options = { - showName: false, - manufacturer: ESPRUINO_COMPANY_CODE, - manufacturerData: JSON.stringify({ name: APP_ID }), - interval: 2000 +// Check for new sensor data and update the advertising sequence +function transmitUpdatedSensorData() { + let data = [ APP_ADVERTISING_DATA ]; // Always advertise at least app name + + if(isNewBarData) { + data.push(encodeBarServiceData()); + isNewBarData = false; } - NRF.setAdvertising({}, options); + if(isNewGpsData && gps.lat && gps.lon) { + data.push(encodeGpsServiceData()); + isNewGpsData = false; + } + + if(isNewHrmData) { + data.push({ 0x2a37: [ 0, hrm.bpm ] }); + isNewHrmData = false; + } + + if(isNewMagData) { + data.push(encodeMagServiceData()); + isNewMagData = false; + } + + NRF.setAdvertising(data, { showName: false, interval: 200 }); +} + + +// Encode the bar service data to fit in a Bluetooth PDU +function encodeBarServiceData() { + let tEncoded = Math.round(bar.temperature * 100); + let pEncoded = Math.round(bar.pressure * 100); + let eEncoded = Math.round(bar.altitude * 100); + + if(bar.temperature < 0) { + tEncoded += 0x10000; + } + if(bar.altitude < 0) { + eEncoded += 0x1000000; + } + + let t = [ tEncoded & 0xff, (tEncoded >> 8) & 0xff ]; + let p = [ pEncoded & 0xff, (pEncoded >> 8) & 0xff, (pEncoded >> 16) & 0xff, + (pEncoded >> 24) & 0xff ]; + let e = [ eEncoded & 0xff, (eEncoded >> 8) & 0xff, (eEncoded >> 16) & 0xff ]; + + return [ + 0x02, 0x01, 0x06, // Flags + 0x05, 0x16, 0x6e, 0x2a, t[0], t[1], // Temperature + 0x07, 0x16, 0x6d, 0x2a, p[0], p[1], p[2], p[3], // Pressure + 0x06, 0x16, 0x6c, 0x2a, e[0], e[1], e[2] // Elevation + ]; +} + + +// Encode the GPS service data using the Location and Speed characteristic +function encodeGpsServiceData() { + let latEncoded = Math.round(gps.lat * 10000000); + let lonEncoded = Math.round(gps.lon * 10000000); + let hEncoded = Math.round(gps.course * 100); + let sEncoded = Math.round(1000 * gps.speed / 36); + + if(gps.lat < 0) { + latEncoded += 0x100000000; + } + if(gps.lon < 0) { + lonEncoded += 0x100000000; + } + + let s = [ sEncoded & 0xff, (sEncoded >> 8) & 0xff ]; + let lat = [ latEncoded & 0xff, (latEncoded >> 8) & 0xff, + (latEncoded >> 16) & 0xff, (latEncoded >> 24) & 0xff ]; + let lon = [ lonEncoded & 0xff, (lonEncoded >> 8) & 0xff, + (lonEncoded >> 16) & 0xff, (lonEncoded >> 24) & 0xff ]; + let h = [ hEncoded & 0xff, (hEncoded >> 8) & 0xff ]; + + return [ + 0x02, 0x01, 0x06, // Flags + 0x11, 0x16, 0x67, 0x2a, 0x95, 0x02, s[0], s[1], lat[0], lat[1], lat[2], + lat[3], lon[0], lon[1], lon[2], lon[3], h[0], h[1] // Location and Speed + ]; +} + + +// Encode the mag service data using the magnetic flux density 3D characteristic +function encodeMagServiceData() { + let xEncoded = mag.x; // TODO: units??? + let yEncoded = mag.y; + let zEncoded = mag.z; + + if(xEncoded < 0) { + xEncoded += 0x10000; + } + if(yEncoded < 0) { + yEncoded += 0x10000; + } + if(yEncoded < 0) { + yEncoded += 0x10000; + } + + let x = [ xEncoded & 0xff, (xEncoded >> 8) & 0xff ]; + let y = [ yEncoded & 0xff, (yEncoded >> 8) & 0xff ]; + let z = [ zEncoded & 0xff, (zEncoded >> 8) & 0xff ]; + + return [ + 0x02, 0x01, 0x06, // Flags + 0x09, 0x16, 0xa1, 0x2a, x[0], x[1], y[0], y[1], z[0], z[1] // Mag 3D + ]; } // Update acceleration Bangle.on('accel', function(newAcc) { acc = newAcc; + isNewAccData = true; if(isAccMenu) { accMenu.x.value = acc.x.toFixed(2); @@ -119,6 +227,7 @@ Bangle.on('accel', function(newAcc) { // Update barometer Bangle.on('pressure', function(newBar) { bar = newBar; + isNewBarData = true; if(isBarMenu) { barMenu.Altitude.value = bar.altitude.toFixed(1) + 'm'; @@ -131,6 +240,7 @@ Bangle.on('pressure', function(newBar) { // Update GPS Bangle.on('GPS', function(newGps) { gps = newGps; + isNewGpsData = true; if(isGpsMenu) { gpsMenu.Lat.value = gps.lat.toFixed(4); @@ -145,6 +255,7 @@ Bangle.on('GPS', function(newGps) { // Update heart rate monitor Bangle.on('HRM', function(newHrm) { hrm = newHrm; + isNewHrmData = true; if(isHrmMenu) { hrmMenu.BPM.value = hrm.bpm; @@ -156,6 +267,7 @@ Bangle.on('HRM', function(newHrm) { // Update magnetometer Bangle.on('mag', function(newMag) { mag = newMag; + isNewMagData = true; if(isMagMenu) { magMenu.x.value = mag.x; @@ -169,9 +281,9 @@ Bangle.on('mag', function(newMag) { // On start: enable sensors and display main menu g.clear(); -transmitAppName(); Bangle.setBarometerPower(isBarEnabled, APP_ID); Bangle.setGPSPower(isGpsEnabled, APP_ID); Bangle.setHRMPower(isHrmEnabled, APP_ID); Bangle.setCompassPower(isMagEnabled, APP_ID); -E.showMenu(mainMenu); \ No newline at end of file +E.showMenu(mainMenu); +setInterval(transmitUpdatedSensorData, 1000); \ No newline at end of file diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index c676e3828..64844dcbc 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -39,3 +39,4 @@ 0.34: Remove Quiet Mode LCD settings: now handled by Quiet Mode Schedule app 0.35: Change App/Widget settings to 'App Settings' so it fits on Bangle screen 0.36: Added 'Utils' menu with helpful utilities for restoring Bangle.js +0.37: Going into passkey menu now saves settings with passkey diff --git a/apps/setting/settings.js b/apps/setting/settings.js index f55f9937f..9cba09d6c 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -276,8 +276,10 @@ function showPasskeyMenu() { showBLEMenu(); } }; - if (!settings.passkey || settings.passkey.length!=6) + if (!settings.passkey || settings.passkey.length!=6) { settings.passkey = "123456"; + updateSettings(); + } for (var i=0;i<6;i++) (function(i){ menu[`Digit ${i+1}`] = { value : 0|settings.passkey[i], diff --git a/apps/showimg/ChangeLog b/apps/showimg/ChangeLog new file mode 100644 index 000000000..296bc78d0 --- /dev/null +++ b/apps/showimg/ChangeLog @@ -0,0 +1,2 @@ +0.1: Initial release +0.2: Fixed launcher image diff --git a/apps/showimg/README.md b/apps/showimg/README.md new file mode 100644 index 000000000..9d7c0067a --- /dev/null +++ b/apps/showimg/README.md @@ -0,0 +1,3 @@ +Displays an image. I use this app to show my vaccination certificate. +The image is read from the file "showimage.user.img". +Returns to watch face after 60s/button push. diff --git a/apps/showimg/app-icon.js b/apps/showimg/app-icon.js new file mode 100644 index 000000000..abb1eb434 --- /dev/null +++ b/apps/showimg/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkBIf4Aah//BRQAMDowUNC5AARC4YKKL5gTC+B3TCpAyIC5oNBEA4XNJwS4GC55pHC8TEHC57QHC4wSEC5YpEC6YwEC5oEEC5x3DC6ZHbC7PwcYxfNAYYXPJA4XQDAwKEBYQXJIoReHC5gMFAAojBC5QUIC5Y5JMgYXIUQYJFPggXMAwICCBAYXMCAQJDDwQUCC5QOCUwQdEC5QqFDghNFC5wrEC5gQDPgoTCDYYXFMAgXaCQoXJEwZ4FLQbhFC4imDAAglFC5QAGBgYXKIoYWIC5YYFG4ZkDC4YjCYYwAJC4gASC6THFH5pqGAAY")) diff --git a/apps/showimg/app.js b/apps/showimg/app.js new file mode 100644 index 000000000..e00385bd7 --- /dev/null +++ b/apps/showimg/app.js @@ -0,0 +1,16 @@ +g.reset(); +g.clear(); +g.drawImage(require("Storage").read("showimg.user.img"),0,0); +drawTimeout = setTimeout(function() { + load(); +}, 60000); +setWatch(function() { + load(); +}, BTN, { repeat:false, edge:'falling' }); +var savedOptions=Bangle.getOptions(); +Bangle.setLCDBrightness(1); +var newOptions={ + lockTimeout:60000, + backlightTimeout:60000 +}; +Bangle.setOptions(newOptions); diff --git a/apps/showimg/app.png b/apps/showimg/app.png new file mode 100644 index 000000000..306db9b42 Binary files /dev/null and b/apps/showimg/app.png differ diff --git a/apps/speedalt2/ChangeLog b/apps/speedalt2/ChangeLog index 91f01988e..bd338f8b2 100644 --- a/apps/speedalt2/ChangeLog +++ b/apps/speedalt2/ChangeLog @@ -1,2 +1,3 @@ 0.01: Initial import. 0.07: Add swipe to change screens. +1.06: Misc memory and screen optimisations. diff --git a/apps/speedalt2/app.js b/apps/speedalt2/app.js index 0db9629c7..fd53cf7c3 100644 --- a/apps/speedalt2/app.js +++ b/apps/speedalt2/app.js @@ -1,11 +1,9 @@ /* Speed and Altitude [speedalt2] Mike Bennett mike[at]kereru.com -0.01 : Initial -0.06 : Add Posn screen -0.07 : Add swipe to change screens same as BTN3 +1.10 : add inverted colours */ -var v = '1.05'; +var v = '1.10'; /*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */ var KalmanFilter = (function () { @@ -173,6 +171,15 @@ var KalmanFilter = (function () { var buf = Graphics.createArrayBuffer(240,160,2,{msb:true}); +let LED = // LED as minimal and only definition (as instance / singleton) +{ isOn: false // status on / off, not needed if you don't need to ask for it +, set: function(v) { // turn on w/ no arg or truey, else off + g.setColor((this.isOn=(v===undefined||!!v))?1:0,0,0).fillCircle(40,10,10); } +, reset: function() { this.set(false); } // turn off +, write: function(v) { this.set(v); } // turn on w/ no arg or truey, else off +, toggle: function() { this.set( ! this.isOn); } // toggle the LED +}, LED1 = LED; // LED1 as 'synonym' for LED + // Load fonts //require("Font7x11Numeric7Seg").add(Graphics); @@ -183,17 +190,16 @@ var canDraw = 1; var time = ''; // Last time string displayed. Re displayed in background colour to remove before drawing new time. var tmrLP; // Timer for delay in switching to low power after screen turns off -var max = {}; -max.spd = 0; -max.alt = 0; -max.n = 0; // counter. Only start comparing for max after a certain number of fixes to allow kalman filter to have smoohed the data. +var maxSpd = 0; +var maxAlt = 0; +var maxN = 0; // counter. Only start comparing for max after a certain number of fixes to allow kalman filter to have smoohed the data. var emulator = (process.env.BOARD=="EMSCRIPTEN")?1:0; // 1 = running in emulator. Supplies test values; var wp = {}; // Waypoint to use for distance from cur position. -function nxtWp(inc){ - cfg.wp+=inc; +function nxtWp(){ + cfg.wp++; loadWp(); } @@ -227,7 +233,8 @@ function drawScrn(dat) { if (!canDraw) return; buf.clear(); - + buf.setBgColor(0); + var n; n = dat.val.toString(); @@ -252,29 +259,21 @@ function drawScrn(dat) { buf.setFontVector(35); buf.drawString(dat.unit,5,164); - if ( dat.max ) drawMax(); // MAX display indicator - if ( dat.wp ) drawWP(); // Waypoint name - - //Sats - if ( dat.sat ) { - if ( dat.age > 10 ) { - if ( dat.age > 90 ) dat.age = '>90'; - drawSats('Age:'+dat.age); - } - else drawSats('Sats:'+dat.sats); - } - + drawMax(dat.max); // MAX display indicator + drawWP(dat.wp); // Waypoint name + drawSats(dat.sats); + g.reset(); g.drawImage(img,0,40); - if ( pwrSav ) LED1.reset(); - else LED1.set(); + LED1.write(!pwrSav); } function drawPosn(dat) { if (!canDraw) return; buf.clear(); + buf.setBgColor(0); var x, y; x=210; @@ -293,20 +292,12 @@ function drawPosn(dat) { buf.drawString(dat.ew,x,y+70); - //Sats - if ( dat.sat ) { - if ( dat.age > 10 ) { - if ( dat.age > 90 ) dat.age = '>90'; - drawSats('Age:'+dat.age); - } - else drawSats('Sats:'+dat.sats); - } + drawSats(dat.sats); g.reset(); g.drawImage(img,0,40); - if ( pwrSav ) LED1.reset(); - else LED1.set(); + LED1.write(!pwrSav); } @@ -314,6 +305,8 @@ function drawClock() { if (!canDraw) return; buf.clear(); + buf.setBgColor(0); + var x, y; x=185; y=0; @@ -329,19 +322,14 @@ function drawClock() { g.reset(); g.drawImage(img,0,40); - if ( pwrSav ) LED1.reset(); - else LED1.set(); + LED1.write(!pwrSav); } -function drawWP() { - var nm = wp.name; - if ( nm == undefined || nm == 'NONE' || cfg.modeA ==1 ) nm = ''; - buf.setColor(2); - +function drawWP(wp) { + buf.setColor(3); buf.setFontAlign(0,1); //left, bottom buf.setFontVector(48); - buf.drawString(nm.substring(0,8),120,140); - + buf.drawString(wp,120,140); } function drawSats(sats) { @@ -351,16 +339,15 @@ function drawSats(sats) { buf.drawString(sats,240,160); } -function drawMax() { +function drawMax(max) { buf.setFontVector(30); buf.setColor(2); buf.setFontAlign(0,1); //centre, bottom - buf.drawString('MAX',120,164); + buf.drawString(max,120,164); } function onGPS(fix) { - - if ( emulator ) { +if ( emulator ) { fix.fix = 1; fix.speed = 10 + (Math.random()*5); fix.alt = 354 + (Math.random()*50); @@ -382,10 +369,15 @@ function onGPS(fix) { var ns = ''; var ew = ''; var lon = '---.--'; + var sats = '---'; + // Waypoint name + var wpName = wp.name; + if ( wpName == undefined || wpName == 'NONE' ) wpName = ''; + wpName = wpName.substring(0,8); if (fix.fix) lf = fix; - + if (lf.fix) { // Smooth data @@ -393,10 +385,9 @@ function onGPS(fix) { if ( cfg.spdFilt ) lf.speed = spdFilter.filter(lf.speed); if ( cfg.altFilt ) lf.alt = altFilter.filter(lf.alt); lf.smoothed = 1; - if ( max.n <= 15 ) max.n++; + if ( maxN <= 15 ) maxN++; } - // Speed if ( cfg.spd == 0 ) { m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units @@ -407,18 +398,19 @@ function onGPS(fix) { if ( sp < 10 ) sp = sp.toFixed(1); else sp = Math.round(sp); + if (isNaN(sp)) sp = '---'; - if (parseFloat(sp) > parseFloat(max.spd) && max.n > 15 ) max.spd = sp; + if (parseFloat(sp) > parseFloat(maxSpd) && maxN > 15 ) maxSpd = sp; // Altitude al = lf.alt; al = Math.round(parseFloat(al)/parseFloat(cfg.alt)); - - if (parseFloat(al) > parseFloat(max.alt) && max.n > 15 ) max.alt = al; + if (parseFloat(al) > parseFloat(maxAlt) && maxN > 15 ) maxAlt = al; + if (isNaN(al)) al = '---'; // Distance to waypoint di = distance(lf,wp); - if (isNaN(di)) di = 0; + if (isNaN(di)) di = '--------'; // Age of last fix (secs) age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000)); @@ -431,6 +423,13 @@ function onGPS(fix) { ew = 'E'; if ( lf.lon < 0 ) ew = 'W'; lon = Math.abs(lf.lon.toFixed(2)); + + // Sats + if ( age > 10 ) { + sats = 'Age:'+Math.round(age); + if ( age > 90 ) sats = 'Age:>90'; + } + else sats = 'Sats:'+lf.satellites; } @@ -438,23 +437,21 @@ function onGPS(fix) { // Speed if ( showMax ) drawScrn({ - val:max.spd, + val:maxSpd, unit:cfg.spd_unit, - sats:lf.satellites, + sats:sats, age:age, - max:true, - wp:false, - sat:true + max:'MAX', + wp:'' }); // Speed maximums else drawScrn({ val:sp, unit:cfg.spd_unit, - sats:lf.satellites, + sats:sats, age:age, - max:false, - wp:false, - sat:true + max:'SPD', + wp:'' }); } @@ -462,23 +459,21 @@ function onGPS(fix) { // Alt if ( showMax ) drawScrn({ - val:max.alt, + val:maxAlt, unit:cfg.alt_unit, - sats:lf.satellites, + sats:sats, age:age, - max:true, - wp:false, - sat:true + max:'MAX', + wp:'' }); // Alt maximums else drawScrn({ val:al, unit:cfg.alt_unit, - sats:lf.satellites, + sats:sats, age:age, - max:false, - wp:false, - sat:true + max:'ALT', + wp:'' }); } @@ -487,24 +482,22 @@ function onGPS(fix) { drawScrn({ val:di, unit:cfg.dist_unit, - sats:lf.satellites, + sats:sats, age:age, - max:false, - wp:true, - sat:true + max:'DST', + wp:wpName }); } if ( cfg.modeA == 3 ) { // Position - drawPosn({ - sats:lf.satellites, + drawPosn({ + sats:sats, age:age, lat:lat, lon:lon, ns:ns, - ew:ew, - sat:true + ew:ew }); } @@ -534,9 +527,9 @@ function nextFunc(dur) { if ( cfg.modeA == 0 || cfg.modeA == 1 ) { // Spd+Alt mode - Switch between fix and MAX if ( dur < 2 ) showMax = !showMax; // Short press toggle fix/max display - else { max.spd = 0; max.alt = 0; } // Long press resets max values. + else { maxSpd = 0; maxAlt = 0; } // Long press resets max values. } - else if ( cfg.modeA == 2) nxtWp(1); // Dist mode - Select next waypoint + else if ( cfg.modeA == 2) nxtWp(); // Dist mode - Select next waypoint onGPS(lf); } @@ -545,7 +538,7 @@ function updateClock() { if (!canDraw) return; if ( cfg.modeA != 4 ) return; drawClock(); - if ( emulator ) {max.spd++;max.alt++;} + if ( emulator ) {maxSpd++;maxAlt++;} } function startDraw(){ @@ -585,7 +578,6 @@ function setButtons(){ setWatch(function(e){ pwrSav=!pwrSav; if ( pwrSav ) { - LED1.reset(); var s = require('Storage').readJSON('setting.json',1)||{}; var t = s.timeout||10; Bangle.setLCDTimeout(t); @@ -593,8 +585,8 @@ function setButtons(){ else { Bangle.setLCDTimeout(0); // Bangle.setLCDPower(1); - LED1.set(); } + LED1.write(!pwrSav); }, BTN2, {repeat:true,edge:"falling"}); // BTN3 - next screen @@ -690,7 +682,8 @@ var img = { }; if ( cfg.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFF6,0xDFFF]); -if ( cfg.colour == 2 ) img.palette = new Uint16Array([0,0xFF800,0xFAE0,0xF813]); +if ( cfg.colour == 2 ) img.palette = new Uint16Array([0,0xF800,0xFAE0,0xF813]); +if ( cfg.colour == 3 ) img.palette = new Uint16Array([0xFFFF,0x007F,0x0054,0x0054]); var SCREENACCESS = { withApp:true, diff --git a/apps/speedalt2/settings.js b/apps/speedalt2/settings.js index 96174a89b..fe30d88df 100644 --- a/apps/speedalt2/settings.js +++ b/apps/speedalt2/settings.js @@ -65,7 +65,8 @@ '< Back': function() { E.showMenu(appMenu); }, 'Default' : function() { setColour(0); }, 'Hi Contrast' : function() { setColour(1); }, - 'Night' : function() { setColour(2); } + 'Night' : function() { setColour(2); }, + 'Inverted' : function() { setColour(3); } }; const kalMenu = { diff --git a/apps/stopwatch/README.md b/apps/stopwatch/README.md index 30a9306d1..ceeafaefc 100644 --- a/apps/stopwatch/README.md +++ b/apps/stopwatch/README.md @@ -31,3 +31,6 @@ Which one is which ? ![](A.jpg) ![](B.jpg) + + +Written by: [Hugh Barney](https://github.com/hughbarney) For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/) diff --git a/apps/widpedom/ChangeLog b/apps/widpedom/ChangeLog index 2f36c7647..c033ea505 100644 --- a/apps/widpedom/ChangeLog +++ b/apps/widpedom/ChangeLog @@ -19,3 +19,4 @@ Stop goal drawing outside widget area Fix issue with widget overwrite in large font mode Memory usage enhancements +0.20: Fix issue where step count would randomly reset diff --git a/apps/widpedom/widget.js b/apps/widpedom/widget.js index 3c861cf54..0ec0780c9 100644 --- a/apps/widpedom/widget.js +++ b/apps/widpedom/widget.js @@ -53,7 +53,7 @@ E.on('kill', () => { if (!settings) { loadSettings() } let d = { - lastUpdate : lastUpdate.toISOString(), + lastUpdate : lastUpdate.valueOf(), stepsToday : stp_today, settings : settings, }; diff --git a/core b/core index 5a6cf40b4..b033af017 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 5a6cf40b4030ba909bf5f88b646ac05e41ce4c03 +Subproject commit b033af017f6789a6a7777e6ef1428d94995a9b8b diff --git a/lang/de_DE.json b/lang/de_DE.json index 02dd72189..80d0e74bb 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -6,10 +6,18 @@ "Hours" : "Stunden", "Minutes" : "Minuten", "Enabled" : "Aktiviert", - "Settings" : "Einstellungen" + "Settings" : "Einstellungen", + "Save" : "Speichern", + "Back" : "Zurück", + "Repeat" : "Wiederholen", + "Delete" : "Löschen", + "Sleep" : "Schlummern", + "Alarms" : "Wecker", + "New Alarm" : "Neuer Wecker", + "ALARM!" : "ALARM!" }, "alarm": { "//":"App-specific overrides", - "Alarm" : "Alarm" + "rpt" : "Wdh." } } diff --git a/lang/en_GB.json b/lang/en_GB.json new file mode 100644 index 000000000..e85fe8029 --- /dev/null +++ b/lang/en_GB.json @@ -0,0 +1,9 @@ +{ + "//":"British English language translations - the default strings in apps are all english anyway, so no need to have translations for most things", + "GLOBAL": { + "//":"Translations that apply for all apps", + }, + "alarm": { + "//":"App-specific overrides", + } +} diff --git a/lang/es_ES.json b/lang/es_ES.json new file mode 100644 index 000000000..0671c4ab8 --- /dev/null +++ b/lang/es_ES.json @@ -0,0 +1,21 @@ +{ + "//":"Spanish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Alarmas", + "Hours" : "Horas", + "Minutes" : "Minutos", + "Enabled" : "Activados", + "New Alarm" : "Alarma nueva", + "Save" : "Grabar", + "Back" : "Atrás", + "Repeat" : "Repetición", + "Delete" : "Borrar", + "ALARM!" : "ALARM", + "Sleep" : "Dormir" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "rep." + } +} diff --git a/lang/fi_FI.json b/lang/fi_FI.json new file mode 100644 index 000000000..eb1d826d8 --- /dev/null +++ b/lang/fi_FI.json @@ -0,0 +1,21 @@ +{ + "//":"Finnish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Hälytykset", + "Hours" : "Tunnit", + "Minutes" : "Minuutit", + "Enabled" : "Aktivoitu", + "New Alarm" : "Uusi hälytys", + "Save" : "Tallenna", + "Back" : "Paluu", + "Repeat" : "Toista", + "Delete" : "Poista", + "ALARM!" : "ALARM", + "Sleep" : "Nukkuminen" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "toistaa" + } +} diff --git a/lang/fr_FR.json b/lang/fr_FR.json new file mode 100644 index 000000000..209574424 --- /dev/null +++ b/lang/fr_FR.json @@ -0,0 +1,21 @@ +{ + "//":"French language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Réveils", + "Hours" : "Heures", + "Minutes" : "Minutes", + "Enabled" : "Activé", + "New Alarm" : "Nouveau Réveil", + "Save" : "Sauvegarder", + "Back" : "Retour", + "Repeat" : "Répétition", + "Delete" : "Supprimer", + "ALARM!" : "ALARM!", + "Sleep" : "Sommeil" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "rép." + } +} diff --git a/lang/hu_HU.json b/lang/hu_HU.json new file mode 100644 index 000000000..8e5df6ed7 --- /dev/null +++ b/lang/hu_HU.json @@ -0,0 +1,21 @@ +{ + "//":"Spanish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Riasztások", + "Hours" : "Óra", + "Minutes" : "Perc", + "Enabled" : "Aktiválva", + "New Alarm" : "Új riasztás", + "Save" : "Mentés", + "Back" : "Vissza", + "Repeat" : "Ismétlés", + "Delete" : "Törlés", + "ALARM!" : "ALARM!", + "Sleep" : "Alvás" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "ismétlés" + } +} diff --git a/lang/index.json b/lang/index.json index 7f4bbee37..f17bf5e03 100644 --- a/lang/index.json +++ b/lang/index.json @@ -1,3 +1,12 @@ [ - "de_DE.json" + {"code":"en_GB","name":"British English","url":"en_GB.json"}, + {"code":"de_DE","name":"German","url":"de_DE.json"}, + {"code":"es_ES","name":"Spanish","url":"es_ES.json"}, + {"code":"fi_FI","name":"Finnish","url":"fi_FI.json"}, + {"code":"fr_FR","name":"French","url":"fr_FR.json"}, + {"code":"hu_HU","name":"Hungarian","url":"hu_HU.json"}, + {"code":"it_IT","name":"Italian","url":"it_IT.json"}, + {"code":"nl_NL","name":"Dutch","url":"nl_NL.json"}, + {"code":"sv_SE","name":"Swedish","url":"sv_SE.json"}, + {"code":"tr_TR","name":"Turkish","url":"tr_TR.json"} ] diff --git a/lang/it_IT.json b/lang/it_IT.json new file mode 100644 index 000000000..184c80238 --- /dev/null +++ b/lang/it_IT.json @@ -0,0 +1,21 @@ +{ + "//":"Italian language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Allarmi", + "Hours" : "Ore", + "Minutes" : "Minuti", + "Enabled" : "Attivato", + "New Alarm" : "Nuovo allarme", + "Save" : "Salvare", + "Back" : "Indietro", + "Repeat" : "Ripetere", + "Delete" : "Cancellare", + "ALARM!" : "ALARM!", + "Sleep" : "Dormire" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "ripetere" + } +} diff --git a/lang/nl_NL.json b/lang/nl_NL.json new file mode 100644 index 000000000..a04e46928 --- /dev/null +++ b/lang/nl_NL.json @@ -0,0 +1,21 @@ +{ + "//":"Dutch language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Alarmen", + "Hours" : "Uren", + "Minutes" : "Minuten", + "Enabled" : "Geactiveerd", + "New Alarm" : "Nieuw alarm", + "Save" : "Opslaan", + "Back" : "Terug", + "Repeat" : "Herhalen", + "Delete" : "Verwijderen", + "ALARM!" : "ALARV.", + "Sleep" : "Stand-by" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "herhalen" + } +} diff --git a/lang/sv_SE.json b/lang/sv_SE.json new file mode 100644 index 000000000..3a006c2bf --- /dev/null +++ b/lang/sv_SE.json @@ -0,0 +1,21 @@ +{ + "//":"Swedish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Larm", + "Hours" : "Timmar", + "Minutes" : "Minuter", + "Enabled" : "Aktiverad", + "New Alarm" : "Ny alarm", + "Save" : "Spara", + "Back" : "Tillbaka", + "Repeat" : "Upprepning", + "Delete" : "Radera", + "ALARM!" : "ALURH!", + "Sleep" : "Sömn" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "uppr." + } +} diff --git a/lang/tr_TR.json b/lang/tr_TR.json new file mode 100644 index 000000000..c59bc7d6b --- /dev/null +++ b/lang/tr_TR.json @@ -0,0 +1,21 @@ +{ + "//":"Turkish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Alarmlar", + "Hours" : "Saat", + "Minutes" : "Dakika", + "Enabled" : "Etkinleştirildi", + "New Alarm" : "Yeni alarm", + "Save" : "Sakla", + "Back" : "Geriye", + "Repeat" : "Yineleme", + "Delete" : "Sil", + "ALARM!" : "ALARM!", + "Sleep" : "Uyku" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "yineleme" + } +} diff --git a/loader.js b/loader.js index 680cd0f94..768f5f38f 100644 --- a/loader.js +++ b/loader.js @@ -11,7 +11,7 @@ if (window.location.host=="banglejs.com") { 'This is not the official Bangle.js App Loader - you can try the Official Version here.'; } -var RECOMMENDED_VERSION = "2v10"; +var RECOMMENDED_VERSION = "2v11"; // could check http://www.espruino.com/json/BANGLEJS.json for this // We're only interested in Bangles