mirror of https://github.com/espruino/BangleApps
commit
1e32d090d0
|
@ -0,0 +1,2 @@
|
|||
0.01: Initial commit
|
||||
0.02: Added Alt modes
|
|
@ -0,0 +1,38 @@
|
|||
# DSKY Clock
|
||||
|
||||
This is a clockface inspired by the Apollo DSKY interface.
|
||||
|
||||
## Features
|
||||
- battery level indicator (PROG indicator)
|
||||
- month (VERB indicator)
|
||||
- date (NOUN indicator)
|
||||
- Current local time (DATA 1)
|
||||
- UTC time (DATA 2)
|
||||
- Step counter (DATA 3)
|
||||
|
||||
Indicator lights are:
|
||||
- COMP ACTY (GPS/HRM active)
|
||||
- MSG (Messages waiting)
|
||||
- LOCK (Screen is locked)
|
||||
- BT (Bluetooth is disconnected)
|
||||
- BATT (Low battery or battery charging)
|
||||
- ALARM (Alarm is set)
|
||||
- STEP (Reached STEP goal))
|
||||
|
||||
----
|
||||
## Alt Modes
|
||||
Swipe left/right to switch between different PROGRAM modes.
|
||||
|
||||
- HRM (BPM/Confidence)
|
||||
- GPS (Latitude/Longitude/Speed)
|
||||
- Weather (Humidity/Rain/Temperature/Wind/Condition Code)
|
||||
- Accelerometer (X/Y/Z)
|
||||
- Compass (HEADING)
|
||||
|
||||
In the interests of usability these change the "LIGHT" on the left side to show the current mode.
|
||||
|
||||
----
|
||||
This is my first watch face, may add features and customization later.
|
||||
|
||||
## Creator
|
||||
Written by Carl Chan | [github](https://github.com/carlchan)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkIkQAFkIEDiQMGABUAiQtIu1nu9wgETC40QDIIAHu4ABC4MCkURiVCAAMimQXJswABAgMDC4IADLIQLBBAYcHF4QXSg4XDiEBgIXCkIXCAQgADgwvFBwIXFF4sGuwXHI4cxCIQ4CF5gXCR4ZfJF5SPXI4S4EFgQGBsAvMdAikCAwJ5BF5YXJu7vMI4RABI4gvCmQXJDIQEIL5YRBgxHDAgR3D7vd6gCB6QXEHwIXDCoQGBC4YACC4sGs5fDAgQvE6lEpvd6YXECwkGAgQvFrovDU4QXJL4pHIRQtnR4oXGkIlCAAK6BDwgvPgwEDSIgXLgynBAYhHMC4UHOQIWBAIMHBAIXCmXdqioBC4UyIoYXBeQNmCoJOCF4VVCwJHGH4UHGAQXEF4JHFF4ZHEAYJHEU5RCBI4SoBC4JHFpoBCC4jWDdoaPFiIADiS/FABIXIL4QvCRwK/IisAiEAgIXBL4ZzCPIZfDCAIVBAAIXCU4gXEX4xfJBwNmXoS/GC40yd4hcCIoa/EF40xdYS9BMQNnC5ovER4JFDU6K/NgJHHXoQBBSQbzCI5C/Cg9mAAQaBAYSnNNwJcBMANwAIQXKlS/EbIIBDC4UDa5gvBMAYvFAAsQI4bsCPQYFBC5MAC4RcCCgZiCBQNTXAIXEiMikJOCiBUEBAcTA="))
|
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,381 @@
|
|||
//Init
|
||||
var Layout = require("Layout");
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
require("FontTeletext5x9Ascii").add(Graphics);
|
||||
|
||||
const Light_on='#fff';
|
||||
const Light_off='#554';
|
||||
const Light_warn='#f90';
|
||||
const Light_COMPACTY='#0F0';
|
||||
const Light_width=43;
|
||||
const Light_height=25;
|
||||
const EL7_height=30;
|
||||
const LightFont='Teletext5x9Ascii';
|
||||
const DataFont='7x11Numeric7Seg:2';
|
||||
var mode = 0;
|
||||
|
||||
if (global.WIDGETS) {require("widget_utils").swipeOn();} // If `dsky_clock` was fast loaded into we seemingly need to hide the widgets before setting the layout so elements are not moved down.
|
||||
|
||||
var layout = new Layout(
|
||||
{type:"h", c:[
|
||||
{type:"",width:6},
|
||||
{ type:"v", c: [
|
||||
{type:"txt", font:LightFont, col:"#000", bgCol:"#555", id:'L1', label:"UPLINK\nACTY", width:Light_width, height:Light_height},
|
||||
{type:"txt", font:LightFont, col:"#000", bgCol:"#555", id:'L2', label:"TEMP", width:Light_width, height:Light_height },
|
||||
{type:"txt", font:LightFont, col:"#000", bgCol:"#555", id:'L3', label:"GIMBAL\nLOCK", width:Light_width, height:Light_height },
|
||||
{type:"txt", font:LightFont, col:"#000", bgCol:"#555", id:'L4', label:"STBY", width:Light_width, height:Light_height },
|
||||
{type:"txt", font:LightFont, col:"#000", bgCol:"#555", id:'L5', label:"PROG", width:Light_width, height:Light_height },
|
||||
{type:"txt", font:LightFont, col:"#000", bgCol:"#eee", id:'L6', label:"OPR ERR", width:Light_width, height:Light_height },
|
||||
]},
|
||||
{ type:"", width:20},
|
||||
{ type:"v", c: [
|
||||
{type:"",height:2},
|
||||
{type:"h", c: [
|
||||
{type:"", width:50},{type:"txt", font:"6x8", col:"#000",bgCol:"#0F0", label:"PROG", width:25, height:10},
|
||||
]},
|
||||
{type:"h", c: [
|
||||
{type:"",width:10},
|
||||
{type:"txt", font:"6x8", col:"#000", bgCol:"#000", id:"COMPACTY", label:"COMP\nACTY", width:26, height:26 },
|
||||
{type:"",width:17},
|
||||
{type:"txt", font:DataFont, col:"#0F0", bgCol:"#000",label:"00", id:"PROG", fillx:1, height:EL7_height },
|
||||
]},
|
||||
{type:"",height:1},
|
||||
{type:"h", c: [
|
||||
{type:"txt", font:"6x8", col:"#000", bgCol:"#0F0",label:"VERB", width:25, height:10},
|
||||
{type:"",width:30},
|
||||
{type:"txt", font:"6x8", col:"#000",bgCol:"#0F0",label:"NOUN", width:25, height:10},
|
||||
]},
|
||||
{type:"h", c: [
|
||||
{type:"txt", font:DataFont, col:"#0F0", bgCol:"#000", label:"00", id:"VERB", fillx:1, height:EL7_height},
|
||||
{type:"txt", font:DataFont, col:"#0F0", bgCol:"#000", label:"00", id:"NOUN", fillx:1, height:EL7_height},
|
||||
]},
|
||||
{ type:"",bgCol:'#070', width:80, height:2 },
|
||||
{type:"txt", font:DataFont, col:"#0F0", bgCol:"#000", label:"00000", id:"R1", halign:1, fillx:1, height:EL7_height},
|
||||
{type:"txt", font:DataFont, col:"#0F0", bgCol:"#000", label:"00000", id:"R2", halign:1, fillx:1, height:EL7_height},
|
||||
{type:"txt", font:DataFont, col:"#0F0", bgCol:"#000", label:"00000", id:"R3", halign:1, fillx:1, height:EL7_height},
|
||||
]},
|
||||
{type:"",width:5},
|
||||
],
|
||||
lazy:true},
|
||||
{btns:[
|
||||
{label:"", cb: Bangle.showLauncher}
|
||||
], lazy:true});
|
||||
layout.update();
|
||||
|
||||
//support functioe_ns
|
||||
|
||||
function getWeather() {
|
||||
var weather = {};
|
||||
try {
|
||||
weather = require("Storage").readJSON('weather.json', 1).weather;
|
||||
} catch(e) {
|
||||
return {};
|
||||
}
|
||||
return weather;
|
||||
}
|
||||
|
||||
function getdatetime(){
|
||||
var datetime = [];
|
||||
var d = new Date();
|
||||
// var offsets = require("Storage").readJSON("worldclock.settings.json") || [];
|
||||
// var meridian = require("locale").meridian(d);
|
||||
datetime.clock = require("locale").time(d, 1);
|
||||
datetime.month = d.getMonth()+1;
|
||||
datetime.day = d.getDate();
|
||||
datetime.localtime=String(d.getHours()).padStart(2,'0')+String(d.getMinutes()).padStart(2,'0');
|
||||
let utchour=((d.getHours()+(Math.round(d.getTimezoneOffset()/60))) % 24);
|
||||
datetime.utctime=String(utchour).padStart(2,'0')+String(d.getMinutes()).padStart(2,'0');
|
||||
return datetime;
|
||||
}
|
||||
|
||||
function getSteps(){
|
||||
let steps=Bangle.getHealthStatus("day").steps;
|
||||
steps = typeof steps !== 'undefined' ? steps:0;
|
||||
return steps;
|
||||
}
|
||||
|
||||
function getStepGoal(){
|
||||
let stepGoal = (require("Storage").readJSON("health.json",1)||10000).stepGoal;
|
||||
stepGoal = typeof stepGoal !== 'undefined' ? stepGoal:10000;
|
||||
return stepGoal;
|
||||
}
|
||||
|
||||
function isAlarmSet(){
|
||||
let alarmStatus = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on);
|
||||
return alarmStatus;
|
||||
}
|
||||
|
||||
function isMessagesNotify(){
|
||||
if (require("Storage").read("messages.json")!==undefined) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getHRM(){
|
||||
let hrm=Bangle.getHealthStatus('last');
|
||||
hrm = typeof hrm !== 'undefined' ? hrm:0;
|
||||
return hrm;
|
||||
}
|
||||
|
||||
function isBTConnected(){
|
||||
return NRF.getSecurityStatus().connected;
|
||||
}
|
||||
|
||||
function getBattery(){
|
||||
let battlevel = E.getBattery();
|
||||
if (Bangle.isCharging()) {
|
||||
battlevel = -1;
|
||||
} else if (battlevel >= 100) {
|
||||
battlevel = 99;
|
||||
}
|
||||
battlevel=String(battlevel);
|
||||
return battlevel;
|
||||
}
|
||||
|
||||
function isActive(){
|
||||
if (Bangle.isCompassOn() || Bangle.isGPSOn() || Bangle.isHRMOn() || Bangle.isBarometerOn() ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function setLight(id,label,check,onColour,offColour){
|
||||
// print('setlight:',id); //debug
|
||||
layout.clear(layout[id]);
|
||||
onColour = typeof onColour !== 'undefined' ? onColour:Light_on;
|
||||
offColour = typeof offColour !== 'undefined' ? offColour:Light_off;
|
||||
if (label !== '') {
|
||||
layout[id].label=label;
|
||||
}
|
||||
if (check) {
|
||||
layout[id].bgCol=onColour;
|
||||
} else {
|
||||
layout[id].bgCol=offColour;
|
||||
}
|
||||
layout.render(layout[id]);
|
||||
}
|
||||
|
||||
function setDATA(id,label) {
|
||||
layout.clear(layout[id]);
|
||||
let data='-----';
|
||||
let sign='';
|
||||
try {
|
||||
if (!isNaN(label)) {
|
||||
if (label < 0) {
|
||||
label=Math.abs(label);
|
||||
sign='-';
|
||||
}
|
||||
data=String(String(label).toString(16)).toUpperCase().padStart(5,'0').substring(0,5);
|
||||
data=sign+data;
|
||||
}
|
||||
} catch(e) {
|
||||
data='-----';
|
||||
}
|
||||
layout[id].label=data;
|
||||
layout.render(layout[id]);
|
||||
}
|
||||
|
||||
function setWORD(id,label){
|
||||
layout.clear(layout[id]);
|
||||
let data='--';
|
||||
if (!isNaN(label)) {
|
||||
data=String(String(label).toString(16)).toUpperCase().padStart(2,'0').substring(0,2);
|
||||
}
|
||||
// print(id, data); //debug
|
||||
layout[id].label=data;
|
||||
layout.render(layout[id]);
|
||||
}
|
||||
|
||||
function draw_bg(){
|
||||
g.setColor('#666');
|
||||
g.fillRect(0,0,176,176);
|
||||
g.setColor('#000');
|
||||
g.fillRect(69,2,172,174);
|
||||
g.fillCircle(59,10,5);
|
||||
g.fillCircle(59,166,5);
|
||||
}
|
||||
|
||||
// actual display
|
||||
function drawMain(){
|
||||
let datetime=getdatetime();
|
||||
|
||||
setDATA('R1',datetime.localtime);
|
||||
setDATA('R2',datetime.utctime);
|
||||
setDATA('R3',getSteps());
|
||||
|
||||
setWORD('PROG',getBattery());
|
||||
setWORD('VERB',datetime.month);
|
||||
setWORD('NOUN',datetime.day);
|
||||
|
||||
setLight('COMPACTY','',isActive(),Light_COMPACTY);
|
||||
setLight('L1','MSG',isMessagesNotify());
|
||||
setLight('L2','LOCK',Bangle.isLocked());
|
||||
setLight('L3','BT',!isBTConnected(),Light_warn);
|
||||
setLight('L4','BATT',(getBattery()<=20),Light_warn);
|
||||
setLight('L5','ALARM',isAlarmSet(),Light_warn);
|
||||
setLight('L6','STEP',(getSteps()>=getStepGoal()),'#0a0');
|
||||
|
||||
// layout.setUI();
|
||||
layout.forgetLazyState();
|
||||
layout.render();
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
////// ALT modes /////
|
||||
var AltDrawTimer;
|
||||
function drawAlt(mode) {
|
||||
if (AltDrawTimer) clearTimeout(AltDrawTimer);
|
||||
mode = typeof mode !== 'undefined' ? mode:0;
|
||||
mode=Math.abs(mode);
|
||||
// print('drawAlt: ', mode); // debug
|
||||
// Show mode in PROG
|
||||
setWORD('PROG',mode);
|
||||
setWORD('NOUN','');
|
||||
setWORD('VERB','');
|
||||
// Disable Battery warning light in to show PROG no longer shows battery level
|
||||
setLight('L4','BATT',false);
|
||||
setDATA('R1');
|
||||
setDATA('R2');
|
||||
setDATA('R3');
|
||||
switch (mode) {
|
||||
case 1:
|
||||
setLight('L6','HRM',true);
|
||||
mode_HRM();
|
||||
break;
|
||||
case 2:
|
||||
setLight('L6','TEMP',true);
|
||||
mode_weather();
|
||||
break;
|
||||
case 3:
|
||||
setLight('L6','GPS',true);
|
||||
mode_GPS();
|
||||
break;
|
||||
case 4:
|
||||
setLight('L6','ACCEL',true);
|
||||
mode_accel();
|
||||
break;
|
||||
case 5:
|
||||
setLight('L6','HDG',true);
|
||||
mode_compass();
|
||||
break;
|
||||
default:
|
||||
drawMain();
|
||||
}
|
||||
layout.render();
|
||||
}
|
||||
|
||||
function mode_HRM() {
|
||||
setLight('COMPACTY','',true,Light_COMPACTY);
|
||||
AltDrawTimer = setTimeout( function() {
|
||||
Bangle.setHRMPower(true, 'dsky_clock');
|
||||
let hrm=getHRM();
|
||||
setDATA('R1',hrm.bpm);
|
||||
setDATA('R2',hrm.bpmConfidence);
|
||||
setDATA('R3',getSteps());
|
||||
mode_HRM();
|
||||
}, 5000);
|
||||
Bangle.setHRMPower(false);
|
||||
}
|
||||
|
||||
function mode_weather() {
|
||||
let weather=getWeather();
|
||||
try {
|
||||
weather.temp = Math.round(weather.temp-273.15);
|
||||
setDATA('R1',weather.temp);
|
||||
setDATA('R2',weather.hum);
|
||||
setDATA('R3',weather.code);
|
||||
setWORD('NOUN',weather.hum);
|
||||
setWORD('VERB',weather.rain);
|
||||
} catch(e) {
|
||||
setDATA('R1','-----');
|
||||
setDATA('R2','-----');
|
||||
setDATA('R3','-----');
|
||||
setDATA('R1','--');
|
||||
setDATA('R1','--');
|
||||
}
|
||||
}
|
||||
|
||||
function mode_compass() {
|
||||
AltDrawTimer = setTimeout ( function() {
|
||||
setLight('COMPACTY','',true,Light_COMPACTY); //isCompassOn seems to be incorrect?
|
||||
Bangle.setCompassPower(1);
|
||||
let compass=Bangle.getCompass();
|
||||
setDATA('R1',compass.heading);
|
||||
setDATA('R2');
|
||||
setDATA('R3');
|
||||
mode_compass();
|
||||
}, 200);
|
||||
Bangle.setCompassPower(0);
|
||||
}
|
||||
|
||||
function mode_GPS() {
|
||||
setLight('COMPACTY','',true,Light_COMPACTY);
|
||||
AltDrawTimer = setTimeout( function() {
|
||||
Bangle.setGPSPower(1,'dsky_clock');
|
||||
let gps=Bangle.getGPSFix();
|
||||
setWORD('NOUN',gps.fix);
|
||||
setWORD('VERB',gps.satellites);
|
||||
setDATA('R1',gps.lat);
|
||||
setDATA('R2',gps.lon);
|
||||
setDATA('R3',gps.speed);
|
||||
mode_GPS();
|
||||
}, 5000);
|
||||
Bangle.setGPSPower(0);
|
||||
}
|
||||
|
||||
function mode_accel() {
|
||||
AltDrawTimer = setTimeout( function() {
|
||||
setLight('COMPACTY','',isActive(),Light_COMPACTY);
|
||||
let accel=Bangle.getAccel();
|
||||
setDATA('R1',accel.x);
|
||||
setDATA('R2',accel.y);
|
||||
setDATA('R3',accel.z);
|
||||
mode_accel();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
//////////// Main
|
||||
|
||||
var drawTimeout;
|
||||
// schedule a draw for the next minute
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
mode = 0;
|
||||
if (AltDrawTimer) clearTimeout(AltDrawTimer);
|
||||
drawMain();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
Bangle.on('lock',on=>{
|
||||
mode = 0;
|
||||
drawMain(); // draw immediately
|
||||
});
|
||||
Bangle.on("message",function() { setLight('COMPACTY','',isActive(),Light_COMPACTY);});
|
||||
Bangle.on('charging',drawMain);
|
||||
NRF.on('connect',function() { setLight('L3','BT',!isBTConnected(),Light_warn); });
|
||||
NRF.on('disconnect',function() { setLight('L3','BT',!isBTConnected(),Light_warn); });
|
||||
|
||||
Bangle.on('swipe', function(directionLR) {
|
||||
if (directionLR == 1) {
|
||||
mode=mode-1;
|
||||
}
|
||||
if (directionLR == -1) {
|
||||
mode=mode+1;
|
||||
}
|
||||
if (mode < 0 ) { mode=5; }
|
||||
mode=(mode % 6);
|
||||
drawAlt(mode);
|
||||
});
|
||||
|
||||
g.clear();
|
||||
draw_bg();
|
||||
drawMain();
|
||||
|
||||
Bangle.CLOCK = 1;
|
||||
Bangle.loadWidgets(); // loading widgets after drawing the layout in `drawMain()` to display the app UI ASAP.
|
||||
require("widget_utils").swipeOn(); // hide widgets, make them visible with a swipe
|
|
@ -0,0 +1,16 @@
|
|||
{ "id": "dsky_clock",
|
||||
"name": "DSKY Clock",
|
||||
"icon": "app-icon.png",
|
||||
"screenshots": [{"url":"screenshot1.png"}],
|
||||
"version": "0.02",
|
||||
"description": "A clockface inspired by the Apollo DSKY interface.",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
"supports":["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"dsky_clock.app.js","url":"app.js"},
|
||||
{"name":"dsky_clock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
Loading…
Reference in New Issue