Merge remote-tracking branch 'origin/master'
153
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.14",
|
||||
"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"},
|
||||
|
@ -132,7 +132,7 @@
|
|||
{
|
||||
"id": "health",
|
||||
"name": "Health Tracking",
|
||||
"version": "0.08",
|
||||
"version": "0.09",
|
||||
"description": "Logs health data and provides an app to view it (requires firmware 2v10.100 or later)",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,health",
|
||||
|
@ -167,7 +167,7 @@
|
|||
{
|
||||
"id": "setting",
|
||||
"name": "Settings",
|
||||
"version": "0.36",
|
||||
"version": "0.38",
|
||||
"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",
|
||||
|
@ -554,7 +554,7 @@
|
|||
{
|
||||
"id": "impwclock",
|
||||
"name": "Imprecise Word Clock",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "Imprecise word clock for vacations, weekends, and those who never need accurate time.",
|
||||
"icon": "clock-impword.png",
|
||||
"type": "clock",
|
||||
|
@ -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}
|
||||
]
|
||||
},
|
||||
|
@ -2428,7 +2429,7 @@
|
|||
{
|
||||
"id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "Simple calendar",
|
||||
"icon": "calendar.png",
|
||||
"screenshots": [{"url":"screenshot_calendar.png"}],
|
||||
|
@ -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",
|
||||
|
@ -3895,7 +3896,7 @@
|
|||
"id": "qmsched",
|
||||
"name": "Quiet Mode Schedule and Widget",
|
||||
"shortName": "Quiet Mode",
|
||||
"version": "0.05",
|
||||
"version": "0.06",
|
||||
"description": "Automatically turn Quiet Mode on or off at set times, and change LCD options while Quiet Mode is active.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot_b1_main.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_lcd.png"},
|
||||
|
@ -4013,11 +4014,12 @@
|
|||
{
|
||||
"id": "thermom",
|
||||
"name": "Thermometer",
|
||||
"version": "0.03",
|
||||
"description": "Displays the current temperature in degree Celsius, updated every 20 seconds",
|
||||
"version": "0.05",
|
||||
"description": "Displays the current temperature in degree Celsius/Fahrenheit (depending on locale), updates every 10 seconds with average of last 5 readings.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"thermom.app.js","url":"app.js"},
|
||||
|
@ -4079,7 +4081,7 @@
|
|||
"id": "thermomF",
|
||||
"name": "Fahrenheit Temp",
|
||||
"version": "0.01",
|
||||
"description": "A modification of the Thermometer App to display temprature in Fahrenheit",
|
||||
"description": "[NOT RECOMMENDED] A modification of the Thermometer App to display temprature in Fahrenheit. Please use the 'Thermometer App' and install 'Languages' to get the temperature in the correct format for your locale.",
|
||||
"icon": "thermf.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
|
@ -4208,10 +4210,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",
|
||||
|
@ -4485,7 +4487,7 @@
|
|||
"name": "LCARS Clock",
|
||||
"shortName":"LCARS",
|
||||
"icon": "lcars.png",
|
||||
"version":"0.06",
|
||||
"version":"0.07",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"description": "Library Computer Access Retrieval System (LCARS) clock.",
|
||||
|
@ -4494,7 +4496,8 @@
|
|||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"lcars.app.js","url":"lcars.app.js"},
|
||||
{"name":"lcars.img","url":"lcars.icon.js","evaluate":true}
|
||||
{"name":"lcars.img","url":"lcars.icon.js","evaluate":true},
|
||||
{"name":"lcars.settings.js","url":"lcars.settings.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "binwatch",
|
||||
|
@ -4649,7 +4652,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 +4725,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 +4861,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 +4879,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 +4921,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 +4945,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 +4976,91 @@
|
|||
{"name":"coretemp.app.js","url":"coretemp.js"},
|
||||
{"name":"coretemp.img","url":"coretemp-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "showimg",
|
||||
"name": "simple image viewer",
|
||||
"shortName":"showImage",
|
||||
"version":"0.2",
|
||||
"description": "Displays the image in \"showimg.user.img\". The file has to be uploaded via the espruino IDE. Returns to watch face after 60s or button push. I use it to display my vaccination certificate.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"showimg.app.js","url":"app.js"},
|
||||
{"name":"showimg.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lapcounter",
|
||||
"name": "Lap Counter",
|
||||
"version": "0.01",
|
||||
"description": "Click button to count laps. Shows count and total time snapshot (like a stopwatch, but laid back).",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "app",
|
||||
"tags": "tool,outdoors",
|
||||
"readme":"README.md",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"lapcounter.app.js","url":"app.js"},
|
||||
{"name":"lapcounter.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "pebbled",
|
||||
"name": "Pebble Clock with distance",
|
||||
"shortName": "Pebble + distance",
|
||||
"version": "0.1",
|
||||
"description": "Fork of Pebble Clock with distance in KM. Both step count and the distance are on the main screen. Default step length = 0.75m (can be changed in settings).",
|
||||
"readme": "README.md",
|
||||
"icon": "pebbled.png",
|
||||
"screenshots": [{"url":"pebble_screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock,distance",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"pebbled.app.js","url":"pebbled.app.js"},
|
||||
{"name":"pebbled.settings.js","url":"pebbled.settings.js"},
|
||||
{"name":"pebbled.img","url":"pebbled.icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "circlesclock",
|
||||
"name": "Circles clock",
|
||||
"shortName":"Circles clock",
|
||||
"version":"0.02",
|
||||
"description": "A clock with circles for different data at the bottom in a probably familiar style",
|
||||
"icon": "app.png",
|
||||
"dependencies": {"widpedom":"app"},
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"allow_emulator":true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"circlesclock.app.js","url":"app.js"},
|
||||
{"name":"circlesclock.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"circlesclock.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"circlesclock.json"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ltherm",
|
||||
"name": "Localized Thermometer",
|
||||
"shortName": "Thermometer",
|
||||
"version": "0.01",
|
||||
"description": "Displays the current temperature in localized units.",
|
||||
"icon": "thermf.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"ltherm.app.js","url":"app.js"},
|
||||
{"name":"ltherm.img","url":"icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
0.04: Fix tapping at very bottom of list, exit on inactivity
|
||||
0.03: Add "Calculating" placeholder, update JSON save format
|
||||
0.02: Fix JSON save format
|
||||
0.01: First release
|
||||
0.02: Fix JSON save format
|
||||
0.03: Add "Calculating" placeholder, update JSON save format
|
||||
0.04: Fix tapping at very bottom of list, exit on inactivity
|
||||
|
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAMGAAAAAAYDAAAAAAwBgAAAABgAwAAAABAAQAAAABAAQAAAABAAQAAAABAAQAAAABAAQAAAABgAwAAAAAwBgAAAAAYDAAAAAAMGAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAP+AAAAAAf/AAAAAA//gAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAA//gAAAAAf/AAAAAAP+AAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
|
||||
require("heatshrink").decompress(atob("mEwgIurg/wAocMjAFDjEMIAkGAodggYFDoBLEAq4jFF4o7FI4pTFOLsP/AFDj/8Aoc//wFDv//As4vFHYpHFOLoAPA=="))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: Basic calendar
|
||||
0.02: Make Bangle 2 compatible
|
||||
0.03: Add setting to start week on Sunday
|
||||
0.04: Add setting to switch color schemes. On Bangle 2 non-dithering colors will be used by default. Use localized names for months and days of the week (Language app needed).
|
||||
|
|
|
@ -9,5 +9,6 @@ Basic calendar
|
|||
|
||||
## Settings
|
||||
|
||||
- Starts on Sunday: whether the calendar should start on Sunday (default is Monday).
|
||||
- Starts Sunday: whether the calendar should start on Sunday (default is Monday).
|
||||
- B2 Colors: use non-dithering colors (default, recommended for Bangle 2) or the original color scheme.
|
||||
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
require("heatshrink").decompress(
|
||||
atob(
|
||||
"mEwxH+AH4A/ADuIUCARRDhgePCKIv13YAEDoYJFAA4RJFyQvcGBYRGy4dDy4uLCJgv/DoOBDgOBF5oRLF6IeBDgIvNCJYvQDwQuNCJovRADov/F9OsAEgv/F/4vhwIACAqYv/F/4vnd94vvX/4v/F/7vvF96//F/4v/d94v/F/4wsFxQwjFxgA/AH4A/AH4AZA=="
|
||||
)
|
||||
)
|
||||
require("heatshrink").decompress(atob("mEwwcCpMkyQC3wAIFgIRJn8JAoeQ/gRYwB0Bn57F/gCBHAgfCn8EDgdI/kSAoIR8oBkFgAFCCIysKCPM//4AKZAgR3/0Aj+Ag/ggP4gF/CPpr/Nf5r/NfYRhw4RL8IRDyEAABUJCIYC/AVI="))
|
|
@ -1,6 +1,6 @@
|
|||
const maxX = g.getWidth();
|
||||
const maxY = g.getHeight();
|
||||
const fontSize = g.getWidth()>200?2:1;
|
||||
const fontSize = g.getWidth() > 200 ? 2 : 1;
|
||||
const rowN = 7;
|
||||
const colN = 7;
|
||||
const headerH = maxY / 7;
|
||||
|
@ -10,26 +10,109 @@ const color1 = "#035AA6";
|
|||
const color2 = "#4192D9";
|
||||
const color3 = "#026873";
|
||||
const color4 = "#038C8C";
|
||||
const color5 = "#03A696";
|
||||
const gray1 = "#bbbbbb";
|
||||
const black = "#000000";
|
||||
const white = "#ffffff";
|
||||
const gray1 = "#444444";
|
||||
const gray2 = "#888888";
|
||||
const gray3 = "#bbbbbb";
|
||||
const red = "#d41706";
|
||||
const blue = "#0000ff";
|
||||
const yellow = "#ffff00";
|
||||
|
||||
let settings = require('Storage').readJSON("calendar.json", true) || {};
|
||||
if (settings.startOnSun === undefined)
|
||||
settings.startOnSun = false;
|
||||
if (settings.ndColors === undefined)
|
||||
if (process.env.HWVERSION == 2) {
|
||||
settings.ndColors = true;
|
||||
} else {
|
||||
settings.ndColors = false;
|
||||
}
|
||||
|
||||
if (settings.ndColors === true) {
|
||||
let bgColor = white;
|
||||
let bgColorMonth = blue;
|
||||
let bgColorDow = black;
|
||||
let bgColorWeekend = yellow;
|
||||
let fgOtherMonth = blue;
|
||||
let fgSameMonth = black;
|
||||
} else {
|
||||
let bgColor = color4;
|
||||
let bgColorMonth = color1;
|
||||
let bgColorDow = color2;
|
||||
let bgColorWeekend = color3;
|
||||
let fgOtherMonth = gray1;
|
||||
let fgSameMonth = white;
|
||||
}
|
||||
|
||||
function getDowLbls(locale) {
|
||||
let dowLbls;
|
||||
//TODO: Find some clever way to generate this programmatically from locale lib
|
||||
switch (locale) {
|
||||
case "de_AT":
|
||||
case "de_CH":
|
||||
case "de_DE":
|
||||
if (settings.startOnSun) {
|
||||
dowLbls = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||
}
|
||||
break;
|
||||
case "nl_NL":
|
||||
if (settings.startOnSun) {
|
||||
dowLbls = ["zo", "ma", "di", "wo", "do", "vr", "za"];
|
||||
} else {
|
||||
dowLbls = ["ma", "di", "wo", "do", "vr", "za", "zo"];
|
||||
}
|
||||
break;
|
||||
case "fr_BE":
|
||||
case "fr_CH":
|
||||
case "fr_FR":
|
||||
if (settings.startOnSun) {
|
||||
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
|
||||
}
|
||||
break;
|
||||
case "sv_SE":
|
||||
if (settings.startOnSun) {
|
||||
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
|
||||
}
|
||||
break;
|
||||
case "it_CH":
|
||||
case "it_IT":
|
||||
if (settings.startOnSun) {
|
||||
dowLbls = ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"];
|
||||
}
|
||||
break;
|
||||
case "oc_FR":
|
||||
if (settings.startOnSun) {
|
||||
dowLbls = ["dg", "dl", "dm", "dc", "dj", "dv", "ds"];
|
||||
} else {
|
||||
dowLbls = ["dl", "dm", "dc", "dj", "dv", "ds", "dg"];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (settings.startOnSun) {
|
||||
dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
|
||||
}
|
||||
break;
|
||||
}
|
||||
return dowLbls;
|
||||
}
|
||||
|
||||
function drawCalendar(date) {
|
||||
g.setBgColor(color4);
|
||||
g.setBgColor(bgColor);
|
||||
g.clearRect(0, 0, maxX, maxY);
|
||||
g.setBgColor(color1);
|
||||
g.setBgColor(bgColorMonth);
|
||||
g.clearRect(0, 0, maxX, headerH);
|
||||
g.setBgColor(color2);
|
||||
g.setBgColor(bgColorDow);
|
||||
g.clearRect(0, headerH, maxX, headerH + rowH);
|
||||
g.setBgColor(color3);
|
||||
g.setBgColor(bgColorWeekend);
|
||||
g.clearRect(colW * 5, headerH + rowH, maxX, maxY);
|
||||
for (let y = headerH; y < maxY; y += rowH) {
|
||||
g.drawLine(0, y, maxX, y);
|
||||
|
@ -40,24 +123,11 @@ function drawCalendar(date) {
|
|||
|
||||
const month = date.getMonth();
|
||||
const year = date.getFullYear();
|
||||
const monthMap = {
|
||||
0: "January",
|
||||
1: "February",
|
||||
2: "March",
|
||||
3: "April",
|
||||
4: "May",
|
||||
5: "June",
|
||||
6: "July",
|
||||
7: "August",
|
||||
8: "September",
|
||||
9: "October",
|
||||
10: "November",
|
||||
11: "December"
|
||||
};
|
||||
const localeMonth = require('locale').month(date);
|
||||
g.setFontAlign(0, 0);
|
||||
g.setFont("6x8", fontSize);
|
||||
g.setColor(white);
|
||||
g.drawString(`${monthMap[month]} ${year}`, maxX / 2, headerH / 2);
|
||||
g.drawString(`${localeMonth} ${year}`, maxX / 2, headerH / 2);
|
||||
g.drawPoly([10, headerH / 2, 20, 10, 20, headerH - 10], true);
|
||||
g.drawPoly(
|
||||
[maxX - 10, headerH / 2, maxX - 20, 10, maxX - 20, headerH - 10],
|
||||
|
@ -65,12 +135,7 @@ function drawCalendar(date) {
|
|||
);
|
||||
|
||||
g.setFont("6x8", fontSize);
|
||||
let dowLbls;
|
||||
if (settings.startOnSun) {
|
||||
dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
|
||||
}
|
||||
let dowLbls = getDowLbls(require('locale').name);
|
||||
dowLbls.forEach((lbl, i) => {
|
||||
g.drawString(lbl, i * colW + colW / 2, headerH + rowH / 2);
|
||||
});
|
||||
|
@ -120,14 +185,19 @@ function drawCalendar(date) {
|
|||
today.year === year && today.month === month && today.day === day - 50;
|
||||
if (isToday) {
|
||||
g.setColor(red);
|
||||
let x1 = x * colW;
|
||||
let y1 = y * rowH + headerH + rowH;
|
||||
let x2 = x * colW + colW;
|
||||
let y2 = y * rowH + headerH + rowH + rowH;
|
||||
g.drawRect(x1, y1, x2, y2);
|
||||
g.drawRect(
|
||||
x * colW,
|
||||
y * rowH + headerH + rowH,
|
||||
x * colW + colW - 1,
|
||||
y * rowH + headerH + rowH + rowH
|
||||
x1 + 1,
|
||||
y1 + 1,
|
||||
x2 - 1,
|
||||
y2 - 1
|
||||
);
|
||||
}
|
||||
g.setColor(day < 50 ? gray3 : white);
|
||||
g.setColor(day < 50 ? fgOtherMonth : fgSameMonth);
|
||||
g.drawString(
|
||||
(day > 50 ? day - 50 : day).toString(),
|
||||
x * colW + colW / 2,
|
||||
|
@ -145,10 +215,10 @@ const today = {
|
|||
};
|
||||
drawCalendar(date);
|
||||
clearWatch();
|
||||
Bangle.on("touch",area=>{
|
||||
Bangle.on("touch", area => {
|
||||
const month = date.getMonth();
|
||||
let prevMonth;
|
||||
if (area==1) {
|
||||
if (area == 1) {
|
||||
let prevMonth = month > 0 ? month - 1 : 11;
|
||||
if (prevMonth === 11) date.setFullYear(date.getFullYear() - 1);
|
||||
date.setMonth(prevMonth);
|
||||
|
|
Before Width: | Height: | Size: 540 B After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -1,24 +1,38 @@
|
|||
(function(back) {
|
||||
(function (back) {
|
||||
var FILE = "calendar.json";
|
||||
var settings = require('Storage').readJSON(FILE, true) || {};
|
||||
if (settings.startOnSun === undefined)
|
||||
settings.startOnSun = true;
|
||||
settings.startOnSun = false;
|
||||
if (settings.ndColors === undefined)
|
||||
if (process.env.HWVERSION == 2) {
|
||||
settings.ndColors = true;
|
||||
} else {
|
||||
settings.ndColors = false;
|
||||
}
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
E.showMenu({
|
||||
"" : { "title" : "Calendar" },
|
||||
"< Back" : () => back(),
|
||||
'Start on Sunday': {
|
||||
"": { "title": "Calendar" },
|
||||
"< Back": () => back(),
|
||||
'Start Sunday': {
|
||||
value: settings.startOnSun,
|
||||
format: v => v?"Yes":"No",
|
||||
format: v => v ? "Yes" : "No",
|
||||
onchange: v => {
|
||||
settings.startOnSun = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'B2 Colors': {
|
||||
value: settings.ndColors,
|
||||
format: v => v ? "Yes" : "No",
|
||||
onchange: v => {
|
||||
settings.ndColors = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New clock
|
||||
0.02: Fix icon & add battery warn functionality
|
|
@ -0,0 +1,21 @@
|
|||
# Circles clock
|
||||
|
||||
A clock with circles for different data at the bottom in a probably familiar style
|
||||
|
||||
It shows besides time, date and day of week the following information:
|
||||
* Steps (requires [pedometer widget](https://banglejs.com/apps/#pedometer))
|
||||
* Heart rate (when screen is on and unlocked)
|
||||
* Battery (including charging and battery low)
|
||||
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
## TODO
|
||||
* Show weather information
|
||||
|
||||
## Creator
|
||||
Marco ([myxor](https://github.com/myxor))
|
||||
|
||||
## Icons
|
||||
Icons taken from [materialdesignicons](https://materialdesignicons.com) under Apache License 2.0
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwcCIf4ALv///gFCv0Agf+CJP/wAODAwPAEpAjCCIX8h4RMj/+g/8gP4CA4LBDoP/GpkH8EP4/8LIIRMAQIOCCJU/CgQOBEwMPI5ARCR4YRJgP/gB3CI5Z0CCIiABfHRfEj+BAoN+n4FBLIkP/8chwRBx5cC//8v4REhytDgYRCv//8fxEYwRFgfxA4I1FRgI1D+JHE/7FINZzCBAAc4CRU4/kB44FCjgRKLQRlBPQ4RHgYCB/jpBABB6BPoKzBCJYAGuD/vAB1JkgLJm3bAgUCpMnwDdCPwIFChu27dgAoMSCIP+FAQRB+AFBtoRBtgFByQCBRIIoBAocDtonBAQWQdgXAgVIAocDEAUNwEEyEHBYUSoE//gRCsI7BxvACIILDCIcBCIYFCCJ3/wIRCIIYRBI4h6CAoJrDLJYRDDwJ9LAoKhBoMDUIcEgFwUIQREgUBaAcIkhPCAAQzBAAUBdIhhDAAMGCIkAkAFEdAQAFA=="))
|
|
@ -0,0 +1,235 @@
|
|||
const locale = require("locale");
|
||||
const heatshrink = require("heatshrink");
|
||||
|
||||
const shoesIcon = heatshrink.decompress(atob("h0OwYJGgmAAgUBkgECgVJB4cSoAUDyEBkARDpADBhMAyQRBgVAkgmDhIUDAAuQAgY1DAAYA="));
|
||||
const heartIcon = heatshrink.decompress(atob("h0OwYOLkmQhMkgACByVJgESpIFBpEEBAIFBCgIFCCgsABwcAgQOCAAMSpAwDyBNM"));
|
||||
const powerIcon = heatshrink.decompress(atob("h0OwYQNsAED7AEDmwEDtu2AgUbtuABwXbBIUN23AAoYOCgEDFIgODABI"));
|
||||
const powerIconGreen = heatshrink.decompress(atob("h0OwYQNkAEDpAEDiQEDkmSAgUJkmABwVJBIUEyVAAoYOCgEBFIgODABI"));
|
||||
const powerIconRed = heatshrink.decompress(atob("h0OwYQNoAEDyAEDkgEDpIFDiVJBweSAgUJkmAAoYZDgQpEBwYAJA"));
|
||||
|
||||
const SETTINGS_FILE = "circlesclock.json";
|
||||
let settings;
|
||||
|
||||
function loadSettings() {
|
||||
settings = require("Storage").readJSON(SETTINGS_FILE, 1) || {
|
||||
'maxHR': 200,
|
||||
'stepGoal': 10000,
|
||||
'batteryWarn': 30
|
||||
};
|
||||
}
|
||||
|
||||
const colorFg = '#fff';
|
||||
const colorBg = '#000';
|
||||
const colorGrey = '#808080';
|
||||
const colorRed = '#ff0000';
|
||||
const colorGreen = '#00ff00';
|
||||
|
||||
let hrtValue;
|
||||
|
||||
const h = g.getHeight();
|
||||
const w = g.getWidth();
|
||||
const hOffset = 30;
|
||||
const h1 = Math.round(1 * h / 5 - hOffset);
|
||||
const h2 = Math.round(3 * h / 5 - hOffset);
|
||||
const h3 = Math.round(8 * h / 8 - hOffset);
|
||||
const w1 = Math.round(w / 6);
|
||||
const w2 = Math.round(3 * w / 6);
|
||||
const w3 = Math.round(5 * w / 6);
|
||||
const radiusOuter = 22;
|
||||
const radiusInner = 16;
|
||||
|
||||
function draw() {
|
||||
g.reset();
|
||||
g.setColor(colorBg);
|
||||
g.fillRect(0, 0, w, h);
|
||||
|
||||
// time
|
||||
g.setFont("Vector:50");
|
||||
g.setFontAlign(-1, -1);
|
||||
g.setColor(colorFg);
|
||||
g.drawString(locale.time(new Date(), 1), w / 10, h1 + 8);
|
||||
|
||||
// date & dow
|
||||
g.setFont("Vector:20");
|
||||
g.setFontAlign(-1, 0);
|
||||
g.drawString(locale.date(new Date()), w / 10, h2);
|
||||
g.drawString(locale.dow(new Date()), w / 10, h2 + 22);
|
||||
|
||||
// Steps circle
|
||||
drawSteps();
|
||||
|
||||
// Heart circle
|
||||
drawHeartRate();
|
||||
|
||||
// Battery circle
|
||||
drawBattery();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function drawSteps() {
|
||||
const steps = getSteps();
|
||||
const blue = '#0000ff';
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w1, h3, radiusOuter);
|
||||
|
||||
const stepGoal = settings.stepGoal;
|
||||
if (stepGoal > 0) {
|
||||
let percent = steps / stepGoal;
|
||||
if (stepGoal < steps) percent = 1;
|
||||
drawGauge(w1, h3, percent, blue);
|
||||
}
|
||||
|
||||
g.setColor(colorBg);
|
||||
g.fillCircle(w1, h3, radiusInner);
|
||||
|
||||
g.fillPoly([w1, h3, w1 - 15, h3 + radiusOuter + 5, w1 + 15, h3 + radiusOuter + 5]);
|
||||
|
||||
g.setFont("Vector:12");
|
||||
g.setFontAlign(0, 0);
|
||||
g.setColor(colorFg);
|
||||
g.drawString(shortValue(steps), w1 + 2, h3);
|
||||
|
||||
g.drawImage(shoesIcon, w1 - 6, h3 + radiusOuter - 6);
|
||||
}
|
||||
|
||||
function drawHeartRate() {
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w2, h3, radiusOuter);
|
||||
|
||||
if (hrtValue != undefined) {
|
||||
const percent = hrtValue / settings.maxHR;
|
||||
drawGauge(w2, h3, percent, colorRed);
|
||||
}
|
||||
|
||||
g.setColor(colorBg);
|
||||
g.fillCircle(w2, h3, radiusInner);
|
||||
|
||||
g.fillPoly([w2, h3, w2 - 15, h3 + radiusOuter + 5, w2 + 15, h3 + radiusOuter + 5]);
|
||||
|
||||
g.setFont("Vector:12");
|
||||
g.setFontAlign(0, 0);
|
||||
g.setColor(colorFg);
|
||||
g.drawString(hrtValue != undefined ? hrtValue : "-", w2, h3);
|
||||
|
||||
g.drawImage(heartIcon, w2 - 6, h3 + radiusOuter - 6);
|
||||
}
|
||||
|
||||
function drawBattery() {
|
||||
const battery = E.getBattery();
|
||||
const yellow = '#ffff00';
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w3, h3, radiusOuter);
|
||||
|
||||
if (battery > 0) {
|
||||
const percent = battery / 100;
|
||||
drawGauge(w3, h3, percent, yellow);
|
||||
}
|
||||
|
||||
g.setColor(colorBg);
|
||||
g.fillCircle(w3, h3, radiusInner);
|
||||
|
||||
g.fillPoly([w3, h3, w3 - 15, h3 + radiusOuter + 5, w3 + 15, h3 + radiusOuter + 5]);
|
||||
|
||||
g.setFont("Vector:12");
|
||||
g.setFontAlign(0, 0);
|
||||
|
||||
let icon = powerIcon;
|
||||
let color = colorFg;
|
||||
if (Bangle.isCharging()) {
|
||||
color = colorGreen;
|
||||
icon = powerIconGreen;
|
||||
}
|
||||
else {
|
||||
if (settings.batteryWarn != undefined && battery <= settings.batteryWarn) {
|
||||
color = colorRed;
|
||||
icon = powerIconRed;
|
||||
}
|
||||
}
|
||||
g.setColor(color);
|
||||
g.drawString(battery + '%', w3, h3);
|
||||
|
||||
g.drawImage(icon, w3 - 6, h3 + radiusOuter - 6);
|
||||
}
|
||||
|
||||
function radians(a) {
|
||||
return a * Math.PI / 180;
|
||||
}
|
||||
|
||||
|
||||
function drawGauge(cx, cy, percent, color) {
|
||||
let offset = 30;
|
||||
let end = 300;
|
||||
var i = 0;
|
||||
var r = radiusInner + 3;
|
||||
|
||||
if (percent > 1) percent = 1;
|
||||
|
||||
var startrot = -offset;
|
||||
var endrot = startrot - ((end - offset) * percent);
|
||||
|
||||
g.setColor(color);
|
||||
|
||||
// draw gauge
|
||||
for (i = startrot; i > endrot; i -= 4) {
|
||||
x = cx + r * Math.sin(radians(i));
|
||||
y = cy + r * Math.cos(radians(i));
|
||||
g.fillCircle(x, y, 4);
|
||||
}
|
||||
}
|
||||
|
||||
function shortValue(v) {
|
||||
if (isNaN(v)) return '-';
|
||||
if (v <= 999) return v;
|
||||
if (v >= 1000 && v < 10000) {
|
||||
v = Math.floor(v / 100) * 100;
|
||||
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
|
||||
}
|
||||
if (v >= 10000) {
|
||||
v = Math.floor(v / 1000) * 1000;
|
||||
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
|
||||
}
|
||||
}
|
||||
|
||||
function getSteps() {
|
||||
if (WIDGETS.wpedom !== undefined) {
|
||||
return WIDGETS.wpedom.getSteps();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Bangle.on('lock', function(isLocked) {
|
||||
if (!isLocked) {
|
||||
Bangle.setHRMPower(1, "watch");
|
||||
} else {
|
||||
Bangle.setHRMPower(0, "watch");
|
||||
}
|
||||
drawHeartRate();
|
||||
drawSteps();
|
||||
});
|
||||
|
||||
Bangle.on('HRM', function(hrm) {
|
||||
//if(hrm.confidence > 90){
|
||||
hrtValue = hrm.bpm;
|
||||
if (Bangle.isLCDOn())
|
||||
drawHeartRate();
|
||||
//} else {
|
||||
// hrtValue = undefined;
|
||||
//}
|
||||
});
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
/*
|
||||
* we are not drawing the widgets as we are taking over the whole screen
|
||||
* so we will blank out the draw() functions of each widget and change the
|
||||
* area to the top bar doesn't get cleared.
|
||||
*/
|
||||
for (let wd of WIDGETS) {
|
||||
wd.draw = () => {};
|
||||
wd.area = "";
|
||||
}
|
||||
loadSettings();
|
||||
setInterval(draw, 60000);
|
||||
draw();
|
||||
Bangle.setUI("clock");
|
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1,43 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "circlesclock.json";
|
||||
const storage = require('Storage');
|
||||
let settings = storage.readJSON(SETTINGS_FILE, 1) || {};
|
||||
function save(key, value) {
|
||||
settings[key] = value;
|
||||
storage.write(SETTINGS_FILE, settings);
|
||||
}
|
||||
E.showMenu({
|
||||
'': { 'title': 'circlesclock' },
|
||||
'max heartrate': {
|
||||
value: "maxHR" in settings ? settings.maxHR : 200,
|
||||
min: 20,
|
||||
max : 250,
|
||||
step: 10,
|
||||
format: x => {
|
||||
return x;
|
||||
},
|
||||
onchange: x => save('maxHR', x),
|
||||
},
|
||||
'step goal': {
|
||||
value: "stepGoal" in settings ? settings.stepGoal : 10000,
|
||||
min: 2000,
|
||||
max : 50000,
|
||||
step: 2000,
|
||||
format: x => {
|
||||
return x;
|
||||
},
|
||||
onchange: x => save('stepGoal', x),
|
||||
},
|
||||
'battery warn lvl': {
|
||||
value: "batteryWarn" in settings ? settings.batteryWarn : 30,
|
||||
min: 10,
|
||||
max : 100,
|
||||
step: 10,
|
||||
format: x => {
|
||||
return x + '%';
|
||||
},
|
||||
onchange: x => save('batteryWarn', x),
|
||||
},
|
||||
'< Back': back,
|
||||
});
|
||||
});
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New clock!
|
||||
0.02: Load steps from Health Tracking app (if installed)
|
||||
0.03: ...
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.1: New app
|
||||
0.01: New app
|
||||
|
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("/wA/AH4A/AH4A/ACmsAEQuMlcAAD0rGBQKBFr4ADGBOsqwvjqwvJRsCRFF/8Gg4ADEZYQEgwvWg8+AAgwKCJgvQDgoABF5IRMF5xEBJpBhGCJwvNDQM4AYMNAAQaBnCAFCJ4vNIwQeBAAkxQAwGCmIRFFwIRDF64dDgwGBgwRNF/4v/F/4v/F/4v/F/4dJmIdECIkxF7MHFwUHhoACg4eCAYIACCJ4vNDQIgCAAgICKwoROF5yAEAAgtFCKAvQJpAAICJgvQgEGg4ADFxIwCAAcGBYovRADov6qwvjqwvJ1gvjEoIvHGASRgRoIuJGAYAhFxQA/AH4A/AH4A/ABQ"))
|
||||
require("heatshrink").decompress(atob("mEw4UA///A4N551ulxL/ACkK1QAG0ALBlNVAA1oBYOlBY9aBYO1BY9eBYOVBY9WBbf/+oIBr//BYlX//9BYN///VC599qtX6oBBqt9BYYRBr/1AIIdBBf4L/BY6bLZcb7MBau1BY9eBYOlBY9aBYMpBY9oBYMK1QAG0ALBAH4ASA"))
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
0.04: reset to clock after 2 mins of inactivity
|
||||
0.05: add Bangle 2 version
|
||||
0.06: Adds settings page (hide clocks or launchers)
|
||||
0.06: Adds setting for directly launching app on touch for Bangle 2
|
||||
0.07: Adds setting for directly launching app on touch for Bangle 2
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
0.06: Fix daily health summary for movement (a line got deleted!)
|
||||
0.07: Added coloured bar charts
|
||||
0.08: Suppress bleed through of E.showMenu's when displaying bar charts
|
||||
0.09: Fix file naming so months are 1-based (not 0) (fix #1119)
|
||||
|
|
|
@ -27,7 +27,7 @@ Bangle.on("health", health => {
|
|||
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
|
||||
|
||||
function getRecordFN(d) {
|
||||
return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw";
|
||||
return "health-"+d.getFullYear()+"-"+(d.getMonth()+1)+".raw";
|
||||
}
|
||||
function getRecordIdx(d) {
|
||||
return (DB_RECORDS_PER_DAY*(d.getDate()-1)) +
|
||||
|
|
|
@ -6,7 +6,7 @@ const DB_HEADER_LEN = 8;
|
|||
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
|
||||
|
||||
function getRecordFN(d) {
|
||||
return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw";
|
||||
return "health-"+d.getFullYear()+"-"+(d.getMonth()+1)+".raw";
|
||||
}
|
||||
function getRecordIdx(d) {
|
||||
return (DB_RECORDS_PER_DAY*(d.getDate()-1)) +
|
||||
|
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1 @@
|
|||
0.01: first release
|
|
@ -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).
|
||||
|
||||

|
||||
|
||||
## 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)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AAkQgEBAREAC6oABdZQXkI6wuKC5iPUFxoXIOpoX/C6QFCC6IsCC6ZEDC/4XcPooXOFgoXQIgwX/C7IUFC5wsIC5ouCC6hcJC5h1DF9YwBChCPOAH4A/AH4Ap"))
|
|
@ -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);
|
After Width: | Height: | Size: 749 B |
After Width: | Height: | Size: 6.4 KiB |
|
@ -3,4 +3,5 @@
|
|||
0.03: New design with different icons if gps, hrm or compass is on.
|
||||
0.04: Inluded LCARS Logo.
|
||||
0.05: Additional icons for (1) charging and (2) bat < 30%.
|
||||
0.06: Fix - Alarm disabled, if clock was closed
|
||||
0.06: Fix - Alarm disabled, if clock was closed.
|
||||
0.07: Added settings to adjust data that is shown for each row.
|
|
@ -5,10 +5,9 @@ Note: To display the steps, its necessary to install
|
|||
the [Pedometer widget](https://banglejs.com/apps/#pedometer%20widget).
|
||||
|
||||
## Features
|
||||
* Shows the time
|
||||
* Shows the date
|
||||
* Shows the current battery level in %
|
||||
* Shows the number of daily steps
|
||||
* LCARS Style watch face
|
||||
* Shows satate (charging, out of battery etc.)
|
||||
* SHows data that can be configured (steps, HRM, temperature etc.)
|
||||
* Swipe left/right to activate an alarm
|
||||
|
||||
## Icons
|
||||
|
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 8.5 KiB |
|
@ -1,8 +1,21 @@
|
|||
const filename = "lcars.setting.json";
|
||||
const SETTINGS_FILE = "lcars.setting.json";
|
||||
const Storage = require("Storage");
|
||||
let settings = Storage.readJSON(filename,1) || {
|
||||
|
||||
|
||||
// ...and overwrite them with any saved values
|
||||
// This way saved values are preserved if a new version adds more settings
|
||||
const storage = require('Storage')
|
||||
let settings = {
|
||||
alarm: -1,
|
||||
dataRow1: "Battery",
|
||||
dataRow2: "Steps",
|
||||
dataRow3: "Temp."
|
||||
};
|
||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||
for (const key in saved_settings) {
|
||||
settings[key] = saved_settings[key]
|
||||
}
|
||||
let hrmValue = 0;
|
||||
|
||||
/*
|
||||
* Requirements and globals
|
||||
|
@ -12,7 +25,7 @@ const locale = require('locale');
|
|||
var backgroundImage = {
|
||||
width : 176, height : 151, bpp : 3,
|
||||
transparent : 2,
|
||||
buffer : require("heatshrink").decompress(atob("AAUEufPnnzATkAg4daIIXnz15ATvkwEDDrUAgPHQDyDghyAeQcNzJQ0cuPHATCDBDrUDJQ1AgAA3jjOF+BA4T4KDFyBB5Qf4ABQAaD9QAaD/QesH8CD/n/8Qf8//+AQfsB///GQ6D2h5BJQf6D7/yD8jl/IIIABjiD5n4/DAAWAQe8B//8QYfH//x4CD2HwMDQIf4AoP4Qesf/56BQYYFBuP/Qev//0AQYoKBn/gQecH/lwQwQADBYaDzGoZBHR4OAQehBKj5BBsuWrICDBAIAofYZBFBAZ6qIJJ6DQZBB3IAiDDgZBygJ6EIIn8IOqDKIIscuPHAQdwINkHIJEfIIPnz15AQeAINT+CHwcPAYI1BIIU8+fPAQbOqg56BQYcAgKD4IIv4RgSDCAQSD34AIC//wBYSDyO4P+IIoIB+E/8AFBQeL7B//HHYJKE+P/AoSDygF/QQJBF//4AoSDygEBQYgFBj/xZYaDzgE/PoIAE/wMDQeZBB/jICAAMcuAMDQevgQwR0CvyD3gP/BAxBEQek4A40OQe4ANQegAMQf6D/AAccQf8Ak6DFyCD/QfcDQYueIPMAuaDE+fBIPMOQYoCb8glB7dt2wCW2EAgKDFATkAg2atOmAS5eBhKDigyDZ2zHCjiD/AAMChEgwQCcQb4AiQb5BiQbscuPHATyDfyfPnnzATnwQbsBQD6DghKAeQcJoHiFBggCYQYVhdwQATgOmgVPNAnOECwAGQYIZXgM2dI1wIL2aoCDYibsF4CD/QcGYILGmyaDFwCD/QfaADQf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D4jCD/ADKDnILSD/Qf6DEHO6DJIP6D/Qf6D/QY8cuPHAQdAQfPz588AQeAQf8cuCD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6DqoCD5HO6DJIP6D/Qf6D/QY8cuPHAQdwE7sGzCDZ+fPngCDwBBe7aD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/QfcTQYvAQf6DgzVAQbECp6DE5yD5gCDFATqDCsOAIKtB00AhKDEATnwQYVt2wCXQwKDltOmAS6IC2aD82BBCQccaQbGAA=="))
|
||||
buffer : require("heatshrink").decompress(atob("AAdx48cATsAg4daIAX3799ATv2wEFDrUAgNHQDyDghaAeQcJKG86D4gRKGgAA4jxKFuBB5iaDF6BB5ZwyD6QAYCC4CD/Qf6Dzg/gQf8H/iD/n//wCD9gP///wQfpBKQf6D4h5BB/yD8jl/IIIABjiD5n4/DAAWAQe8B//8QYfHj//PAaDzHwICCAAP4gYCBQep6DIIYFBRgKD1j/+gB9BQYYKBn/gQen/+BBFQAUH/iDzGoZBHJoOAQeRBDj5BHj6PB0WKlACDJQIAofYZBFBAZBBAGMHPQZB8QYZAEIIcDIOiDI/hB3QZBBFjlx44CDuBBpg4DCIJEfIIPnz15AQeAQeH8gIDBGoJBCnnz54CDZ1UHPQMHIIUAIIKD3II6MBQYQCCQeI1B+BBC/BKCBASGCQeK5B/xBC4BKEn/gAoKDyj//45BFj/xZYSDzgF/IAP+JQrLCQecAgKDBF4cHQYKJDQecAn6EBAAiJEQeZBB/jICAAMcvwMDQevgQwR0CIIiDzgP/BA1/4CD3nAHGhyD3ABqD0ABiD/Qf4ADjiD/gEnQYuQQf6D7gaDFzxB5gFzQYnz4BB5hyDFATfkEoIdagEBQYoCcgEHDrReBhKDhwEBQbYABjiD/AH4A/AH4AGiFx48cATsAg4daIIWSpMkATuQEbkAgJfbQckJQDyDhZxQA1gRKFpBA4gEQQYtwIPMSQYtAIPKADQfqADAQRA5Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf4A/AH4A/AH4A/AFkcuPHAQdAIPOSpMkAQaD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf4A/AH4A/AH4A/AGUcuPHAQdwIPOSpMkAQaD/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf6D/Qf4AciSDFoCD/QfcCQYtIIPMAQYoC6gEJQYgC6gEBQf7HCQf4ABiiD9"))
|
||||
}
|
||||
|
||||
var iconEarth = {
|
||||
|
@ -95,6 +108,33 @@ function queueDraw() {
|
|||
}
|
||||
|
||||
|
||||
function printData(key, y){
|
||||
g.setFontAlign(-1,-1,0);
|
||||
|
||||
if(key == "Battery"){
|
||||
var bat = E.getBattery();
|
||||
g.drawString("BAT:", 30, y);
|
||||
g.drawString(bat+ "%", 68, y);
|
||||
|
||||
} else if(key == "Steps"){
|
||||
var steps = getSteps();
|
||||
g.drawString("STEP:", 30, y);
|
||||
g.drawString(steps, 68, y);
|
||||
|
||||
} else if(key == "Temp."){
|
||||
var temperature = Math.floor(E.getTemperature());
|
||||
g.drawString("TEMP:", 30, y);
|
||||
g.drawString(temperature + "C", 69, y);
|
||||
|
||||
} else if(key == "HRM"){
|
||||
g.drawString("HRM:", 30, y);
|
||||
g.drawString(hrmValue, 69, y);
|
||||
|
||||
} else {
|
||||
g.drawString("NOT FOUND", 30, y);
|
||||
}
|
||||
}
|
||||
|
||||
function draw(){
|
||||
|
||||
// First handle alarm to show this correctly afterwards
|
||||
|
@ -125,7 +165,7 @@ function draw(){
|
|||
// Alarm within symbol
|
||||
g.setFontAlign(0,0,0);
|
||||
g.setFontAntonioSmall();
|
||||
g.drawString(iconImg.text, 115+25, 102);
|
||||
g.drawString(iconImg.text, 115+25, 105);
|
||||
if(isAlarmEnabled() > 0){
|
||||
g.drawString(getAlarmMinutes(), 115+25, 115+25);
|
||||
}
|
||||
|
@ -147,18 +187,9 @@ function draw(){
|
|||
g.drawString(dayName, 100, 55);
|
||||
|
||||
// Draw battery
|
||||
g.drawString("BAT:", 25, 98);
|
||||
g.drawString(bat+ "%", 62, 98);
|
||||
|
||||
// Draw steps
|
||||
var steps = getSteps();
|
||||
g.drawString("STEP:", 25, 121);
|
||||
g.drawString(steps, 62, 121);
|
||||
|
||||
// Temperature
|
||||
g.setFontAlign(-1,-1,0);
|
||||
g.drawString("TEMP:", 25, 144);
|
||||
g.drawString(Math.floor(E.getTemperature()) + "C", 62, 144);
|
||||
printData(settings.dataRow1, 98);
|
||||
printData(settings.dataRow2, 121);
|
||||
printData(settings.dataRow3, 144);
|
||||
|
||||
// Queue draw in one minute
|
||||
queueDraw();
|
||||
|
@ -182,6 +213,12 @@ function stepsWidget() {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
* HRM Listener
|
||||
*/
|
||||
Bangle.on('HRM', function (hrm) {
|
||||
hrmValue = hrm.bpm;
|
||||
});
|
||||
|
||||
/*
|
||||
* Handle alarm
|
||||
|
@ -220,7 +257,7 @@ function handleAlarm(){
|
|||
|
||||
// Update alarm state to disabled
|
||||
settings.alarm = -1;
|
||||
Storage.writeJSON(filename, settings);
|
||||
Storage.writeJSON(SETTINGS_FILE, settings);
|
||||
}
|
||||
|
||||
|
||||
|
@ -250,7 +287,7 @@ Bangle.on('swipe',function(dir) {
|
|||
draw();
|
||||
|
||||
// Update alarm state
|
||||
Storage.writeJSON(filename, settings);
|
||||
Storage.writeJSON(SETTINGS_FILE, settings);
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "lcars.setting.json";
|
||||
|
||||
// initialize with default settings...
|
||||
const storage = require('Storage')
|
||||
let settings = {
|
||||
alarm: -1,
|
||||
dataRow1: "Battery",
|
||||
dataRow2: "Steps",
|
||||
dataRow3: "Temp."
|
||||
};
|
||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||
for (const key in saved_settings) {
|
||||
settings[key] = saved_settings[key]
|
||||
}
|
||||
|
||||
function save() {
|
||||
storage.write(SETTINGS_FILE, settings)
|
||||
}
|
||||
|
||||
var data_options = ['Battery', 'Steps', 'Temp.', "HRM"];
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'LCARS Clock' },
|
||||
'< Back': back,
|
||||
'Row 1': {
|
||||
value: 0 | data_options.indexOf(settings.dataRow1),
|
||||
min: 0, max: 3,
|
||||
format: v => data_options[v],
|
||||
onchange: v => {
|
||||
settings.dataRow1 = data_options[v];
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Row 2': {
|
||||
value: 0 | data_options.indexOf(settings.dataRow2),
|
||||
min: 0, max: 3,
|
||||
format: v => data_options[v],
|
||||
onchange: v => {
|
||||
settings.dataRow2 = data_options[v];
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Row 3': {
|
||||
value: 0 | data_options.indexOf(settings.dataRow3),
|
||||
min: 0, max: 3,
|
||||
format: v => data_options[v],
|
||||
onchange: v => {
|
||||
settings.dataRow3 = data_options[v];
|
||||
save();
|
||||
},
|
||||
}
|
||||
});
|
||||
})
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 2.9 KiB |
|
@ -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:
|
||||
|
@ -144,7 +154,8 @@ var locales = {
|
|||
month: "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus", "< Back": "< Zurück" }
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
|
||||
"< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" }
|
||||
},
|
||||
"en_US": {
|
||||
lang: "en_US",
|
||||
|
@ -317,13 +328,15 @@ var locales = {
|
|||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: { 0: "", 1: "" },
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%y" }, // Sonntag, 1. März 2020 // 01.03.20
|
||||
abmonth: "Jän,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez",
|
||||
month: "Jänner,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus" }
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
|
||||
"< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" }
|
||||
},
|
||||
"en_IL": {
|
||||
lang: "en_IL",
|
||||
|
@ -359,7 +372,8 @@ var locales = {
|
|||
month: "enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre",
|
||||
abday: "dom,lun,mar,mié,jue,vie,sáb",
|
||||
day: "domingo,lunes,martes,miércoles,jueves,viernes,sábado",
|
||||
trans: { yes: "sí", Yes: "Sí", no: "no", No: "No", ok: "ok", on: "on", off: "off" }
|
||||
trans: { yes: "sí", Yes: "Sí", no: "no", No: "No", ok: "ok", on: "on", off: "off",
|
||||
"< Back": "< Atrás", "Delete": "Borrar ", "Mark Unread": "Marcar como no leído" }
|
||||
},
|
||||
"fr_BE": {
|
||||
lang: "fr_BE",
|
||||
|
@ -631,6 +645,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",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# Thermometer
|
||||
|
||||
Localized Bangle.js 2 thermometer app. It also starts maintaining an average of the temperature to help lower the margin of error after 10 consecutive readings; due to the low quality die-thermometer.
|
|
@ -0,0 +1,25 @@
|
|||
function drawTemperature() {
|
||||
g.reset(1).clearRect(0,24,g.getWidth(),g.getHeight());
|
||||
g.setFont("6x8",2).setFontAlign(0,0);
|
||||
var x = g.getWidth()/2;
|
||||
var y = g.getHeight()/2 + 10;
|
||||
g.drawString("Temp", x, y - 45);
|
||||
g.setFontVector(70).setFontAlign(0,0);
|
||||
var h = E.getTemperature();
|
||||
if (avg.length < 10) {
|
||||
avg[avg.length] = h;
|
||||
} else {
|
||||
avg.shift();
|
||||
avg[avg.length] = h;
|
||||
h = ((avg[0] + avg[1] + avg[2] + avg[3] + avg[4] + avg[5] + avg[6] + avg[7] + avg[8] + avg[9]) / 10);
|
||||
}
|
||||
var t = require('locale').temp(h);
|
||||
g.drawString(t, x, y);
|
||||
}
|
||||
const avg = [];
|
||||
setInterval(function() {
|
||||
drawTemperature();
|
||||
}, 2000);
|
||||
E.showMessage("Loading...");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AH4AChGIxGAC6eIAQgARFgUIC9ReCAYJgSC7BHDF6gUBC6ovWI/5Hga/6P/ABsCkABDC/4XxkQXDkQuSAQwXPDQkAC6BBCkQDDC6MCmczFoIXQCQQXBDgQXP2EA2YXBncAhYXR3YXB3YXRCQWznYcCC6ICBAYYXPhYrBApAwPFyQqCIoYuRLwZgDAH4A/"))
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -17,4 +17,7 @@
|
|||
0.12: Extra app-specific notification icons
|
||||
New animated notifcationicon (instead of large blinking 'MESSAGES')
|
||||
Added screenshots
|
||||
|
||||
0.13: Add /*LANG*/ comments for internationalisation
|
||||
Add 'Delete All' option to message options
|
||||
Now update correctly when 'require("messages").clearAll()' is called
|
||||
0.14: Hide widget when all unread notifications are dismissed from phone
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -30,6 +30,10 @@ exports.pushMessage = function(event) {
|
|||
require("Storage").writeJSON("messages.json",messages);
|
||||
// if in app, process immediately
|
||||
if (inApp) return onMessagesModified(mIdx<0 ? {id:event.id} : messages[mIdx]);
|
||||
// if we've removed the last new message, hide the widget
|
||||
if (event.t=="remove" && !messages.some(m=>m.new)) {
|
||||
if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.hide();
|
||||
}
|
||||
// ok, saved now - we only care if it's new
|
||||
if (event.t!="add") {
|
||||
return;
|
||||
|
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("MDABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
|
||||
require("heatshrink").decompress(atob("mEwwIHEgPAAocP+AFDv4FDgf/Aoc/AocB/4FDh4FEv4FEAgIFIDgQFR+FwAoeAAof8gAFDLoIFC/wyBAoQ4CAoXgAoh0CAtybCAoJPBAoahDAoMHAoicBAoM54EfAoJqCAoQUBAoYUBAoYCBAoXgZAIFC4AFCCgOAYYI1CZIRHB/AFDcwmAAoj9Dj6mCdoQaBAAYWDgA"))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.1: first release
|
|
@ -0,0 +1,22 @@
|
|||
# Pebble with distance and steps
|
||||
|
||||
- Forked from [Pebble](https://github.com/espruino/BangleApps/tree/master/apps/pebble)
|
||||
- Added distance in km (kilometers) based on step length (can be changed in settings and is equal 0.75m by default)
|
||||
- Battery warning changed to 15% instead of 30%
|
||||
|
||||

|
||||

|
||||
|
||||
Initially written by: [Hugh Barney](https://github.com/hughbarney)
|
||||
|
||||
Forked and changed by [RomanistHere](https://github.com/RomanistHere)
|
||||
|
||||
For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
|
||||
## How to measure step length
|
||||
|
||||
It's much easier than you think. When you're walking, just note number of current steps at two points and then see the distance in any map service. For example, your route from bus station to home. Write number of steps at bus station (let's say 3451) and when you entered your home (3921). You passed 3921 - 3451 = 470 steps. Then see the actual distance in Google maps. Let's say it shows 300 meters. So your step length (in settings) used in app should be 300 / 470 = 0.64. After you have set it, the displayed distance at the main screen should be more accurate.
|
||||
|
||||
## Plans
|
||||
|
||||
Make step length depend on height/sex/age for lazy ones who don't want to measure it.
|
After Width: | Height: | Size: 25 KiB |
|
@ -0,0 +1,129 @@
|
|||
Graphics.prototype.setFontLECO1976Regular42 = function(scale) {
|
||||
// Actual height 42 (41 - 0)
|
||||
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60+(scale<<8)+(1<<16));
|
||||
};
|
||||
|
||||
Graphics.prototype.setFontLECO1976Regular22 = function(scale) {
|
||||
// Actual height 22 (21 - 0)
|
||||
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22+(scale<<8)+(1<<16));
|
||||
};
|
||||
|
||||
const SETTINGS_FILE = "pebbleDistance.json";
|
||||
let settings;
|
||||
|
||||
function loadSettings() {
|
||||
settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green', 'avStep': 0.75};
|
||||
}
|
||||
|
||||
var img = require("heatshrink").decompress(atob("oFAwkEogA/AH4A/AH4A/AH4A/AE8AAAoeXoAfeDQUBmcyD7A+Dh///8QD649CiAfaHwUvD4sEHy0DDYIfEICg+Cn4fHICY+DD4nxcgojOHwgfEIAYfRCIQaDD4ZAFD5r7DH4//kAfRCIZ/GAAnwD5p9DX44fTHgYSBf4ofVDAQEBl4fFUAgfOXoQzBgIfFBAIfPP4RAEAoYAB+cRiK/SG4h/WIBAfXIA7CBAAswD55AHn6fUIBMCD65AHl4gCmcziAfQQJqfQQJpiDgk0IDXxQLRAEECaBM+QgRYRYgUIA0CD4ggSQJiDCiAKBICszAAswD55AHABKBVD7BAFABIqBD5pAFABPxD55AOD6BADiIAJQAyxLABwf/gaAPAH4A/AH4ARA=="));
|
||||
|
||||
const h = g.getHeight();
|
||||
const w = g.getWidth();
|
||||
const ha = 2*h/5 - 11;
|
||||
const h2 = 3*h/5 - 19;
|
||||
const h3 = 7*h/8 - 10;
|
||||
|
||||
let batteryWarning = false;
|
||||
|
||||
function draw() {
|
||||
let date = new Date();
|
||||
let da = date.toString().split(" ");
|
||||
let timeStr = da[4].substr(0,5);
|
||||
const t = 6;
|
||||
const stps = getSteps();
|
||||
|
||||
// turn the warning on once we have dipped below 15%
|
||||
if (E.getBattery() < 15)
|
||||
batteryWarning = true;
|
||||
|
||||
// turn the warning off once we have dipped above 20%
|
||||
if (E.getBattery() > 20)
|
||||
batteryWarning = false;
|
||||
|
||||
g.reset();
|
||||
g.setColor(settings.bg);
|
||||
g.fillRect(0, 0, w, h2 - t);
|
||||
|
||||
// contrast bar
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(0, h2 - t, w, h2);
|
||||
|
||||
// day and steps
|
||||
if (settings.color == 'Blue' || settings.color == 'Red')
|
||||
g.setColor('#fff'); // white on blue or red best contrast
|
||||
else
|
||||
g.setColor('#000'); // otherwise black regardless of theme
|
||||
|
||||
g.setFontLECO1976Regular22();
|
||||
g.setFontAlign(0, -1);
|
||||
g.drawString(da[0].toUpperCase(), w/4, ha); // day of week
|
||||
g.drawString(stps, 3*w/4, ha);
|
||||
|
||||
// time
|
||||
// white on red for battery warning
|
||||
g.setColor(!batteryWarning ? g.theme.bg : '#f00');
|
||||
g.fillRect(0, h2, w, h3);
|
||||
|
||||
g.setFontLECO1976Regular42();
|
||||
g.setFontAlign(0, -1);
|
||||
g.setColor(!batteryWarning ? g.theme.fg : '#fff');
|
||||
g.drawString(timeStr, w/2, h2 + 8);
|
||||
|
||||
// contrast bar
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(0, h3, w, h3 + t);
|
||||
|
||||
// the bottom
|
||||
g.setColor(settings.bg);
|
||||
g.fillRect(0, h3 + t, w, h);
|
||||
|
||||
g.setColor(settings.bg);
|
||||
g.drawImage(img, w/2 + ((w/2) - 64)/2, -2, { scale: 1 });
|
||||
drawCalendar(((w/2) - 42)/2, 11, 42, 4, da[2]);
|
||||
|
||||
// distance
|
||||
if (settings.color == 'Blue' || settings.color == 'Red')
|
||||
g.setColor('#fff'); // white on blue or red best contrast
|
||||
else
|
||||
g.setColor('#000'); // otherwise black regardless of theme
|
||||
g.drawString((stps / 1000 * settings.avStep).toFixed(2) + ' KM', w/2, ha + 107);
|
||||
}
|
||||
|
||||
// at x,y width:wi thicknes:th
|
||||
function drawCalendar(x,y,wi,th,str) {
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(x, y, x + wi, y + wi);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(x + th, y + th, x + wi - th, y + wi - th);
|
||||
g.setColor(g.theme.fg);
|
||||
|
||||
let hook_t = 6;
|
||||
// first calendar hook, one third in
|
||||
g.fillRect(x + (wi/3) - (th/2), y - hook_t, x + wi/3 + th - (th/2), y + hook_t);
|
||||
// second calendar hook, two thirds in
|
||||
g.fillRect(x + (2*wi/3) -(th/2), y - hook_t, x + 2*wi/3 + th - (th/2), y + hook_t);
|
||||
|
||||
g.setFontLECO1976Regular22();
|
||||
g.setFontAlign(0, 0);
|
||||
g.drawString(str, x + wi/2, y + wi/2 + th);
|
||||
}
|
||||
|
||||
function getSteps() {
|
||||
if (WIDGETS.wpedom !== undefined) {
|
||||
return WIDGETS.wpedom.getSteps();
|
||||
}
|
||||
return '0';
|
||||
}
|
||||
|
||||
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, 15000); // refresh every 15s
|
||||
draw();
|
||||
Bangle.setUI("clock");
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4kB78A/4ACj/fn8Rz/Or987+M38hv8Rn++z9Cn8y/9rj9Tr+diIAHiAxDgIOICpYADCywyNCxQYMSxouVGBIWODBDgRC7hGQJAwWRGAguSC7JICCyYXYJAQXWLyhICC94LKu8Gqk1gGD+AjQ//C6Ei2HS0AXQ/U2gEquFVC6Pyk8AlssmwXK3oGF+fi2Et0v2C4ONoAPFhsiwBfFhU0lvS2wXBsVUC4vSkwHF90Etlso0L6CSBkowEFwNgM5sImQwEFw4AJGAguQGAsQ6UvxAAQ2UloMYkQAU0MZmUjmYAQmUi+MRSowAM4UmiMRSowALhiKBC4MQGCIuBgAXCGCAuCgIXBiLGCFyQXCGAJINsToBC4cQgGMI50AIwYwCACAWEGAQAOFwowRCwwwPFw4wPCxAYNCxRJLCxYxKCxwyGORI"))
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,48 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "pebbleDistance.json";
|
||||
|
||||
// initialize with default settings...
|
||||
let s = {'bg': '#0f0', 'color': 'Green', 'avStep': 0.75};
|
||||
|
||||
// ...and overwrite them with any saved values
|
||||
// This way saved values are preserved if a new version adds more settings
|
||||
const storage = require('Storage');
|
||||
let settings = storage.readJSON(SETTINGS_FILE, 1) || s;
|
||||
const saved = settings || {};
|
||||
for (const key in saved) {
|
||||
s[key] = saved[key];
|
||||
}
|
||||
|
||||
function save() {
|
||||
settings = s;
|
||||
storage.write(SETTINGS_FILE, settings);
|
||||
}
|
||||
|
||||
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue'];
|
||||
var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f'];
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'Pebble Clock' },
|
||||
'< Back': back,
|
||||
'Color': {
|
||||
value: 0 | color_options.indexOf(s.color),
|
||||
min: 0, max: 5,
|
||||
format: v => color_options[v],
|
||||
onchange: v => {
|
||||
s.color = color_options[v];
|
||||
s.bg = bg_code[v];
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Step length': {
|
||||
value: 0.75 || s.avStep,
|
||||
min: 0.2,
|
||||
max: 1.5,
|
||||
step: 0.01,
|
||||
onchange : v => {
|
||||
s.avStep = v;
|
||||
save();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
After Width: | Height: | Size: 161 KiB |
|
@ -16,8 +16,8 @@ the ability to check the _exact_ time, hands free, without the impact on battery
|
|||
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.
|
||||
You can also override the system 12/24 hour setting just for this face here, since it's, well, a rather different experience than with numeric displays.
|
||||
|
||||
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.
|
||||
In some previous 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.
|
||||
|
||||
One other thing: there's some integration with system timers and alarms; they will show as small pips at the appropriate places
|
||||
in the day around the display. When they come within an hour, the pips turn to crosses relating to the minute hand, and the minute
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.00: Initial check-in.
|
||||
0.01: Add tap-to-decorate feature. Bugfixes.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
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
|
|
@ -10,14 +10,21 @@ Then launch the linked apps directly from the clock screen by simply drawing the
|
|||
|
||||
## Add Pattern Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Manage Pattern Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## Detailed Steps
|
||||
|
||||
|
|
Before Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.1 KiB |
|
@ -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
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
@ -2,4 +2,5 @@
|
|||
0.02: Add widget
|
||||
0.03: Bangle.js 2 support
|
||||
0.04: Move Quiet Mode LCD options from global settings to this app
|
||||
0.05: Avoid immediately redrawing widgets on load
|
||||
0.05: Avoid immediately redrawing widgets on load
|
||||
0.06: Fix: don't try to redraw widget when widgets not loaded
|
|
@ -19,5 +19,5 @@ exports.setMode = function(mode) {
|
|||
{quiet:mode}
|
||||
));
|
||||
exports.applyOptions(mode);
|
||||
if (WIDGETS && "qmsched" in WIDGETS) WIDGETS["qmsched"].draw();
|
||||
if (typeof WIDGETS === "object" && "qmsched" in WIDGETS) WIDGETS["qmsched"].draw();
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: Cloning torch and making it red :D
|
||||
0.02: Modify for setUI and Bangle 2
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
E.showMenu(mainMenu);
|
||||
setInterval(transmitUpdatedSensorData, 1000);
|
|
@ -39,3 +39,5 @@
|
|||
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
|
||||
0.38: Restructed menus as per forum discussion
|
||||
|
|
|
@ -58,9 +58,40 @@ function resetSettings() {
|
|||
settings = storage.readJSON('setting.json', 1);
|
||||
if (!settings) resetSettings();
|
||||
|
||||
const boolFormat = v => v ? "On" : "Off";
|
||||
const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
||||
|
||||
function showMainMenu() {
|
||||
|
||||
const mainmenu = {
|
||||
'': { 'title': 'Settings' },
|
||||
'< Back': ()=>load(),
|
||||
/*LANG*/'Apps': ()=>showAppSettingsMenu(),
|
||||
/*LANG*/'Bluetooth': ()=>showBLEMenu(),
|
||||
/*LANG*/'System': ()=>showSystemMenu(),
|
||||
/*LANG*/'Alerts': ()=>showAlertsMenu(),
|
||||
/*LANG*/'Utils': ()=>showUtilMenu(),
|
||||
/*LANG*/'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() }
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
function showSystemMenu() {
|
||||
|
||||
const mainmenu = {
|
||||
'': { 'title': 'System' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
/*LANG*/'Theme': ()=>showThemeMenu(),
|
||||
/*LANG*/'LCD': ()=>showLCDMenu(),
|
||||
/*LANG*/'Locale': ()=>showLocaleMenu(),
|
||||
/*LANG*/'Select Clock': ()=>showClockMenu(),
|
||||
/*LANG*/'Set Time': ()=>showSetTimeMenu()
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
function showAlertsMenu() {
|
||||
var beepMenuItem;
|
||||
if (BANGLEJS2) {
|
||||
beepMenuItem = {
|
||||
|
@ -77,7 +108,7 @@ function showMainMenu() {
|
|||
};
|
||||
} else { // Bangle.js 1
|
||||
var beepV = [false, true, "vib"];
|
||||
var beepN = ["Off", "Piezo", "Vibrate"];
|
||||
var beepN = [/*LANG*/"Off", /*LANG*/"Piezo", /*LANG*/"Vibrate"];
|
||||
beepMenuItem = {
|
||||
value: Math.max(0 | beepV.indexOf(settings.beep),0),
|
||||
min: 0, max: beepV.length-1,
|
||||
|
@ -91,14 +122,11 @@ function showMainMenu() {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
const mainmenu = {
|
||||
'': { 'title': 'Settings' },
|
||||
'< Back': ()=>load(),
|
||||
'App Settings': ()=>showAppSettingsMenu(),
|
||||
'BLE': ()=>showBLEMenu(),
|
||||
'Beep': beepMenuItem,
|
||||
'Vibration': {
|
||||
'': { 'title': 'Alerts' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
/*LANG*/'Beep': beepMenuItem,
|
||||
/*LANG*/'Vibration': {
|
||||
value: settings.vibrate,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
|
@ -110,7 +138,7 @@ function showMainMenu() {
|
|||
}
|
||||
}
|
||||
},
|
||||
"Quiet Mode": {
|
||||
/*LANG*/"Quiet Mode": {
|
||||
value: settings.quiet|0,
|
||||
format: v => ["Off", "Alarms", "Silent"][v%3],
|
||||
onchange: v => {
|
||||
|
@ -119,23 +147,18 @@ function showMainMenu() {
|
|||
updateOptions();
|
||||
if ("qmsched" in WIDGETS) WIDGETS["qmsched"].draw();
|
||||
},
|
||||
},
|
||||
'Locale': ()=>showLocaleMenu(),
|
||||
'Select Clock': ()=>showClockMenu(),
|
||||
'Set Time': ()=>showSetTimeMenu(),
|
||||
'LCD': ()=>showLCDMenu(),
|
||||
'Theme': ()=>showThemeMenu(),
|
||||
'Utils': ()=>showUtilMenu(),
|
||||
'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() },
|
||||
}
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
|
||||
function showBLEMenu() {
|
||||
var hidV = [false, "kbmedia", "kb", "joy"];
|
||||
var hidN = ["Off", "Kbrd & Media", "Kbrd","Joystick"];
|
||||
E.showMenu({
|
||||
'': { 'title': 'Bluetooth' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'Make Connectable': ()=>makeConnectable(),
|
||||
'BLE': {
|
||||
|
@ -190,7 +213,7 @@ function showThemeMenu() {
|
|||
}
|
||||
var m = E.showMenu({
|
||||
'':{title:'Theme'},
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'Dark BW': ()=>{
|
||||
upd({
|
||||
fg:cl("#fff"), bg:cl("#000"),
|
||||
|
@ -276,8 +299,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],
|
||||
|
@ -333,7 +358,7 @@ function showWhitelistMenu() {
|
|||
function showLCDMenu() {
|
||||
const lcdMenu = {
|
||||
'': { 'title': 'LCD' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'LCD Brightness': {
|
||||
value: settings.brightness,
|
||||
min: 0.1,
|
||||
|
@ -445,7 +470,7 @@ function showLCDMenu() {
|
|||
function showLocaleMenu() {
|
||||
const localemenu = {
|
||||
'': { 'title': 'Locale' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'Time Zone': {
|
||||
value: settings.timezone,
|
||||
min: -11,
|
||||
|
@ -549,7 +574,7 @@ function showClockMenu() {
|
|||
'': {
|
||||
'title': 'Select Clock',
|
||||
},
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
};
|
||||
clockApps.forEach((app, index) => {
|
||||
var label = app.name;
|
||||
|
@ -576,7 +601,7 @@ function showSetTimeMenu() {
|
|||
'': { 'title': 'Set Time' },
|
||||
'< Back': function () {
|
||||
setTime(d.getTime() / 1000);
|
||||
showMainMenu();
|
||||
showSystemMenu();
|
||||
},
|
||||
'Hour': {
|
||||
value: d.getHours(),
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.1: Initial release
|
||||
0.2: Fixed launcher image
|
|
@ -0,0 +1,3 @@
|
|||
Displays an image. I use this app to show my vaccination certificate.
|
||||
The image is read from the file "showimg.user.img".
|
||||
Returns to watch face after 60s/button push.
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkBIf4Aah//BRQAMDowUNC5AARC4YKKL5gTC+B3TCpAyIC5oNBEA4XNJwS4GC55pHC8TEHC57QHC4wSEC5YpEC6YwEC5oEEC5x3DC6ZHbC7PwcYxfNAYYXPJA4XQDAwKEBYQXJIoReHC5gMFAAojBC5QUIC5Y5JMgYXIUQYJFPggXMAwICCBAYXMCAQJDDwQUCC5QOCUwQdEC5QqFDghNFC5wrEC5gQDPgoTCDYYXFMAgXaCQoXJEwZ4FLQbhFC4imDAAglFC5QAGBgYXKIoYWIC5YYFG4ZkDC4YjCYYwAJC4gASC6THFH5pqGAAY"))
|
|
@ -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);
|
After Width: | Height: | Size: 4.4 KiB |