Merge branch 'espruino:master' into gpsnav_b2
|
@ -15,3 +15,5 @@
|
|||
0.15: Added option for a dynamic mode to show widgets only if unlocked.
|
||||
0.16: You can now show your agenda if your calendar is synced with Gadgetbridge.
|
||||
0.17: Fix - Step count was no more shown in the menu.
|
||||
0.18: Set timer for an agenda entry by simply clicking in the middle of the screen. Only one timer can be set.
|
||||
0.19: Fix - Compatibility with "Digital clock widget"
|
||||
|
|
|
@ -7,7 +7,7 @@ A very minimalistic clock to mainly show date and time.
|
|||
The BW clock provides many features and also 3rd party integrations:
|
||||
- Bangle data such as steps, heart rate, battery or charging state.
|
||||
- A timer can be set directly. *Requirement: Scheduler library*
|
||||
- Show agenda entries. *Requirement: Gadgetbridge calendar sync enabled*
|
||||
- Show agenda entries. A timer for an agenda entry can also be set by simply clicking in the middle of the screen. This can be used to not forget a meeting etc. Note that only one agenda-timer can be set at a time. *Requirement: Gadgetbridge calendar sync enabled*
|
||||
- Weather temperature as well as the wind speed can be shown. *Requirement: Weather app*
|
||||
- HomeAssistant triggers can be executed directly. *Requirement: HomeAssistant app*
|
||||
|
||||
|
|
|
@ -8,10 +8,16 @@ const storage = require('Storage');
|
|||
* Statics
|
||||
*/
|
||||
const SETTINGS_FILE = "bwclk.setting.json";
|
||||
const TIMER_IDX = "bwclk";
|
||||
const TIMER_IDX = "bwclk_timer";
|
||||
const TIMER_AGENDA_IDX = "bwclk_agenda";
|
||||
const W = g.getWidth();
|
||||
const H = g.getHeight();
|
||||
|
||||
/************
|
||||
* Global data
|
||||
*/
|
||||
var pressureData;
|
||||
|
||||
/************
|
||||
* Settings
|
||||
*/
|
||||
|
@ -181,6 +187,14 @@ function imgAgenda() {
|
|||
}
|
||||
}
|
||||
|
||||
function imgMountain() {
|
||||
return {
|
||||
width : 24, height : 24, bpp : 1,
|
||||
transparent : 1,
|
||||
buffer : atob("//////////////////////3///n///D//uZ//E8//A+/+Z+f8//P5//n7//3z//zn//5AAAAAAAA////////////////////")
|
||||
}
|
||||
}
|
||||
|
||||
/************
|
||||
* 2D MENU with entries of:
|
||||
* [name, icon, opt[customDownFun], opt[customUpFun], opt[customCenterFun]]
|
||||
|
@ -195,6 +209,7 @@ var menu = [
|
|||
function(){ return [ E.getBattery() + "%", Bangle.isCharging() ? imgCharging() : imgBattery() ] },
|
||||
function(){ return [ getSteps(), imgSteps() ] },
|
||||
function(){ return [ Math.round(Bangle.getHealthStatus("last").bpm) + " bpm", imgBpm()] },
|
||||
function(){ return [ getAltitude(), imgMountain() ]},
|
||||
]
|
||||
]
|
||||
|
||||
|
@ -205,8 +220,8 @@ try{
|
|||
require('sched');
|
||||
menu.push([
|
||||
function(){
|
||||
var text = isAlarmEnabled() ? getAlarmMinutes() + " min." : "Timer";
|
||||
return [text, imgTimer(), () => decreaseAlarm(), () => increaseAlarm(), null ]
|
||||
var text = isAlarmEnabled(TIMER_IDX) ? getAlarmMinutes(TIMER_IDX) + " min." : "Timer";
|
||||
return [text, imgTimer(), () => decreaseAlarm(TIMER_IDX), () => increaseAlarm(TIMER_IDX), null ]
|
||||
},
|
||||
]);
|
||||
} catch(ex) {
|
||||
|
@ -219,6 +234,7 @@ try{
|
|||
* Note that we handle the agenda differently in order to hide old entries...
|
||||
*/
|
||||
var agendaIdx = 0;
|
||||
var agendaTimerIdx = 0;
|
||||
if(storage.readJSON("android.calendar.json") !== undefined){
|
||||
function nextAgendaEntry(){
|
||||
agendaIdx += 1;
|
||||
|
@ -246,9 +262,43 @@ if(storage.readJSON("android.calendar.json") !== undefined){
|
|||
var title = entry.title.slice(0,14);
|
||||
var date = new Date(entry.timestamp*1000);
|
||||
var dateStr = locale.date(date).replace(/\d\d\d\d/,"");
|
||||
dateStr += entry.durationInSeconds < 86400 ? " / " + locale.time(date,1) : "";
|
||||
dateStr += entry.durationInSeconds < 86400 ? "/ " + locale.time(date,1) : "";
|
||||
|
||||
return [title + "\n" + dateStr, imgAgenda(), () => nextAgendaEntry(), () => previousAgendaEntry(), null]
|
||||
function dynImgAgenda(){
|
||||
if(isAlarmEnabled(TIMER_AGENDA_IDX) && agendaTimerIdx == agendaIdx){
|
||||
return imgTimer();
|
||||
} else {
|
||||
return imgAgenda();
|
||||
}
|
||||
}
|
||||
|
||||
return [title + "\n" + dateStr, dynImgAgenda(), () => nextAgendaEntry(), () => previousAgendaEntry(), function(){
|
||||
try{
|
||||
var alarm = require('sched')
|
||||
|
||||
// If other time, we disable the old one and enable this one.
|
||||
if(agendaIdx != agendaTimerIdx){
|
||||
agendaTimerIdx = -1;
|
||||
alarm.setAlarm(TIMER_AGENDA_IDX, undefined);
|
||||
}
|
||||
|
||||
// Disable alarm if enabled
|
||||
if(isAlarmEnabled(TIMER_AGENDA_IDX)){
|
||||
agendaTimerIdx = -1;
|
||||
alarm.setAlarm(TIMER_AGENDA_IDX, undefined);
|
||||
alarm.reload();
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, set alarm for given event
|
||||
agendaTimerIdx = agendaIdx;
|
||||
alarm.setAlarm(TIMER_AGENDA_IDX, {
|
||||
msg: title,
|
||||
timer : parseInt((date - now)),
|
||||
});
|
||||
alarm.reload();
|
||||
} catch(ex){ }
|
||||
}]
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
@ -330,6 +380,14 @@ function getSteps() {
|
|||
}
|
||||
|
||||
|
||||
function getAltitude(){
|
||||
if(pressureData && pressureData.altitude){
|
||||
return Math.round(pressureData.altitude) + "m";
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
|
||||
function getWeather(){
|
||||
var weatherJson;
|
||||
|
||||
|
@ -364,10 +422,10 @@ function getWeather(){
|
|||
}
|
||||
|
||||
|
||||
function isAlarmEnabled(){
|
||||
function isAlarmEnabled(idx){
|
||||
try{
|
||||
var alarm = require('sched');
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
var alarmObj = alarm.getAlarm(idx);
|
||||
if(alarmObj===undefined || !alarmObj.on){
|
||||
return false;
|
||||
}
|
||||
|
@ -379,22 +437,22 @@ function isAlarmEnabled(){
|
|||
}
|
||||
|
||||
|
||||
function getAlarmMinutes(){
|
||||
if(!isAlarmEnabled()){
|
||||
function getAlarmMinutes(idx){
|
||||
if(!isAlarmEnabled(idx)){
|
||||
return -1;
|
||||
}
|
||||
|
||||
var alarm = require('sched');
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
var alarmObj = alarm.getAlarm(idx);
|
||||
return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000));
|
||||
}
|
||||
|
||||
|
||||
function increaseAlarm(){
|
||||
function increaseAlarm(idx){
|
||||
try{
|
||||
var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0;
|
||||
var alarm = require('sched')
|
||||
alarm.setAlarm(TIMER_IDX, {
|
||||
var minutes = isAlarmEnabled(idx) ? getAlarmMinutes(idx) : 0;
|
||||
var alarm = require('sched');
|
||||
alarm.setAlarm(idx, {
|
||||
timer : (minutes+5)*60*1000,
|
||||
});
|
||||
alarm.reload();
|
||||
|
@ -402,16 +460,16 @@ function increaseAlarm(){
|
|||
}
|
||||
|
||||
|
||||
function decreaseAlarm(){
|
||||
function decreaseAlarm(idx){
|
||||
try{
|
||||
var minutes = getAlarmMinutes();
|
||||
var minutes = getAlarmMinutes(idx);
|
||||
minutes -= 5;
|
||||
|
||||
var alarm = require('sched')
|
||||
alarm.setAlarm(TIMER_IDX, undefined);
|
||||
alarm.setAlarm(idx, undefined);
|
||||
|
||||
if(minutes > 0){
|
||||
alarm.setAlarm(TIMER_IDX, {
|
||||
alarm.setAlarm(idx, {
|
||||
timer : minutes*60*1000,
|
||||
});
|
||||
}
|
||||
|
@ -421,6 +479,19 @@ function decreaseAlarm(){
|
|||
}
|
||||
|
||||
|
||||
function handleAsyncData(){
|
||||
try{
|
||||
|
||||
if (settings.menuPosX == 1){
|
||||
Bangle.getPressure().then(data=>{
|
||||
pressureData = data
|
||||
});
|
||||
}
|
||||
|
||||
}catch(ex){ }
|
||||
}
|
||||
|
||||
|
||||
/************
|
||||
* DRAW
|
||||
*/
|
||||
|
@ -428,6 +499,9 @@ function draw() {
|
|||
// Queue draw again
|
||||
queueDraw();
|
||||
|
||||
// Now lets measure some data..
|
||||
handleAsyncData();
|
||||
|
||||
// Draw clock
|
||||
drawDate();
|
||||
drawTime();
|
||||
|
@ -670,6 +744,7 @@ Bangle.on('touch', function(btn, e){
|
|||
menuEntry[4]();
|
||||
setTimeout(()=>{
|
||||
Bangle.buzz(80, 0.6);
|
||||
drawTime();
|
||||
}, 250);
|
||||
} catch(ex){
|
||||
// In case it fails, we simply ignore it.
|
||||
|
@ -698,6 +773,9 @@ E.on("kill", function(){
|
|||
// dark/light theme as well as the colors.
|
||||
g.setTheme({bg:g.theme.fg,fg:g.theme.bg, dark:!g.theme.dark}).clear();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
||||
// Load widgets and draw clock the first time
|
||||
Bangle.loadWidgets();
|
||||
|
||||
|
@ -707,6 +785,3 @@ for (let wd of WIDGETS) {wd._draw=wd.draw; wd._area=wd.area;}
|
|||
|
||||
// Draw first time
|
||||
draw();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "bwclk",
|
||||
"name": "BW Clock",
|
||||
"version": "0.17",
|
||||
"version": "0.19",
|
||||
"description": "A very minimalistic clock to mainly show date and time.",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
|
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.4 KiB |
|
@ -0,0 +1,2 @@
|
|||
0.01: Initial version
|
||||
0.02: More compact rendering & app icon
|
|
@ -0,0 +1,9 @@
|
|||
# Calendar Clock - Your day at a glance
|
||||
|
||||
This clock shows a chronological view of your current and future events.
|
||||
It uses events synced from Gadgetbridge to achieve this.
|
||||
|
||||
The current time and date is highlighted in cyan.
|
||||
|
||||
## Screenshot
|
||||

|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwgpm5gAB4AVRhgWCAAQWWDCARC/4ACJR4uB54WDAAP8DBotFGIgXLFwv4GAouQC4gwMLooXF/gXJOowXGJBIXBCIgXQxgXLMAIXXMAmIC5OIx4XJhH/wAXIxnIC78IxGIHoIABI44MBC4wQBEQIDB5gXGPAJgEC6IxBC5oABC4wwDa4YTCxAWD5nPDAzvGFYgAB5AXWJBK+GcAq5CGBIuBC5X4GBIJBdoQXB/GIx4CDPJAuEC5JoCDAgWBFwYXJxCBIFwYXKYwoACCwZ3IPQoWIC5YABGYIABCwpHKAQYMBCwwX/C5QAMC8R3/R/4XNhAXNwAXHgGIABgWIAFwA=="))
|
|
@ -0,0 +1,119 @@
|
|||
var calendar = [];
|
||||
var current = [];
|
||||
var next = [];
|
||||
|
||||
function updateCalendar() {
|
||||
calendar = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||
calendar = calendar.filter(e => isActive(e) || getTime() <= e.timestamp);
|
||||
calendar.sort((a,b) => a.timestamp - b.timestamp);
|
||||
|
||||
current = calendar.filter(isActive);
|
||||
next = calendar.filter(e=>!isActive(e));
|
||||
}
|
||||
|
||||
function isActive(event) {
|
||||
var timeActive = getTime() - event.timestamp;
|
||||
return timeActive >= 0 && timeActive <= event.durationInSeconds;
|
||||
}
|
||||
function zp(str) {
|
||||
return ("0"+str).substr(-2);
|
||||
}
|
||||
|
||||
function drawEventHeader(event, y) {
|
||||
g.setFont("Vector", 24);
|
||||
|
||||
var time = isActive(event) ? new Date() : new Date(event.timestamp * 1000);
|
||||
var timeStr = zp(time.getHours()) + ":" + zp(time.getMinutes());
|
||||
g.drawString(timeStr, 5, y);
|
||||
y += 24;
|
||||
|
||||
g.setFont("12x20", 1);
|
||||
if (isActive(event)) {
|
||||
g.drawString(zp(time.getDate())+". " + require("locale").month(time,1),15*timeStr.length,y-21);
|
||||
} else {
|
||||
var offset = 0-time.getTimezoneOffset()/1440;
|
||||
var days = Math.floor((time.getTime()/1000)/86400+offset)-Math.floor(getTime()/86400+offset);
|
||||
if(days > 0) {
|
||||
var daysStr = days===1?/*LANG*/"tomorrow":/*LANG*/"in "+days+/*LANG*/" days";
|
||||
g.drawString(daysStr,15*timeStr.length,y-21);
|
||||
}
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
function drawEventBody(event, y) {
|
||||
g.setFont("12x20", 1);
|
||||
var lines = g.wrapString(event.title, g.getWidth()-10);
|
||||
if (lines.length > 2) {
|
||||
lines = lines.slice(0,2);
|
||||
lines[1] = lines[1].slice(0,-3)+"...";
|
||||
}
|
||||
g.drawString(lines.join('\n'), 5, y);
|
||||
y+=20 * lines.length;
|
||||
if(event.location) {
|
||||
g.drawImage(atob("DBSBAA8D/H/nDuB+B+B+B3Dn/j/B+A8A8AYAYAYAAAAAAA=="),5,y);
|
||||
g.drawString(event.location, 20, y);
|
||||
y+=20;
|
||||
}
|
||||
y+=5;
|
||||
return y;
|
||||
}
|
||||
|
||||
function drawEvent(event, y) {
|
||||
y = drawEventHeader(event, y);
|
||||
y = drawEventBody(event, y);
|
||||
return y;
|
||||
}
|
||||
|
||||
var curEventHeight = 0;
|
||||
|
||||
function drawCurrentEvents(y) {
|
||||
g.setColor("#0ff");
|
||||
g.clearRect(5, y, g.getWidth() - 5, y + curEventHeight);
|
||||
curEventHeight = y;
|
||||
|
||||
if(current.length === 0) {
|
||||
y = drawEvent({timestamp: getTime(), durationInSeconds: 100}, y);
|
||||
} else {
|
||||
y = drawEventHeader(current[0], y);
|
||||
for (var e of current) {
|
||||
y = drawEventBody(e, y);
|
||||
}
|
||||
}
|
||||
curEventHeight = y - curEventHeight;
|
||||
return y;
|
||||
}
|
||||
|
||||
function drawFutureEvents(y) {
|
||||
g.setColor(g.theme.fg);
|
||||
for (var e of next) {
|
||||
y = drawEvent(e, y);
|
||||
if(y>g.getHeight())break;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
function fullRedraw() {
|
||||
g.clearRect(5,24,g.getWidth()-5,g.getHeight());
|
||||
updateCalendar();
|
||||
var y = 30;
|
||||
y = drawCurrentEvents(y);
|
||||
drawFutureEvents(y);
|
||||
}
|
||||
|
||||
function redraw() {
|
||||
g.reset();
|
||||
if (current.find(e=>!isActive(e)) || next.find(isActive)) {
|
||||
fullRedraw();
|
||||
} else {
|
||||
drawCurrentEvents(30);
|
||||
}
|
||||
}
|
||||
|
||||
g.clear();
|
||||
fullRedraw();
|
||||
var minuteInterval = setInterval(redraw, 60 * 1000);
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
Bangle.setUI("clock");
|
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 692 B |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "calclock",
|
||||
"name": "Calendar Clock",
|
||||
"shortName": "CalClock",
|
||||
"version": "0.02",
|
||||
"description": "Show the current and upcoming events synchronized from Gadgetbridge",
|
||||
"icon": "calclock.png",
|
||||
"type": "clock",
|
||||
"tags": "clock agenda",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"calclock.app.js","url":"calclock.js"},
|
||||
{"name":"calclock.img","url":"calclock-icon.js","evaluate":true}
|
||||
],
|
||||
"screenshots": [{"url":"screenshot.png"}]
|
||||
}
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,2 @@
|
|||
0.01: Initial Creation
|
||||
0.02: Fixed some sleep bugs. Added a sleep mode toggle
|
|
@ -1,12 +1,13 @@
|
|||
{
|
||||
"id": "chimer",
|
||||
"name": "Chimer",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A fork of Hour Chime that adds extra features such as: \n - Buzz or beep on every 60, 30 or 15 minutes. \n - Reapeat Chime up to 3 times \n - Set hours to disable chime",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
"tags": "widget",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.MD",
|
||||
"storage": [
|
||||
{ "name": "chimer.wid.js", "url": "widget.js" },
|
||||
{ "name": "chimer.settings.js", "url": "settings.js" }
|
||||
|
|
|
@ -36,14 +36,21 @@
|
|||
Repetition: {
|
||||
value: settings.repeat,
|
||||
min: 1,
|
||||
max: 3,
|
||||
max: 5,
|
||||
format: (v) => v,
|
||||
onchange: (v) => {
|
||||
settings.repeat = v;
|
||||
writeSettings(settings);
|
||||
},
|
||||
},
|
||||
"Start Hour": {
|
||||
"Sleep Mode": {
|
||||
value: !!settings.sleep,
|
||||
onchange: (v) => {
|
||||
settings.sleep = v;
|
||||
writeSettings(settings);
|
||||
},
|
||||
},
|
||||
"Sleep Start": {
|
||||
value: settings.start,
|
||||
min: 0,
|
||||
max: 23,
|
||||
|
@ -53,7 +60,7 @@
|
|||
writeSettings(settings);
|
||||
},
|
||||
},
|
||||
"End Hour": {
|
||||
"Sleep End": {
|
||||
value: settings.end,
|
||||
min: 0,
|
||||
max: 23,
|
||||
|
@ -71,6 +78,7 @@
|
|||
type: 1,
|
||||
freq: 0,
|
||||
repeat: 1,
|
||||
sleep: true,
|
||||
start: 6,
|
||||
end: 22,
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
type: 1,
|
||||
freq: 0,
|
||||
repeat: 1,
|
||||
sleep: true,
|
||||
start: 6,
|
||||
end: 22,
|
||||
};
|
||||
|
@ -32,7 +33,7 @@
|
|||
} else {
|
||||
return;
|
||||
}
|
||||
sleep(100);
|
||||
sleep(150);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +45,11 @@
|
|||
m = now.getMinutes(),
|
||||
s = now.getSeconds(),
|
||||
ms = now.getMilliseconds();
|
||||
if (h > settings.end || h < settings.start) {
|
||||
if (
|
||||
(settings.sleep && h > settings.end) ||
|
||||
(settings.sleep && h >= settings.end && m !== 0) ||
|
||||
(settings.sleep && h < settings.start)
|
||||
) {
|
||||
var mLeft = 60 - m,
|
||||
sLeft = mLeft * 60 - s,
|
||||
msLeft = sLeft * 1000 - ms;
|
||||
|
@ -52,7 +57,8 @@
|
|||
return;
|
||||
}
|
||||
if (settings.freq === 1) {
|
||||
if ((m !== lastMinute && m === 0) || m === 30) chime();
|
||||
if ((m !== lastMinute && m === 0) || (m !== lastMinute && m === 30))
|
||||
chime();
|
||||
lastHour = h;
|
||||
lastMinute = m;
|
||||
// check again in 30 minutes
|
||||
|
@ -70,7 +76,12 @@
|
|||
}
|
||||
setTimeout(check, msLeft);
|
||||
} else if (settings.freq === 2) {
|
||||
if ((m !== lastMinute && m === 0) || m === 15 || m === 30 || m === 45)
|
||||
if (
|
||||
(m !== lastMinute && m === 0) ||
|
||||
(m !== lastMinute && m === 15) ||
|
||||
(m !== lastMinute && m === 30) ||
|
||||
(m !== lastMinute && m === 45)
|
||||
)
|
||||
chime();
|
||||
lastHour = h;
|
||||
lastMinute = m;
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
0.06: Periodically update so the always on display does show current GPS fix
|
||||
0.07: Alternative marker icon (configurable via settings)
|
||||
0.08: Add ability to hide the icon when GPS is off, for a cleaner appearance.
|
||||
0.09: Do not take widget space if icon is hidden
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "widgps",
|
||||
"name": "GPS Widget",
|
||||
"version": "0.08",
|
||||
"version": "0.09",
|
||||
"description": "Tiny widget to show the power and fix status of the GPS/GNSS",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
|
|
|
@ -11,13 +11,14 @@ var interval;
|
|||
var oldSetGPSPower = Bangle.setGPSPower;
|
||||
Bangle.setGPSPower = function(on, id) {
|
||||
var isGPSon = oldSetGPSPower(on, id);
|
||||
WIDGETS.gps.draw();
|
||||
WIDGETS.gps.width = !isGPSon && settings.hideWhenGpsOff ? 0 : 24;
|
||||
Bangle.drawWidgets();
|
||||
return isGPSon;
|
||||
};
|
||||
|
||||
WIDGETS.gps = {
|
||||
area : "tr",
|
||||
width : 24,
|
||||
width : !Bangle.isGPSOn() && settings.hideWhenGpsOff ? 0 : 24,
|
||||
draw : function() {
|
||||
g.reset();
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial Medical Alert Widget!
|
|
@ -0,0 +1,23 @@
|
|||
# Medical Alert Widget
|
||||
|
||||
Shows a medical alert logo in the top right widget area, and a medical alert message in the bottom widget area.
|
||||
|
||||
**Note:** this is not a replacement for a medical alert band but hopefully a useful addition.
|
||||
|
||||
## Features
|
||||
|
||||
Implemented:
|
||||
|
||||
- Basic medical alert logo and message
|
||||
- Only display bottom widget on clocks
|
||||
- High contrast colours depending on theme
|
||||
|
||||
Future:
|
||||
|
||||
- Configure when to show bottom widget (always/never/clocks)
|
||||
- Configure medical alert text
|
||||
- Show details when touched
|
||||
|
||||
## Creator
|
||||
|
||||
James Taylor ([jt-nti](https://github.com/jt-nti))
|
|
@ -0,0 +1,15 @@
|
|||
{ "id": "widmeda",
|
||||
"name": "Medical Alert Widget",
|
||||
"shortName":"Medical Alert",
|
||||
"version":"0.01",
|
||||
"description": "Display a medical alert in the bottom widget section.",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
"tags": "health,medical,tools,widget",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"screenshots": [{"url":"screenshot_light.png"}],
|
||||
"storage": [
|
||||
{"name":"widmeda.wid.js","url":"widget.js"}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,30 @@
|
|||
(() => {
|
||||
// Top right star of life logo
|
||||
WIDGETS["widmedatr"]={
|
||||
area: "tr",
|
||||
width: 24,
|
||||
draw: function() {
|
||||
g.reset();
|
||||
g.setColor("#f00");
|
||||
g.drawImage(atob("FhYBAAAAA/AAD8AAPwAc/OD/P8P8/x/z/n+/+P5/wP58A/nwP5/x/v/n/P+P8/w/z/Bz84APwAA/AAD8AAAAAA=="), this.x + 1, this.y + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Bottom medical alert message
|
||||
WIDGETS["widmedabl"]={
|
||||
area: "bl",
|
||||
width: Bangle.CLOCK?Bangle.appRect.w:0,
|
||||
draw: function() {
|
||||
// Only show the widget on clocks
|
||||
if (!Bangle.CLOCK) return;
|
||||
|
||||
g.reset();
|
||||
g.setBgColor(g.theme.dark ? "#fff" : "#f00");
|
||||
g.setColor(g.theme.dark ? "#f00" : "#fff");
|
||||
g.setFont("Vector",18);
|
||||
g.setFontAlign(0,0);
|
||||
g.clearRect(this.x, this.y, this.x + this.width - 1, this.y + 23);
|
||||
g.drawString("MEDICAL ALERT", this.width / 2, this.y + ( 23 / 2 ));
|
||||
}
|
||||
};
|
||||
})();
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,2 @@
|
|||
0.01: New Widget!
|
||||
0.02: Fix calling null on draw
|
|
@ -0,0 +1,15 @@
|
|||
# Twenties
|
||||
|
||||
Follow the [20-20-20 rule](https://www.aoa.org/AOA/Images/Patients/Eye%20Conditions/20-20-20-rule.pdf) with discrete reminders. Your BangleJS will buzz every 20 minutes for you to look away from your screen, and then buzz 20 seconds later to look back. Additionally, alternate between standing and sitting every 20 minutes to be standing for [more than 30 minutes](https://uwaterloo.ca/kinesiology-health-sciences/how-long-should-you-stand-rather-sit-your-work-station) per hour.
|
||||
|
||||
## Usage
|
||||
|
||||
Download this widget and, as long as your watch-face supports widgets, it will automatically run in the background.
|
||||
|
||||
## Features
|
||||
|
||||
Vibrate to remind you to stand up and look away for healthy living.
|
||||
|
||||
## Creator
|
||||
|
||||
[@splch](https://github.com/splch/)
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"id": "widtwenties",
|
||||
"name": "Twenties",
|
||||
"shortName": "twenties",
|
||||
"version": "0.02",
|
||||
"description": "Buzzes every 20m to stand / sit and look 20ft away for 20s.",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
"tags": "widget,tools",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [{ "name": "widtwenties.wid.js", "url": "widget.js" }]
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// WIDGETS = {}; // <-- for development only
|
||||
|
||||
/* run widgets in their own function scope so
|
||||
they don't interfere with currently-running apps */
|
||||
(() => {
|
||||
const move = 20 * 60 * 1000; // 20 minutes
|
||||
const look = 20 * 1000; // 20 seconds
|
||||
|
||||
buzz = _ => {
|
||||
Bangle.buzz().then(_ => {
|
||||
setTimeout(Bangle.buzz, look);
|
||||
});
|
||||
};
|
||||
|
||||
// add widget
|
||||
WIDGETS.twenties = {
|
||||
buzz: buzz,
|
||||
draw: _ => { return null; },
|
||||
};
|
||||
|
||||
setInterval(WIDGETS.twenties.buzz, move); // buzz to stand / sit
|
||||
})();
|
||||
|
||||
// Bangle.drawWidgets(); // <-- for development only
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -225,6 +225,13 @@ apps.forEach((app,appIdx) => {
|
|||
console.log("=====================================================");
|
||||
ERROR(`App ${app.id}'s ${file.name} is a JS file but isn't valid JS`, {file:appDirRelative+file.url});
|
||||
}
|
||||
// clock app checks
|
||||
if (app.type=="clock") {
|
||||
var a = fileContents.indexOf("Bangle.loadWidgets()");
|
||||
var b = fileContents.indexOf("Bangle.setUI(");
|
||||
if (a>=0 && b>=0 && a<b)
|
||||
WARN(`Clock ${app.id} file calls loadWidgets before setUI (clock widget/etc won't be aware a clock app is running)`, {file:appDirRelative+file.url});
|
||||
}
|
||||
}
|
||||
for (const key in file) {
|
||||
if (!STORAGE_KEYS.includes(key)) ERROR(`App ${app.id} file ${file.name} has unknown key ${key}`, {file:appDirRelative+file.url});
|
||||
|
|