Merge branch 'espruino:master' into widmp

pull/2018/head
deirdreobyrne 2022-07-04 13:46:43 +01:00 committed by GitHub
commit 7bea535086
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 894 additions and 176 deletions

View File

@ -11,3 +11,4 @@
0.10: Fix SMS bug
0.12: Use default Bangle formatter for booleans
0.13: Added Bangle.http function (see Readme file for more info)
0.14: Fix timeout of http function not beeing cleaned up

View File

@ -122,14 +122,14 @@
"http":function() {
//get the promise and call the promise resolve
if (Bangle.httpRequest === undefined) return;
var objID=Bangle.httpRequest[event.id];
if (objID === undefined) return; //already timedout or wrong id
var request=Bangle.httpRequest[event.id];
if (request === undefined) return; //already timedout or wrong id
delete Bangle.httpRequest[event.id];
clearInterval(objID.t); //t = timeout variable
clearTimeout(request.t); //t = timeout variable
if(event.err!==undefined) //if is error
objID.j(event.err); //r = reJect function
request.j(event.err); //r = reJect function
else
objID.r(event); //r = resolve function
request.r(event); //r = resolve function
}
};
var h = HANDLERS[event.t];

View File

@ -2,7 +2,7 @@
"id": "android",
"name": "Android Integration",
"shortName": "Android",
"version": "0.13",
"version": "0.14",
"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,gadgetbridge",

View File

@ -9,4 +9,5 @@
0.09: Larger font size if colon is hidden to improve readability further.
0.10: HomeAssistant integration if HomeAssistant is installed.
0.11: Performance improvements.
0.12: Implements a 2D menu.
0.12: Implements a 2D menu.
0.13: Clicks < 24px are for widgets, if fullscreen mode is disabled.

View File

@ -20,13 +20,15 @@ and click up/down to move into this sub-menu. You can then click in the middle o
to e.g. send a trigger via HomeAssistant once you selected it.
```
Bpm ...
| |
Steps 10 min. ... ...
| | | |
Battery 5 min Temp. Trigger1
| | | |
+5min
|
Bangle -- Timer[Optional] -- Weather[Optional] -- HomeAssistant [Optional]
| | | |
Bpm -5min Temperature Trigger1
| | |
Steps ... ...
|
Battery
```
## Settings

View File

@ -166,17 +166,8 @@ function imgHomeAssistant() {
/************
* 2D MENU with entries of:
* [name, icon, opt[customUpFun], opt[customDownFun], opt[customCenterFun]]
* [name, icon, opt[customDownFun], opt[customUpFun], opt[customCenterFun]]
*
* An example is shown below:
*
* Bpm ...
* | |
* Steps 10 min. ... ...
* | | | |
* Battery 5-min Temp. Trigger1
* | | | |
* BangleJs -- Timer -- Weather[Optional] -- HomeAssistant [Optional]
*/
var menu = [
[
@ -198,7 +189,7 @@ try{
menu.push([
function(){
var text = isAlarmEnabled() ? getAlarmMinutes() + " min." : "Timer";
return [text, imgTimer(), () => increaseAlarm(), () => decreaseAlarm(), null ]
return [text, imgTimer(), () => decreaseAlarm(), () => increaseAlarm(), null ]
},
]);
} catch(ex) {
@ -531,18 +522,19 @@ Bangle.on('charging',function(charging) {
});
Bangle.on('touch', function(btn, e){
var left = parseInt(g.getWidth() * 0.3);
var widget_size = settings.fullscreen ? 0 : 20; // Its not exactly 24px -- empirically it seems that 20 worked better...
var left = parseInt(g.getWidth() * 0.22);
var right = g.getWidth() - left;
var upper = parseInt(g.getHeight() * 0.3);
var upper = parseInt(g.getHeight() * 0.22) + widget_size;
var lower = g.getHeight() - upper;
var is_left = e.x < left;
var is_right = e.x > right;
var is_upper = e.y < upper;
var is_lower = e.y > lower;
var is_left = e.x < left && !is_upper && !is_lower;
var is_right = e.x > right && !is_upper && !is_lower;
var is_center = !is_upper && !is_lower && !is_left && !is_right;
if(is_upper){
if(is_lower){
Bangle.buzz(40, 0.6);
settings.menuPosY = (settings.menuPosY+1) % menu[settings.menuPosX].length;
@ -555,7 +547,11 @@ Bangle.on('touch', function(btn, e){
drawTime();
}
if(is_lower){
if(is_upper){
if(e.y < widget_size){
return;
}
Bangle.buzz(40, 0.6);
settings.menuPosY = settings.menuPosY-1;
settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].length-1 : settings.menuPosY;

View File

@ -1,7 +1,7 @@
{
"id": "bwclk",
"name": "BW Clock",
"version": "0.12",
"version": "0.13",
"description": "A very minimalistic clock with date and time in focus.",
"readme": "README.md",
"icon": "app.png",

View File

@ -1,3 +1,5 @@
0.01: New App!
0.02: Show status info on display
Allow recording to Bangle
0.03: Allow downloading recorded files
Make it work with more BTHRM configs

View File

@ -6,7 +6,7 @@ This app can use [BTHRM](https://banglejs.com/apps/#bthrm) as a reference.
## Steps for usage
* (Optional) Install [BTHRM](https://banglejs.com/apps/#bthrm) as reference (use ECG based sensor for best accuracy).
* Configure BTHRM to "Both"-Mode. This prevents data beeing lost because BTHRM can replace the HRM-events data with BTHRM data.
* Configure BTHRM to "Both"-Mode or use a version >= 0.12. This prevents data beeing lost because BTHRM can replace the HRM-events data with BTHRM data.
* Click "Start" in browser.
* Wait until the "Events" number starts to grow, that means there are events recorded.
* Record for some time, since BTHRM and HRM often need some seconds to start getting useful values. Consider 2000 events a useful minimum.

View File

@ -1,18 +1,42 @@
<html>
<head>
<title>Bangle.js Accelerometer streaming</title>
<link rel="stylesheet" href="../../css/spectre.min.css">
</head>
<body>
<script src="https://www.puck-js.com/puck.js"></script>
<p></div><input type="checkbox" id="chkLocal">Store on Bangle (file named log.csv, download with IDE)</input></p>
<p>
<button id="btnConnect">Start</button>
<button id="btnStop">Stop</button>
<button id="btnReset">Reset</button>
<button id="btnSave">Save CSV</button>
<div class="form-group">
<label class="form-switch">
<input type="checkbox" id="chkLocal">
<i class="form-icon"></i> Store on bangle (file named log.csv)
</label>
</div>
</p>
<p>
<button id="btnConnect" class="btn btn-primary">Start</button>
<button id="btnStop" class="btn btn-secondary">Stop</button>
<button id="btnReset" class="btn btn-secondary">Reset</button>
<button id="btnSave" class="btn btn-primary">Save CSV</button>
<button id="btnDownload" class="btn btn-primary">Download CSV</button>
</p>
<p id="result"></p>
<script>
function saveCSV(filename, csvData) {
let a = document.createElement("a"),
file = new Blob([csvData], {type: "Comma-separated value file"});
let url = URL.createObjectURL(file);
a.href = url;
a.download = filename+".csv";
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
function createCode(){
//modes: 1 BT, 2 File
return "var method=" + (document.getElementById("chkLocal").checked ? 2 : 1) + ";\n" + String.raw`
@ -32,7 +56,12 @@ function createCode(){
return gotBTHRM && gotHRM && gotHRMraw && gotAcc;
}
let bthrmSettings = (require("Storage").readJSON("bthrm.json",1) || {});
Bangle.setHRMPower(1);
if (bthrmSettings.replace) Bangle.origSetHRMPower(1);
if (Bangle.setBTHRMPower){
Bangle.setBTHRMPower(1);
} else {
@ -99,7 +128,11 @@ function createCode(){
Bangle.on("accel", writeAccDirect);
}
Bangle.on("HRM-raw", writeHRMraw);
Bangle.on("HRM", writeHRM);
if (bthrmSettings.replace){
Bangle.origOn("HRM", writeHRM);
} else {
Bangle.on("HRM", writeHRM);
}
Bangle.on("BTHRM", writeBTHRM);
g.clear();
@ -164,7 +197,6 @@ function createCode(){
var connection;
var lineCount=-1;
function stop (){
connection.reconnect((c)=>{
c.write("load();\n");
@ -173,32 +205,74 @@ function stop (){
});
}
document.getElementById("chkLocal").addEventListener("click", function() {
function updateButtons(){
document.getElementById("btnSave").disabled = document.getElementById("chkLocal").checked;
document.getElementById("btnDownload").disabled = !document.getElementById("chkLocal").checked;
document.getElementById("btnReset").disabled = document.getElementById("chkLocal").checked;
document.getElementById("btnStop").disabled = document.getElementById("chkLocal").checked;
}
updateButtons();
document.getElementById("chkLocal").addEventListener("click", function() {
reset();
updateButtons();
});
window.addEventListener("message", function(event) {
let msg = event.data;
if (msg.type=="readstoragefilersp") {
saveCSV("log.csv", msg.data);
}
}, false);
document.getElementById("btnDownload").addEventListener("click", function() {
if (connection) {
stop();
}
console.log("Loading data from BangleJs...");
try {
window.postMessage({type:"readstoragefile",data:"log.csv",id:0});
} catch(ex) {
console.log("(Warning) Could not load apikey from BangleJs.");
console.log(ex);
}
});
document.getElementById("btnSave").addEventListener("click", function() {
var h = document.createElement('a');
h.href = 'data:text/csv;charset=utf-8,' + encodeURI(localStorage.getItem("data"));
h.target = '_blank';
h.download = "DATA.csv";
h.click();
saveCSV("log.csv", localStorage.getItem("data"));
});
function reset(){
document.getElementById("result").innerText="";
}
document.getElementById("btnReset").addEventListener("click", function() {
if (connection) {
stop();
}
document.getElementById("result").innerText="";
lineCount=-1;
localStorage.removeItem("data");
reset();
});
document.getElementById("btnStop").addEventListener("click", function() {
if (connection) {
stop();
}
});
function connect(connectionHandler){
Puck.connect(function(c) {
if (!c) {
console.log("Couldn't connect!\n");
return;
}
connection = c;
connectionHandler(c);
});
}
document.getElementById("btnConnect").addEventListener("click", function() {
localStorage.setItem("data", "");
lineCount=-1;
@ -206,12 +280,7 @@ document.getElementById("btnConnect").addEventListener("click", function() {
stop();
document.getElementById("result").innerText="0";
}
Puck.connect(function(c) {
if (!c) {
console.log("Couldn't connect!\n");
return;
}
connection = c;
connect(function(connection) {
var buf = "";
connection.on("data", function(d) {
buf += d;
@ -220,7 +289,7 @@ document.getElementById("btnConnect").addEventListener("click", function() {
l.forEach(onLine);
});
connection.write("reset();\n", function() {
setTimeout(function() {
setTimeout(function() {
connection.write("\x03\x10if(1){"+createCode()+"}\n",
function() { console.log("Ready..."); });
}, 1500);

View File

@ -2,14 +2,14 @@
"id": "hrmaccevents",
"name": "HRM Accelerometer event recorder",
"shortName": "HRM ACC recorder",
"version": "0.02",
"version": "0.03",
"type": "RAM",
"description": "Record HRM and accelerometer events in high resolution to CSV files in your browser",
"icon": "app.png",
"tags": "debug",
"supports": ["BANGLEJS","BANGLEJS2"],
"custom": "custom.html",
"customConnect": false,
"customConnect": true,
"readme": "README.md",
"storage": [ ]
}

View File

@ -6,3 +6,4 @@
0.20: Add theme support
0.21: Add Settings
0.22: Use default Bangle formatter for booleans
0.23: Added note to configure position in "my location" if not done yet. Small fixes.

View File

@ -8,6 +8,8 @@ If watch is locked, seconds get refreshed every 10 seconds.
## Usage
Location for sun set / rise set with mylocation app.
Provide names and the UTC offsets for up to three other timezones in the app store. These are stored in a json file on your watch. UTC offsets can be decimal (e.g., 5.5 for India).
The clock does not handle summer time / daylight saving time changes automatically. If one of your three locations changes its UTC offset, you can simply change the setting in the app store and update. Currently the clock only supports 24 hour time format for the additional time zones.
@ -21,11 +23,5 @@ Please use [the Espruino Forum](http://forum.espruino.com/microcosms/1424/) if y
Created by Hank.
Based on the great work of
=================
World Clock - 4 time zones
Made by [Scott Hale](https://www.github.com/computermacgyver), based upon the [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock).
===== a n d =====
Sun Clock
[Sun Clock](https://github.com/espruino/BangleApps/tree/master/apps/sunclock)
=================
Based on the great work of "World Clock - 4 time zones". Made by [Scott Hale](https://www.github.com/computermacgyver), based upon the [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock).
And Sun Clock [Sun Clock](https://github.com/espruino/BangleApps/tree/master/apps/sunclock)

View File

@ -46,10 +46,10 @@ setting = require("Storage").readJSON("setting.json",1);
E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ
SunCalc = require("hsuncalc.js");
const LOCATION_FILE = "mylocation.json";
var rise = "07:00";
var set = "20:00";
var pos = {altitude: 20, azimuth: 135};
var noonpos = {altitude: 37, azimuth: 180};
var rise = "read";
var set = "...";
//var pos = {altitude: 20, azimuth: 135};
//var noonpos = {altitude: 37, azimuth: 180};
//=======Sun
var ampm = "AM";
@ -113,19 +113,19 @@ g.setBgColor(g.theme.bg);
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
// schedule a draw for the next second
function queueDrawSeconds() {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = setTimeout(function() {
drawTimeoutSeconds = undefined;
drawSeconds();
//console.log("TO: " + secondsTimeout);
}, secondsTimeout - (Date.now() % secondsTimeout));
drawTimeoutSeconds = undefined;
drawSeconds();
//console.log("TO: " + secondsTimeout);
}, secondsTimeout - (Date.now() % secondsTimeout));
}
function doublenum(x) {
@ -137,12 +137,17 @@ function getCurrentTimeFromOffset(dt, offset) {
}
function updatePos() {
coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":53.3,"lon":10.1,"location":"Pattensen"};
pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon);
coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":0,"lon":0,"location":"-"}; //{"lat":53.3,"lon":10.1,"location":"Pattensen"};
if (coord.lat != 0 && coord.lon != 0) {
//pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon);
times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon);
rise = times.sunrise.toString().split(" ")[4].substr(0,5);
set = times.sunset.toString().split(" ")[4].substr(0,5);
noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon);
rise = "^" + times.sunrise.toString().split(" ")[4].substr(0,5);
set = "v" + times.sunset.toString().split(" ")[4].substr(0,5);
//noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon);
} else {
rise = null;
set = null;
}
}
@ -152,11 +157,7 @@ function drawSeconds() {
var da = d.toString().split(" ");
// default draw styles
g.reset();
g.setBgColor(g.theme.bg);
// drawSting centered
g.setFontAlign(0, 0);
g.reset().setBgColor(g.theme.bg).setFontAlign(0, 0);
// draw time
var time = da[4].split(":");
@ -187,11 +188,7 @@ function draw() {
var da = d.toString().split(" ");
// default draw styles
g.reset();
g.setBgColor(g.theme.bg);
// drawSting centered
g.setFontAlign(0, 0);
g.reset().setBgColor(g.theme.bg).setFontAlign(0, 0);
// draw time
var time = da[4].split(":");
@ -255,35 +252,31 @@ function draw() {
var date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)];
// For a single secondary timezone, draw it bigger and drop time zone to second line
const xOffset = 30;
g.setFont(font, secondaryTimeFontSize);
g.drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
g.setFont(font, secondaryTimeZoneFontSize);
g.drawString(offset[OFFSET_TIME_ZONE], xyCenter, yposTime2 + 30, true);
g.setFont(font, secondaryTimeFontSize).drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
g.setFont(font, secondaryTimeZoneFontSize).drawString(offset[OFFSET_TIME_ZONE], xyCenter, yposTime2 + 30, true);
// draw Day, name of month, Date
g.setFont(font, secondaryTimeZoneFontSize);
g.drawString(date, xyCenter, yposDate, true);
g.setFont(font, secondaryTimeZoneFontSize).drawString(date, xyCenter, yposDate, true);
} else if (index < 3) {
// For > 1 extra timezones, render as columns / rows
g.setFont(font, secondaryRowColFontSize);
g.setFontAlign(-1, 0);
g.setFont(font, secondaryRowColFontSize).setFontAlign(-1, 0);
g.drawString(
offset[OFFSET_TIME_ZONE],
xcol1,
yposWorld + index * 15,
true
);
g.setFontAlign(1, 0);
g.drawString(`${hours}:${minutes}`, xcol2, yposWorld + index * 15, true);
g.setFontAlign(1, 0).drawString(`${hours}:${minutes}`, xcol2, yposWorld + index * 15, true);
}
});
if (showSunInfo) {
g.setFontAlign(-1, 0);
g.setFont("Vector",12);
g.drawString(`^${rise}`, 10, 3 + yposWorld + 3 * 15, true); // draw riseset
g.setFontAlign(1, 0);
g.drawString(`v${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw riseset
if (rise != null){
g.setFontAlign(-1, 0).setFont("Vector",12).drawString(`${rise}`, 10, 3 + yposWorld + 3 * 15, true); // draw rise
g.setFontAlign(1, 0).drawString(`${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw set
} else {
g.setFontAlign(-1, 0).setFont("Vector",11).drawString("set city in \'my location\' app!", 10, 3 + yposWorld + 3 * 15, true);
}
}
//debug settings
//g.setFontAlign(1, 0);
@ -291,7 +284,6 @@ function draw() {
//g.drawString(showSunInfo, xcol2, 3 + yposWorld + 3 * 15, true);
//g.drawString(colorWhenDark, xcol2, 3 + yposWorld + 3 * 15, true);
queueDraw();
if (secondsMode != "none") queueDrawSeconds();
@ -317,6 +309,7 @@ if (!Bangle.isLocked()) { // Initial state
if (showSunInfo) {
if (PosInterval != 0) clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins
updatePos();
}
secondsTimeout = 1000;
@ -326,9 +319,8 @@ if (!Bangle.isLocked()) { // Initial state
}
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
draw(); // draw immediately, queue redraw
if (showSunInfo) updatePos();
}else{
if (secondsMode == "always") secondsTimeout = 1000;
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
@ -343,20 +335,19 @@ if (!Bangle.isLocked()) { // Initial state
if (showSunInfo) {
if (PosInterval != 0) clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
updatePos();
}
draw(); // draw immediately, queue redraw
if (showSunInfo) updatePos();
}
Bangle.on('lock',on=>{
if (!on) { // UNlocked
if (showSunInfo) {
if (PosInterval != 0) clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins
updatePos();
}
secondsTimeout = 1000;
@ -368,7 +359,6 @@ Bangle.on('lock',on=>{
drawTimeout = undefined;
draw(); // draw immediately, queue redraw
if (showSunInfo) updatePos();
}else{ // locked
if (secondsMode == "always") secondsTimeout = 1000;
@ -381,9 +371,11 @@ Bangle.on('lock',on=>{
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
if (PosInterval != 0) clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
if (showSunInfo) {
if (PosInterval != 0) clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
updatePos();
}
draw(); // draw immediately, queue redraw
if (showSunInfo) updatePos();
}
});

View File

@ -30,8 +30,8 @@
}
} catch(e){
offsets=[
[true,"London",0],
[true,"NY",-5],
[true,"London",1],
[true,"NY",-4],
[true, "Denver",-6],
];

View File

@ -2,7 +2,7 @@
"id": "hworldclock",
"name": "Hanks World Clock",
"shortName": "Hanks World Clock",
"version": "0.22",
"version": "0.23",
"description": "Current time zone plus up to three others",
"allow_emulator":true,
"icon": "app.png",

View File

@ -1,3 +1,4 @@
0.01: New keyboard
0.02: Introduce setting "Show help button?". Make setting firstLaunch invisible by removing corresponding code from settings.js. Add marker that shows when character selection timeout has run out. Display opened text on launch when editing existing text string. Perfect horizontal alignment of buttons. Tweak help message letter casing.
0.03: Use default Bangle formatter for booleans
0.04: Allow moving the cursor

View File

@ -2,7 +2,7 @@
A library that provides the ability to input text in a style familiar to anyone who had a mobile phone before they went all touchscreen.
Swipe right for Space, left for Backspace, and up/down for Caps lock. Tap the '?' button in the app if you need a reminder!
Swipe right for Space, left for Backspace, down for cursor moving mode, and up for Caps lock. Swipe left and right to move the cursor in moving mode. Tap the '?' button in the app if you need a reminder!
At time of writing, only the [Noteify app](http://microco.sm/out/Ffe9i) uses a keyboard.

View File

@ -17,18 +17,50 @@ exports.input = function(options) {
"4":"GHI4","5":"JKL5","6":"MNO6",
"7":"PQRS7","8":"TUV80","9":"WXYZ9",
};
var helpMessage = 'Swipe:\nRight: Space\nLeft:Backspace\nUp/Down: Caps lock\n';
var helpMessage = 'Swipe:\nRight: Space\nLeft:Backspace\nUp: Caps lock\nDown:Move mode';
var charTimeout; // timeout after a key is pressed
var charCurrent; // current character (index in letters)
var charIndex; // index in letters[charCurrent]
var textIndex = text.length;
var textWidth = settings.showHelpBtn ? 10 : 14;
var caps = true;
var layout;
var btnWidth = g.getWidth()/3
var btnWidth = g.getWidth()/3;
function getMoveChar(){
return "\x00\x0B\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00@\x1F\xE1\x00\x10\x00\x10\x01\x0F\xF0\x04\x01\x00";
}
function getMoreChar(){
return "\x00\x0B\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDB\x1B`\x00\x00\x00";
}
function getCursorChar(){
return "\x00\x0B\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xAA\xAA\x80"; }
function displayText(hideMarker) {
layout.clear(layout.text);
layout.text.label = text.slice(settings.showHelpBtn ? -11 : -13) + (hideMarker ? " " : "_");
let charsBeforeCursor = textIndex;
let charsAfterCursor = Math.min(text.length - textIndex, (textWidth)/2);
let start = textIndex - Math.ceil(textWidth - charsAfterCursor);
let startMore = false;
if (start > 0) {start++; startMore = true}
if (start < 0) start = 0;
let cursor = textIndex + 1;
let end = cursor + Math.floor(start + textWidth - cursor);
if (end <= text.length) {end--; if (startMore) end--;}
if (end > text.length) end = text.length;
let pre = (start > 0 ? getMoreChar() : "") + text.slice(start, cursor);
let post = text.slice(cursor, end) + (end < text.length - 1 ? getMoreChar() : "");
layout.text.label = pre + (hideMarker ? " " : (moveMode? getMoveChar():getCursorChar())) + post;
layout.render(layout.text);
}
@ -41,8 +73,11 @@ exports.input = function(options) {
function backspace() {
deactivateTimeout(charTimeout);
text = text.slice(0, -1);
newCharacter();
if (textIndex > -1){
text = text.slice(0, textIndex) + text.slice(textIndex + 1);
if (textIndex > -1) textIndex --;
newCharacter();
}
}
function setCaps() {
@ -55,6 +90,7 @@ exports.input = function(options) {
function newCharacter(ch) {
displayText();
if (ch && textIndex < text.length) textIndex ++;
charCurrent = ch;
charIndex = 0;
}
@ -69,7 +105,11 @@ exports.input = function(options) {
newCharacter(key);
}
var newLetter = letters[charCurrent][charIndex];
text += (caps ? newLetter.toUpperCase() : newLetter.toLowerCase());
let pre = text.slice(0, textIndex);
let post = text.slice(textIndex, text.length);
text = pre + (caps ? newLetter.toUpperCase() : newLetter.toLowerCase()) + post;
// set a timeout
charTimeout = setTimeout(function() {
charTimeout = undefined;
@ -78,14 +118,29 @@ exports.input = function(options) {
displayText(charTimeout);
}
var moveMode = false;
function onSwipe(dirLeftRight, dirUpDown) {
if (dirUpDown) {
if (dirUpDown == -1) {
setCaps();
} else if (dirUpDown == 1) {
moveMode = !moveMode;
displayText(false);
} else if (dirLeftRight == 1) {
text += ' ';
newCharacter();
if (!moveMode){
text = text.slice(0, textIndex + 1) + " " + text.slice(++textIndex);
newCharacter();
} else {
if (textIndex < text.length) textIndex++;
displayText(false);
}
} else if (dirLeftRight == -1) {
backspace();
if (!moveMode){
backspace();
} else {
if (textIndex > -1) textIndex--;
displayText(false);
}
}
}

View File

@ -1,6 +1,6 @@
{ "id": "kbmulti",
"name": "Multitap keyboard",
"version":"0.03",
"version":"0.04",
"description": "A library for text input via multitap/T9 style keypad",
"icon": "app.png",
"type":"textinput",

View File

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

View File

@ -0,0 +1,49 @@
# Presentation Timer
*Forked from Stopwatch Touch*
Simple application to keep track of slides and
time during a presentation. Useful for conferences,
lectures or any presentation with a somewhat strict timing.
The interface is pretty simple, it shows a stopwatch
and the number of the current slide (based on the time),
when the time for the last slide is approaching,
the button becomes red, when it passed,
the time will go on for another half a minute and stop automatically.
The only way to upload personalized timings is
by uploading a CSV to the bangle (i.e. from the IDE),
in the future I'll possibly figure out a better way.
Each line in the file (which must be called `presentation_timer.csv`)
contains the time in minutes at which the slide
is supposed to finish and the slide number,
separated by a semicolon.
For instance the line `1.5;1` means that slide 1
is lasting until 1 minutes 30 seconds (yes it's decimal),
after another slide will start.
The only requirement is that timings are increasing,
so slides number don't have to be consecutive,
some can be skipped and they can even be short texts
(be careful with that, I didn't test it).
At the moment the app is just quick and dirty
but it should do its job.
## Screenshots
![](screenshot1.png)
![](screenshot2.png)
![](screenshot3.png)
![](screenshot4.png)
## Example configuration file
_presentation_timer.csv_
```csv
1.5;1
2;2
2.5;3
3;4
```

View File

@ -0,0 +1,15 @@
{
"id": "presentation_timer",
"name": "Presentation Timer",
"version": "0.01",
"description": "A touch based presentation timer for Bangle JS 2",
"icon": "presentation_timer.png",
"screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"},{"url":"screenshot4.png"}],
"tags": "tools,app",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"presentation_timer.app.js","url":"presentation_timer.app.js"},
{"name":"presentation_timer.img","url":"presentation_timer.icon.js","evaluate":true}
]
}

View File

@ -0,0 +1,272 @@
let w = g.getWidth();
let h = g.getHeight();
let tTotal = Date.now();
let tStart = tTotal;
let tCurrent = tTotal;
let running = false;
let timeY = 2*h/5;
let displayInterval;
let redrawButtons = true;
const iconScale = g.getWidth() / 178; // scale up/down based on Bangle 2 size
// 24 pixel images, scale to watch
// 1 bit optimal, image string, no E.toArrayBuffer()
const pause_img = atob("GBiBAf////////////////wYP/wYP/wYP/wYP/wYP/wYP/wYP/wYP/wYP/wYP/wYP/wYP/wYP/wYP/wYP/wYP////////////////w==");
const play_img = atob("GBjBAP//AAAAAAAAAAAIAAAOAAAPgAAP4AAP+AAP/AAP/wAP/8AP//AP//gP//gP//AP/8AP/wAP/AAP+AAP4AAPgAAOAAAIAAAAAAAAAAA=");
const reset_img = atob("GBiBAf////////////AAD+AAB+f/5+f/5+f/5+cA5+cA5+cA5+cA5+cA5+cA5+cA5+cA5+f/5+f/5+f/5+AAB/AAD////////////w==");
const margin = 0.5; //half a minute tolerance
//dummy default values
var slides = [
[0.3, 1],
[0.5, 2],
[0.7, 3],
[1,4]
];
function log_debug(o) {
//console.log(o);
}
//first must be a number
function readSlides() {
let csv = require("Storage").read("presentation_timer.csv");
if(!csv) return;
let lines = csv.split("\n").filter(e=>e);
log_debug("Loading "+lines.length+" slides");
slides = lines.map(line=>{let s=line.split(";");return [+s[0],s[1]];});
}
function timeToText(t) {
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 drawButtons() {
log_debug("drawButtons()");
if (!running && tCurrent == tTotal) {
bigPlayPauseBtn.draw();
} else if (!running && tCurrent != tTotal) {
resetBtn.draw();
smallPlayPauseBtn.draw();
} else {
bigPlayPauseBtn.draw();
}
redrawButtons = false;
}
//not efficient but damn easy
function findSlide(time) {
time /= 60000;
//change colour for the last 30 seconds
if(time > slides[slides.length-1][0] - margin && bigPlayPauseBtn.color!="#f00") {
bigPlayPauseBtn.color="#f00";
drawButtons();
}
for(let i=0; i<slides.length; i++) {
if(slides[i][0] > time)
return slides[i][1];
}
//stop automatically
if(time > slides[slides.length-1][0] + margin) {
bigPlayPauseBtn.color="#0ff"; //restore
stopTimer();
}
return /*LANG*/"end!";
}
function drawTime() {
log_debug("drawTime()");
let Tt = tCurrent-tTotal;
let Ttxt = timeToText(Tt);
Ttxt += "\n"+findSlide(Tt);
// total time
g.setFont("Vector",38); // check
g.setFontAlign(0,0);
g.clearRect(0, timeY - 42, w, timeY + 42);
g.setColor(g.theme.fg);
g.drawString(Ttxt, w/2, timeY);
}
function draw() {
let last = tCurrent;
if (running) tCurrent = Date.now();
g.setColor(g.theme.fg);
if (redrawButtons) drawButtons();
drawTime();
}
function startTimer() {
log_debug("startTimer()");
draw();
displayInterval = setInterval(draw, 100);
}
function stopTimer() {
log_debug("stopTimer()");
if (displayInterval) {
clearInterval(displayInterval);
displayInterval = undefined;
}
}
// BTN stop start
function stopStart() {
log_debug("stopStart()");
if (running)
stopTimer();
running = !running;
Bangle.buzz();
if (running)
tStart = Date.now() + tStart- tCurrent;
tTotal = Date.now() + tTotal - tCurrent;
tCurrent = Date.now();
setButtonImages();
redrawButtons = true;
if (running) {
startTimer();
} else {
draw();
}
}
function setButtonImages() {
if (running) {
bigPlayPauseBtn.setImage(pause_img);
smallPlayPauseBtn.setImage(pause_img);
resetBtn.setImage(reset_img);
} else {
bigPlayPauseBtn.setImage(play_img);
smallPlayPauseBtn.setImage(play_img);
resetBtn.setImage(reset_img);
}
}
// lap or reset
function lapReset() {
log_debug("lapReset()");
if (!running && tStart != tCurrent) {
redrawButtons = true;
Bangle.buzz();
tStart = tCurrent = tTotal = Date.now();
g.clearRect(0,24,w,h);
draw();
}
}
// simple on screen button class
function BUTTON(name,x,y,w,h,c,f,i) {
this.name = name;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = c;
this.callback = f;
this.img = i;
}
BUTTON.prototype.setImage = function(i) {
this.img = i;
}
// if pressed the callback
BUTTON.prototype.check = function(x,y) {
//console.log(this.name + ":check() x=" + x + " y=" + y +"\n");
if (x>= this.x && x<= (this.x + this.w) && y>= this.y && y<= (this.y + this.h)) {
log_debug(this.name + ":callback\n");
this.callback();
return true;
}
return false;
};
BUTTON.prototype.draw = function() {
g.setColor(this.color);
g.fillRect(this.x, this.y, this.x + this.w, this.y + this.h);
g.setColor("#000"); // the icons and boxes are drawn black
if (this.img != undefined) {
let iw = iconScale * 24; // the images were loaded as 24 pixels, we will scale
let ix = this.x + ((this.w - iw) /2);
let iy = this.y + ((this.h - iw) /2);
log_debug("g.drawImage(" + ix + "," + iy + "{scale: " + iconScale + "})");
g.drawImage(this.img, ix, iy, {scale: iconScale});
}
g.drawRect(this.x, this.y, this.x + this.w, this.y + this.h);
};
var bigPlayPauseBtn = new BUTTON("big",0, 3*h/4 ,w, h/4, "#0ff", stopStart, play_img);
var smallPlayPauseBtn = new BUTTON("small",w/2, 3*h/4 ,w/2, h/4, "#0ff", stopStart, play_img);
var resetBtn = new BUTTON("rst",0, 3*h/4, w/2, h/4, "#ff0", lapReset, pause_img);
bigPlayPauseBtn.setImage(play_img);
smallPlayPauseBtn.setImage(play_img);
resetBtn.setImage(pause_img);
Bangle.on('touch', function(button, xy) {
var x = xy.x;
var y = xy.y;
// adjust for outside the dimension of the screen
// http://forum.espruino.com/conversations/371867/#comment16406025
if (y > h) y = h;
if (y < 0) y = 0;
if (x > w) x = w;
if (x < 0) x = 0;
// not running, and reset
if (!running && tCurrent == tTotal && bigPlayPauseBtn.check(x, y)) return;
// paused and hit play
if (!running && tCurrent != tTotal && smallPlayPauseBtn.check(x, y)) return;
// paused and press reset
if (!running && tCurrent != tTotal && resetBtn.check(x, y)) return;
// must be running
if (running && bigPlayPauseBtn.check(x, y)) return;
});
// Stop updates when LCD is off, restart when on
Bangle.on('lcdPower',on=>{
if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
// Clear the screen once, at startup
g.setTheme({bg:"#000",fg:"#fff",dark:true}).clear();
// above not working, hence using next 2 lines
g.setColor("#000");
g.fillRect(0,0,w,h);
Bangle.loadWidgets();
Bangle.drawWidgets();
draw();
setWatch(() => load(), BTN, { repeat: false, edge: "falling" });
readSlides();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+AC1WwIZXACmBF7FWAH4Ae/0WAFiQBF9+sAFgv/AAvXAAgvmFgoyWF6IuLGCIvPFpoxRF5wlIwIKJF8lWwIvjQpIvKGBgv8cpWBF5QwLF/4vrEJQvNGBQv/F5FWSCq/XGAVWB5DviEgRiJF8gxDF9q+SF5owWEJYv9GCggMF5wwSD5ovPGCAeOF6AwODp4vRGJYbRF6YAbF/4v/F8eBAYYECAYYvRACFWqwEGwNWwIeSF7IEFAD5VBGhpekAo6QiEYo1LR0QpGBgyOhAxCQfKIIhFGxpegA44+HF85gRA=="))

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -5,4 +5,5 @@
0.05: BUGFIX: pedometer widget interfered with the clock Font Alignment
0.06: Use Bangle.setUI for button/launcher handling
0.07: Support for Bangle.js 2 and themes
0.08: Removed "wake LCD on face-up"-feature: A watch-face should not set things like "wake LCD on face-up".
0.08: Removed "wake LCD on face-up"-feature: A watch-face should not set things like "wake LCD on face-up".
0.09: Added button control toggle and other live controls to new settings screen.

View File

@ -6,22 +6,49 @@ Inspired by the Pebble sliding clock, old times are scrolled off the screen and
## Usage
### Button 1
### Bangle 2
The Bangle 2 has Live Controls switched **off** by default so the colour and language have to be changed from the setting Menu.
Please locate the Sliding Text clock under the setting->apps menu.
With the Live Controls switched on:
#### Bottom right hand corner press
press the bottom right hand corner of the screen to change the colour
| White | Black | Gray | Red |
|----------------------|----------------------|----------------------|----------------------|
| ![](b2_color-01.jpg) | ![](b2_color-02.jpg) | ![](b2_color-03.jpg) | ![](b2_color-04.jpg) |
#### Top right hand corner press
press the top right hand corner of the screen to change the language
### Bangle 1
By Default the Live Controls (The side buttons) are switched on, which means the clock face can be controlled dynamically using the 2 side buttons on the right hand side
#### Button 1
Use Button 1 (the top right button) to change the language
| English | English (Traditional) | French | Japanese (Romanji) |
| ---- | ---- | ---- | ---- |
| ![](./format-01.jpg) | ![](format-02.jpg) | ![](format-03.jpg) |![](format-04.jpg) |
| ![](format-01.jpg) | ![](format-02.jpg) | ![](format-03.jpg) |![](format-04.jpg) |
| **German** | **Spanish** | | |
| ![](./format-05.jpg) | ![](format-06.jpg) | | |
| ![](format-05.jpg) | ![](format-06.jpg) | | |
### Button 3
#### Button 3
Button 3 (bottom right button) is used to change the colour
| Black | Red | Gray | Purple |
| ---- | ---- | ---- | ---- |
| ![](./color-01.jpg) | ![](color-02.jpg) | ![](color-03.jpg) | ![](color-04.jpg) |
| ![](b1_color-01.jpg) | ![](b1_color-02.jpg) | ![](b1_color-03.jpg) | ![](b1_color-04.jpg) |
#### Settings
To turn off the Live Controls and change the settings statically please visit the settings menu. The settings menu will allow you to:
- Colour Scheme
- Language
- Live Controls (On or Off)
## Further Details

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -1,7 +1,7 @@
{
"id": "slidingtext",
"name": "Sliding Clock",
"version": "0.08",
"version": "0.09",
"description": "Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently English, French, Japanese, Spanish and German are supported",
"icon": "slidingtext.png",
"type": "clock",
@ -12,6 +12,7 @@
"allow_emulator": false,
"storage": [
{"name":"slidingtext.app.js","url":"slidingtext.js"},
{"name":"slidingtext.settings.js","url":"slidingtext.settings.js"},
{"name":"slidingtext.img","url":"slidingtext-icon.js","evaluate":true},
{"name":"slidingtext.locale.en.js","url":"slidingtext.locale.en.js"},
{"name":"slidingtext.locale.en2.js","url":"slidingtext.locale.en2.js"},
@ -21,5 +22,6 @@
{"name":"slidingtext.locale.jp.js","url":"slidingtext.locale.jp.js"},
{"name":"slidingtext.locale.de.js","url":"slidingtext.locale.de.js"},
{"name":"slidingtext.dtfmt.js","url":"slidingtext.dtfmt.js"}
]
],
"data": [{"name": "slidingtext.settings.json"}]
}

View File

@ -7,6 +7,7 @@ class DateFormatter {
* to the lines of text on the screen
*/
name(){return "no name";}
shortName(){return "no short name"}
formatDate(date){
return ["no","date","defined"];
}

View File

@ -5,11 +5,17 @@
*/
const color_schemes = [
{
name: "white",
background : [1.0,1.0,1.0],
main_bar: [0.0,0.0,0.0],
other_bars: [0.1,0.1,0.1],
},
{
name: "black",
background : [0.0,0.0,0.0],
main_bar: [1.0,1.0,1.0],
other_bars: [0.85,0.85,0.85],
other_bars: [0.9,0.9,0.9],
},
{
name: "red",
@ -81,7 +87,7 @@ function reset_commands(){
function has_commands(){
return command_stack_high_priority.length > 0 ||
command_stack_low_priority.lenth > 0;
command_stack_low_priority.length > 0;
}
class ShiftText {
@ -244,15 +250,19 @@ function setRowDisplays(y, heights) {
y += heights[i];
}
}
if (g.getHeight()>200)
function bangleVersion(){
return (g.getHeight()>200)? 1 : 2;
}
if (bangleVersion()<2)
setRowDisplays(50, [40,30,30,30,40]);
else
setRowDisplays(34, [35,25,25,25,35]);
function nextColorTheme(){
//console.log("next color theme");
color_scheme_index += 1;
if(color_scheme_index >= row_displays.length){
if(color_scheme_index > row_displays.length){
color_scheme_index = 0;
}
setColorScheme(color_schemes[color_scheme_index]);
@ -411,8 +421,7 @@ function draw_clock(){
reset_commands();
date = display_time(date);
console.log("draw_clock:" + last_draw_time.toISOString() + " display:" + date.toISOString());
// for debugging only
//date.setMinutes(37);
var rows = date_formatter.formatDate(date);
var display;
for (var i = 0; i < rows.length; i++) {
@ -495,7 +504,7 @@ function set_colorscheme(colorscheme_name){
function set_dateformat(dateformat_name){
console.log("setting date format:" + dateformat_name);
for (var i=0; i < date_formatters.length; i++) {
if(date_formatters[i].name() == dateformat_name){
if(date_formatters[i].shortName() == dateformat_name){
date_formatter_idx = i;
date_formatter = date_formatters[date_formatter_idx];
console.log("match");
@ -503,6 +512,7 @@ function set_dateformat(dateformat_name){
}
}
var enable_live_controls = false;
const PREFERENCE_FILE = "slidingtext.settings.json";
/**
* Called on startup to set the watch to the last preference settings
@ -510,7 +520,7 @@ const PREFERENCE_FILE = "slidingtext.settings.json";
function load_settings(){
var setScheme = false;
try{
settings = require("Storage").readJSON(PREFERENCE_FILE);
var settings = require("Storage").readJSON(PREFERENCE_FILE);
if(settings != null){
console.log("loaded:" + JSON.stringify(settings));
if(settings.color_scheme != null){
@ -520,9 +530,15 @@ function load_settings(){
if(settings.date_format != null){
set_dateformat(settings.date_format);
}
if(settings.enable_live_controls == null){
settings.enable_live_controls = (bangleVersion() <= 1);
}
enable_live_controls = settings.enable_live_controls;
} else {
console.log("no settings to load");
enable_live_controls = (bangleVersion() <= 1);
}
console.log("enable_live_controls=" + enable_live_controls);
} catch(e){
console.log("failed to load settings:" + e);
}
@ -536,24 +552,30 @@ function load_settings(){
*/
function save_settings(){
var settings = {
date_format : date_formatter.name(),
date_format : date_formatter.shortName(),
color_scheme : color_schemes[color_scheme_index].name,
enable_live_controls: enable_live_controls
};
console.log("saving:" + JSON.stringify(settings));
require("Storage").writeJSON(PREFERENCE_FILE,settings);
}
function button1pressed() {
changeFormatter();
save_settings();
console.log("button1pressed");
if (enable_live_controls) {
changeFormatter();
save_settings();
}
}
function button3pressed() {
console.log("button3pressed");
nextColorTheme();
reset_clock(true);
draw_clock();
save_settings();
if (enable_live_controls) {
nextColorTheme();
reset_clock(true);
draw_clock();
save_settings();
}
}
// The interval reference for updating the clock

View File

@ -66,6 +66,7 @@ function germanMinsToText(mins) {
class GermanDateFormatter extends DateFormatter {
constructor() { super();}
name(){return "German";}
shortName(){return "de"}
formatDate(date){
var mins = date.getMinutes();
var hourOfDay = date.getHours();

View File

@ -5,6 +5,7 @@ const numberToText = require("slidingtext.utils.en.js").numberToText;
class EnglishDateFormatter extends DateFormatter {
constructor() { super();}
name(){return "English";}
shortName(){return "en"}
formatDate(date){
var hours_txt = hoursToText(date.getHours());
var mins_txt = numberToText(date.getMinutes());

View File

@ -7,6 +7,7 @@ class EnglishTraditionalDateFormatter extends DateFormatter {
super();
}
name(){return "English (Traditional)";}
shortName(){return "en2"}
formatDate(date){
var mins = date.getMinutes();
var hourOfDay = date.getHours();

View File

@ -47,6 +47,7 @@ function spanishMinsToText(mins){
class SpanishDateFormatter extends DateFormatter {
constructor() { super();}
name(){return "Spanish";}
shortName(){return "es"}
formatDate(date){
var mins = date.getMinutes();
var hourOfDay = date.getHours();

View File

@ -31,6 +31,7 @@ function frenchHeures(hours){
class FrenchDateFormatter extends DateFormatter {
constructor() { super(); }
name(){return "French";}
shortName(){return "fr"}
formatDate(date){
var hours = frenchHoursToText(date.getHours());
var heures = frenchHeures(date.getHours());

View File

@ -61,6 +61,7 @@ function japaneseMinsToText(mins){
class JapaneseDateFormatter extends DateFormatter {
constructor() { super(); }
name(){return "Japanese (Romanji)";}
shortName(){return "jp"}
formatDate(date){
var hours_txt = japaneseHoursToText(date.getHours());
var mins_txt = japaneseMinsToText(date.getMinutes());

View File

@ -0,0 +1,65 @@
(function(back) {
const PREFERENCE_FILE = "slidingtext.settings.json";
var settings = Object.assign({},
require('Storage').readJSON(PREFERENCE_FILE, true) || {});
// the screen controls are defaulted on for a bangle 1 and off for a bangle 2
if(settings.enable_live_controls == null){
settings.enable_live_controls = (g.getHeight()> 200);
}
console.log("loaded:" + JSON.stringify(settings));
const LANGUAGES_FILE = "slidingtext.languages.json";
const LANGUAGES_DEFAULT = ["en","en2"];
var locales = null;
try {
locales = require("Storage").readJSON(LANGUAGES_FILE);
} catch(e) {
console.log("failed to load languages:" + e);
}
if(locales == null || locales.length == 0){
locales = LANGUAGES_DEFAULT;
console.log("defaulting languages to locale:" + locales);
}
function writeSettings() {
console.log("saving:" + JSON.stringify(settings));
require('Storage').writeJSON(PREFERENCE_FILE, settings);
}
// Helper method which uses int-based menu item for set of string values
function stringItems(startvalue, writer, values) {
return {
value: (startvalue === undefined ? 0 : values.indexOf(startvalue)),
format: v => values[v],
min: 0,
max: values.length - 1,
wrap: true,
step: 1,
onchange: v => {
writer(values[v]);
writeSettings();
}
};
}
// Helper method which breaks string set settings down to local settings object
function stringInSettings(name, values) {
return stringItems(settings[name], v => settings[name] = v, values);
}
// Show the menu
E.showMenu({
"" : { "title" : "Sliding Text" },
"< Back" : () => back(),
"Colour": stringInSettings("color_scheme", ["white", "black", "red","grey","purple","blue"]),
"Languages": stringInSettings("date_format", locales),
"Live Control": {
value: (settings.enable_live_controls !== undefined ? settings.enable_live_controls : true),
format: v => v ? "On" : "Off",
onchange: v => {
settings.enable_live_controls = v;
writeSettings();
}
},
});
})

View File

@ -9,3 +9,6 @@
0.10: Bug fix
0.11: Avoid too many notifications. Change disconnected colour to red.
0.12: Prevent repeated execution of `draw()` from the current app.
0.13: Added "connection restored" notification. Fixed restoring of the watchface.
0.14: Added configuration option
0.15: Added option to hide widget when connected

View File

@ -1,13 +1,17 @@
{
"id": "widbt_notify",
"name": "Bluetooth Widget with Notification",
"version": "0.12",
"version": "0.15",
"description": "Show the current Bluetooth connection status in the top right of the clock and vibrate when disconnected.",
"icon": "widget.png",
"type": "widget",
"tags": "widget,bluetooth",
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"widbt_notify.wid.js","url":"widget.js"}
]
{"name":"widbt_notify.wid.js","url":"widget.js"},
{"name":"widbt_notify.settings.js","url":"settings.js"}
],
"data": [
{"name":"widbt_notify.json"}
]
}

View File

@ -0,0 +1,69 @@
(function(back) {
var FILE = "widbt_notify.json";
var settings = Object.assign({
secondsOnUnlock: false,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
// Helper method which uses int-based menu item for set of string values
function stringItems(startvalue, writer, values) {
return {
value: (startvalue === undefined ? 0 : values.indexOf(startvalue)),
format: v => values[v],
min: 0,
max: values.length - 1,
wrap: true,
step: 1,
onchange: v => {
writer(values[v]);
writeSettings();
}
};
}
// Helper method which breaks string set settings down to local settings object
function stringInSettings(name, values) {
return stringItems(settings[name], v => settings[name] = v, values);
}
var mainmenu = {
"": {
"title": "Bluetooth Widget WN"
},
"< Back": () => back(),
"Show Widget": {
value: (settings.showWidget !== undefined ? settings.showWidget : true),
onchange: v => {
settings.showWidget = v;
writeSettings();
}
},
"Buzz on Connect": {
value: (settings.buzzOnConnect !== undefined ? settings.buzzOnConnect : true),
onchange: v => {
settings.buzzOnConnect = v;
writeSettings();
}
},
"Buzz on loss": {
value: (settings.buzzOnLoss !== undefined ? settings.buzzOnLoss : true),
onchange: v => {
settings.buzzOnLoss = v;
writeSettings();
}
},
"Hide connected": {
value: (settings.hideConnected !== undefined ? settings.hideConnected : false),
onchange: v => {
settings.hideConnected = v;
writeSettings();
}
}
};
E.showMenu(mainmenu);
});

View File

@ -2,42 +2,106 @@ WIDGETS.bluetooth_notify = {
area: "tr",
width: 15,
warningEnabled: 1,
// ------------ Settings -------- very lame - need to improve
readshowWidget: function() {
var showWidget;
const SETTINGSFILE = "widbt_notify.json";
function def (value, def) {return value !== undefined ? value : def;}
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
showWidget = def(settings.showWidget, true);
return showWidget;
},
readBuzzOnConnect: function() {
var buzzOnConnect;
const SETTINGSFILE = "widbt_notify.json";
function def (value, def) {return value !== undefined ? value : def;}
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
buzzOnConnect = def(settings.buzzOnConnect, true);
return buzzOnConnect;
},
readBuzzOnLoss: function() {
var buzzOnLoss;
const SETTINGSFILE = "widbt_notify.json";
function def (value, def) {return value !== undefined ? value : def;}
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
buzzOnLoss = def(settings.buzzOnLoss, true);
return buzzOnLoss;
},
readHideConnected: function() {
var hideConnected;
const SETTINGSFILE = "widbt_notify.json";
function def (value, def) {return value !== undefined ? value : def;}
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
hideConnected = def(settings.hideConnected, true);
return hideConnected;
},
// ------------ Settings --------
draw: function() {
g.reset();
if (NRF.getSecurityStatus().connected) {
g.setColor((g.getBPP() > 8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
} else {
// g.setColor(g.theme.dark ? "#666" : "#999");
g.setColor("#f00"); // red is easier to distinguish from blue
if (WIDGETS.bluetooth_notify.readshowWidget()){
g.reset();
if (NRF.getSecurityStatus().connected) {
if (!WIDGETS.bluetooth_notify.readHideConnected()) {
g.setColor((g.getBPP() > 8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="), 2 + this.x, 2 + this.y);
}
} else {
// g.setColor(g.theme.dark ? "#666" : "#999");
g.setColor("#f00"); // red is easier to distinguish from blue
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="), 2 + this.x, 2 + this.y);
}
}
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="), 2 + this.x, 2 + this.y);
},
redrawCurrentApp: function(){
if(typeof(draw)=='function'){
g.clear();
draw();
Bangle.loadWidgets();
Bangle.drawWidgets();
}else{
load(); // fallback. This might reset some variables
}
},
connect: function() {
WIDGETS.bluetooth_notify.draw();
if(WIDGETS.bluetooth_notify.warningEnabled == 1){
E.showMessage(/*LANG*/'Connection\nrestored.', 'Bluetooth');
setTimeout(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
WIDGETS.bluetooth_notify.warningEnabled = 0;
setTimeout('WIDGETS.bluetooth_notify.warningEnabled = 1;', 30000); // don't buzz for the next 30 seconds.
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
if(!quiet && WIDGETS.bluetooth_notify.readBuzzOnConnect()){
Bangle.buzz(700, 1); // buzz on connection resume
}
}
WIDGETS.bluetooth_notify.draw();
},
disconnect: function() {
if(WIDGETS.bluetooth_notify.warningEnabled == 1){
E.showMessage(/*LANG*/'Connection\nlost.', 'Bluetooth');
setTimeout(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
WIDGETS.bluetooth_notify.warningEnabled = 0;
setTimeout('WIDGETS.bluetooth_notify.warningEnabled = 1;', 30000); // don't buzz for the next 30 seconds.
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
if(!quiet){
Bangle.buzz(700, 1); // buzz on connection loss
if(WIDGETS.bluetooth_notify.warningEnabled == 1){
E.showMessage(/*LANG*/ 'Connection\nlost.', 'Bluetooth');
setTimeout(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
WIDGETS.bluetooth_notify.warningEnabled = 0;
setTimeout('WIDGETS.bluetooth_notify.warningEnabled = 1;', 30000); // don't buzz for the next 30 seconds.
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
if(!quiet && WIDGETS.bluetooth_notify.readBuzzOnLoss()){
Bangle.buzz(700, 1); // buzz on connection loss
}
}
}
WIDGETS.bluetooth_notify.draw();
}
};