diff --git a/apps/clockcal/ChangeLog b/apps/clockcal/ChangeLog new file mode 100644 index 000000000..e874c8c67 --- /dev/null +++ b/apps/clockcal/ChangeLog @@ -0,0 +1 @@ +0.01: Initial upload diff --git a/apps/clockcal/README.md b/apps/clockcal/README.md new file mode 100644 index 000000000..c19ee54a6 --- /dev/null +++ b/apps/clockcal/README.md @@ -0,0 +1,21 @@ +# Clock & Calendar by Michael + +This is my "Hello World". I first made this watchface almost 10 years ago for my original Pebble and Pebble Time and I missed this so much, that I had to write it for the BangleJS2. +I know that it seems redundant because there already **is** a *time&cal*-app, but it didn't fit my style. + +- locked screen with only one minimal update/minute +- ![locked screen](https://foostuff.github.io/BangleApps/apps/clockcal/screenshot.png) +- unlocked screen (twist?) with seconds +- ![unlocked screen](https://foostuff.github.io/BangleApps/apps/clockcal/screenshot2.png) + +## Configurable Features +- Number of calendar rows (weeks) +- Buzz on connect/disconnect (I know, this should be an extra widget, but for now, it is included) +- Clock Mode (24h/12h). Doesn't have an am/pm indicator. It's only there because it was easy. +- First day of the week +- Red Saturday +- Red Sunday + +## Feedback +The clock works for me in a 24h/MondayFirst/WeekendFree environment but is not well-tested with other settings. +So if something isn't working, please tell me: https://github.com/foostuff/BangleApps/issues diff --git a/apps/clockcal/app-icon.js b/apps/clockcal/app-icon.js new file mode 100644 index 000000000..5bab7853e --- /dev/null +++ b/apps/clockcal/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkECqMCkQACiEDkIXQuUnkUBkESiYXPgN/u8jgEx/8vC6E3k9xiH//8/C6BHCPQMSL6EDO4cgaf4A/ACEC+YFDl4FEAAM/+ISHbIIECh4FB+QWEA4PwCQsfC4gVBkYGDgP/mQ4CCQk/iAXEAQTiCgMiDQQSFiATDBgQXCgILBEQkQBwYrEC4sPLQRpCBwoXECgUCC4oSBAggXHNQRfDV4X/JgQXJBIIXFgYuDC5QKBiE/C4f/bwgXJmanGJgoSDiTQBmQMBE4JYBfwJ5BBYMiYQISEB4IAB+KdCAgfwAwTrCn4SDiczAAMwGwMTmR0CmECBgRSBCQwA/AGsBgEQAgYABAwcHu93s4GBqAXEmLrCiYICmICBj4XEgvABIMMqECiIXCgQXCegLYBC4NwF4VcAQNV4EPkEhF4REBgYXCiQvCu4UCAQMFJYRfKgxGBuxfGLgkjFgMCkMBmEjgEigZaBI4XFMYcRC4kBmRhBkMQgI5DF4MFgAXCLARfCFoIvDkZmBhnF4sA5gvDYghfEHIQJDAAhQBIAPwVQMTgQvCNIMhAwJfBR4MMU4JRB+RJBiUQgUDVwMgYwMBgcwX4amBqBQBiTqBgUQh8RmJhCL4IvC4HMR4ZaEAgIBBL4LBDL5EBmI5BkQvBXwIGBmMPMwMvkEFR4VcR4UgU4MSC4UQmIJBn7dBiQNBqoXBPYNQh8Q+MB+MvgEvG4JyBj8A+RkBhlQd4ZHBiBYCL4bBELxEAA==")) \ No newline at end of file diff --git a/apps/clockcal/app.js b/apps/clockcal/app.js new file mode 100644 index 000000000..fc299912f --- /dev/null +++ b/apps/clockcal/app.js @@ -0,0 +1,119 @@ +Bangle.loadWidgets(); + +var s = Object.assign({ + CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets. + BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. Will be extra widget eventually + MODE24: true, //24h mode vs 12h mode + FIRSTDAYOFFSET: 6, //First day of the week: 0-6: Sun, Sat, Fri, Thu, Wed, Tue, Mon + REDSUN: true, // Use red color for sunday? + REDSAT: true, // Use red color for saturday? +}, require('Storage').readJSON("clockcal.json", true) || {}); + +const h = g.getHeight(); +const w = g.getWidth(); +const CELL_W = w / 7; +const CELL_H = 15; +const CAL_Y = h - s.CAL_ROWS * CELL_H; +const DEBUG = false; + +function drawMinutes() { + if (DEBUG) console.log("|-->minutes"); + var d = new Date(); + var hours = s.MODE24 ? d.getHours().toString().padStart(2, ' ') : ((d.getHours() + 24) % 12 || 12).toString().padStart(2, ' '); + var minutes = d.getMinutes().toString().padStart(2, '0'); + var textColor = NRF.getSecurityStatus().connected ? '#fff' : '#f00'; + var size = 50; + var clock_x = (w - 20) / 2; + if (dimSeconds) { + size = 65; + clock_x = 4 + (w / 2); + } + g.setBgColor(0); + g.setColor(textColor); + g.setFont("Vector", size); + g.setFontAlign(0, 1); + g.drawString(hours + ":" + minutes, clock_x, CAL_Y - 10, 1); + var nextminute = (61 - d.getSeconds()); + if (typeof minuteInterval !== "undefined") clearTimeout(minuteInterval); + minuteInterval = setTimeout(drawMinutes, nextminute * 1000); +} + +function drawSeconds() { + if (DEBUG) console.log("|--->seconds"); + var d = new Date(); + g.setColor(); + g.fillRect(w - 31, CAL_Y - 36, w - 3, CAL_Y - 19); + g.setBgColor(0); + g.setColor('#fff'); + g.setFont("Vector", 24); + g.setFontAlign(1, 1); + g.drawString(" " + d.getSeconds().toString().padStart(2, '0'), w, CAL_Y - 13); + if (typeof secondInterval !== "undefined") clearTimeout(secondInterval); + if (!dimSeconds) secondInterval = setTimeout(drawSeconds, 1000); +} + +function drawCalendar() { + if (DEBUG) console.log("CALENDAR"); + var d = new Date(); + g.reset(); + g.setBgColor(0); + g.clear(); + drawMinutes(); + if (!dimSeconds) drawSeconds(); + const dow = (s.FIRSTDAYOFFSET + d.getDay()) % 7; //MO=0, SU=6 + const today = d.getDate(); + var rD = new Date(d.getTime()); + rD.setDate(rD.getDate() - dow); + var rDate = rD.getDate(); + g.setFontAlign(1, 1); + for (var y = 1; y <= s.CAL_ROWS; y++) { + for (var x = 1; x <= 7; x++) { + bottomrightX = x * CELL_W - 2; + bottomrightY = y * CELL_H + CAL_Y; + g.setFont("Vector", 16); + var fg = ((s.REDSUN && rD.getDay() == 0) || (s.REDSAT && rD.getDay() == 6)) ? '#f00' : '#fff'; + if (y == 1 && today == rDate) { + g.setColor('#0f0'); + g.fillRect(bottomrightX - CELL_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2); + g.setColor('#000'); + g.drawString(rDate, bottomrightX, bottomrightY); + } + else { + g.setColor(fg); + g.drawString(rDate, bottomrightX, bottomrightY); + } + rD.setDate(rDate + 1); + rDate = rD.getDate(); + } + } + Bangle.drawWidgets(); + + var nextday = (3600 * 24) - (d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds() + 1); + if (DEBUG) console.log("Next Day:" + (nextday / 3600)); + if (typeof dayInterval !== "undefined") clearTimeout(dayInterval); + dayInterval = setTimeout(drawCalendar, nextday * 1000); +} + +function BTevent() { + drawMinutes(); + if (s.BUZZ_ON_BT) { + var interval = (NRF.getSecurityStatus().connected) ? 100 : 500; + Bangle.buzz(interval); + setTimeout(function () { Bangle.buzz(interval); }, interval * 3); + } +} + +//register events +Bangle.on('lock', locked => { + if (typeof secondInterval !== "undefined") clearTimeout(secondInterval); + dimSeconds = locked; //dim seconds if lock=on + drawCalendar(); +}); +NRF.on('connect', BTevent); +NRF.on('disconnect', BTevent); + + +dimSeconds = Bangle.isLocked(); +drawCalendar(); + +Bangle.setUI("clock"); diff --git a/apps/clockcal/app.png b/apps/clockcal/app.png new file mode 100644 index 000000000..2e2e4461e Binary files /dev/null and b/apps/clockcal/app.png differ diff --git a/apps/clockcal/metadata.json b/apps/clockcal/metadata.json new file mode 100644 index 000000000..ccc84a980 --- /dev/null +++ b/apps/clockcal/metadata.json @@ -0,0 +1,19 @@ +{ + "id": "clockcal", + "name": "Clock & Calendar", + "version": "0.01", + "description": "Clock with Calendar", + "readme":"README.md", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"}], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS","BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"clockcal.app.js","url":"app.js"}, + {"name":"clockcal.settings.js","url":"settings.js"}, + {"name":"clockcal.img","url":"app-icon.js","evaluate":true} + ], + "data": [{"name":"clockcal.json"}] +} diff --git a/apps/clockcal/screenshot.png b/apps/clockcal/screenshot.png new file mode 100644 index 000000000..fcfde0c4a Binary files /dev/null and b/apps/clockcal/screenshot.png differ diff --git a/apps/clockcal/screenshot2.png b/apps/clockcal/screenshot2.png new file mode 100644 index 000000000..98acfa9a0 Binary files /dev/null and b/apps/clockcal/screenshot2.png differ diff --git a/apps/clockcal/settings.js b/apps/clockcal/settings.js new file mode 100644 index 000000000..cc2a78181 --- /dev/null +++ b/apps/clockcal/settings.js @@ -0,0 +1,92 @@ +(function (back) { + var FILE = "clockcal.json"; + + settings = Object.assign({ + CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets. + BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. Will be extra widget eventually + MODE24: true, //24h mode vs 12h mode + FIRSTDAY: 6, //First day of the week: mo, tu, we, th, fr, sa, su + REDSUN: true, // Use red color for sunday? + REDSAT: true, // Use red color for saturday? + }, require('Storage').readJSON(FILE, true) || {}); + + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + + menu = { + "": { "title": "Clock & Calendar" }, + "< Back": () => back(), + 'Buzz(dis)conn.?': { + value: settings.BUZZ_ON_BT, + format: v => v ? "On" : "Off", + onchange: v => { + settings.BUZZ_ON_BT = v; + writeSettings(); + } + }, + '#Calendar Rows': { + value: settings.CAL_ROWS, + min: 0, max: 6, + onchange: v => { + settings.CAL_ROWS = v; + writeSettings(); + } + }, + 'Clock mode': { + value: settings.MODE24, + format: v => v ? "24h" : "12h", + onchange: v => { + settings.MODE24 = v; + writeSettings(); + } + }, + 'First Day': { + value: settings.FIRSTDAY, + min: 0, max: 6, + format: v => ["Sun", "Sat", "Fri", "Thu", "Wed", "Tue", "Mon"][v], + onchange: v => { + settings.FIRSTDAY = v; + writeSettings(); + } + }, + 'Red Saturday?': { + value: settings.REDSAT, + format: v => v ? "On" : "Off", + onchange: v => { + settings.REDSAT = v; + writeSettings(); + } + }, + 'Red Sunday?': { + value: settings.REDSUN, + format: v => v ? "On" : "Off", + onchange: v => { + settings.REDSUN = v; + writeSettings(); + } + }, + 'Load deafauls?': { + value: 0, + min: 0, max: 1, + format: v => ["No", "Yes"][v], + onchange: v => { + if (v == 1) { + settings = { + CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets. + BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. + MODE24: true, //24h mode vs 12h mode + FIRSTDAY: 6, //First day of the week: mo, tu, we, th, fr, sa, su + REDSUN: true, // Use red color for sunday? + REDSAT: true, // Use red color for saturday? + }; + writeSettings(); + load() + } + } + }, + } + // Show the menu + E.showMenu(menu); +}) diff --git a/apps/daisy/ChangeLog b/apps/daisy/ChangeLog index 254124d4e..115c8f2ff 100644 --- a/apps/daisy/ChangeLog +++ b/apps/daisy/ChangeLog @@ -1,3 +1,4 @@ 0.01: first release 0.02: added settings menu to change color 0.03: fix metadata.json to allow setting as clock +0.04: added heart rate which is switched on when cycled to it through up/down touch on rhs diff --git a/apps/daisy/README.md b/apps/daisy/README.md index 3f22f5dd9..12a55ddfd 100644 --- a/apps/daisy/README.md +++ b/apps/daisy/README.md @@ -1,4 +1,4 @@ -# Daisy +# Daisy ![](app.png) *A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, @@ -8,15 +8,20 @@ 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/) -* Derived from `The Ring` proof of concept and the [Pastel clock](https://banglejs.com/apps/?q=pastel) +* Derived from [The Ring](https://banglejs.com/apps/?id=thering) proof of concept and the [Pastel clock](https://banglejs.com/apps/?q=pastel) * Includes the [Lazybones](https://banglejs.com/apps/?q=lazybones) Idle warning timer -* Touch the top right/top left to cycle through the info display (Day, Date, Steps, Sunrise, Sunset) +* Touch the top right/top left to cycle through the info display (Day, Date, Steps, Sunrise, Sunset, Heart Rate) +* The heart rate monitor is turned on only when Heart rate is selected and will take a few seconds to settle +* The heart value is displayed in RED if the confidence value is less than 50% +* NOTE: The heart rate monitor of Bangle JS 2 is not very accurate when moving about. +See [#1248](https://github.com/espruino/BangleApps/issues/1248) * Uses mylocation.json from MyLocation app to calculate sunrise and sunset times for your location +* If your Sunrise, Sunset times look odd make sure you have setup your location using +[MyLocation](https://banglejs.com/apps/?id=mylocation) * The screen is updated every minute to save battery power * Uses the [BloggerSansLight](https://www.1001fonts.com/rounded-fonts.html?page=3) font, which if free for commercial use ## Future Development -* Add a heart rate option in the information line that turns on when selected * Use mini icons in the information line rather that text * Add weather icons as per Pastel clock * Add a lock icon to the screen @@ -25,5 +30,3 @@ Forum](http://forum.espruino.com/microcosms/1424/) ![](screenshot_daisy1.png) It is worth looking at the real thing though as the screenshot does not do it justice. -(Though I need to redo this photo at some point) -![](screenshot_daisy2.jpg) diff --git a/apps/daisy/app.js b/apps/daisy/app.js index 2717f94db..01d177a32 100644 --- a/apps/daisy/app.js +++ b/apps/daisy/app.js @@ -14,16 +14,19 @@ let warned = 0; let idle = false; let IDLE_MINUTES = 26; -var pal1; // palette for 0-40% -var pal2; // palette for 50-100% -const infoWidth = 50; -const infoHeight = 14; +let pal1; // palette for 0-40% +let pal2; // palette for 50-100% +const infoLine = (3*h/4) - 6; +const infoWidth = 56; +const infoHeight = 11; var drawingSteps = false; function log_debug(o) { //print(o); } +var hrmImg = require("heatshrink").decompress(atob("i0WgIKHgPh8Ef5/g///44CBz///1///5A4PnBQk///wA4PBA4MDA4MH/+Ah/8gEP4EAjw0GA")); + // https://www.1001fonts.com/rounded-fonts.html?page=3 Graphics.prototype.setFontBloggerSansLight46 = function(scale) { // Actual height 46 (45 - 0) @@ -109,7 +112,8 @@ const infoData = { ID_SR: { calc: () => 'Sunrise: ' + sunRise }, ID_SS: { calc: () => 'Sunset: ' + sunSet }, ID_STEP: { calc: () => 'Steps: ' + getSteps() }, - ID_BATT: { calc: () => 'Battery: ' + E.getBattery() + '%' } + ID_BATT: { calc: () => 'Battery: ' + E.getBattery() + '%' }, + ID_HRM: { calc: () => hrmCurrent } }; const infoList = Object.keys(infoData).sort(); @@ -121,6 +125,9 @@ function nextInfo() { if (idx === infoList.length - 1) infoMode = infoList[0]; else infoMode = infoList[idx + 1]; } + // power HRM on/off accordingly + Bangle.setHRMPower(infoMode == "ID_HRM" ? 1 : 0); + resetHrm(); } function prevInfo() { @@ -129,8 +136,125 @@ function prevInfo() { if (idx === 0) infoMode = infoList[infoList.length - 1]; else infoMode = infoList[idx - 1]; } + // power HRM on/off accordingly + Bangle.setHRMPower(infoMode == "ID_HRM" ? 1 : 0); + resetHrm(); } +function clearInfo() { + g.setColor(g.theme.bg); + //g.setColor(g.theme.fg); + g.fillRect((w/2) - infoWidth, infoLine - infoHeight, (w/2) + infoWidth, infoLine + infoHeight); +} + +function drawInfo() { + clearInfo(); + g.setColor(g.theme.fg); + setSmallFont(); + g.setFontAlign(0,0); + + if (infoMode == "ID_HRM") { + clearInfo(); + g.setColor('#f00'); // red + drawHeartIcon(); + } else { + g.drawString((infoData[infoMode].calc()), w/2, infoLine); + } +} + +function drawHeartIcon() { + g.drawImage(hrmImg, (w/2) - infoHeight - 20, infoLine - infoHeight); +} + +function drawHrm() { + if (idle) return; // dont draw while prompting + var d = new Date(); + clearInfo(); + g.setColor(d.getSeconds()&1 ? '#f00' : g.theme.bg); + drawHeartIcon(); + setSmallFont(); + g.setFontAlign(-1,0); // left + g.setColor(hrmConfidence >= 50 ? g.theme.fg : '#f00'); + g.drawString(hrmCurrent, (w/2) + 10, infoLine); +} + +function draw() { + if (!idle) + drawClock(); + else + drawIdle(); + queueDraw(); +} + +function drawClock() { + var date = new Date(); + var timeStr = require("locale").time(date,1); + var da = date.toString().split(" "); + var time = da[4].substr(0,5); + var hh = da[4].substr(0,2); + var mm = da[4].substr(3,2); + var steps = getSteps(); + var p_steps = Math.round(100*(steps/10000)); + + g.reset(); + g.setColor(g.theme.bg); + g.fillRect(0, 0, w, h); + g.drawImage(getGaugeImage(p_steps), 0, 0); + setLargeFont(); + + g.setColor(settings.fg); + g.setFontAlign(1,0); // right aligned + g.drawString(hh, (w/2) - 1, h/2); + + g.setColor(g.theme.fg); + g.setFontAlign(-1,0); // left aligned + g.drawString(mm, (w/2) + 1, h/2); + + drawInfo(); + + // recalc sunrise / sunset every hour + if (drawCount % 60 == 0) + updateSunRiseSunSet(new Date(), location.lat, location.lon); + drawCount++; +} + +function drawSteps() { + if (drawingSteps) return; + drawingSteps = true; + clearInfo(); + setSmallFont(); + g.setFontAlign(0,0); + g.setColor(g.theme.fg); + g.drawString('Steps ' + getSteps(), w/2, (3*h/4) - 4); + drawingSteps = false; +} + +///////////////// GAUGE images ///////////////////////////////////// + +var hrmCurrent = "--"; +var hrmConfidence = 0; + +function resetHrm() { + hrmCurrent = "--"; + hrmConfidence = 0; + if (infoMode == "ID_HRM") { + clearInfo(); + g.setColor('#f00'); // red + drawHeartIcon(); + } +} + +Bangle.on('HRM', function(hrm) { + hrmCurrent = hrm.bpm; + hrmConfidence = hrm.confidence; + log_debug("HRM=" + hrm.bpm + " (" + hrm.confidence + ")"); + if (infoMode == "ID_HRM" ) drawHrm(); +}); + + +///////////////// GAUGE images ///////////////////////////////////// + + // putting into 1 function like this, rather than individual variables // reduces ram usage from 70%-13% function getGaugeImage(p) { @@ -247,68 +371,6 @@ function getGaugeImage(p) { }; } -function draw() { - if (!idle) - drawClock(); - else - drawIdle(); - queueDraw(); -} - -function drawClock() { - var date = new Date(); - var timeStr = require("locale").time(date,1); - var da = date.toString().split(" "); - var time = da[4].substr(0,5); - var hh = da[4].substr(0,2); - var mm = da[4].substr(3,2); - var steps = getSteps(); - var p_steps = Math.round(100*(steps/10000)); - - g.reset(); - g.setColor(g.theme.bg); - g.fillRect(0, 0, w, h); - g.drawImage(getGaugeImage(p_steps), 0, 0); - setLargeFont(); - - g.setColor(settings.fg); - g.setFontAlign(1,0); // right aligned - g.drawString(hh, (w/2) - 1, h/2); - - g.setColor(g.theme.fg); - g.setFontAlign(-1,0); // left aligned - g.drawString(mm, (w/2) + 1, h/2); - - setSmallFont(); - g.setFontAlign(0,0); // left aligned - g.drawString((infoData[infoMode].calc()), w/2, (3*h/4) - 4); - - // recalc sunrise / sunset every hour - if (drawCount % 60 == 0) - updateSunRiseSunSet(new Date(), location.lat, location.lon); - drawCount++; -} - -function drawSteps() { - if (drawingSteps) return; - drawingSteps = true; - setSmallFont(); - g.setFontAlign(0,0); - var steps = getSteps(); - g.setColor(g.theme.bg); - g.fillRect((w/2) - infoWidth, (3*h/4) - infoHeight, (w/2) + infoWidth, (3*h/4) + infoHeight); - g.setColor(g.theme.fg); - g.drawString('Steps ' + steps, w/2, (3*h/4) - 4); - drawingSteps = false; -} - -/* -Bangle.on('step', s => { - drawSteps(); -}); -*/ - - ///////////////// IDLE TIMER ///////////////////////////////////// function drawIdle() { @@ -392,6 +454,8 @@ Bangle.on('step', s => { } idle = false; warned = 0; + + if (infoMode == "ID_STEP") drawSteps(); }); function checkIdle() { diff --git a/apps/daisy/metadata.json b/apps/daisy/metadata.json index f4a9fcbbe..5e53f2d5e 100644 --- a/apps/daisy/metadata.json +++ b/apps/daisy/metadata.json @@ -1,6 +1,6 @@ { "id": "daisy", "name": "Daisy", - "version":"0.03", + "version":"0.04", "dependencies": {"mylocation":"app"}, "description": "A clock based on the Pastel clock with large ring guage for steps", "icon": "app.png", diff --git a/apps/dtlaunch/ChangeLog b/apps/dtlaunch/ChangeLog index 556472eaa..811784b39 100644 --- a/apps/dtlaunch/ChangeLog +++ b/apps/dtlaunch/ChangeLog @@ -6,3 +6,5 @@ 0.06: Adds settings page (hide clocks or launchers) 0.07: Adds setting for directly launching app on touch for Bangle 2 0.08: Optimize line wrapping for Bangle 2 +0.09: fix the trasparent widget bar if there are no widgets for Bangle 2 +0.10: added "one click exit" setting for Bangle 2 diff --git a/apps/dtlaunch/app-b2.js b/apps/dtlaunch/app-b2.js index 96e562add..e0f7f825f 100644 --- a/apps/dtlaunch/app-b2.js +++ b/apps/dtlaunch/app-b2.js @@ -6,8 +6,12 @@ var settings = Object.assign({ showClocks: true, showLaunchers: true, direct: false, + oneClickExit:false }, require('Storage').readJSON("dtlaunch.json", true) || {}); +if( settings.oneClickExit) + setWatch(_=> load(), BTN1); + var s = require("Storage"); var apps = s.list(/\.info$/).map(app=>{ var a=s.readJSON(app,1); @@ -125,5 +129,6 @@ Bangle.on("touch",(_,p)=>{ }); Bangle.loadWidgets(); +g.clear(); Bangle.drawWidgets(); drawPage(0); diff --git a/apps/dtlaunch/metadata.json b/apps/dtlaunch/metadata.json index 6cd1dbe73..7a4094e54 100644 --- a/apps/dtlaunch/metadata.json +++ b/apps/dtlaunch/metadata.json @@ -1,7 +1,7 @@ { "id": "dtlaunch", "name": "Desktop Launcher", - "version": "0.08", + "version": "0.10", "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"}], "icon": "icon.png", diff --git a/apps/dtlaunch/settings-b2.js b/apps/dtlaunch/settings-b2.js index 7f667d213..8eca46a7e 100644 --- a/apps/dtlaunch/settings-b2.js +++ b/apps/dtlaunch/settings-b2.js @@ -4,7 +4,8 @@ var settings = Object.assign({ showClocks: true, showLaunchers: true, - direct: false + direct: false, + oneClickExit:false }, require('Storage').readJSON(FILE, true) || {}); function writeSettings() { @@ -37,6 +38,14 @@ settings.direct = v; writeSettings(); } + }, + 'One click exit': { + value: settings.oneClickExit, + format: v => v?"On":"Off", + onchange: v => { + settings.oneClickExit = v; + writeSettings(); + } } }); }) diff --git a/apps/gpsautotime/ChangeLog b/apps/gpsautotime/ChangeLog index 5560f00bc..2827c9e5c 100644 --- a/apps/gpsautotime/ChangeLog +++ b/apps/gpsautotime/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Set Bangle.js 2 compatible diff --git a/apps/gpsautotime/metadata.json b/apps/gpsautotime/metadata.json index a64a45f6d..766961276 100644 --- a/apps/gpsautotime/metadata.json +++ b/apps/gpsautotime/metadata.json @@ -2,12 +2,12 @@ "id": "gpsautotime", "name": "GPS auto time", "shortName": "GPS auto time", - "version": "0.01", + "version": "0.02", "description": "A widget that automatically updates the Bangle.js time to the GPS time whenever there is a valid GPS fix.", "icon": "widget.png", "type": "widget", "tags": "widget,gps", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS","BANGLEJS2"], "storage": [ {"name":"gpsautotime.wid.js","url":"widget.js"} ] diff --git a/apps/ratchet_launch/ChangeLog b/apps/ratchet_launch/ChangeLog new file mode 100644 index 000000000..af7f83942 --- /dev/null +++ b/apps/ratchet_launch/ChangeLog @@ -0,0 +1 @@ +0.01: Initial release diff --git a/apps/ratchet_launch/README.md b/apps/ratchet_launch/README.md new file mode 100644 index 000000000..15df463d0 --- /dev/null +++ b/apps/ratchet_launch/README.md @@ -0,0 +1,15 @@ +# Rachet Launcher + +Ratchet Launcher is a fork of the default Launcher with modified user interaction. Instead of free scrolling, apps are selected by swiping up and down, but in discrete "ticks", just like in the settings menus. + +**WARNING: Untested on Bangle.js v1! Please test and give feedback.** + +## Usage +- Choose app: Swipe up/down (top/bottom button on Bangle.js v1) +- Launch app: Tap screen (center button on Bangle.js v1) +- Return to clock: Swipe three ticks beyond first/last app in list + +## Installation +1. Install Ratchet Launcher using App Loader +2. Uninstall default Launcher +3. Reload diff --git a/apps/ratchet_launch/app.js b/apps/ratchet_launch/app.js new file mode 100644 index 000000000..11018080a --- /dev/null +++ b/apps/ratchet_launch/app.js @@ -0,0 +1,149 @@ +var Storage = require("Storage"); +var Layout = require("Layout"); + +var font = g.getFonts().includes("6x15") ? "6x15" : "6x8:2"; +var largeFont = g.getFonts().includes("12x20") ? "12x20" : "6x8:3"; +var currentApp = 0; +var overscroll = 0; +var blankImage = Graphics.createImage(` `); +var rowHeight = g.getHeight()/3; + +// Load apps list +var apps = Storage.list(/\.info$/).map(app=>{ + var a=Storage.readJSON(app,1); + return a&&{ + name:a.name, + type:a.type, + icon:a.icon ? Storage.read(a.icon) : a.icon, + sortorder:a.sortorder, + src:a.src + }; +}).filter(app=>app && ( + app.type=="app" +// || (app.type=="clock" && settings.showClocks) + || !app.type +)); +apps.sort((a,b)=>{ + var n=(0|a.sortorder)-(0|b.sortorder); + if (n) return n; // do sortorder first + if (a.nameb.name) return 1; + return 0; +}); + +// Uncomment for testing in the emulator without apps: +// apps = [ +// { +// name:"Test", +// type:"app", +// icon:blankImage, +// sortorder:undefined, +// src:"" +// }, +// { +// name:"Test 2", +// type:"app", +// icon:blankImage, +// sortorder:undefined, +// src:"" +// }, +// ]; + +// Initialize layout +var layout = new Layout({ + type:"v", c:[ + // A row for the previous app + { type:"h", height:rowHeight, c:[ + {type: "img", id:"prev_icon", src:blankImage, width:48, height:48, scale:0.8, pad:8}, + {type: "txt", id:"prev_name", label:"", font:font, fillx:1, wrap:1}, + ]}, + // A row for the current app + { type:"h", height:rowHeight, c:[ + {type: "img", id:"cur_icon", src:blankImage, width:48, height:48}, + {type: "txt", id:"cur_name", label:"", font:largeFont, fillx:1, wrap:1}, + ]}, + // A row for the next app + { type:"h", height:rowHeight, c:[ + {type: "img", id:"next_icon", src:blankImage, width:48, height:48, scale:0.8, pad:8}, + {type: "txt", id:"next_name", label:"", font:font, fillx:1, wrap:1}, + ]}, + ] +}); + +// Drawing logic +function render() { + if (!apps.length) { + E.showMessage(/*LANG*/"No apps"); + return load(); + } + + // Previous app + if (currentApp > 0) { + layout.prev_icon.src = apps[currentApp-1].icon; + layout.prev_name.label = apps[currentApp-1].name; + } else { + layout.prev_icon.src = blankImage; + layout.prev_name.label = ""; + } + + // Current app + layout.cur_icon.src = apps[currentApp].icon; + layout.cur_name.label = apps[currentApp].name; + + // Next app + if (currentApp < apps.length-1) { + layout.next_icon.src = apps[currentApp+1].icon; + layout.next_name.label = apps[currentApp+1].name; + } else { + layout.next_icon.src = blankImage; + layout.next_name.label = ""; + } + + g.clear(); + layout.render(); +} + +// Launch the currently selected app +function launch() { + var app = apps[currentApp]; + if (!app) return; + if (!app.src || Storage.read(app.src)===undefined) { + E.showMessage(/*LANG*/"App Source\nNot found"); + setTimeout(render, 2000); + } else { + E.showMessage(/*LANG*/"Loading..."); + load(app.src); + } +} + +// Select previous/next app +function move(step) { + if ((currentApp == 0 && step < 0) || (currentApp >= apps.length-1 && step > 0)) { + // When we hit the end of the list (top or bottom), the step is + // counted towards the overscroll value. When the overscroll + // threshold is exceeded, we return to the clock face. + overscroll += step; + } else { + // This is the default case: the step is countedf towards the currentApp index + currentApp += step; + overscroll = 0; + return render(); + } + + // Overscroll threshold reached, return to clock + if (Math.abs(overscroll) > 3) { + Bangle.buzz(500, 1); + return load(); + } +} + +// Wire up user input +Bangle.setUI('updown', dir => { + if (!dir) launch(); + else { + if (process.env.HWVERSION==2) dir *= -1; // "natural scrolling" on touch screen + move(dir); + } +}); + +render(); diff --git a/apps/ratchet_launch/app.png b/apps/ratchet_launch/app.png new file mode 100644 index 000000000..a27ab48ed Binary files /dev/null and b/apps/ratchet_launch/app.png differ diff --git a/apps/ratchet_launch/metadata.json b/apps/ratchet_launch/metadata.json new file mode 100644 index 000000000..14ffec34a --- /dev/null +++ b/apps/ratchet_launch/metadata.json @@ -0,0 +1,16 @@ +{ + "id": "ratchet_launch", + "name": "Ratchet Launcher", + "shortName": "Ratchet", + "version": "0.01", + "description": "Launcher with discrete scrolling for quicker app selection", + "icon": "app.png", + "type": "launch", + "tags": "tool,system,launcher", + "supports": ["BANGLEJS2","BANGLEJS"], + "storage": [ + {"name":"ratchet_launch.app.js","url":"app.js"} + ], + "sortorder": -10, + "readme":"README.md" +} diff --git a/apps/smclock/ChangeLog b/apps/smclock/ChangeLog index b029d805d..0300d5ceb 100644 --- a/apps/smclock/ChangeLog +++ b/apps/smclock/ChangeLog @@ -1,2 +1,4 @@ 0.01: Initial version 0.02: Add battery level +0.03: Fix battery display when full +0.04: Add support for settings diff --git a/apps/smclock/README.md b/apps/smclock/README.md index 7b5613147..635292d0c 100644 --- a/apps/smclock/README.md +++ b/apps/smclock/README.md @@ -3,3 +3,21 @@ Just a simple watch face for the Banglejs2. It shows battery level in the upper left corner, date information in the upper right, and time information in the bottom. + +![](screenshot.png) + +## Settings + +**Analog Clock:** + +**Human Readable Date:** When the setting is on, the date is shown in a more human-friendly format (e.g. "Oct 2"), otherwise the date is shown in a standard format (e.g. "02/10"). Default is off. + +**Show Week Info:** When the setting is on, the weekday and week number are shown in the upper right box. When the setting is off, the full year is shown instead. Default is off. + +**Vector Font:** When the setting is on, the app uses Espruino's vector font, otherwise it uses the default font. Default is off. + +## Using the app + +Monogram Watch Face can be selected as the default clock or it can be run manually from the launcher. Its settings can be accessed and changed via the relevant menu. + +Tapping on the "Alerts" area will replace the current time display with the time of the most immediate alert. diff --git a/apps/smclock/app.js b/apps/smclock/app.js index 6aff72a46..350c0dd07 100644 --- a/apps/smclock/app.js +++ b/apps/smclock/app.js @@ -1,48 +1,81 @@ +const SETTINGSFILE = "smclock.json"; const background = { - width : 176, height : 176, bpp : 3, - transparent : 1, - buffer : require("heatshrink").decompress(atob("/4A/AH4ACUb8H9MkyVJAThB/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/INP/AH4A/AAX8Yz4Afn5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/INI=")) + width: 176, + height: 176, + bpp: 3, + transparent: 1, + buffer: require("heatshrink").decompress( + atob( + "/4A/AH4ACUb8H9MkyVJAThB/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/INP/AH4A/AAX8Yz4Afn5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/INI=" + ) + ), }; +const monthName = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; +const weekday = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; -const weekday = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]; -var level = -1; +// dynamic variables +var batLevel = -1; +var batColor = [0, 0, 0]; -function ISO8601_week_no(date) { //copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480 - var tdt = new Date(date.valueOf()); - var dayn = (date.getDay() + 6) % 7; - tdt.setDate(tdt.getDate() - dayn + 3); - var firstThursday = tdt.valueOf(); - tdt.setMonth(0, 1); - if (tdt.getDay() !== 4) { - tdt.setMonth(0, 1 + ((4 - tdt.getDay()) + 7) % 7); - } - return 1 + Math.ceil((firstThursday - tdt) / 604800000); +// settings variables +var dateFormat; +var drawInterval; +var pollInterval; +var showAnalogFace; +var showWeekInfo; +var useVectorFont; + +// load settings +function loadSettings() { + // Helper function default setting + function def(value, def) {return value !== undefined ? value : def;} + var settings = require("Storage").readJSON(SETTINGSFILE, true) || {}; + + dateFormat = def(settings.dateFormat, "Short"); + drawInterval = def(settings.drawInterval, 10); + pollInterval = def(settings.pollInterval, 60); + showAnalogFace = def(settings.showAnalogFace, false); + showWeekInfo = def(settings.showWeekInfo, false); + useVectorFont = def(settings.useVectorFont, false); +} + +// copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480 +function ISO8601_week_no(date) { + var tdt = new Date(date.valueOf()); + var dayn = (date.getDay() + 6) % 7; + tdt.setDate(tdt.getDate() - dayn + 3); + var firstThursday = tdt.valueOf(); + tdt.setMonth(0, 1); + if (tdt.getDay() !== 4) { + tdt.setMonth(0, 1 + ((4 - tdt.getDay() + 7) % 7)); + } + return 1 + Math.ceil((firstThursday - tdt) / 604800000); } function d02(value) { - return ('0' + value).substr(-2); + return ("0" + value).substr(-2); } function pollBattery() { - level = E.getBattery(); - return level; + batLevel = E.getBattery(); } function getBatteryColor(level) { var color; if (level < 0) { - level = pollBattery(); + pollBattery(); + level = batLevel; } - if(level>80) { - color = [0,0,1]; - } else if(level>60) { - color = [0,1,1]; - } else if(level>40) { - color = [0,1,0]; - } else if(level>20) { - color = [1,1,0]; + if (level > 80) { + color = [0, 0, 1]; + } else if (level > 60) { + color = [0, 1, 1]; + } else if (level > 40) { + color = [0, 1, 0]; + } else if (level > 20) { + color = [1, 1, 0]; } else { - color = [1,0,0]; + color = [1, 0, 0]; } return color; } @@ -50,57 +83,110 @@ function getBatteryColor(level) { function draw() { g.drawImage(background); - const color = getBatteryColor(); - const bat = d02(E.getBattery()) + "%"; + const color = getBatteryColor(batLevel); + var bat = ""; const d = new Date(); const day = d.getDate(); - const month = (d.getMonth() + 1); + const month = d.getMonth() + 1; const week = d02(ISO8601_week_no(d)); - const date1 = d02(day) + "/" + d02(month); - const date2 = weekday[d.getDay()] + " " + d02(week); + var date1 = ""; + var date2 = ""; const h = d.getHours(); const m = d.getMinutes(); const time = d02(h) + ":" + d02(m); + if (E.getBattery() < 100) { + bat = d02(E.getBattery()) + "%"; + } else { + bat = E.getBattery() + "%"; + } + g.reset(); - g.setColor(0, 0, 0); - g.setFont("Vector", 20); - g.drawString(date1, 105, 20, false); - g.setFont("Vector", 16); - g.drawString(date2, 105, 55, false); - + // draw battery info g.setColor(1, 1, 1); - g.setFont("Vector", 60); - g.drawString(time, 10, 108, false); - - g.setColor(1, 1, 1); - g.setFont("Vector", 16); - g.drawString("Bat:", 12, 22, false); + if (useVectorFont == true) { + g.setFont("Vector", 16); + g.drawString("Bat:", 12, 22, false); + } else { + g.setFont("4x6", 2); + g.drawString("Bat:", 10, 22, false); + } g.setColor(color[0], color[1], color[2]); - g.drawString(bat, 52, 22, false); + if (batLevel < 100) { + g.drawString(bat, 52, 22, false); + } else { + g.drawString(bat, 46, 22, false); + } + + // draw date info + g.setColor(0, 0, 0); + if (useVectorFont == true) { + g.setFont("Vector", 20); + } else { + g.setFont("6x8", 2); + } + if (dateFormat == "Short") { + date1 = d02(day) + "/" + d02(month); + g.drawString(date1, 105, 20, false); + } else { + date1 = monthName[month - 1] + d02(day); + g.drawString(date1, 104, 20, false); + } + + // draw week info + if (showWeekInfo == true) { + date2 = weekday[d.getDay()] + " " + d02(week) + if (useVectorFont == true) { + g.setFont("Vector", 18); + } else { + g.setFont("6x8", 2); + } + g.drawString(date2, 105, 55, false); + } else { + date2 = d.getFullYear(); + if (useVectorFont == true) { + g.setFont("Vector", 22); + g.drawString(date2, 105, 55, false); + } else { + g.setFont("4x6", 3); + g.drawString(date2, 108, 55, false); + } + } + + // draw time + g.setColor(1, 1, 1); + if (useVectorFont == true) { + g.setFont("Vector", 60); + g.drawString(time, 10, 108, false); + } else { + g.setFont("6x8", 5); + g.drawString(time, 14, 112, false); + } } +loadSettings(); + g.clear(); pollBattery(); draw(); -var batInterval = setInterval(pollBattery, 60000); -var drawInterval = setInterval(draw, 10000); +var batInterval = setInterval(pollBattery, pollInterval * 1000); +var actualDrawInterval = setInterval(draw, drawInterval * 1000); // Stop updates when LCD is off, restart when on -Bangle.on('lcdPower',on=>{ +Bangle.on("lcdPower", (on) => { if (batInterval) clearInterval(batInterval); batInterval = undefined; - if (drawInterval) clearInterval(drawInterval); - drawInterval = undefined; + if (actualDrawInterval) clearInterval(actualDrawInterval); + actualDrawInterval = undefined; if (on) { - batInterval = setInterval(pollBattery, 60000); - drawInterval = setInterval(draw, 10000); - + batInterval = setInterval(pollBattery, pollInterval * 1000); + actualDrawInterval = setInterval(draw, drawInterval * 1000); + pollBattery(); - draw(); // draw immediately + draw(); } }); diff --git a/apps/smclock/metadata.json b/apps/smclock/metadata.json index 1783ca7bf..cc995d587 100644 --- a/apps/smclock/metadata.json +++ b/apps/smclock/metadata.json @@ -1,16 +1,20 @@ { - "id":"smclock", - "name":"Monogram Watch Face", - "shortName":"MonoClock", - "icon":"app.png", - "version":"0.02", + "id": "smclock", + "name": "Monogram Watch Face", + "shortName": "MonoClock", + "icon": "app.png", + "screenshots": [{ "url": "screenshot.png" }], + "version": "0.04", "description": "A simple watchface based on my stylised monogram.", - "tags":"clock", - "readme":"README.md", - "supports" : ["BANGLEJS2"], + "type": "clock", + "tags": "clock", + "readme": "README.md", + "supports": ["BANGLEJS", "BANGLEJS2"], "allow_emulator": true, "storage": [ - {"name":"smclock.app.js","url":"app.js"}, - {"name":"smclock.img","url":"app-icon.js","evaluate":true} - ] + { "name": "smclock.app.js", "url": "app.js" }, + { "name": "smclock.settings.js", "url": "settings.js" }, + { "name": "smclock.img", "url": "app-icon.js", "evaluate": true } + ], + "data": [{ "name": "smclock.json" }] } diff --git a/apps/smclock/screenshot.png b/apps/smclock/screenshot.png new file mode 100644 index 000000000..c0e0bd0ee Binary files /dev/null and b/apps/smclock/screenshot.png differ diff --git a/apps/smclock/settings.js b/apps/smclock/settings.js new file mode 100644 index 000000000..a6c7d1b98 --- /dev/null +++ b/apps/smclock/settings.js @@ -0,0 +1,94 @@ +// settings menu for Monogram Watch Face +// Anton Clock settings were used as template +// helper functions taken from Anton Clock + +(function (back) { + var FILE = "smclock.json"; + // load settings from the file + // assign default values if it doesn't exist + var settings = Object.assign({ + dateFormat: "Short", + drawInterval: 10, + pollInterval: 60, + showAnalogFace: false, + showWeekInfo: false, + useVectorFont: false, + }, require("Storage").readJSON(FILE, true) || {}); + + // write the new settings to the file + function writeSettings() {require("Storage").writeJSON(FILE, settings);} + + // helper method which uses int-based menu item for set of string values + function stringItems(startvalue, writer, values) { + return { + value: startvalue === undefined ? 0 : values.indexOf(startvalue), + format: v => values[v], + min: 0, + max: values.length - 1, + wrap: true, + step: 1, + onchange: v => { + writer(values[v]); + writeSettings(); + }, + }; + } + + // helper method which breaks string set settings down to local settings object + function stringInSettings(name, values) { + return stringItems(settings[name], (v) => (settings[name] = v), values); + } + + // settings menu + var mainmenu = { + "": {title: "Monogram Clock",}, + "< Back": () => back(), + "Analog Face": { + value: + settings.showAnalogFace !== undefined ? settings.showAnalogFace : false, + format: v => v ? "On" : "Off", + onchange: v => { + settings.showAnalogFace = v; + writeSettings(); + }, + }, + Date: stringInSettings("dateFormat", ["Long", "Short"]), + "Draw Interval": { + value: settings.drawInterval, + onchange: v => { + settings.drawInterval = v; + writeSettings(); + }, + }, + "Poll Interval": { + value: settings.pollInterval, + onchange: v => { + settings.pollInterval = v; + writeSettings(); + }, + }, + "Week Info": { + value: + settings.showWeekInfo !== undefined ? settings.showWeekInfo : false, + format: v => v ? "On" : "Off", + onchange: v => { + settings.showWeekInfo = v; + writeSettings(); + }, + }, + "Vector Font": { + value: + settings.useVectorFont !== undefined ? settings.useVectorFont : false, + format: v => v ? "On" : "Off", + onchange: v => { + settings.useVectorFont = v; + writeSettings(); + }, + }, + }; + + // Actually display the menu + E.showMenu(mainmenu); +}); + +// end of file diff --git a/lang/nl_NL.json b/lang/nl_NL.json index 0a39fefb2..117d4feeb 100644 --- a/lang/nl_NL.json +++ b/lang/nl_NL.json @@ -11,10 +11,10 @@ "Back": "Terug", "Repeat": "Herhalen", "Delete": "Verwijderen", - "ALARM!": "ALARV.", - "Sleep": "Stand-by", + "ALARM!": "ALARM!", + "Sleep": "Standby", "New Timer": "Nieuwe Timer", - "(repeat)": "(herhaling)", + "(repeat)": "(herhaal)", "music": "muziek", "week": "week", "Auto snooze": "Auto snooze", @@ -31,7 +31,7 @@ "minimum": "minimum", "valid period": "geldige periode", "heartrate": "hartslag", - "battery warn": "batterijwaarschuwing", + "battery warn": "batterijwaarsch.", "data": "gegevens", "step length": "staplengte", "min. confidence": "min. vertrouwen", @@ -47,7 +47,7 @@ "Yes\ndefinitely": "Ja\nzeker", "STEPS": "STAPPEN", "Show clocks": "Toon klokken", - "Record Run": "Record run", + "Record Run": "Rondje opnemen", "No Messages": "Geen berichten.", "View Message": "Bekijk bericht", "Piezo": "Piƫzo", @@ -63,7 +63,7 @@ "Make Connectable": "Maak Verbindbaar", "Quiet Mode": "Rustige modus", "BLE": "BLE", - "Dark BW": "Donker BW", + "Dark BW": "Donkere modus", "Apps": "Apps", "Programmable": "Programmeerbaar", "Vibration": "Trilling", @@ -82,32 +82,32 @@ "Remove": "Verwijder", "Add Device": "Apparaat toevoegen", "Connect device\nto add to\nwhitelist": "Apparaat aansluiten\ntoe te voegen aan\nwhitelist", - "Wake on Twist": "Wake on Twist", - "Wake on BTN2": "Wake op BTN2", - "Wake on BTN1": "Wake op BTN1", - "Wake on FaceUp": "Wakker worden op FaceUp", + "Wake on Twist": "Aangaan bij draaien", + "Wake on BTN2": "Aangaan bij BTN2", + "Wake on BTN1": "Aangaan bij BTN1", + "Wake on FaceUp": "Aangaan bij FaceUp", "Log": "Log", "Debug Info": "Debug info", - "Wake on BTN3": "Wake op BTN3", - "Flatten Battery": "Batterij plat maken", + "Wake on BTN3": "Aangaan bij BTN3", + "Flatten Battery": "Batterij leegmaken", "Rewrite Settings": "Instellingen herschrijven", - "Compact Storage": "Compacte opslag", - "Utilities": "Nutsbedrijven", + "Compact Storage": "Comprimeer opslag", + "Utilities": "Gereedschap", "Clock Style": "Klok Stijl", "Time Zone": "Tijdzone", - "Twist Timeout": "Time-out draaien", - "Twist Max Y": "Twist Max Y", - "Twist Threshold": "Twist Drempel", - "Wake on Touch": "Wakker worden bij aanraking", - "Compacting...\nTakes approx\n1 minute": "Verdichten...\nDuurt ongeveer\n1 minuut", - "Reset to Defaults": "Terugzetten op standaardwaarden", + "Twist Timeout": "Draaien time-out", + "Twist Max Y": "Draaien Max Y", + "Twist Threshold": "Draaien vanaf", + "Wake on Touch": "Aangaan bij aanraking", + "Compacting...\nTakes approx\n1 minute": "Comprimeren...\nDuurt ongeveer\n1 minuut", + "Reset to Defaults": "Terug naar standaardwaarden", "No Clocks Found": "Geen klokken gevonden", "Month": "Maand", - "Minute": "Minuutje", + "Minute": "Minuut", "Flattening battery - this can take hours.\nLong-press button to cancel": "Batterij leegmaken - dit kan uren duren.\nDruk lang op de knop om te annuleren", "Sleep Phase Alarm": "Slaapfase alarm", "Second": "Tweede", - "Turn Off": "Zet uit.", + "Turn Off": "Uitzetten", "Hour": "Uur", "Storage": "Opslag", "Date": "Datum", @@ -144,7 +144,7 @@ "Hide": "Verberg", "Messages": "Berichten", "Error in settings": "Fout in instellingen", - "BACK": "ACHTER", + "BACK": "TERUG", "Whitelist": "Whitelist", "Set Time": "Tijd instellen", "Disable": "Uitschakelen", @@ -162,7 +162,7 @@ "Loading": "Laden", "Music": "Muziek", "color": "kleur", - "off": "van", + "off": "uit", "Off": "Uit", "Theme": "Thema" },