Merge branch 'espruino:master' into master
|
@ -282,8 +282,11 @@ and which gives information about the app for the Launcher.
|
||||||
"dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above)
|
"dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above)
|
||||||
"dependencies" : { "messages":"app" } // optional, depend on a specific app ID
|
"dependencies" : { "messages":"app" } // optional, depend on a specific app ID
|
||||||
// for instance this will use notify/notifyfs is they exist, or will pull in 'notify'
|
// for instance this will use notify/notifyfs is they exist, or will pull in 'notify'
|
||||||
"dependencies" : { "messageicons":"module" } // optional, depend on a specific library to be used with 'require'
|
"dependencies" : { "messageicons":"module" } // optional, depend on a specific library to be used with 'require' - see provides_modules
|
||||||
|
"dependencies" : { "message":"widget" } // optional, depend on a specific type of widget - see provides_widgets
|
||||||
"provides_modules" : ["messageicons"] // optional, this app provides a module that can be used with 'require'
|
"provides_modules" : ["messageicons"] // optional, this app provides a module that can be used with 'require'
|
||||||
|
"provides_widgets" : ["battery"] // optional, this app provides a type of widget - 'alarm/battery/bluetooth/pedometer/message'
|
||||||
|
"default" : true, // set if an app is the default implementer of something (a widget/module/etc)
|
||||||
"readme": "README.md", // if supplied, a link to a markdown-style text file
|
"readme": "README.md", // if supplied, a link to a markdown-style text file
|
||||||
// that contains more information about this app (usage, etc)
|
// that contains more information about this app (usage, etc)
|
||||||
// A 'Read more...' link will be added under the app
|
// A 'Read more...' link will be added under the app
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
0.02: Design improvements and fixes.
|
0.02: Design improvements and fixes.
|
||||||
0.03: Indicate battery level through line occurrence.
|
0.03: Indicate battery level through line occurrence.
|
||||||
0.04: Use widget_utils module.
|
0.04: Use widget_utils module.
|
||||||
|
0.05: Support for clkinfo.
|
|
@ -10,7 +10,9 @@ The original output of stable diffusion is shown here:
|
||||||
|
|
||||||
My implementation is shown below. Note that horizontal lines occur randomly, but the
|
My implementation is shown below. Note that horizontal lines occur randomly, but the
|
||||||
probability is correlated with the battery level. So if your screen contains only
|
probability is correlated with the battery level. So if your screen contains only
|
||||||
a few lines its time to charge your bangle again ;)
|
a few lines its time to charge your bangle again ;) Also note that the upper text
|
||||||
|
implementes the clkinfo module and can be configured via touch left/right/up/down.
|
||||||
|
Touch at the center to trigger the selected action.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
/**
|
/************************************************
|
||||||
* AI Clock
|
* AI Clock
|
||||||
*/
|
*/
|
||||||
|
const storage = require('Storage');
|
||||||
|
const clock_info = require("clock_info");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* Assets
|
||||||
|
*/
|
||||||
require("Font7x11Numeric7Seg").add(Graphics);
|
require("Font7x11Numeric7Seg").add(Graphics);
|
||||||
Graphics.prototype.setFontGochiHand = function(scale) {
|
Graphics.prototype.setFontGochiHand = function(scale) {
|
||||||
// Actual height 27 (29 - 3)
|
// Actual height 27 (29 - 3)
|
||||||
|
@ -13,7 +21,7 @@ Graphics.prototype.setFontGochiHand = function(scale) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/************************************************
|
||||||
* Set some important constants such as width, height and center
|
* Set some important constants such as width, height and center
|
||||||
*/
|
*/
|
||||||
var W = g.getWidth(),R=W/2;
|
var W = g.getWidth(),R=W/2;
|
||||||
|
@ -21,6 +29,120 @@ var H = g.getHeight();
|
||||||
var cx = W/2;
|
var cx = W/2;
|
||||||
var cy = H/2;
|
var cy = H/2;
|
||||||
var drawTimeout;
|
var drawTimeout;
|
||||||
|
var lock_input = false;
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* SETTINGS
|
||||||
|
*/
|
||||||
|
const SETTINGS_FILE = "aiclock.setting.json";
|
||||||
|
let settings = {
|
||||||
|
menuPosX: 0,
|
||||||
|
menuPosY: 0,
|
||||||
|
};
|
||||||
|
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||||
|
for (const key in saved_settings) {
|
||||||
|
settings[key] = saved_settings[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* Menu
|
||||||
|
*/
|
||||||
|
function getDate(){
|
||||||
|
var date = new Date();
|
||||||
|
return ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Custom clockItems menu - therefore, its added here and not in a clkinfo.js file.
|
||||||
|
var clockItems = {
|
||||||
|
name: getDate(),
|
||||||
|
img: null,
|
||||||
|
items: [
|
||||||
|
{ name: "Week",
|
||||||
|
get: () => ({ text: "Week " + weekOfYear(), img: null}),
|
||||||
|
show: function() { clockItems.items[0].emit("redraw"); },
|
||||||
|
hide: function () {}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
function weekOfYear() {
|
||||||
|
var date = new Date();
|
||||||
|
date.setHours(0, 0, 0, 0);
|
||||||
|
// Thursday in current week decides the year.
|
||||||
|
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
|
||||||
|
// January 4 is always in week 1.
|
||||||
|
var week1 = new Date(date.getFullYear(), 0, 4);
|
||||||
|
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
|
||||||
|
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
|
||||||
|
- 3 + (week1.getDay() + 6) % 7) / 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Load menu
|
||||||
|
var menu = clock_info.load();
|
||||||
|
menu = menu.concat(clockItems);
|
||||||
|
|
||||||
|
|
||||||
|
// Ensure that our settings are still in range (e.g. app uninstall). Otherwise reset the position it.
|
||||||
|
if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPosX].items.length ){
|
||||||
|
settings.menuPosX = 0;
|
||||||
|
settings.menuPosY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set draw functions for each item
|
||||||
|
menu.forEach((menuItm, x) => {
|
||||||
|
menuItm.items.forEach((item, y) => {
|
||||||
|
function drawItem() {
|
||||||
|
// For the clock, we have a special case, as we don't wanna redraw
|
||||||
|
// immediately when something changes. Instead, we update data each minute
|
||||||
|
// to save some battery etc. Therefore, we hide (and disable the listener)
|
||||||
|
// immedeately after redraw...
|
||||||
|
item.hide();
|
||||||
|
|
||||||
|
// After drawing the item, we enable inputs again...
|
||||||
|
lock_input = false;
|
||||||
|
|
||||||
|
var info = item.get();
|
||||||
|
drawMenuItem(info.text, info.img);
|
||||||
|
}
|
||||||
|
|
||||||
|
item.on('redraw', drawItem);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function canRunMenuItem(){
|
||||||
|
if(settings.menuPosY == 0){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var menuEntry = menu[settings.menuPosX];
|
||||||
|
var item = menuEntry.items[settings.menuPosY-1];
|
||||||
|
return item.run !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function runMenuItem(){
|
||||||
|
if(settings.menuPosY == 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var menuEntry = menu[settings.menuPosX];
|
||||||
|
var item = menuEntry.items[settings.menuPosY-1];
|
||||||
|
try{
|
||||||
|
var ret = item.run();
|
||||||
|
if(ret){
|
||||||
|
Bangle.buzz(300, 0.6);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
// Simply ignore it...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Based on the great multi clock from https://github.com/jeffmer/BangleApps/
|
* Based on the great multi clock from https://github.com/jeffmer/BangleApps/
|
||||||
|
@ -76,7 +198,50 @@ function toAngle(a){
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawMenuItem(text, image){
|
||||||
|
if(text == null){
|
||||||
|
drawTime();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// image = atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA==");
|
||||||
|
|
||||||
|
text = String(text);
|
||||||
|
|
||||||
|
g.reset().setBgColor("#fff").setColor("#000");
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
g.setFont("Vector", 20);
|
||||||
|
|
||||||
|
var imgWidth = image == null ? 0 : 24;
|
||||||
|
var strWidth = g.stringWidth(text);
|
||||||
|
var strHeight = text.split('\n').length > 1 ? 40 : Math.max(24, imgWidth+2);
|
||||||
|
var w = imgWidth + strWidth;
|
||||||
|
|
||||||
|
g.clearRect(cx-w/2-8, 40-strHeight/2-1, cx+w/2+4, 40+strHeight/2)
|
||||||
|
|
||||||
|
// Draw right line as designed by stable diffusion
|
||||||
|
g.drawLine(cx+w/2+5, 40-strHeight/2-1, cx+w/2+5, 40+strHeight/2);
|
||||||
|
g.drawLine(cx+w/2+6, 40-strHeight/2-1, cx+w/2+6, 40+strHeight/2);
|
||||||
|
g.drawLine(cx+w/2+7, 40-strHeight/2-1, cx+w/2+7, 40+strHeight/2);
|
||||||
|
|
||||||
|
// And finally the text
|
||||||
|
g.drawString(text, cx+imgWidth/2, 42);
|
||||||
|
g.drawString(text, cx+1+imgWidth/2, 41);
|
||||||
|
|
||||||
|
if(image != null) {
|
||||||
|
var scale = image.width ? imgWidth / image.width : 1;
|
||||||
|
g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 41-12, {scale: scale});
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function drawTime(){
|
function drawTime(){
|
||||||
|
// Draw digital time first
|
||||||
|
drawDigits();
|
||||||
|
|
||||||
|
// And now the analog time
|
||||||
var drawHourHand = g.drawRotRect.bind(g,8,12,R-38);
|
var drawHourHand = g.drawRotRect.bind(g,8,12,R-38);
|
||||||
var drawMinuteHand = g.drawRotRect.bind(g,6,12,R-12 );
|
var drawMinuteHand = g.drawRotRect.bind(g,6,12,R-12 );
|
||||||
|
|
||||||
|
@ -90,13 +255,6 @@ function drawTime(){
|
||||||
h += date.getMinutes()/60.0;
|
h += date.getMinutes()/60.0;
|
||||||
h = parseInt(h*360/12);
|
h = parseInt(h*360/12);
|
||||||
|
|
||||||
// Draw minute and hour bg
|
|
||||||
g.setColor(g.theme.bg);
|
|
||||||
drawHourHand(toAngle(h-3));
|
|
||||||
drawHourHand(toAngle(h+3));
|
|
||||||
drawMinuteHand(toAngle(m-2));
|
|
||||||
drawMinuteHand(toAngle(m+3));
|
|
||||||
|
|
||||||
// Draw minute and hour fg
|
// Draw minute and hour fg
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
drawHourHand(h);
|
drawHourHand(h);
|
||||||
|
@ -104,28 +262,6 @@ function drawTime(){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function drawDate(){
|
|
||||||
var date = new Date();
|
|
||||||
g.setFontAlign(0,0);
|
|
||||||
g.setFontGochiHand();
|
|
||||||
|
|
||||||
var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2);
|
|
||||||
var w = g.stringWidth(text);
|
|
||||||
g.setColor(g.theme.bg);
|
|
||||||
g.fillRect(cx-w/2-4, 20, cx+w/2+4, 40+12);
|
|
||||||
|
|
||||||
g.setColor(g.theme.fg);
|
|
||||||
// Draw right line as designed by stable diffusion
|
|
||||||
g.drawLine(cx+w/2+5, 20, cx+w/2+5, 40+12);
|
|
||||||
g.drawLine(cx+w/2+6, 20, cx+w/2+6, 40+12);
|
|
||||||
g.drawLine(cx+w/2+7, 20, cx+w/2+7, 40+12);
|
|
||||||
|
|
||||||
// And finally the text
|
|
||||||
g.drawString(text, cx, 40);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function drawDigits(){
|
function drawDigits(){
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
|
|
||||||
|
@ -156,20 +292,35 @@ function drawDigits(){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawDate(){
|
||||||
|
var menuEntry = menu[settings.menuPosX];
|
||||||
|
|
||||||
|
// The first entry is the overview...
|
||||||
|
if(settings.menuPosY == 0){
|
||||||
|
drawMenuItem(menuEntry.name, menuEntry.img);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw item if needed
|
||||||
|
lock_input = true;
|
||||||
|
var item = menuEntry.items[settings.menuPosY-1];
|
||||||
|
item.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function draw(){
|
function draw(){
|
||||||
// Queue draw in one minute
|
// Queue draw in one minute
|
||||||
queueDraw();
|
queueDraw();
|
||||||
|
|
||||||
|
|
||||||
g.reset();
|
g.reset();
|
||||||
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
||||||
|
|
||||||
g.setColor(1,1,1);
|
g.setColor(1,1,1);
|
||||||
|
|
||||||
drawBackground();
|
drawBackground();
|
||||||
drawDate();
|
drawDate();
|
||||||
drawDigits();
|
|
||||||
drawTime();
|
|
||||||
drawCircle(Bangle.isLocked());
|
drawCircle(Bangle.isLocked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +341,68 @@ Bangle.on('lock', function(isLocked) {
|
||||||
drawCircle(isLocked);
|
drawCircle(isLocked);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Bangle.on('touch', function(btn, e){
|
||||||
|
var left = parseInt(g.getWidth() * 0.22);
|
||||||
|
var right = g.getWidth() - left;
|
||||||
|
var upper = parseInt(g.getHeight() * 0.22);
|
||||||
|
var lower = g.getHeight() - upper;
|
||||||
|
|
||||||
|
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(lock_input){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_lower){
|
||||||
|
Bangle.buzz(40, 0.6);
|
||||||
|
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
|
||||||
|
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_upper){
|
||||||
|
Bangle.buzz(40, 0.6);
|
||||||
|
settings.menuPosY = settings.menuPosY-1;
|
||||||
|
settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].items.length : settings.menuPosY;
|
||||||
|
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_right){
|
||||||
|
Bangle.buzz(40, 0.6);
|
||||||
|
settings.menuPosX = (settings.menuPosX+1) % menu.length;
|
||||||
|
settings.menuPosY = 0;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_left){
|
||||||
|
Bangle.buzz(40, 0.6);
|
||||||
|
settings.menuPosY = 0;
|
||||||
|
settings.menuPosX = settings.menuPosX-1;
|
||||||
|
settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_center){
|
||||||
|
if(canRunMenuItem()){
|
||||||
|
runMenuItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
E.on("kill", function(){
|
||||||
|
try{
|
||||||
|
storage.write(SETTINGS_FILE, settings);
|
||||||
|
} catch(ex){
|
||||||
|
// If this fails, we still kill the app...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some helpers
|
* Some helpers
|
||||||
|
@ -203,7 +416,6 @@ function queueDraw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lets start widgets, listen for btn etc.
|
* Lets start widgets, listen for btn etc.
|
||||||
*/
|
*/
|
||||||
|
@ -216,6 +428,7 @@ Bangle.loadWidgets();
|
||||||
* area to the top bar doesn't get cleared.
|
* area to the top bar doesn't get cleared.
|
||||||
*/
|
*/
|
||||||
require('widget_utils').hide();
|
require('widget_utils').hide();
|
||||||
|
|
||||||
// Clear the screen once, at startup and draw clock
|
// Clear the screen once, at startup and draw clock
|
||||||
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear();
|
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear();
|
||||||
draw();
|
draw();
|
||||||
|
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.4 KiB |
|
@ -3,7 +3,7 @@
|
||||||
"name": "AI Clock",
|
"name": "AI Clock",
|
||||||
"shortName":"AI Clock",
|
"shortName":"AI Clock",
|
||||||
"icon": "aiclock.png",
|
"icon": "aiclock.png",
|
||||||
"version":"0.04",
|
"version":"0.05",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"supports": ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",
|
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",
|
||||||
|
@ -11,7 +11,9 @@
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
{"url":"orig.png"},
|
{"url":"orig.png"},
|
||||||
{"url":"impl.png"}
|
{"url":"impl.png"},
|
||||||
|
{"url":"impl_2.png"},
|
||||||
|
{"url":"impl_3.png"}
|
||||||
],
|
],
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"aiclock.app.js","url":"aiclock.app.js"},
|
{"name":"aiclock.app.js","url":"aiclock.app.js"},
|
||||||
|
|
|
@ -36,4 +36,4 @@
|
||||||
0.33: Allow hiding timers&alarms
|
0.33: Allow hiding timers&alarms
|
||||||
0.34: Add "Confirm" option to alarm/timer edit menus
|
0.34: Add "Confirm" option to alarm/timer edit menus
|
||||||
0.35: Add automatic translation of more strings
|
0.35: Add automatic translation of more strings
|
||||||
|
0.36: alarm widget moved out of app
|
||||||
|
|
|
@ -2,17 +2,16 @@
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Alarms & Timers",
|
"name": "Alarms & Timers",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.35",
|
"version": "0.36",
|
||||||
"description": "Set alarms and timers on your Bangle",
|
"description": "Set alarms and timers on your Bangle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm",
|
||||||
"supports": [ "BANGLEJS", "BANGLEJS2" ],
|
"supports": [ "BANGLEJS", "BANGLEJS2" ],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"dependencies": { "scheduler":"type" },
|
"dependencies": { "scheduler":"type", "alarm":"widget" },
|
||||||
"storage": [
|
"storage": [
|
||||||
{ "name": "alarm.app.js", "url": "app.js" },
|
{ "name": "alarm.app.js", "url": "app.js" },
|
||||||
{ "name": "alarm.img", "url": "app-icon.js", "evaluate": true },
|
{ "name": "alarm.img", "url": "app-icon.js", "evaluate": true }
|
||||||
{ "name": "alarm.wid.js", "url": "widget.js" }
|
|
||||||
],
|
],
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
{ "url": "screenshot-1.png" },
|
{ "url": "screenshot-1.png" },
|
||||||
|
|
|
@ -16,4 +16,5 @@
|
||||||
0.16: Bangle.http now fails immediately if there is no Bluetooth connection (fix #2152)
|
0.16: Bangle.http now fails immediately if there is no Bluetooth connection (fix #2152)
|
||||||
0.17: Now kick off Calendar sync as soon as connected to Gadgetbridge
|
0.17: Now kick off Calendar sync as soon as connected to Gadgetbridge
|
||||||
0.18: Use new message library
|
0.18: Use new message library
|
||||||
If connected to Gadgetbridge, allow GPS forwarding from phone (Gadgetbridge code still not merged)
|
If connected to Gadgetbridge, allow GPS forwarding from phone (Gadgetbridge code still not merged)
|
||||||
|
0.19: Add automatic translation for a couple of strings.
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
t:event.cmd=="incoming"?"add":"remove",
|
t:event.cmd=="incoming"?"add":"remove",
|
||||||
id:"call", src:"Phone",
|
id:"call", src:"Phone",
|
||||||
positive:true, negative:true,
|
positive:true, negative:true,
|
||||||
title:event.name||"Call", body:"Incoming call\n"+event.number});
|
title:event.name||/*LANG*/"Call", body:/*LANG*/"Incoming call\n"+event.number});
|
||||||
require("messages").pushMessage(event);
|
require("messages").pushMessage(event);
|
||||||
},
|
},
|
||||||
"alarm" : function() {
|
"alarm" : function() {
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
Bangle.http = (url,options)=>{
|
Bangle.http = (url,options)=>{
|
||||||
options = options||{};
|
options = options||{};
|
||||||
if (!NRF.getSecurityStatus().connected)
|
if (!NRF.getSecurityStatus().connected)
|
||||||
return Promise.reject("Not connected to Bluetooth");
|
return Promise.reject(/*LANG*/"Not connected to Bluetooth");
|
||||||
if (Bangle.httpRequest === undefined)
|
if (Bangle.httpRequest === undefined)
|
||||||
Bangle.httpRequest={};
|
Bangle.httpRequest={};
|
||||||
if (options.id === undefined) {
|
if (options.id === undefined) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "android",
|
"id": "android",
|
||||||
"name": "Android Integration",
|
"name": "Android Integration",
|
||||||
"shortName": "Android",
|
"shortName": "Android",
|
||||||
"version": "0.18",
|
"version": "0.19",
|
||||||
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,system,messages,notifications,gadgetbridge",
|
"tags": "tool,system,messages,notifications,gadgetbridge",
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: Create astrocalc app
|
0.01: Create astrocalc app
|
||||||
0.02: Store last GPS lock, can be used instead of waiting for new GPS on start
|
0.02: Store last GPS lock, can be used instead of waiting for new GPS on start
|
||||||
|
0.03: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* Calculate the Sun and Moon positions based on watch GPS and display graphically
|
* Calculate the Sun and Moon positions based on watch GPS and display graphically
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const SunCalc = require("suncalc.js");
|
const SunCalc = require("suncalc"); // from modules folder
|
||||||
const storage = require("Storage");
|
const storage = require("Storage");
|
||||||
const LAST_GPS_FILE = "astrocalc.gps.json";
|
const LAST_GPS_FILE = "astrocalc.gps.json";
|
||||||
let lastGPS = (storage.readJSON(LAST_GPS_FILE, 1) || null);
|
let lastGPS = (storage.readJSON(LAST_GPS_FILE, 1) || null);
|
||||||
|
@ -385,4 +385,4 @@ function init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let m;
|
let m;
|
||||||
init();
|
init();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "astrocalc",
|
"id": "astrocalc",
|
||||||
"name": "Astrocalc",
|
"name": "Astrocalc",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Calculates interesting information on the sun and moon cycles for the current day based on your location.",
|
"description": "Calculates interesting information on the sun and moon cycles for the current day based on your location.",
|
||||||
"icon": "astrocalc.png",
|
"icon": "astrocalc.png",
|
||||||
"tags": "app,sun,moon,cycles,tool,outdoors",
|
"tags": "app,sun,moon,cycles,tool,outdoors",
|
||||||
|
@ -9,7 +9,6 @@
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"astrocalc.app.js","url":"astrocalc-app.js"},
|
{"name":"astrocalc.app.js","url":"astrocalc-app.js"},
|
||||||
{"name":"suncalc.js","url":"suncalc.js"},
|
|
||||||
{"name":"astrocalc.img","url":"astrocalc-icon.js","evaluate":true},
|
{"name":"astrocalc.img","url":"astrocalc-icon.js","evaluate":true},
|
||||||
{"name":"first-quarter.img","url":"first-quarter-icon.js","evaluate":true},
|
{"name":"first-quarter.img","url":"first-quarter-icon.js","evaluate":true},
|
||||||
{"name":"last-quarter.img","url":"last-quarter-icon.js","evaluate":true},
|
{"name":"last-quarter.img","url":"last-quarter-icon.js","evaluate":true},
|
||||||
|
|
|
@ -1,328 +0,0 @@
|
||||||
/*
|
|
||||||
(c) 2011-2015, Vladimir Agafonkin
|
|
||||||
SunCalc is a JavaScript library for calculating sun/moon position and light phases.
|
|
||||||
https://github.com/mourner/suncalc
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function () { 'use strict';
|
|
||||||
|
|
||||||
// shortcuts for easier to read formulas
|
|
||||||
|
|
||||||
var PI = Math.PI,
|
|
||||||
sin = Math.sin,
|
|
||||||
cos = Math.cos,
|
|
||||||
tan = Math.tan,
|
|
||||||
asin = Math.asin,
|
|
||||||
atan = Math.atan2,
|
|
||||||
acos = Math.acos,
|
|
||||||
rad = PI / 180;
|
|
||||||
|
|
||||||
// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
|
|
||||||
|
|
||||||
|
|
||||||
// date/time constants and conversions
|
|
||||||
|
|
||||||
var dayMs = 1000 * 60 * 60 * 24,
|
|
||||||
J1970 = 2440588,
|
|
||||||
J2000 = 2451545;
|
|
||||||
|
|
||||||
function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
|
|
||||||
function fromJulian(j) { return (j + 0.5 - J1970) * dayMs; }
|
|
||||||
function toDays(date) { return toJulian(date) - J2000; }
|
|
||||||
|
|
||||||
|
|
||||||
// general calculations for position
|
|
||||||
|
|
||||||
var e = rad * 23.4397; // obliquity of the Earth
|
|
||||||
|
|
||||||
function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); }
|
|
||||||
function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
|
|
||||||
|
|
||||||
function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
|
|
||||||
function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }
|
|
||||||
|
|
||||||
function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; }
|
|
||||||
|
|
||||||
function astroRefraction(h) {
|
|
||||||
if (h < 0) // the following formula works for positive altitudes only.
|
|
||||||
h = 0; // if h = -0.08901179 a div/0 would occur.
|
|
||||||
|
|
||||||
// formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
|
|
||||||
// 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad:
|
|
||||||
return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179));
|
|
||||||
}
|
|
||||||
|
|
||||||
// general sun calculations
|
|
||||||
|
|
||||||
function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
|
|
||||||
|
|
||||||
function eclipticLongitude(M) {
|
|
||||||
|
|
||||||
var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
|
|
||||||
P = rad * 102.9372; // perihelion of the Earth
|
|
||||||
|
|
||||||
return M + C + P + PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sunCoords(d) {
|
|
||||||
|
|
||||||
var M = solarMeanAnomaly(d),
|
|
||||||
L = eclipticLongitude(M);
|
|
||||||
|
|
||||||
return {
|
|
||||||
dec: declination(L, 0),
|
|
||||||
ra: rightAscension(L, 0)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var SunCalc = {};
|
|
||||||
|
|
||||||
|
|
||||||
// calculates sun position for a given date and latitude/longitude
|
|
||||||
|
|
||||||
SunCalc.getPosition = function (date, lat, lng) {
|
|
||||||
|
|
||||||
var lw = rad * -lng,
|
|
||||||
phi = rad * lat,
|
|
||||||
d = toDays(date),
|
|
||||||
|
|
||||||
c = sunCoords(d),
|
|
||||||
H = siderealTime(d, lw) - c.ra;
|
|
||||||
|
|
||||||
return {
|
|
||||||
azimuth: azimuth(H, phi, c.dec),
|
|
||||||
altitude: altitude(H, phi, c.dec)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// sun times configuration (angle, morning name, evening name)
|
|
||||||
|
|
||||||
var times = SunCalc.times = [
|
|
||||||
[-0.833, 'sunrise', 'sunset' ],
|
|
||||||
[ -0.3, 'sunriseEnd', 'sunsetStart' ],
|
|
||||||
[ -6, 'dawn', 'dusk' ],
|
|
||||||
[ -12, 'nauticalDawn', 'nauticalDusk'],
|
|
||||||
[ -18, 'nightEnd', 'night' ],
|
|
||||||
[ 6, 'goldenHourEnd', 'goldenHour' ]
|
|
||||||
];
|
|
||||||
|
|
||||||
// adds a custom time to the times config
|
|
||||||
|
|
||||||
SunCalc.addTime = function (angle, riseName, setName) {
|
|
||||||
times.push([angle, riseName, setName]);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// calculations for sun times
|
|
||||||
|
|
||||||
var J0 = 0.0009;
|
|
||||||
|
|
||||||
function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
|
|
||||||
|
|
||||||
function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
|
|
||||||
function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
|
|
||||||
|
|
||||||
function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
|
|
||||||
function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; }
|
|
||||||
|
|
||||||
// returns set time for the given sun altitude
|
|
||||||
function getSetJ(h, lw, phi, dec, n, M, L) {
|
|
||||||
|
|
||||||
var w = hourAngle(h, phi, dec),
|
|
||||||
a = approxTransit(w, lw, n);
|
|
||||||
return solarTransitJ(a, M, L);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// calculates sun times for a given date, latitude/longitude, and, optionally,
|
|
||||||
// the observer height (in meters) relative to the horizon
|
|
||||||
|
|
||||||
SunCalc.getTimes = function (date, lat, lng, height) {
|
|
||||||
|
|
||||||
height = height || 0;
|
|
||||||
|
|
||||||
var lw = rad * -lng,
|
|
||||||
phi = rad * lat,
|
|
||||||
|
|
||||||
dh = observerAngle(height),
|
|
||||||
|
|
||||||
d = toDays(date),
|
|
||||||
n = julianCycle(d, lw),
|
|
||||||
ds = approxTransit(0, lw, n),
|
|
||||||
|
|
||||||
M = solarMeanAnomaly(ds),
|
|
||||||
L = eclipticLongitude(M),
|
|
||||||
dec = declination(L, 0),
|
|
||||||
|
|
||||||
Jnoon = solarTransitJ(ds, M, L),
|
|
||||||
|
|
||||||
i, len, time, h0, Jset, Jrise;
|
|
||||||
|
|
||||||
|
|
||||||
var result = {
|
|
||||||
solarNoon: new Date(fromJulian(Jnoon)),
|
|
||||||
nadir: new Date(fromJulian(Jnoon - 0.5))
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i = 0, len = times.length; i < len; i += 1) {
|
|
||||||
time = times[i];
|
|
||||||
h0 = (time[0] + dh) * rad;
|
|
||||||
|
|
||||||
Jset = getSetJ(h0, lw, phi, dec, n, M, L);
|
|
||||||
Jrise = Jnoon - (Jset - Jnoon);
|
|
||||||
|
|
||||||
result[time[1]] = new Date(fromJulian(Jrise) - (dayMs / 2));
|
|
||||||
result[time[2]] = new Date(fromJulian(Jset) + (dayMs / 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas
|
|
||||||
|
|
||||||
function moonCoords(d) { // geocentric ecliptic coordinates of the moon
|
|
||||||
|
|
||||||
var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude
|
|
||||||
M = rad * (134.963 + 13.064993 * d), // mean anomaly
|
|
||||||
F = rad * (93.272 + 13.229350 * d), // mean distance
|
|
||||||
|
|
||||||
l = L + rad * 6.289 * sin(M), // longitude
|
|
||||||
b = rad * 5.128 * sin(F), // latitude
|
|
||||||
dt = 385001 - 20905 * cos(M); // distance to the moon in km
|
|
||||||
|
|
||||||
return {
|
|
||||||
ra: rightAscension(l, b),
|
|
||||||
dec: declination(l, b),
|
|
||||||
dist: dt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
SunCalc.getMoonPosition = function (date, lat, lng) {
|
|
||||||
|
|
||||||
var lw = rad * -lng,
|
|
||||||
phi = rad * lat,
|
|
||||||
d = toDays(date),
|
|
||||||
|
|
||||||
c = moonCoords(d),
|
|
||||||
H = siderealTime(d, lw) - c.ra,
|
|
||||||
h = altitude(H, phi, c.dec),
|
|
||||||
// formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
|
|
||||||
pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H));
|
|
||||||
|
|
||||||
h = h + astroRefraction(h); // altitude correction for refraction
|
|
||||||
|
|
||||||
return {
|
|
||||||
azimuth: azimuth(H, phi, c.dec),
|
|
||||||
altitude: h,
|
|
||||||
distance: c.dist,
|
|
||||||
parallacticAngle: pa
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// calculations for illumination parameters of the moon,
|
|
||||||
// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
|
|
||||||
// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
|
|
||||||
|
|
||||||
// Function updated from gist: https://gist.github.com/endel/dfe6bb2fbe679781948c
|
|
||||||
|
|
||||||
SunCalc.getMoonIllumination = function (date) {
|
|
||||||
let month = date.getMonth();
|
|
||||||
let year = date.getFullYear();
|
|
||||||
let day = date.getDate();
|
|
||||||
|
|
||||||
let c = 0;
|
|
||||||
let e = 0;
|
|
||||||
let jd = 0;
|
|
||||||
let b = 0;
|
|
||||||
|
|
||||||
if (month < 3) {
|
|
||||||
year--;
|
|
||||||
month += 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
++month;
|
|
||||||
c = 365.25 * year;
|
|
||||||
e = 30.6 * month;
|
|
||||||
jd = c + e + day - 694039.09; // jd is total days elapsed
|
|
||||||
jd /= 29.5305882; // divide by the moon cycle
|
|
||||||
b = parseInt(jd); // int(jd) -> b, take integer part of jd
|
|
||||||
jd -= b; // subtract integer part to leave fractional part of original jd
|
|
||||||
b = Math.round(jd * 8); // scale fraction from 0-8 and round
|
|
||||||
|
|
||||||
if (b >= 8) b = 0; // 0 and 8 are the same so turn 8 into 0
|
|
||||||
|
|
||||||
return {phase: b};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function hoursLater(date, h) {
|
|
||||||
return new Date(date.valueOf() + h * dayMs / 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
|
|
||||||
|
|
||||||
SunCalc.getMoonTimes = function (date, lat, lng, inUTC) {
|
|
||||||
var t = date;
|
|
||||||
if (inUTC) t.setUTCHours(0, 0, 0, 0);
|
|
||||||
else t.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
var hc = 0.133 * rad,
|
|
||||||
h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc,
|
|
||||||
h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx;
|
|
||||||
|
|
||||||
// go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
|
|
||||||
for (var i = 1; i <= 24; i += 2) {
|
|
||||||
h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc;
|
|
||||||
h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc;
|
|
||||||
|
|
||||||
a = (h0 + h2) / 2 - h1;
|
|
||||||
b = (h2 - h0) / 2;
|
|
||||||
xe = -b / (2 * a);
|
|
||||||
ye = (a * xe + b) * xe + h1;
|
|
||||||
d = b * b - 4 * a * h1;
|
|
||||||
roots = 0;
|
|
||||||
|
|
||||||
if (d >= 0) {
|
|
||||||
dx = Math.sqrt(d) / (Math.abs(a) * 2);
|
|
||||||
x1 = xe - dx;
|
|
||||||
x2 = xe + dx;
|
|
||||||
if (Math.abs(x1) <= 1) roots++;
|
|
||||||
if (Math.abs(x2) <= 1) roots++;
|
|
||||||
if (x1 < -1) x1 = x2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roots === 1) {
|
|
||||||
if (h0 < 0) rise = i + x1;
|
|
||||||
else set = i + x1;
|
|
||||||
|
|
||||||
} else if (roots === 2) {
|
|
||||||
rise = i + (ye < 0 ? x2 : x1);
|
|
||||||
set = i + (ye < 0 ? x1 : x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rise && set) break;
|
|
||||||
|
|
||||||
h0 = h2;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = {};
|
|
||||||
|
|
||||||
if (rise) result.rise = hoursLater(t, rise);
|
|
||||||
if (set) result.set = hoursLater(t, set);
|
|
||||||
|
|
||||||
if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// export as Node module / AMD module / browser variable
|
|
||||||
if (typeof exports === 'object' && typeof module !== 'undefined') module.exports = SunCalc;
|
|
||||||
else if (typeof define === 'function' && define.amd) define(SunCalc);
|
|
||||||
else global.SunCalc = SunCalc;
|
|
||||||
|
|
||||||
}());
|
|
|
@ -61,3 +61,6 @@
|
||||||
0.52: Ensure heading patch for pre-2v15.68 firmware applies to getCompass
|
0.52: Ensure heading patch for pre-2v15.68 firmware applies to getCompass
|
||||||
0.53: Add polyfills for pre-2v15.135 firmware for Bangle.load and Bangle.showClock
|
0.53: Add polyfills for pre-2v15.135 firmware for Bangle.load and Bangle.showClock
|
||||||
0.54: Fix for invalid version comparison in polyfill
|
0.54: Fix for invalid version comparison in polyfill
|
||||||
|
0.55: Add toLocalISOString polyfill for pre-2v15 firmwares
|
||||||
|
Only add boot info comments if settings.bootDebug was set
|
||||||
|
If settings.bootDebug is set, output timing for each section of .boot0
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
/* This rewrites boot0.js based on current settings. If settings changed then it
|
/* This rewrites boot0.js based on current settings. If settings changed then it
|
||||||
recalculates, but this avoids us doing a whole bunch of reconfiguration most
|
recalculates, but this avoids us doing a whole bunch of reconfiguration most
|
||||||
of the time. */
|
of the time. */
|
||||||
|
{ // execute in our own scope so we don't have to free variables...
|
||||||
E.showMessage(/*LANG*/"Updating boot0...");
|
E.showMessage(/*LANG*/"Updating boot0...");
|
||||||
var s = require('Storage').readJSON('setting.json',1)||{};
|
let s = require('Storage').readJSON('setting.json',1)||{};
|
||||||
var BANGLEJS2 = process.env.HWVERSION==2; // Is Bangle.js 2
|
const BANGLEJS2 = process.env.HWVERSION==2; // Is Bangle.js 2
|
||||||
var FWVERSION = parseFloat(process.env.VERSION.replace("v","").replace(/\.(\d\d)$/,".0$1"));
|
const FWVERSION = parseFloat(process.env.VERSION.replace("v","").replace(/\.(\d\d)$/,".0$1"));
|
||||||
var boot = "", bootPost = "";
|
const DEBUG = s.bootDebug; // we can set this to enable debugging output in boot0
|
||||||
|
let boot = "", bootPost = "";
|
||||||
|
if (DEBUG) {
|
||||||
|
boot += "var _tm=Date.now()\n";
|
||||||
|
bootPost += "delete _tm;";
|
||||||
|
}
|
||||||
if (require('Storage').hash) { // new in 2v11 - helps ensure files haven't changed
|
if (require('Storage').hash) { // new in 2v11 - helps ensure files haven't changed
|
||||||
var CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.boot\.js/)+E.CRC32(process.env.GIT_COMMIT);
|
let CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.boot\.js/)+E.CRC32(process.env.GIT_COMMIT);
|
||||||
boot += `if (E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.boot\\.js/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
|
boot += `if (E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.boot\\.js/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
|
||||||
} else {
|
} else {
|
||||||
var CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))+E.CRC32(process.env.GIT_COMMIT);
|
let CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))+E.CRC32(process.env.GIT_COMMIT);
|
||||||
boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\\.boot\\.js/))+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
|
boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\\.boot\\.js/))+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
|
||||||
}
|
}
|
||||||
boot += ` { eval(require('Storage').read('bootupdate.js')); throw "Storage Updated!"}\n`;
|
boot += ` { eval(require('Storage').read('bootupdate.js')); throw "Storage Updated!"}\n`;
|
||||||
|
@ -88,14 +94,25 @@ delete Bangle.showClock;
|
||||||
if (!Bangle.showClock) boot += `Bangle.showClock = ()=>{load(".bootcde")};\n`;
|
if (!Bangle.showClock) boot += `Bangle.showClock = ()=>{load(".bootcde")};\n`;
|
||||||
delete Bangle.load;
|
delete Bangle.load;
|
||||||
if (!Bangle.load) boot += `Bangle.load = load;\n`;
|
if (!Bangle.load) boot += `Bangle.load = load;\n`;
|
||||||
|
let date = new Date();
|
||||||
|
delete date.toLocalISOString; // toLocalISOString was only introduced in 2v15
|
||||||
|
if (!date.toLocalISOString) boot += `Date.prototype.toLocalISOString = function() {
|
||||||
|
var o = this.getTimezoneOffset();
|
||||||
|
var d = new Date(this.getTime() - o*60000);
|
||||||
|
var sign = o>0?"-":"+";
|
||||||
|
o = Math.abs(o);
|
||||||
|
return d.toISOString().slice(0,-1)+sign+Math.floor(o/60).toString().padStart(2,0)+(o%60).toString().padStart(2,0);
|
||||||
|
};\n`;
|
||||||
|
|
||||||
|
// show timings
|
||||||
|
if (DEBUG) boot += `print(".boot0",0|(Date.now()-_tm),"ms");_tm=Date.now();\n`
|
||||||
// ================================================== BOOT.JS
|
// ================================================== BOOT.JS
|
||||||
// Append *.boot.js files
|
// Append *.boot.js files
|
||||||
// These could change bleServices/bleServiceOptions if needed
|
// These could change bleServices/bleServiceOptions if needed
|
||||||
var bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{
|
let bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{
|
||||||
var getPriority = /.*\.(\d+)\.boot\.js$/;
|
let getPriority = /.*\.(\d+)\.boot\.js$/;
|
||||||
var aPriority = a.match(getPriority);
|
let aPriority = a.match(getPriority);
|
||||||
var bPriority = b.match(getPriority);
|
let bPriority = b.match(getPriority);
|
||||||
if (aPriority && bPriority){
|
if (aPriority && bPriority){
|
||||||
return parseInt(aPriority[1]) - parseInt(bPriority[1]);
|
return parseInt(aPriority[1]) - parseInt(bPriority[1]);
|
||||||
} else if (aPriority && !bPriority){
|
} else if (aPriority && !bPriority){
|
||||||
|
@ -106,14 +123,16 @@ var bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{
|
||||||
return a==b ? 0 : (a>b ? 1 : -1);
|
return a==b ? 0 : (a>b ? 1 : -1);
|
||||||
});
|
});
|
||||||
// precalculate file size
|
// precalculate file size
|
||||||
var fileSize = boot.length + bootPost.length;
|
let fileSize = boot.length + bootPost.length;
|
||||||
bootFiles.forEach(bootFile=>{
|
bootFiles.forEach(bootFile=>{
|
||||||
// match the size of data we're adding below in bootFiles.forEach
|
// match the size of data we're adding below in bootFiles.forEach
|
||||||
fileSize += 2+bootFile.length+1+require('Storage').read(bootFile).length+2;
|
if (DEBUG) fileSize += 2+bootFile.length+1; // `//${bootFile}\n` comment
|
||||||
|
fileSize += require('Storage').read(bootFile).length+2; // boot code plus ";\n"
|
||||||
|
if (DEBUG) fileSize += 48+E.toJS(bootFile).length; // `print(${E.toJS(bootFile)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n`
|
||||||
});
|
});
|
||||||
// write file in chunks (so as not to use up all RAM)
|
// write file in chunks (so as not to use up all RAM)
|
||||||
require('Storage').write('.boot0',boot,0,fileSize);
|
require('Storage').write('.boot0',boot,0,fileSize);
|
||||||
var fileOffset = boot.length;
|
let fileOffset = boot.length;
|
||||||
bootFiles.forEach(bootFile=>{
|
bootFiles.forEach(bootFile=>{
|
||||||
// we add a semicolon so if the file is wrapped in (function(){ ... }()
|
// we add a semicolon so if the file is wrapped in (function(){ ... }()
|
||||||
// with no semicolon we don't end up with (function(){ ... }()(function(){ ... }()
|
// with no semicolon we don't end up with (function(){ ... }()(function(){ ... }()
|
||||||
|
@ -122,16 +141,18 @@ bootFiles.forEach(bootFile=>{
|
||||||
// "//"+bootFile+"\n"+require('Storage').read(bootFile)+";\n";
|
// "//"+bootFile+"\n"+require('Storage').read(bootFile)+";\n";
|
||||||
// but we need to do this without ever loading everything into RAM as some
|
// but we need to do this without ever loading everything into RAM as some
|
||||||
// boot files seem to be getting pretty big now.
|
// boot files seem to be getting pretty big now.
|
||||||
require('Storage').write('.boot0',"//"+bootFile+"\n",fileOffset);
|
if (DEBUG) {
|
||||||
fileOffset+=2+bootFile.length+1;
|
require('Storage').write('.boot0',`//${bootFile}\n`,fileOffset);
|
||||||
var bf = require('Storage').read(bootFile);
|
fileOffset+=2+bootFile.length+1;
|
||||||
|
}
|
||||||
|
let bf = require('Storage').read(bootFile);
|
||||||
// we can't just write 'bf' in one go because at least in 2v13 and earlier
|
// we can't just write 'bf' in one go because at least in 2v13 and earlier
|
||||||
// Espruino wants to read the whole file into RAM first, and on Bangle.js 1
|
// Espruino wants to read the whole file into RAM first, and on Bangle.js 1
|
||||||
// it can be too big (especially BTHRM).
|
// it can be too big (especially BTHRM).
|
||||||
var bflen = bf.length;
|
let bflen = bf.length;
|
||||||
var bfoffset = 0;
|
let bfoffset = 0;
|
||||||
while (bflen) {
|
while (bflen) {
|
||||||
var bfchunk = Math.min(bflen, 2048);
|
let bfchunk = Math.min(bflen, 2048);
|
||||||
require('Storage').write('.boot0',bf.substr(bfoffset, bfchunk),fileOffset);
|
require('Storage').write('.boot0',bf.substr(bfoffset, bfchunk),fileOffset);
|
||||||
fileOffset+=bfchunk;
|
fileOffset+=bfchunk;
|
||||||
bfoffset+=bfchunk;
|
bfoffset+=bfchunk;
|
||||||
|
@ -139,15 +160,14 @@ bootFiles.forEach(bootFile=>{
|
||||||
}
|
}
|
||||||
require('Storage').write('.boot0',";\n",fileOffset);
|
require('Storage').write('.boot0',";\n",fileOffset);
|
||||||
fileOffset+=2;
|
fileOffset+=2;
|
||||||
|
if (DEBUG) {
|
||||||
|
require('Storage').write('.boot0',`print(${E.toJS(bootFile)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n`,fileOffset);
|
||||||
|
fileOffset += 48+E.toJS(bootFile).length
|
||||||
|
}
|
||||||
});
|
});
|
||||||
require('Storage').write('.boot0',bootPost,fileOffset);
|
require('Storage').write('.boot0',bootPost,fileOffset);
|
||||||
|
|
||||||
delete boot;
|
|
||||||
delete bootPost;
|
|
||||||
delete bootFiles;
|
|
||||||
delete fileSize;
|
|
||||||
delete fileOffset;
|
|
||||||
E.showMessage(/*LANG*/"Reloading...");
|
E.showMessage(/*LANG*/"Reloading...");
|
||||||
eval(require('Storage').read('.boot0'));
|
}
|
||||||
// .bootcde should be run automatically after if required, since
|
// .bootcde should be run automatically after if required, since
|
||||||
// we normally get called automatically from '.boot0'
|
// we normally get called automatically from '.boot0'
|
||||||
|
eval(require('Storage').read('.boot0'));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "boot",
|
"id": "boot",
|
||||||
"name": "Bootloader",
|
"name": "Bootloader",
|
||||||
"version": "0.54",
|
"version": "0.55",
|
||||||
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
||||||
"icon": "bootloader.png",
|
"icon": "bootloader.png",
|
||||||
"type": "bootloader",
|
"type": "bootloader",
|
||||||
|
|
|
@ -40,3 +40,4 @@
|
||||||
0.16: Set powerdownRequested correctly on BTHRM power on
|
0.16: Set powerdownRequested correctly on BTHRM power on
|
||||||
Additional logging on errors
|
Additional logging on errors
|
||||||
Add debug option for disabling active scanning
|
Add debug option for disabling active scanning
|
||||||
|
0.17: New GUI based on layout library
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
var intervalInt;
|
const BPM_FONT_SIZE="19%";
|
||||||
var intervalBt;
|
const VALUE_TIMEOUT=3000;
|
||||||
|
|
||||||
var BODY_LOCS = {
|
var BODY_LOCS = {
|
||||||
0: 'Other',
|
0: 'Other',
|
||||||
|
@ -7,46 +7,119 @@ var BODY_LOCS = {
|
||||||
2: 'Wrist',
|
2: 'Wrist',
|
||||||
3: 'Finger',
|
3: 'Finger',
|
||||||
4: 'Hand',
|
4: 'Hand',
|
||||||
5: 'Ear Lobe',
|
5: 'Earlobe',
|
||||||
6: 'Foot',
|
6: 'Foot',
|
||||||
|
};
|
||||||
|
|
||||||
|
var Layout = require("Layout");
|
||||||
|
|
||||||
|
function border(l,c) {
|
||||||
|
g.setColor(c).drawLine(l.x+l.w*0.05, l.y-4, l.x+l.w*0.95, l.y-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear(y){
|
function getRow(id, text, additionalInfo){
|
||||||
g.reset();
|
let additional = [];
|
||||||
g.clearRect(0,y,g.getWidth(),y+75);
|
let l = {
|
||||||
}
|
type:"h", c: [
|
||||||
|
{
|
||||||
function draw(y, type, event) {
|
type:"v",
|
||||||
clear(y);
|
width: g.getWidth()*0.4,
|
||||||
var px = g.getWidth()/2;
|
c: [
|
||||||
var str = event.bpm + "";
|
{type:"txt", halign:1, font:"8%", label:text, id:id+"text" },
|
||||||
g.reset();
|
{type:"txt", halign:1, font:BPM_FONT_SIZE, label:"--", id:id, bgCol: g.theme.bg }
|
||||||
g.setFontAlign(0,0);
|
]
|
||||||
g.setFontVector(40).drawString(str,px,y+20);
|
},{
|
||||||
str = "Event: " + type;
|
type:undefined, fillx:1
|
||||||
if (type === "HRM") {
|
},{
|
||||||
str += " Confidence: " + event.confidence;
|
type:"v",
|
||||||
g.setFontVector(12).drawString(str,px,y+40);
|
valign: -1,
|
||||||
str = " Source: " + (event.src ? event.src : "internal");
|
width: g.getWidth()*0.45,
|
||||||
g.setFontVector(12).drawString(str,px,y+50);
|
c: additional
|
||||||
|
},{
|
||||||
|
type:undefined, width:g.getWidth()*0.05
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
for (let i of additionalInfo){
|
||||||
|
let label = {type:"txt", font:"6x8", label:i + ":" };
|
||||||
|
let value = {type:"txt", font:"6x8", label:"--", id:id + i };
|
||||||
|
additional.push({type:"h", halign:-1, c:[ label, {type:undefined, fillx:1}, value ]});
|
||||||
}
|
}
|
||||||
if (type === "BTHRM"){
|
|
||||||
if (event.battery) str += " Bat: " + (event.battery ? event.battery : "");
|
return l;
|
||||||
g.setFontVector(12).drawString(str,px,y+40);
|
}
|
||||||
str= "";
|
|
||||||
if (event.location) str += "Loc: " + BODY_LOCS[event.location];
|
var layout = new Layout( {
|
||||||
if (event.rr && event.rr.length > 0) str += " RR: " + event.rr.join(",");
|
type:"v", c: [
|
||||||
g.setFontVector(12).drawString(str,px,y+50);
|
getRow("int", "INT", ["Confidence"]),
|
||||||
str= "";
|
getRow("agg", "HRM", ["Confidence", "Source"]),
|
||||||
if (event.contact) str += " Contact: " + event.contact;
|
getRow("bt", "BT", ["Battery","Location","Contact", "RR", "Energy"]),
|
||||||
if (event.energy) str += " kJoule: " + event.energy.toFixed(0);
|
{ type:undefined, height:8 } //dummy to protect debug output
|
||||||
g.setFontVector(12).drawString(str,px,y+60);
|
]
|
||||||
|
}, {
|
||||||
|
lazy:true
|
||||||
|
});
|
||||||
|
|
||||||
|
var int,agg,bt;
|
||||||
|
var firstEvent = true;
|
||||||
|
|
||||||
|
function draw(){
|
||||||
|
if (!(int || agg || bt)) return;
|
||||||
|
|
||||||
|
if (firstEvent) {
|
||||||
|
g.clearRect(Bangle.appRect);
|
||||||
|
firstEvent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let now = Date.now();
|
||||||
|
|
||||||
|
if (int && int.time > (now - VALUE_TIMEOUT)){
|
||||||
|
layout.int.label = int.bpm;
|
||||||
|
if (!isNaN(int.confidence)) layout.intConfidence.label = int.confidence;
|
||||||
|
} else {
|
||||||
|
layout.int.label = "--";
|
||||||
|
layout.intConfidence.label = "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agg && agg.time > (now - VALUE_TIMEOUT)){
|
||||||
|
layout.agg.label = agg.bpm;
|
||||||
|
if (!isNaN(agg.confidence)) layout.aggConfidence.label = agg.confidence;
|
||||||
|
if (agg.src) layout.aggSource.label = agg.src;
|
||||||
|
} else {
|
||||||
|
layout.agg.label = "--";
|
||||||
|
layout.aggConfidence.label = "--";
|
||||||
|
layout.aggSource.label = "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bt && bt.time > (now - VALUE_TIMEOUT)) {
|
||||||
|
layout.bt.label = bt.bpm;
|
||||||
|
if (!isNaN(bt.battery)) layout.btBattery.label = bt.battery + "%";
|
||||||
|
if (bt.rr) layout.btRR.label = bt.rr.join(",");
|
||||||
|
if (!isNaN(bt.location)) layout.btLocation.label = BODY_LOCS[bt.location];
|
||||||
|
if (bt.contact !== undefined) layout.btContact.label = bt.contact ? "Yes":"No";
|
||||||
|
if (!isNaN(bt.energy)) layout.btEnergy.label = bt.energy.toFixed(0) + "kJ";
|
||||||
|
} else {
|
||||||
|
layout.bt.label = "--";
|
||||||
|
layout.btBattery.label = "--";
|
||||||
|
layout.btRR.label = "--";
|
||||||
|
layout.btLocation.label = "--";
|
||||||
|
layout.btContact.label = "--";
|
||||||
|
layout.btEnergy.label = "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.update();
|
||||||
|
layout.render();
|
||||||
|
let first = true;
|
||||||
|
for (let c of layout.l.c){
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c.type && c.type == "h")
|
||||||
|
border(c,g.theme.fg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstEventBt = true;
|
|
||||||
var firstEventInt = true;
|
|
||||||
|
|
||||||
|
|
||||||
// This can get called for the boot code to show what's happening
|
// This can get called for the boot code to show what's happening
|
||||||
function showStatusInfo(txt) {
|
function showStatusInfo(txt) {
|
||||||
|
@ -57,41 +130,26 @@ function showStatusInfo(txt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBtHrm(e) {
|
function onBtHrm(e) {
|
||||||
if (firstEventBt){
|
bt = e;
|
||||||
clear(24);
|
bt.time = Date.now();
|
||||||
firstEventBt = false;
|
|
||||||
}
|
|
||||||
draw(100, "BTHRM", e);
|
|
||||||
if (e.bpm === 0){
|
|
||||||
Bangle.buzz(100,0.2);
|
|
||||||
}
|
|
||||||
if (intervalBt){
|
|
||||||
clearInterval(intervalBt);
|
|
||||||
}
|
|
||||||
intervalBt = setInterval(()=>{
|
|
||||||
clear(100);
|
|
||||||
}, 2000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onHrm(e) {
|
function onInt(e) {
|
||||||
if (firstEventInt){
|
int = e;
|
||||||
clear(24);
|
int.time = Date.now();
|
||||||
firstEventInt = false;
|
|
||||||
}
|
|
||||||
draw(24, "HRM", e);
|
|
||||||
if (intervalInt){
|
|
||||||
clearInterval(intervalInt);
|
|
||||||
}
|
|
||||||
intervalInt = setInterval(()=>{
|
|
||||||
clear(24);
|
|
||||||
}, 2000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onAgg(e) {
|
||||||
|
agg = e;
|
||||||
|
agg.time = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
var settings = require('Storage').readJSON("bthrm.json", true) || {};
|
var settings = require('Storage').readJSON("bthrm.json", true) || {};
|
||||||
|
|
||||||
Bangle.on('BTHRM', onBtHrm);
|
Bangle.on('BTHRM', onBtHrm);
|
||||||
Bangle.on('HRM', onHrm);
|
Bangle.on('HRM_int', onInt);
|
||||||
|
Bangle.on('HRM', onAgg);
|
||||||
|
|
||||||
|
|
||||||
Bangle.setHRMPower(1,'bthrm');
|
Bangle.setHRMPower(1,'bthrm');
|
||||||
if (!(settings.startWithHrm)){
|
if (!(settings.startWithHrm)){
|
||||||
|
@ -103,10 +161,11 @@ Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
if (Bangle.setBTHRMPower){
|
if (Bangle.setBTHRMPower){
|
||||||
g.reset().setFont("6x8",2).setFontAlign(0,0);
|
g.reset().setFont("6x8",2).setFontAlign(0,0);
|
||||||
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 24);
|
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2);
|
||||||
|
setInterval(draw, 1000);
|
||||||
} else {
|
} else {
|
||||||
g.reset().setFont("6x8",2).setFontAlign(0,0);
|
g.reset().setFont("6x8",2).setFontAlign(0,0);
|
||||||
g.drawString("BTHRM disabled",g.getWidth()/2,g.getHeight()/2 + 32);
|
g.drawString("BTHRM disabled",g.getWidth()/2,g.getHeight()/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
E.on('kill', ()=>Bangle.setBTHRMPower(0,'bthrm'));
|
E.on('kill', ()=>Bangle.setBTHRMPower(0,'bthrm'));
|
||||||
|
|
|
@ -553,14 +553,15 @@ exports.enable = () => {
|
||||||
|
|
||||||
if (settings.replace){
|
if (settings.replace){
|
||||||
// register a listener for original HRM events and emit as HRM_int
|
// register a listener for original HRM events and emit as HRM_int
|
||||||
Bangle.on("HRM", (e) => {
|
Bangle.on("HRM", (o) => {
|
||||||
e.modified = true;
|
let e = Object.assign({},o);
|
||||||
log("Emitting HRM_int", e);
|
log("Emitting HRM_int", e);
|
||||||
Bangle.emit("HRM_int", e);
|
Bangle.emit("HRM_int", e);
|
||||||
if (fallbackActive){
|
if (fallbackActive){
|
||||||
// if fallback to internal HRM is active, emit as HRM_R to which everyone listens
|
// if fallback to internal HRM is active, emit as HRM_R to which everyone listens
|
||||||
log("Emitting HRM_R(int)", e);
|
o.src = "int";
|
||||||
Bangle.emit("HRM_R", e);
|
log("Emitting HRM_R(int)", o);
|
||||||
|
Bangle.emit("HRM_R", o);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -576,6 +577,13 @@ exports.enable = () => {
|
||||||
if (name == "HRM") o("HRM_R", cb);
|
if (name == "HRM") o("HRM_R", cb);
|
||||||
else o(name, cb);
|
else o(name, cb);
|
||||||
})(Bangle.removeListener);
|
})(Bangle.removeListener);
|
||||||
|
} else {
|
||||||
|
Bangle.on("HRM", (o)=>{
|
||||||
|
o.src = "int";
|
||||||
|
let e = Object.assign({},o);
|
||||||
|
log("Emitting HRM_int", e);
|
||||||
|
Bangle.emit("HRM_int", e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Bangle.origSetHRMPower = Bangle.setHRMPower;
|
Bangle.origSetHRMPower = Bangle.setHRMPower;
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
"id": "bthrm",
|
"id": "bthrm",
|
||||||
"name": "Bluetooth Heart Rate Monitor",
|
"name": "Bluetooth Heart Rate Monitor",
|
||||||
"shortName": "BT HRM",
|
"shortName": "BT HRM",
|
||||||
"version": "0.16",
|
"version": "0.17",
|
||||||
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
|
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
"screenshots": [{"url":"screen.png"}],
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "health,bluetooth,hrm,bthrm",
|
"tags": "health,bluetooth,hrm,bthrm",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
|
After Width: | Height: | Size: 3.6 KiB |
|
@ -32,3 +32,9 @@
|
||||||
Use widget_utils if available
|
Use widget_utils if available
|
||||||
0.17: Load circles from clkinfo
|
0.17: Load circles from clkinfo
|
||||||
0.18: Improved clkinfo handling and using it for the weather circle
|
0.18: Improved clkinfo handling and using it for the weather circle
|
||||||
|
0.19: Remove old code and fixing clkinfo handling (fix HRM and other items that change)
|
||||||
|
Remove settings for what is displayed and instead allow circles to be changed by swiping
|
||||||
|
0.20: Add much faster circle rendering (250ms -> 40ms)
|
||||||
|
Add fast load capability
|
||||||
|
0.21: Remade all icons without a palette for dark theme
|
||||||
|
Now re-adds widgets if they were hidden when fast-loading
|
||||||
|
|
|
@ -5,6 +5,7 @@ A clock with three or four circles for different data at the bottom in a probabl
|
||||||
By default the time, date and day of week is shown.
|
By default the time, date and day of week is shown.
|
||||||
|
|
||||||
It can show the following information (this can be configured):
|
It can show the following information (this can be configured):
|
||||||
|
|
||||||
* Steps
|
* Steps
|
||||||
* Steps distance
|
* Steps distance
|
||||||
* Heart rate (automatically updates when screen is on and unlocked)
|
* Heart rate (automatically updates when screen is on and unlocked)
|
||||||
|
@ -14,15 +15,24 @@ It can show the following information (this can be configured):
|
||||||
* Temperature inside circle
|
* Temperature inside circle
|
||||||
* Condition as icon below circle
|
* Condition as icon below circle
|
||||||
* Big weather icon next to clock
|
* Big weather icon next to clock
|
||||||
* Time and progress until next sunrise or sunset (requires [my location app](https://banglejs.com/apps/#mylocation))
|
* Altitude from internal pressure sensor
|
||||||
* Temperature, air pressure or altitude from internal pressure sensor
|
* Active alarms (if `Alarm` app installed)
|
||||||
|
* Sunrise or sunset (if `Sunrise Clockinfo` app installed)
|
||||||
|
|
||||||
|
To change what is shown:
|
||||||
|
|
||||||
The color of each circle can be configured. The following colors are available:
|
* Unlock the watch
|
||||||
|
* Tap on the circle to change (a border is drawn around it)
|
||||||
|
* Swipe up/down to change the guage within the given group
|
||||||
|
* Swipe left/right to change the group (eg. between standard Bangle.js and Alarms/etc)
|
||||||
|
|
||||||
|
Data is provided by ['Clock Info'](http://www.espruino.com/Bangle.js+Clock+Info)
|
||||||
|
so any apps that implement this feature can add extra information to be displayed.
|
||||||
|
|
||||||
|
The color of each circle can be configured from `Settings -> Apps -> Circles Clock`. The following colors are available:
|
||||||
* Basic colors (red, green, blue, yellow, magenta, cyan, black, white)
|
* Basic colors (red, green, blue, yellow, magenta, cyan, black, white)
|
||||||
* Color depending on value (green -> red, red -> green)
|
* Color depending on value (green -> red, red -> green)
|
||||||
|
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||

|

|
||||||

|

|
||||||
|
@ -38,5 +48,5 @@ The color of each circle can be configured. The following colors are available:
|
||||||
Marco ([myxor](https://github.com/myxor))
|
Marco ([myxor](https://github.com/myxor))
|
||||||
|
|
||||||
## Icons
|
## Icons
|
||||||
Most of the icons are taken from [materialdesignicons](https://materialdesignicons.com) under Apache License 2.0 except the big weather icons which are from
|
Most of the icons are taken from [materialdesignicons](https://materialdesignicons.com) under Apache License 2.0 except the big weather icons which are from
|
||||||
[icons8](https://icons8.com/icon/set/weather/small--static--black)
|
[icons8](https://icons8.com/icon/set/weather/small--static--black)
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
let clock_info = require("clock_info");
|
|
||||||
let locale = require("locale");
|
|
||||||
let storage = require("Storage");
|
|
||||||
Graphics.prototype.setFontRobotoRegular50NumericOnly = function(scale) {
|
Graphics.prototype.setFontRobotoRegular50NumericOnly = function(scale) {
|
||||||
// Actual height 39 (40 - 2)
|
// Actual height 39 (40 - 2)
|
||||||
this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAB8AAAAAAAfAAAAAAAPwAAAAAAB8AAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAA4AAAAAAB+AAAAAAD/gAAAAAD/4AAAAAH/4AAAAAP/wAAAAAP/gAAAAAf/gAAAAAf/AAAAAA/+AAAAAB/+AAAAAB/8AAAAAD/4AAAAAH/4AAAAAD/wAAAAAA/wAAAAAAPgAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///wAAAB////gAAA////8AAA/////gAAP////8AAH8AAA/gAB8AAAD4AA+AAAAfAAPAAAADwADwAAAA8AA8AAAAPAAPAAAADwADwAAAA8AA8AAAAPAAPgAAAHwAB8AAAD4AAfwAAD+AAD/////AAA/////wAAH////4AAAf///4AAAB///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAPgAAAAAADwAAAAAAB8AAAAAAAfAAAAAAAHgAAAAAAD4AAAAAAA+AAAAAAAPAAAAAAAH/////wAB/////8AA//////AAP/////wAD/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAAAfgAADwAAP4AAB8AAH+AAA/AAD/gAAfwAB/AAAf8AAfAAAP/AAPgAAH7wAD4AAD88AA8AAB+PAAPAAA/DwADwAAfg8AA8AAPwPAAPAAH4DwADwAH8A8AA+AD+APAAPwB/ADwAB/D/gA8AAf//gAPAAD//wADwAAf/wAA8AAD/4AAPAAAHwAADwAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAADgAAAHwAA+AAAD8AAP4AAB/AAD/AAA/wAA/wAAf4AAD+AAHwAAAPgAD4APAB8AA+ADwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA8AH4APAAPgD+AHwAB8B/wD4AAf7/+B+AAD//v//AAA//x//wAAD/4P/4AAAf8B/4AAAAYAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAHwAAAAAAH8AAAAAAD/AAAAAAD/wAAAAAD/8AAAAAB/vAAAAAB/jwAAAAA/g8AAAAA/wPAAAAAfwDwAAAAf4A8AAAAf4APAAAAP8ADwAAAP8AA8AAAH8AAPAAAD/////8AA//////AAP/////wAD/////8AA//////AAAAAAPAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAB/APwAAH//wD+AAD//8A/wAA///AH+AAP//wAPgAD/B4AB8AA8A+AAfAAPAPAADwADwDwAA8AA8A8AAPAAPAPAADwADwD4AA8AA8A+AAPAAPAPwAHwADwD8AD4AA8AfwD+AAPAH///AADwA///wAA8AH//4AAPAAf/4AAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAD//+AAAAD///4AAAD////AAAB////4AAA/78D/AAAfw8AH4AAPweAA+AAD4PgAHwAB8DwAA8AAfA8AAPAAHgPAADwAD4DwAA8AA+A8AAPAAPAPgAHwADwD4AB8AA8AfgA+AAPAH+B/gAAAA///wAAAAH//4AAAAA//8AAAAAH/8AAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAA8AAAABAAPAAAABwADwAAAB8AA8AAAB/AAPAAAB/wADwAAD/8AA8AAD/8AAPAAD/4AADwAD/4AAA8AD/4AAAPAH/wAAADwH/wAAAA8H/wAAAAPH/wAAAAD3/gAAAAA//gAAAAAP/gAAAAAD/gAAAAAA/AAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwA/4AAAH/Af/AAAH/8P/4AAD//n//AAA//7//4AAfx/+A+AAHwD+AHwAD4AfgB8AA8AHwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA+AH4AfAAHwD+AHwAB/D/4D4AAP/+/n+AAD//n//AAAf/w//gAAB/wH/wAAAHwA/4AAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAD/8AAAAAD//wAAAAB//+AAAAA///wAAAAf4H+APAAH4AfgDwAD8AB8A8AA+AAfAPAAPAADwDwADwAA8B8AA8AAPAfAAPAADwHgADwAA8D4AA+AAeB+AAHwAHg/AAB+ADwfgAAP8D4/4AAD////8AAAf///8AAAB///+AAAAP//+AAAAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAOAAAB8AAHwAAAfgAD8AAAH4AA/AAAB8AAHwAAAOAAA4AAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("DRUcHBwcHBwcHBwcDA=="), 50+(scale<<8)+(1<<16));
|
this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAB8AAAAAAAfAAAAAAAPwAAAAAAB8AAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAA4AAAAAAB+AAAAAAD/gAAAAAD/4AAAAAH/4AAAAAP/wAAAAAP/gAAAAAf/gAAAAAf/AAAAAA/+AAAAAB/+AAAAAB/8AAAAAD/4AAAAAH/4AAAAAD/wAAAAAA/wAAAAAAPgAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///wAAAB////gAAA////8AAA/////gAAP////8AAH8AAA/gAB8AAAD4AA+AAAAfAAPAAAADwADwAAAA8AA8AAAAPAAPAAAADwADwAAAA8AA8AAAAPAAPgAAAHwAB8AAAD4AAfwAAD+AAD/////AAA/////wAAH////4AAAf///4AAAB///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAPgAAAAAADwAAAAAAB8AAAAAAAfAAAAAAAHgAAAAAAD4AAAAAAA+AAAAAAAPAAAAAAAH/////wAB/////8AA//////AAP/////wAD/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAAAfgAADwAAP4AAB8AAH+AAA/AAD/gAAfwAB/AAAf8AAfAAAP/AAPgAAH7wAD4AAD88AA8AAB+PAAPAAA/DwADwAAfg8AA8AAPwPAAPAAH4DwADwAH8A8AA+AD+APAAPwB/ADwAB/D/gA8AAf//gAPAAD//wADwAAf/wAA8AAD/4AAPAAAHwAADwAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAADgAAAHwAA+AAAD8AAP4AAB/AAD/AAA/wAA/wAAf4AAD+AAHwAAAPgAD4APAB8AA+ADwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA8AH4APAAPgD+AHwAB8B/wD4AAf7/+B+AAD//v//AAA//x//wAAD/4P/4AAAf8B/4AAAAYAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAHwAAAAAAH8AAAAAAD/AAAAAAD/wAAAAAD/8AAAAAB/vAAAAAB/jwAAAAA/g8AAAAA/wPAAAAAfwDwAAAAf4A8AAAAf4APAAAAP8ADwAAAP8AA8AAAH8AAPAAAD/////8AA//////AAP/////wAD/////8AA//////AAAAAAPAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAB/APwAAH//wD+AAD//8A/wAA///AH+AAP//wAPgAD/B4AB8AA8A+AAfAAPAPAADwADwDwAA8AA8A8AAPAAPAPAADwADwD4AA8AA8A+AAPAAPAPwAHwADwD8AD4AA8AfwD+AAPAH///AADwA///wAA8AH//4AAPAAf/4AAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAD//+AAAAD///4AAAD////AAAB////4AAA/78D/AAAfw8AH4AAPweAA+AAD4PgAHwAB8DwAA8AAfA8AAPAAHgPAADwAD4DwAA8AA+A8AAPAAPAPgAHwADwD4AB8AA8AfgA+AAPAH+B/gAAAA///wAAAAH//4AAAAA//8AAAAAH/8AAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAA8AAAABAAPAAAABwADwAAAB8AA8AAAB/AAPAAAB/wADwAAD/8AA8AAD/8AAPAAD/4AADwAD/4AAA8AD/4AAAPAH/wAAADwH/wAAAA8H/wAAAAPH/wAAAAD3/gAAAAA//gAAAAAP/gAAAAAD/gAAAAAA/AAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwA/4AAAH/Af/AAAH/8P/4AAD//n//AAA//7//4AAfx/+A+AAHwD+AHwAD4AfgB8AA8AHwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA+AH4AfAAHwD+AHwAB/D/4D4AAP/+/n+AAD//n//AAAf/w//gAAB/wH/wAAAHwA/4AAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAD/8AAAAAD//wAAAAB//+AAAAA///wAAAAf4H+APAAH4AfgDwAD8AB8A8AA+AAfAPAAPAADwDwADwAA8B8AA8AAPAfAAPAADwHgADwAA8D4AA+AAeB+AAHwAHg/AAB+ADwfgAAP8D4/4AAD////8AAAf///8AAAB///+AAAAP//+AAAAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAOAAAB8AAHwAAAfgAD8AAAH4AA/AAAB8AAHwAAAOAAA4AAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("DRUcHBwcHBwcHBwcDA=="), 50+(scale<<8)+(1<<16));
|
||||||
|
@ -13,12 +10,16 @@ Graphics.prototype.setFontRobotoRegular21 = function(scale) {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let clock_info = require("clock_info");
|
||||||
|
let locale = require("locale");
|
||||||
|
let storage = require("Storage");
|
||||||
|
|
||||||
let SETTINGS_FILE = "circlesclock.json";
|
let SETTINGS_FILE = "circlesclock.json";
|
||||||
let settings = Object.assign(
|
let settings = Object.assign(
|
||||||
storage.readJSON("circlesclock.default.json", true) || {},
|
storage.readJSON("circlesclock.default.json", true) || {},
|
||||||
storage.readJSON(SETTINGS_FILE, true) || {}
|
storage.readJSON(SETTINGS_FILE, true) || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
//TODO deprecate this (and perhaps use in the clkinfo module)
|
//TODO deprecate this (and perhaps use in the clkinfo module)
|
||||||
// Load step goal from health app and pedometer widget as fallback
|
// Load step goal from health app and pedometer widget as fallback
|
||||||
if (settings.stepGoal == undefined) {
|
if (settings.stepGoal == undefined) {
|
||||||
|
@ -31,20 +32,10 @@ if (settings.stepGoal == undefined) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let timerHrm; //TODO deprecate this
|
|
||||||
let drawTimeout;
|
let drawTimeout;
|
||||||
|
const showWidgets = settings.showWidgets || false;
|
||||||
/*
|
const circleCount = settings.circleCount || 3;
|
||||||
* Read location from myLocation app
|
const showBigWeather = settings.showBigWeather || false;
|
||||||
*/
|
|
||||||
function getLocation() {
|
|
||||||
return storage.readJSON("mylocation.json", 1) || undefined;
|
|
||||||
}
|
|
||||||
let location = getLocation();
|
|
||||||
|
|
||||||
let showWidgets = settings.showWidgets || false;
|
|
||||||
let circleCount = settings.circleCount || 3;
|
|
||||||
let showBigWeather = settings.showBigWeather || false;
|
|
||||||
|
|
||||||
let hrtValue; //TODO deprecate this
|
let hrtValue; //TODO deprecate this
|
||||||
let now = Math.round(new Date().getTime() / 1000);
|
let now = Math.round(new Date().getTime() / 1000);
|
||||||
|
@ -52,11 +43,6 @@ let now = Math.round(new Date().getTime() / 1000);
|
||||||
// layout values:
|
// layout values:
|
||||||
let colorFg = g.theme.dark ? '#fff' : '#000';
|
let colorFg = g.theme.dark ? '#fff' : '#000';
|
||||||
let colorBg = g.theme.dark ? '#000' : '#fff';
|
let colorBg = g.theme.dark ? '#000' : '#fff';
|
||||||
let colorGrey = '#808080';
|
|
||||||
let colorRed = '#ff0000';
|
|
||||||
let colorGreen = '#008000';
|
|
||||||
let colorBlue = '#0000ff';
|
|
||||||
let colorYellow = '#ffff00';
|
|
||||||
let widgetOffset = showWidgets ? 24 : 0;
|
let widgetOffset = showWidgets ? 24 : 0;
|
||||||
let dowOffset = circleCount == 3 ? 20 : 22; // dow offset relative to date
|
let dowOffset = circleCount == 3 ? 20 : 22; // dow offset relative to date
|
||||||
let h = g.getHeight() - widgetOffset;
|
let h = g.getHeight() - widgetOffset;
|
||||||
|
@ -64,7 +50,7 @@ let w = g.getWidth();
|
||||||
let hOffset = (circleCount == 3 ? 34 : 30) - widgetOffset;
|
let hOffset = (circleCount == 3 ? 34 : 30) - widgetOffset;
|
||||||
let h1 = Math.round(1 * h / 5 - hOffset);
|
let h1 = Math.round(1 * h / 5 - hOffset);
|
||||||
let h2 = Math.round(3 * h / 5 - hOffset);
|
let h2 = Math.round(3 * h / 5 - hOffset);
|
||||||
let h3 = Math.round(8 * h / 8 - hOffset - 3); // circle y position
|
let h3 = Math.round(8 * h / 8 - hOffset - 3); // circle middle y position
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* circle x positions
|
* circle x positions
|
||||||
|
@ -87,58 +73,17 @@ let circlePosX = [
|
||||||
];
|
];
|
||||||
|
|
||||||
let radiusOuter = circleCount == 3 ? 25 : 20;
|
let radiusOuter = circleCount == 3 ? 25 : 20;
|
||||||
|
let radiusBorder = radiusOuter+3; // absolute border of circles
|
||||||
let radiusInner = circleCount == 3 ? 20 : 15;
|
let radiusInner = circleCount == 3 ? 20 : 15;
|
||||||
let circleFontSmall = circleCount == 3 ? "Vector:14" : "Vector:10";
|
let circleFontSmall = circleCount == 3 ? "Vector:14" : "Vector:10";
|
||||||
let circleFont = circleCount == 3 ? "Vector:15" : "Vector:11";
|
let circleFont = circleCount == 3 ? "Vector:15" : "Vector:11";
|
||||||
let circleFontBig = circleCount == 3 ? "Vector:16" : "Vector:12";
|
let circleFontBig = circleCount == 3 ? "Vector:16" : "Vector:12";
|
||||||
let iconOffset = circleCount == 3 ? 6 : 8;
|
let iconOffset = circleCount == 3 ? 6 : 8;
|
||||||
let defaultCircleTypes = ["Bangle/Steps", "Bangle/HRM", "Bangle/Battery", "weather"];
|
|
||||||
|
|
||||||
let circleInfoNum = [
|
|
||||||
0, // circle1
|
|
||||||
0, // circle2
|
|
||||||
0, // circle3
|
|
||||||
0, // circle4
|
|
||||||
];
|
|
||||||
let circleItemNum = [
|
|
||||||
0, // circle1
|
|
||||||
1, // circle2
|
|
||||||
2, // circle3
|
|
||||||
3, // circle4
|
|
||||||
];
|
|
||||||
let weatherCircleNum = 0;
|
|
||||||
let weatherCircleDataNum = 0;
|
|
||||||
let weatherCircleCondNum = 0;
|
|
||||||
let weatherCircleTempNum = 0;
|
|
||||||
|
|
||||||
function hideWidgets() {
|
let draw = function() {
|
||||||
/*
|
let R = Bangle.appRect;
|
||||||
* we are not drawing the widgets as we are taking over the whole screen
|
g.reset().clearRect(R.x,R.y, R.x2, h3-(radiusBorder+1));
|
||||||
* so we will blank out the draw() functions of each widget and change the
|
|
||||||
* area to the top bar doesn't get cleared.
|
|
||||||
*/
|
|
||||||
if (WIDGETS && typeof WIDGETS === "object") {
|
|
||||||
for (let wd of WIDGETS) {
|
|
||||||
wd.draw = () => {};
|
|
||||||
wd.area = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function draw() {
|
|
||||||
g.clear(true);
|
|
||||||
let widgetUtils;
|
|
||||||
|
|
||||||
try {
|
|
||||||
widgetUtils = require("widget_utils");
|
|
||||||
} catch (e) {
|
|
||||||
}
|
|
||||||
if (!showWidgets) {
|
|
||||||
if (widgetUtils) widgetUtils.hide(); else hideWidgets();
|
|
||||||
} else {
|
|
||||||
if (widgetUtils) widgetUtils.show();
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
}
|
|
||||||
|
|
||||||
g.setColor(colorBg);
|
g.setColor(colorBg);
|
||||||
g.fillRect(0, widgetOffset, w, h2 + 22);
|
g.fillRect(0, widgetOffset, w, h2 + 22);
|
||||||
|
@ -180,118 +125,16 @@ function draw() {
|
||||||
if (icon) g.drawImage(icon, w - 48, h1, {scale:0.75});
|
if (icon) g.drawImage(icon, w - 48, h1, {scale:0.75});
|
||||||
}
|
}
|
||||||
|
|
||||||
drawCircle(1);
|
|
||||||
drawCircle(2);
|
|
||||||
drawCircle(3);
|
|
||||||
if (circleCount >= 4) drawCircle(4);
|
|
||||||
|
|
||||||
queueDraw();
|
queueDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawCircle(index) {
|
let getCircleColor = function(index) {
|
||||||
let type = settings['circle' + index];
|
let color = settings["circle" + index + "color"];
|
||||||
if (!type) type = defaultCircleTypes[index - 1];
|
|
||||||
let w = getCircleXPosition(type);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "weather":
|
|
||||||
drawWeather(w);
|
|
||||||
break;
|
|
||||||
case "sunprogress":
|
|
||||||
case "sunProgress":
|
|
||||||
drawSunProgress(w);
|
|
||||||
break;
|
|
||||||
//TODO those are going to be deprecated, keep for backwards compatibility for now
|
|
||||||
//ideally all data should come from some clkinfo
|
|
||||||
case "steps":
|
|
||||||
drawSteps(w);
|
|
||||||
break;
|
|
||||||
case "stepsDist":
|
|
||||||
drawStepsDistance(w);
|
|
||||||
break;
|
|
||||||
case "hr":
|
|
||||||
drawHeartRate(w);
|
|
||||||
break;
|
|
||||||
case "battery":
|
|
||||||
drawBattery(w);
|
|
||||||
break;
|
|
||||||
case "temperature":
|
|
||||||
drawTemperature(w);
|
|
||||||
break;
|
|
||||||
case "pressure":
|
|
||||||
drawPressure(w);
|
|
||||||
break;
|
|
||||||
case "altitude":
|
|
||||||
drawAltitude(w);
|
|
||||||
break;
|
|
||||||
//end deprecated
|
|
||||||
case "empty":
|
|
||||||
// we draw nothing here
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
drawClkInfo(index, w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// serves as cache for quicker lookup of circle positions
|
|
||||||
let circlePositionsCache = [];
|
|
||||||
/*
|
|
||||||
* Looks in the following order if a circle with the given type is somewhere visible/configured
|
|
||||||
* 1. circlePositionsCache
|
|
||||||
* 2. settings
|
|
||||||
* 3. defaultCircleTypes
|
|
||||||
*
|
|
||||||
* In case 2 and 3 the circlePositionsCache will be updated
|
|
||||||
*/
|
|
||||||
function getCirclePosition(type) {
|
|
||||||
if (circlePositionsCache[type] >= 0) {
|
|
||||||
return circlePositionsCache[type];
|
|
||||||
}
|
|
||||||
for (let i = 1; i <= circleCount; i++) {
|
|
||||||
let setting = settings['circle' + i];
|
|
||||||
if (setting == type) {
|
|
||||||
circlePositionsCache[type] = i - 1;
|
|
||||||
return i - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < defaultCircleTypes.length; i++) {
|
|
||||||
if (type == defaultCircleTypes[i] && (!settings || settings['circle' + (i + 1)] == undefined)) {
|
|
||||||
circlePositionsCache[type] = i;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCircleXPosition(type) {
|
|
||||||
let circlePos = getCirclePosition(type);
|
|
||||||
if (circlePos != undefined) {
|
|
||||||
return circlePosX[circlePos];
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCircleEnabled(type) {
|
|
||||||
return getCirclePosition(type) != undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCircleColor(type) {
|
|
||||||
let pos = getCirclePosition(type);
|
|
||||||
let color = settings["circle" + (pos + 1) + "color"];
|
|
||||||
if (color && color != "") return color;
|
if (color && color != "") return color;
|
||||||
|
return g.theme.fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCircleIconColor(type, color, percent) {
|
let getGradientColor = function(color, percent) {
|
||||||
let pos = getCirclePosition(type);
|
|
||||||
let colorizeIcon = settings["circle" + (pos + 1) + "colorizeIcon"] == true;
|
|
||||||
if (colorizeIcon) {
|
|
||||||
return getGradientColor(color, percent);
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getGradientColor(color, percent) {
|
|
||||||
if (isNaN(percent)) percent = 0;
|
if (isNaN(percent)) percent = 0;
|
||||||
if (percent > 1) percent = 1;
|
if (percent > 1) percent = 1;
|
||||||
let colorList = [
|
let colorList = [
|
||||||
|
@ -311,423 +154,16 @@ function getGradientColor(color, percent) {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getImage(graphic, color) {
|
let getCircleIconColor = function(index, color, percent) {
|
||||||
if (!color || color == "") {
|
let colorizeIcon = settings["circle" + index + "colorizeIcon"] == true;
|
||||||
return graphic;
|
if (colorizeIcon) {
|
||||||
|
return getGradientColor(color, percent);
|
||||||
} else {
|
} else {
|
||||||
return {
|
return g.theme.fg;
|
||||||
width: 16,
|
|
||||||
height: 16,
|
|
||||||
bpp: 1,
|
|
||||||
transparent: 0,
|
|
||||||
buffer: E.toArrayBuffer(graphic),
|
|
||||||
palette: new Uint16Array([colorBg, g.toColor(color)])
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawWeather(w) {
|
let drawEmpty = function(img, w, color) {
|
||||||
if (!w) w = getCircleXPosition("weather");
|
|
||||||
let weatherInfo = menu[weatherCircleNum];
|
|
||||||
let weatherCond = weatherCircleCondNum >= 0? weatherInfo.items[weatherCircleCondNum]: undefined;
|
|
||||||
let weatherData = weatherCircleDataNum >= 0? weatherInfo.items[weatherCircleDataNum]: undefined;
|
|
||||||
let weatherTemp = weatherCircleTempNum >= 0? weatherInfo.items[weatherCircleTempNum]: undefined;
|
|
||||||
let color = getCircleColor("weather");
|
|
||||||
let percent = 0;
|
|
||||||
let data = settings.weatherCircleData;
|
|
||||||
let tempString = "?", icon = undefined;
|
|
||||||
let scale = 16/24; //our icons are 16x16 while clkinfo's are 24x24
|
|
||||||
|
|
||||||
if(weatherCond) {
|
|
||||||
weatherCond.show()
|
|
||||||
weatherCond.hide()
|
|
||||||
let data = weatherCond.get()
|
|
||||||
if(settings.legacyWeatherIcons) { //may disappear in future
|
|
||||||
icon = getWeatherIconByCode(data.v);
|
|
||||||
scale = 1;
|
|
||||||
} else
|
|
||||||
icon = data.img;
|
|
||||||
}
|
|
||||||
if(weatherTemp) {
|
|
||||||
weatherTemp.show()
|
|
||||||
weatherTemp.hide()
|
|
||||||
tempString = weatherTemp.get().text;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
if(weatherData) {
|
|
||||||
weatherData.show();
|
|
||||||
weatherData.hide();
|
|
||||||
let data = weatherData.get();
|
|
||||||
if(weatherData.hasRange) percent = (data.v-data.min) / (data.max-data.min);
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
writeCircleText(w, tempString);
|
|
||||||
|
|
||||||
if(icon) {
|
|
||||||
g.setColor(getCircleIconColor("weather", color, percent))
|
|
||||||
.drawImage(icon, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: scale});
|
|
||||||
} else {
|
|
||||||
g.drawString("?", w, h3 + radiusOuter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function drawWeatherOld(w) {
|
|
||||||
if (!w) w = getCircleXPosition("weather");
|
|
||||||
let weather = getWeather();
|
|
||||||
let tempString = weather ? locale.temp(weather.temp - 273.15) : undefined;
|
|
||||||
let code = weather ? weather.code : -1;
|
|
||||||
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
let color = getCircleColor("weather");
|
|
||||||
let percent;
|
|
||||||
let data = settings.weatherCircleData;
|
|
||||||
switch (data) {
|
|
||||||
case "humidity":
|
|
||||||
let humidity = weather ? weather.hum : undefined;
|
|
||||||
if (humidity >= 0) {
|
|
||||||
percent = humidity / 100;
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "wind":
|
|
||||||
if (weather) {
|
|
||||||
let wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
|
|
||||||
if (wind[1] >= 0) {
|
|
||||||
if (wind[2] == "kmh") {
|
|
||||||
wind[1] = windAsBeaufort(wind[1]);
|
|
||||||
}
|
|
||||||
// wind goes from 0 to 12 (see https://en.wikipedia.org/wiki/Beaufort_scale)
|
|
||||||
percent = wind[1] / 12;
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "empty":
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
writeCircleText(w, tempString ? tempString : "?");
|
|
||||||
|
|
||||||
if (code > 0) {
|
|
||||||
let icon = getWeatherIconByCode(code);
|
|
||||||
if (icon) g.drawImage(getImage(icon, getCircleIconColor("weather", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
|
||||||
} else {
|
|
||||||
g.drawString("?", w, h3 + radiusOuter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSunProgress(w) {
|
|
||||||
if (!w) w = getCircleXPosition("sunprogress");
|
|
||||||
let percent = getSunProgress();
|
|
||||||
|
|
||||||
// sunset icons:
|
|
||||||
let sunSetDown = atob("EBCBAAAAAAABgAAAAAATyAZoBCB//gAAAAAGYAPAAYAAAAAA");
|
|
||||||
let sunSetUp = atob("EBCBAAAAAAABgAAAAAATyAZoBCB//gAAAAABgAPABmAAAAAA");
|
|
||||||
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
let color = getCircleColor("sunprogress");
|
|
||||||
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
let icon = sunSetDown;
|
|
||||||
let text = "?";
|
|
||||||
let times = getSunData();
|
|
||||||
if (times != undefined) {
|
|
||||||
let sunRise = Math.round(times.sunrise.getTime() / 1000);
|
|
||||||
let sunSet = Math.round(times.sunset.getTime() / 1000);
|
|
||||||
if (!isDay()) {
|
|
||||||
// night
|
|
||||||
if (now > sunRise) {
|
|
||||||
// after sunRise
|
|
||||||
let upcomingSunRise = sunRise + 60 * 60 * 24;
|
|
||||||
text = formatSeconds(upcomingSunRise - now);
|
|
||||||
} else {
|
|
||||||
text = formatSeconds(sunRise - now);
|
|
||||||
}
|
|
||||||
icon = sunSetUp;
|
|
||||||
} else {
|
|
||||||
// day, approx sunrise tomorrow:
|
|
||||||
text = formatSeconds(sunSet - now);
|
|
||||||
icon = sunSetDown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCircleText(w, text);
|
|
||||||
|
|
||||||
g.drawImage(getImage(icon, getCircleIconColor("sunprogress", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Deprecated but nice as references for clkinfo
|
|
||||||
*/
|
|
||||||
|
|
||||||
function drawSteps(w) {
|
|
||||||
if (!w) w = getCircleXPosition("steps");
|
|
||||||
let steps = getSteps();
|
|
||||||
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
let color = getCircleColor("steps");
|
|
||||||
|
|
||||||
let percent;
|
|
||||||
let stepGoal = settings.stepGoal;
|
|
||||||
if (stepGoal > 0) {
|
|
||||||
percent = steps / stepGoal;
|
|
||||||
if (stepGoal < steps) percent = 1;
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
writeCircleText(w, shortValue(steps));
|
|
||||||
|
|
||||||
g.drawImage(getImage(atob("EBCBAAAACAAcAB4AHgAeABwwADgGeAZ4AHgAMAAAAHAAIAAA"), getCircleIconColor("steps", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawStepsDistance(w) {
|
|
||||||
if (!w) w = getCircleXPosition("stepsDistance");
|
|
||||||
let steps = getSteps();
|
|
||||||
let stepDistance = settings.stepLength;
|
|
||||||
let stepsDistance = Math.round(steps * stepDistance);
|
|
||||||
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
let color = getCircleColor("stepsDistance");
|
|
||||||
|
|
||||||
let percent;
|
|
||||||
let stepDistanceGoal = settings.stepDistanceGoal;
|
|
||||||
if (stepDistanceGoal > 0) {
|
|
||||||
percent = stepsDistance / stepDistanceGoal;
|
|
||||||
if (stepDistanceGoal < stepsDistance) percent = 1;
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
writeCircleText(w, shortValue(stepsDistance));
|
|
||||||
|
|
||||||
g.drawImage(getImage(atob("EBCBAAAACAAcAB4AHgAeABwwADgGeAZ4AHgAMAAAAHAAIAAA"), getCircleIconColor("stepsDistance", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawHeartRate(w) {
|
|
||||||
if (!w) w = getCircleXPosition("hr");
|
|
||||||
|
|
||||||
let heartIcon = atob("EBCBAAAAAAAeeD/8P/x//n/+P/w//B/4D/AH4APAAYAAAAAA");
|
|
||||||
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
let color = getCircleColor("hr");
|
|
||||||
|
|
||||||
let percent;
|
|
||||||
if (hrtValue != undefined) {
|
|
||||||
let minHR = settings.minHR;
|
|
||||||
let maxHR = settings.maxHR;
|
|
||||||
percent = (hrtValue - minHR) / (maxHR - minHR);
|
|
||||||
if (isNaN(percent)) percent = 0;
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
writeCircleText(w, hrtValue != undefined ? hrtValue : "-");
|
|
||||||
|
|
||||||
g.drawImage(getImage(heartIcon, getCircleIconColor("hr", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawBattery(w) {
|
|
||||||
if (!w) w = getCircleXPosition("battery");
|
|
||||||
let battery = E.getBattery();
|
|
||||||
|
|
||||||
let powerIcon = atob("EBCBAAAAA8ADwA/wD/AP8A/wD/AP8A/wD/AP8A/wD/AH4AAA");
|
|
||||||
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
let color = getCircleColor("battery");
|
|
||||||
|
|
||||||
let percent;
|
|
||||||
if (battery > 0) {
|
|
||||||
percent = battery / 100;
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
if (Bangle.isCharging()) {
|
|
||||||
color = colorGreen;
|
|
||||||
} else {
|
|
||||||
if (settings.batteryWarn != undefined && battery <= settings.batteryWarn) {
|
|
||||||
color = colorRed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeCircleText(w, battery + '%');
|
|
||||||
|
|
||||||
g.drawImage(getImage(powerIcon, getCircleIconColor("battery", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
|
||||||
}
|
|
||||||
function drawTemperature(w) {
|
|
||||||
if (!w) w = getCircleXPosition("temperature");
|
|
||||||
|
|
||||||
getPressureValue("temperature").then((temperature) => {
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
let color = getCircleColor("temperature");
|
|
||||||
|
|
||||||
let percent;
|
|
||||||
if (temperature) {
|
|
||||||
let min = -40;
|
|
||||||
let max = 85;
|
|
||||||
percent = (temperature - min) / (max - min);
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
if (temperature)
|
|
||||||
writeCircleText(w, locale.temp(temperature));
|
|
||||||
|
|
||||||
g.drawImage(getImage(atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA"), getCircleIconColor("temperature", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawPressure(w) {
|
|
||||||
if (!w) w = getCircleXPosition("pressure");
|
|
||||||
|
|
||||||
getPressureValue("pressure").then((pressure) => {
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
let color = getCircleColor("pressure");
|
|
||||||
|
|
||||||
let percent;
|
|
||||||
if (pressure && pressure > 0) {
|
|
||||||
let minPressure = 950;
|
|
||||||
let maxPressure = 1050;
|
|
||||||
percent = (pressure - minPressure) / (maxPressure - minPressure);
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
if (pressure)
|
|
||||||
writeCircleText(w, Math.round(pressure));
|
|
||||||
|
|
||||||
g.drawImage(getImage(atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA"), getCircleIconColor("pressure", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawAltitude(w) {
|
|
||||||
if (!w) w = getCircleXPosition("altitude");
|
|
||||||
|
|
||||||
getPressureValue("altitude").then((altitude) => {
|
|
||||||
drawCircleBackground(w);
|
|
||||||
|
|
||||||
let color = getCircleColor("altitude");
|
|
||||||
|
|
||||||
let percent;
|
|
||||||
if (altitude) {
|
|
||||||
let min = 0;
|
|
||||||
let max = 10000;
|
|
||||||
percent = (altitude - min) / (max - min);
|
|
||||||
drawGauge(w, h3, percent, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawInnerCircleAndTriangle(w);
|
|
||||||
|
|
||||||
if (altitude)
|
|
||||||
writeCircleText(w, locale.distance(Math.round(altitude)));
|
|
||||||
|
|
||||||
g.drawImage(getImage(atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA"), getCircleIconColor("altitude", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (Bangle.getHealthStatus) {
|
|
||||||
return Bangle.getHealthStatus("day").steps;
|
|
||||||
}
|
|
||||||
if (WIDGETS && WIDGETS.wpedom !== undefined) {
|
|
||||||
return WIDGETS.wpedom.getSteps();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPressureValue(type) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
if (Bangle.getPressure) {
|
|
||||||
if (!pressureLocked) {
|
|
||||||
pressureLocked = true;
|
|
||||||
if (pressureCache && pressureCache[type]) {
|
|
||||||
resolve(pressureCache[type]);
|
|
||||||
}
|
|
||||||
Bangle.getPressure().then(function(d) {
|
|
||||||
pressureLocked = false;
|
|
||||||
if (d) {
|
|
||||||
pressureCache = d;
|
|
||||||
if (d[type]) {
|
|
||||||
resolve(d[type]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(() => {});
|
|
||||||
} else {
|
|
||||||
if (pressureCache && pressureCache[type]) {
|
|
||||||
resolve(pressureCache[type]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* end deprecated
|
|
||||||
*/
|
|
||||||
|
|
||||||
var menu = null;
|
|
||||||
function reloadMenu() {
|
|
||||||
menu = clock_info.load();
|
|
||||||
for(var i=1; i<5; i++)
|
|
||||||
if(settings['circle'+i].includes("/")) {
|
|
||||||
let parts = settings['circle'+i].split("/");
|
|
||||||
let infoName = parts[0], itemName = parts[1];
|
|
||||||
let infoNum = menu.findIndex(e=>e.name==infoName);
|
|
||||||
let itemNum = 0; //get first if dynamic
|
|
||||||
if(!menu[infoNum].dynamic)
|
|
||||||
itemNum = menu[infoNum].items.findIndex(it=>it.name==itemName);
|
|
||||||
circleInfoNum[i-1] = infoNum;
|
|
||||||
circleItemNum[i-1] = itemNum;
|
|
||||||
} else if(settings['circle'+i] == "weather") {
|
|
||||||
weatherCircleNum = menu.findIndex(e=>e.name.toLowerCase() == "weather");
|
|
||||||
weatherCircleDataNum = menu[weatherCircleNum].items.findIndex(it=>it.name==settings.weatherCircleData);
|
|
||||||
weatherCircleCondNum = menu[weatherCircleNum].items.findIndex(it=>it.name=="condition");
|
|
||||||
weatherCircleTempNum = menu[weatherCircleNum].items.findIndex(it=>it.name=="temperature");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//reload periodically for changes?
|
|
||||||
reloadMenu();
|
|
||||||
|
|
||||||
function drawEmpty(img, w, color) {
|
|
||||||
drawGauge(w, h3, 0, color);
|
drawGauge(w, h3, 0, color);
|
||||||
drawInnerCircleAndTriangle(w);
|
drawInnerCircleAndTriangle(w);
|
||||||
writeCircleText(w, "?");
|
writeCircleText(w, "?");
|
||||||
|
@ -736,66 +172,45 @@ function drawEmpty(img, w, color) {
|
||||||
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
|
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawClkInfo(index, w) {
|
let drawCircle = function(index, item, data) {
|
||||||
var info = menu[circleInfoNum[index-1]];
|
var w = circlePosX[index-1];
|
||||||
var type = settings['circle'+index];
|
|
||||||
if (!w) w = getCircleXPosition(type);
|
|
||||||
drawCircleBackground(w);
|
drawCircleBackground(w);
|
||||||
const color = getCircleColor(type);
|
const color = getCircleColor(index);
|
||||||
var item = info.items[circleItemNum[index-1]];
|
//drawEmpty(info? info.img : null, w, color);
|
||||||
if(!info || !item) {
|
|
||||||
drawEmpty(info? info.img : null, w, color);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
item.show();
|
|
||||||
item.hide();
|
|
||||||
var data=item.get();
|
|
||||||
var img = data.img;
|
var img = data.img;
|
||||||
var percent = 1; //fill up if no range
|
var percent = 1; //fill up if no range
|
||||||
var txt = data.text;
|
var txt = ""+data.text;
|
||||||
if(!img) img = info.img;
|
if (txt.endsWith(" bpm")) txt=txt.slice(0,-4); // hack for heart rate - remove the 'bpm' text
|
||||||
if(item.hasRange) percent = (data.v-data.min) / (data.max-data.min);
|
if(item.hasRange) percent = (data.v-data.min) / (data.max-data.min);
|
||||||
if(data.short) txt = data.short;
|
if(data.short) txt = data.short;
|
||||||
drawGauge(w, h3, percent, color);
|
drawGauge(w, h3, percent, color);
|
||||||
drawInnerCircleAndTriangle(w);
|
drawInnerCircleAndTriangle(w);
|
||||||
writeCircleText(w, txt);
|
writeCircleText(w, txt);
|
||||||
g.setColor(getCircleIconColor(type, color, percent))
|
g.setColor(getCircleIconColor(index, color, percent))
|
||||||
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
|
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* wind goes from 0 to 12 (see https://en.wikipedia.org/wiki/Beaufort_scale)
|
|
||||||
*/
|
|
||||||
function windAsBeaufort(windInKmh) {
|
|
||||||
let beaufort = [2, 6, 12, 20, 29, 39, 50, 62, 75, 89, 103, 118];
|
|
||||||
let l = 0;
|
|
||||||
while (l < beaufort.length && beaufort[l] < windInKmh) {
|
|
||||||
l++;
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Choose weather icon to display based on weather conditition code
|
* Choose weather icon to display based on weather conditition code
|
||||||
* https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
|
* https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
|
||||||
*/
|
*/
|
||||||
function getWeatherIconByCode(code, big) {
|
let getWeatherIconByCode = function(code, big) {
|
||||||
let codeGroup = Math.round(code / 100);
|
let codeGroup = Math.round(code / 100);
|
||||||
if (big == undefined) big = false;
|
if (big == undefined) big = false;
|
||||||
|
|
||||||
// weather icons:
|
// weather icons:
|
||||||
let weatherCloudy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAAAAAfg+AAAAAAAAfHwAAAAAAAA+eAAAAAAAAB54AAAAAAAAHvAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD3gAAAAAAAAeeAAAAAAAAB58AAAAAAAAPj4AAAAAAAB8H4AAAAAAAfgP////////8Af////////gA////////8AAf//////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAAAAAAfgD/Af8H/4//7///////9//z/+AAAAAAAA");
|
let weatherCloudy = big ? atob("QECBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAf/4AAAAAAAD//4AAAAAAA///wAAAAAAH+B/gAAAAAA/AA/AAAAAAH4AB+AAAAAAfAAD8AAAAAD4AAH/wAAAAfAAAP/wAAAB4AAAf/gAAAHgAAB//AAAB+AAACB+AAAfwAAAAB8AAH/AAAAAD4AA/8AAAAAHgAD8AAAAAAeAAfAAAAAAA8AB4AAAAAADwAPgAAAAAAPAA8AAAAAAA8APwAAAAAADwB/AAAAAAAPAP8AAAAAAB8B+AAAAAAAH4PgAAAAAAAHx8AAAAAAAAPngAAAAAAAAeeAAAAAAAAB7wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA94AAAAAAAAHngAAAAAAAAefAAAAAAAAD4+AAAAAAAAfB+AAAAAAAH4D/////////AH////////4AP////////AAH///////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : atob("EBCBAAAAAAAAAAfgD/Af8H/4//7///////9//z/+AAAAAAAA");
|
||||||
let weatherSunny = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAwADwADAAAAHgAPAAeAAAAfAA8AD4AAAA+ADwAfAAAAB8APAD4AAAAD4B+AfAAAAAHw//D4AAAAAPv//fAAAAAAf///4AAAAAA/4H/AAAAAAB+AH4AAAAAAPgAHwAAAAAA8AAPAAAAAAHwAA+AAAAAAeAAB4AAAAAB4AAHgAAAAAPAAAPAAAA//8AAA//8AD//wAAD//wAP//AAAP//AA//8AAA//8AAADwAADwAAAAAHgAAeAAAAAAeAAB4AAAAAB8AAPgAAAAADwAA8AAAAAAPgAHwAAAAAAfgB+AAAAAAD/gf8AAAAAAf///4AAAAAD7//3wAAAAAfD/8PgAAAAD4B+AfAAAAAfADwA+AAAAD4APAB8AAAAfAA8AD4AAAB4ADwAHgAAADAAPAAMAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAQCBAIA8AH4A/wb/YP8A/gB+ARiBAIAYABgAAA");
|
let weatherSunny = big ? atob("QECBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAMAA8AAwAAAB4ADwAHgAAAHwAPAA+AAAAPgA8AHwAAAAfADwA+AAAAA+AfgHwAAAAB8P/w+AAAAAD7//3wAAAAAH///+AAAAAAP+B/wAAAAAAfgB+AAAAAAD4AB8AAAAAAPAADwAAAAAB8AAPgAAAAAHgAAeAAAAAAeAAB4AAAAADwAADwAAAP//AAAP//AA//8AAA//8AD//wAAD//wAP//AAAP//AAAA8AAA8AAAAAB4AAHgAAAAAHgAAeAAAAAAfAAD4AAAAAA8AAPAAAAAAD4AB8AAAAAAH4AfgAAAAAA/4H/AAAAAAH///+AAAAAA+//98AAAAAHw//D4AAAAA+AfgHwAAAAHwA8APgAAAA+ADwAfAAAAHwAPAA+AAAAeAA8AB4AAAAwADwADAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : atob("EBCBAAAAAYAQCBAIA8AH4A/wb/YP8A/gB+ARiBAIAYABgAAA");
|
||||||
let weatherMoon = big ? atob("QEDBAP//wxgAAAYAAAAPAAAAD4AAAA8AAAAPwAAADwAAAA/gAAAPAAAAB/APAP/wAAAH+A8A//AAAAf4DwD/8AAAB/wPAP/wAAAH/gAADwAAAAe+AAAPAAAAB54AAA8AAAAHngAADwAAAAePAAAAAAAAD48OAAAAAAAPDw+AAAAAAB8PD8AAAAAAHg8P4AAAAAA+DwPwAAAAAHwfAfgAAAAB+D4A/AAA8AfwfgB/8AD//+D+AD/8AP//wfgAH/4Af/8B8AAf/wB//APgAAgfgD+AA8AAAAfAH8AHwAAAA+AP8B+AAAAB4Af//4AAAAHgA///gAAAAPAA//8AAAAA8AAf/wAAAADwAAAAAAAAAPAAAAAAAAAA8AcAAAAAAADwD+AAAAAAAfAfgAAAAAAB+D4AAAAAAAB8fAAAAAAAAD54AAAAAAAAHngAAAAAAAAe8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAPeAAAAAAAAB54AAAAAAAAHnwAAAAAAAA+PgAAAAAAAHwfgAAAAAAB+A/////////wB////////+AD////////wAB///////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAP8B/4P/w//D/8f/5//j/8P/w//B/4D/ABgAAA");
|
let weatherMoon = big ? atob("QECBAAAGAAAADwAAAA+AAAAPAAAAD8AAAA8AAAAP4AAADwAAAAfwDwD/8AAAB/gPAP/wAAAH+A8A//AAAAf8DwD/8AAAB/4AAA8AAAAHvgAADwAAAAeeAAAPAAAAB54AAA8AAAAHjwAAAAAAAA+PDgAAAAAADw8PgAAAAAAfDw/AAAAAAB4PD+AAAAAAPg8D8AAAAAB8HwH4AAAAAfg+APwAAPAH8H4Af/AA///g/gA//AD//8H4AB/+AH//AfAAH/8Af/wD4AAIH4A/gAPAAAAHwB/AB8AAAAPgD/AfgAAAAeAH//+AAAAB4AP//4AAAADwAP//AAAAAPAAH/8AAAAA8AAAAAAAAADwAAAAAAAAAPAHAAAAAAAA8A/gAAAAAAHwH4AAAAAAAfg+AAAAAAAAfHwAAAAAAAA+eAAAAAAAAB54AAAAAAAAHvAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD3gAAAAAAAAeeAAAAAAAAB58AAAAAAAAPj4AAAAAAAB8H4AAAAAAAfgP////////8Af////////gA////////8AAf//////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : atob("EBCBAAAAAYAP8B/4P/w//D/8f/5//j/8P/w//B/4D/ABgAAA");
|
||||||
let weatherPartlyCloudy = big ? atob("QEDBAP//wxgAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAABwAPAA4AAAAHgA8AHgAAAAfADwA+AAAAA+AfgHwAAAAB8P/w+AAAAAD7//3wAAAAAH///+BAAAAAP+B/wOAAAAAfgB+B8AAAAD4AD8H4AAAAPAA/wPwAAAB8AH+Af/AAAHgA/AA//AAAeAH4AB/+AADwAfAAH/8A//AD4AAIH4D/8AfAAAAHwP/wB4AAAAPg//AHgAAAAeAA8B+AAAAB4AB4fwAAAADwAHn/AAAAAPAAff8AAAAA8AA/8AAAAADwAD/AAAAAAPAEH4AAAAAA8A4PgAAAAAHwHgcAAAAAAfg+AwAAAAAAfHwAAAAAAAA+eAAAAAAAAB54AAAAAAAAHvAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD3gAAAAAAAAeeAAAAAAAAB58AAAAAAAAPj4AAAAAAAB8H4AAAAAAAfgP////////8Af////////gA////////8AAf//////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAAAYQAMAD8AIQBhoW+AOYBwwOBBgHGAGP/wf+AAA");
|
let weatherPartlyCloudy = big ? atob("QECBAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAcADwAOAAAAB4APAB4AAAAHwA8APgAAAAPgH4B8AAAAAfD/8PgAAAAA+//98AAAAAB////gQAAAAD/gf8DgAAAAH4AfgfAAAAA+AA/B+AAAADwAP8D8AAAAfAB/gH/wAAB4APwAP/wAAHgB+AAf/gAA8AHwAB//AP/wA+AACB+A//AHwAAAB8D/8AeAAAAD4P/wB4AAAAHgAPAfgAAAAeAAeH8AAAAA8AB5/wAAAADwAH3/AAAAAPAAP/AAAAAA8AA/wAAAAADwBB+AAAAAAPAOD4AAAAAB8B4HAAAAAAH4PgMAAAAAAHx8AAAAAAAAPngAAAAAAAAeeAAAAAAAAB7wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA94AAAAAAAAHngAAAAAAAAefAAAAAAAAD4+AAAAAAAAfB+AAAAAAAH4D/////////AH////////4AP////////AAH///////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : atob("EBCBAAAAAAAYQAMAD8AIQBhoW+AOYBwwOBBgHGAGP/wf+AAA");
|
||||||
let weatherRainy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4APAA8AAfg+AA8ADwAAfHwADwAPAAA+eAAPAA8AAB54AAAAAAAAHvAAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AADw8PDwAP8AAPDw8PAA/wAA8PDw8AD3gADw8PDwAeeAAAAAAAAB58AAAAAAAAPj4AAAAAAAB8H4AAAAAAAfgP/w8PDw8P8Af/Dw8PDw/gA/8PDw8PD8AAfw8PDw8OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAPAAAAAAAPAA8AAAAAAA8ADwAAAAAADwAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAH4AwwOBBgGEAOQAJBgjPOEkgGYAZgA8ABgAAA");
|
let weatherRainy = big ? atob("QECBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAf/4AAAAAAAD//4AAAAAAA///wAAAAAAH+B/gAAAAAA/AA/AAAAAAH4AB+AAAAAAfAAD8AAAAAD4AAH/wAAAAfAAAP/wAAAB4AAAf/gAAAHgAAB//AAAB+AAACB+AAAfwAAAAB8AAH/AAAAAD4AA/8AAAAAHgAD8AAAAAAeAAfAAAAAAA8AB4AAAAAADwAPgAAAAAAPAA8AAAAAAA8APwAAAAAADwB/AAAAAAAPAP8AAAAAAB8B+ADwAPAAH4PgAPAA8AAHx8AA8ADwAAPngADwAPAAAeeAAAAAAAAB7wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAA8PDw8AD/AADw8PDwAP8AAPDw8PAA94AA8PDw8AHngAAAAAAAAefAAAAAAAAD4+AAAAAAAAfB+AAAAAAAH4D/8PDw8PD/AH/w8PDw8P4AP/Dw8PDw/AAH8PDw8PDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8ADwAAAAAADwAPAAAAAAAPAA8AAAAAAA8ADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : atob("EBCBAAAAAYAH4AwwOBBgGEAOQAJBgjPOEkgGYAZgA8ABgAAA");
|
||||||
let weatherPartlyRainy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAA8AAfg+AAAADwAAfHwAAAAPAAA+eAAAAA8AAB54AAAADwAAHvAAAAAPAAAP8AAAAA8AAA/wAAAADwAAD/AAAA8PAAAP8AAADw8AAA/wAAAPDwAAD3gAAA8PAAAeeAAADw8AAB58AAAPDwAAPj4AAA8PAAB8H4AADw8AAfgP//8PDw//8Af//w8PD//gA///Dw8P/8AAf/8PDw/+AAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAEEAQAAeADMAYaFvoTmAMMDgQIBxhhiGGG9wDwAGA");
|
let weatherPartlyRainy = big ? atob("QECBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAf/4AAAAAAAD//4AAAAAAA///wAAAAAAH+B/gAAAAAA/AA/AAAAAAH4AB+AAAAAAfAAD8AAAAAD4AAH/wAAAAfAAAP/wAAAB4AAAf/gAAAHgAAB//AAAB+AAACB+AAAfwAAAAB8AAH/AAAAAD4AA/8AAAAAHgAD8AAAAAAeAAfAAAAAAA8AB4AAAAAADwAPgAAAAAAPAA8AAAAAAA8APwAAAAAADwB/AAAAAAAPAP8AAAAAAB8B+AAAAPAAH4PgAAAA8AAHx8AAAADwAAPngAAAAPAAAeeAAAAA8AAB7wAAAADwAAD/AAAAAPAAAP8AAAAA8AAA/wAAAPDwAAD/AAAA8PAAAP8AAADw8AAA94AAAPDwAAHngAAA8PAAAefAAADw8AAD4+AAAPDwAAfB+AAA8PAAH4D///Dw8P//AH//8PDw//4AP//w8PD//AAH//Dw8P/gAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : atob("EBCBAAAAEEAQAAeADMAYaFvoTmAMMDgQIBxhhiGGG9wDwAGA");
|
||||||
let weatherSnowy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAADwAfg+AAAAAPAAfHwAAAAA8AA+eAAAAADwAB54AA8AD/8AHvAADwAP/wAP8AAPAA//AA/wAA8AD/8AD/AA//AA8AAP8AD/8ADwAA/wAP/wAPAAD3gA//AA8AAeeAAPAAAAAB58AA8AAAAAPj4ADwAAAAB8H4APAAAAAfgP/wAA8A//8Af/AADwD//gA/8AAPAP/8AAfwAA8A/+AAAAAA//AAAAAAAAD/8AAAAAAAAP/wAAAAAAAA//AAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAAADwAGAEYg73C50BCAEIC50O9wRiAGAA8AAAAAA");
|
let weatherSnowy = big ? atob("QECBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAf/4AAAAAAAD//4AAAAAAA///wAAAAAAH+B/gAAAAAA/AA/AAAAAAH4AB+AAAAAAfAAD8AAAAAD4AAH/wAAAAfAAAP/wAAAB4AAAf/gAAAHgAAB//AAAB+AAACB+AAAfwAAAAB8AAH/AAAAAD4AA/8AAAAAHgAD8AAAAAAeAAfAAAAAAA8AB4AAAAAADwAPgAAAAAAPAA8AAAAAAA8APwAAAAAADwB/AAAAAAAPAP8AAAAAAB8B+AAAAA8AH4PgAAAADwAHx8AAAAAPAAPngAAAAA8AAeeAAPAA//AB7wAA8AD/8AD/AADwAP/wAP8AAPAA//AA/wAP/wAPAAD/AA//AA8AAP8AD/8ADwAA94AP/wAPAAHngADwAAAAAefAAPAAAAAD4+AA8AAAAAfB+ADwAAAAH4D/8AAPAP//AH/wAA8A//4AP/AADwD//AAH8AAPAP/gAAAAAP/wAAAAAAAA//AAAAAAAAD/8AAAAAAAAP/wAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : atob("EBCBAAAAAAADwAGAEYg73C50BCAEIC50O9wRiAGAA8AAAAAA");
|
||||||
let weatherFoggy = big ? atob("QEDBAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAwADwADAAAAHgAPAAeAAAAfAA8AD4AAAA+ADwAfAAAAB8APAD4AAAAD4B+AfAAAAAHw//D4AAAAAPv//fAAAAAAf///4AAAAAA/4H/AAAAAAB+AH4AAAAAAPgAHwAAAAAA8AAPAAAAAAHwAA+AAAAAAeAAB4AAAAAB4AAHgAAAAAPAAAPAAAAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AD///AADwAAAP//8AAeAAAA///wAB4AAAD///AAPgAAAAAAAAA8AAAAAAAAAHwAAAAAAAAB+AAAAAAAAAf8AAAAD///D/4AAAAP//8P3wAAAA///w8PgAAAD///CAfAAAAAAAAAA+AAAAAAAAAB8AAAAAAAAAD4AAAAAAAAAHgAAP//8PAAMAAA///w8AAAAAD///DwAAAAAP//8PAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAAADwAZgDDA4EGAcQAZAAgAAf74AAAAAd/4AAAAA");
|
let weatherFoggy = big ? atob("QECBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAMAA8AAwAAAB4ADwAHgAAAHwAPAA+AAAAPgA8AHwAAAAfADwA+AAAAA+AfgHwAAAAB8P/w+AAAAAD7//3wAAAAAH///+AAAAAAP+B/wAAAAAAfgB+AAAAAAD4AB8AAAAAAPAADwAAAAAB8AAPgAAAAAHgAAeAAAAAAeAAB4AAAAADwAADwAAAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AA///wAA8AAAD///AAHgAAAP//8AAeAAAA///wAD4AAAAAAAAAPAAAAAAAAAB8AAAAAAAAAfgAAAAAAAAH/AAAAA///w/+AAAAD///D98AAAAP//8PD4AAAA///wgHwAAAAAAAAAPgAAAAAAAAAfAAAAAAAAAA+AAAAAAAAAB4AAD///DwADAAAP//8PAAAAAA///w8AAAAAD///DwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : atob("EBCBAAAAAAADwAZgDDA4EGAcQAZAAgAAf74AAAAAd/4AAAAA");
|
||||||
let weatherStormy = big ? atob("QEDBAP//wxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAB//gAAAAAAAP//gAAAAAAD///AAAAAAAf4H+AAAAAAD8AD8AAAAAAfgAH4AAAAAB8AAPwAAAAAPgAAf/AAAAB8AAA//AAAAHgAAB/+AAAAeAAAH/8AAAH4AAAIH4AAB/AAAAAHwAAf8AAAAAPgAD/wAAAAAeAAPwAAAAAB4AB8AAAAAADwAHgAAAAAAPAA+AAAAAAA8ADwAAAAAADwA/AAAAAAAPAH8AAAAAAA8A/wAAAAAAHwH4AAAAAAAfg+AAAAAAAAfHwAAAAAAAA+eAAAAAAAAB54AAAAD/AAHvAAAAAf4AAP8AAAAB/gAA/wAAAAP8AAD/AAAAA/gAAP8AAAAH+AAA/wAAAAfwAAD3gAAAD/AAAeeAAAAP4AAB58AAAB/AAAPj4AAAH8AAB8H4AAA/gAAfgP//+D//D/8Af//4f/4P/gA///B//B/8AAf/8P/8P+AAAAAAAPgAAAAAAAAB8AAAAAAAAAHwAAAAAAAAA+AAAAAAAAADwAAAAAAAAAfAAAAAAAAAB4AAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : atob("EBCBAAAAAYAH4AwwOBBgGEAOQMJAgjmOGcgAgACAAAAAAAAA");
|
let weatherStormy = big ? atob("QECBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAf/4AAAAAAAD//4AAAAAAA///wAAAAAAH+B/gAAAAAA/AA/AAAAAAH4AB+AAAAAAfAAD8AAAAAD4AAH/wAAAAfAAAP/wAAAB4AAAf/gAAAHgAAB//AAAB+AAACB+AAAfwAAAAB8AAH/AAAAAD4AA/8AAAAAHgAD8AAAAAAeAAfAAAAAAA8AB4AAAAAADwAPgAAAAAAPAA8AAAAAAA8APwAAAAAADwB/AAAAAAAPAP8AAAAAAB8B+AAAAAAAH4PgAAAAAAAHx8AAAAAAAAPngAAAAAAAAeeAAAAA/wAB7wAAAAH+AAD/AAAAAf4AAP8AAAAD/AAA/wAAAAP4AAD/AAAAB/gAAP8AAAAH8AAA94AAAA/wAAHngAAAD+AAAefAAAAfwAAD4+AAAB/AAAfB+AAAP4AAH4D///g//w//AH//+H/+D/4AP//wf/wf/AAH//D//D/gAAAAAAD4AAAAAAAAAfAAAAAAAAAB8AAAAAAAAAPgAAAAAAAAA8AAAAAAAAAHwAAAAAAAAAeAAAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : atob("EBCBAAAAAYAH4AwwOBBgGEAOQMJAgjmOGcgAgACAAAAAAAAA");
|
||||||
let unknown = big ? atob("QEDBAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAf/4AAAAAAAH//4AAAAAAA///wAAAAAAH+B/gAAAAAA/AA/AAAAAAH4AB+AAAAAA/AAD4AAAAAD4H4HwAAAAAfB/4PgAAAAB8P/weAAAAAHg//h4AAAAA+Hw+HwAAAAD4eB8PAAAAAP/wDw8AAAAA//APDwAAAAD/8A8PAAAAAH/gDw8AAAAAAAAfDwAAAAAAAH4fAAAAAAAB/B4AAAAAAAf4HgAAAAAAD/A+AAAAAAAfwHwAAAAAAD8A+AAAAAAAPgH4AAAAAAB8B/AAAAAAAHgf4AAAAAAA+H+AAAAAAADwfwAAAAAAAPD8AAAAAAAA8PAAAAAAAAD/8AAAAAAAAP/wAAAAAAAA//AAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf+AAAAAAAAD/8AAAAAAAAP/wAAAAAAAA//AAAAAAAADw8AAAAAAAAPDwAAAAAAAA8PAAAAAAAADw8AAAAAAAAP/wAAAAAAAA//AAAAAAAAD/8AAAAAAAAH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") : undefined;
|
let unknown = big ? atob("QECBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAH/+AAAAAAAB//+AAAAAAAP//8AAAAAAB/gf4AAAAAAPwAPwAAAAAB+AAfgAAAAAPwAA+AAAAAA+B+B8AAAAAHwf+D4AAAAAfD/8HgAAAAB4P/4eAAAAAPh8Ph8AAAAA+HgfDwAAAAD/8A8PAAAAAP/wDw8AAAAA//APDwAAAAB/4A8PAAAAAAAAHw8AAAAAAAB+HwAAAAAAAfweAAAAAAAH+B4AAAAAAA/wPgAAAAAAH8B8AAAAAAA/APgAAAAAAD4B+AAAAAAAfAfwAAAAAAB4H+AAAAAAAPh/gAAAAAAA8H8AAAAAAADw/AAAAAAAAPDwAAAAAAAA//AAAAAAAAD/8AAAAAAAAP/wAAAAAAAAf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/gAAAAAAAA//AAAAAAAAD/8AAAAAAAAP/wAAAAAAAA8PAAAAAAAADw8AAAAAAAAPDwAAAAAAAA8PAAAAAAAAD/8AAAAAAAAP/wAAAAAAAA//AAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") : undefined;
|
||||||
|
|
||||||
switch (codeGroup) {
|
switch (codeGroup) {
|
||||||
case 2:
|
case 2:
|
||||||
return weatherStormy;
|
return weatherStormy;
|
||||||
|
@ -823,7 +238,9 @@ function getWeatherIconByCode(code, big) {
|
||||||
case 8:
|
case 8:
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 800:
|
case 800:
|
||||||
return isDay() ? weatherSunny : weatherMoon;
|
var hr = (new Date()).getHours();
|
||||||
|
var isDay = (hr>6) && (hr<=18); // fixme we don't want to include ALL of suncalc just to choose one icon
|
||||||
|
return isDay ? weatherSunny : weatherMoon;
|
||||||
case 801:
|
case 801:
|
||||||
return weatherPartlyCloudy;
|
return weatherPartlyCloudy;
|
||||||
case 802:
|
case 802:
|
||||||
|
@ -836,80 +253,19 @@ function getWeatherIconByCode(code, big) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function isDay() {
|
|
||||||
let times = getSunData();
|
|
||||||
if (times == undefined) return true;
|
|
||||||
let sunRise = Math.round(times.sunrise.getTime() / 1000);
|
|
||||||
let sunSet = Math.round(times.sunset.getTime() / 1000);
|
|
||||||
|
|
||||||
return (now > sunRise && now < sunSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatSeconds(s) {
|
|
||||||
if (s > 60 * 60) { // hours
|
|
||||||
return Math.round(s / (60 * 60)) + "h";
|
|
||||||
}
|
|
||||||
if (s > 60) { // minutes
|
|
||||||
return Math.round(s / 60) + "m";
|
|
||||||
}
|
|
||||||
return "<1m";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSunData() {
|
|
||||||
if (location != undefined && location.lat != undefined) {
|
|
||||||
let SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
|
|
||||||
// get today's sunlight times for lat/lon
|
|
||||||
return SunCalc ? SunCalc.getTimes(new Date(), location.lat, location.lon) : undefined;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculated progress of the sun between sunrise and sunset in percent
|
|
||||||
*
|
|
||||||
* Taken from rebble app and modified
|
|
||||||
*/
|
|
||||||
function getSunProgress() {
|
|
||||||
let times = getSunData();
|
|
||||||
if (times == undefined) return 0;
|
|
||||||
let sunRise = Math.round(times.sunrise.getTime() / 1000);
|
|
||||||
let sunSet = Math.round(times.sunset.getTime() / 1000);
|
|
||||||
|
|
||||||
if (isDay()) {
|
|
||||||
// during day
|
|
||||||
let dayLength = sunSet - sunRise;
|
|
||||||
if (now > sunRise) {
|
|
||||||
return (now - sunRise) / dayLength;
|
|
||||||
} else {
|
|
||||||
return (sunRise - now) / dayLength;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// during night
|
|
||||||
if (now < sunRise) {
|
|
||||||
let prevSunSet = sunSet - 60 * 60 * 24;
|
|
||||||
return 1 - (sunRise - now) / (sunRise - prevSunSet);
|
|
||||||
} else {
|
|
||||||
let upcomingSunRise = sunRise + 60 * 60 * 24;
|
|
||||||
return (upcomingSunRise - now) / (upcomingSunRise - sunSet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Draws the background and the grey circle
|
* Draws the background and the grey circle
|
||||||
*/
|
*/
|
||||||
function drawCircleBackground(w) {
|
let drawCircleBackground = function(w) {
|
||||||
g.clearRect(w - radiusOuter - 3, h3 - radiusOuter - 3, w + radiusOuter + 3, h3 + radiusOuter + 3);
|
|
||||||
// Draw rectangle background:
|
// Draw rectangle background:
|
||||||
g.setColor(colorBg);
|
g.setColor(colorBg);
|
||||||
g.fillRect(w - radiusOuter - 3, h3 - radiusOuter - 3, w + radiusOuter + 3, h3 + radiusOuter + 3);
|
g.fillRect(w - radiusBorder, h3 - radiusBorder, w + radiusBorder, g.getHeight()-1);
|
||||||
// Draw grey background circle:
|
// Draw grey background circle:
|
||||||
g.setColor(colorGrey);
|
g.setColor('#808080'); // grey
|
||||||
g.fillCircle(w, h3, radiusOuter);
|
g.fillCircle(w, h3, radiusOuter);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawInnerCircleAndTriangle(w) {
|
let drawInnerCircleAndTriangle = function(w) {
|
||||||
// Draw inner circle
|
// Draw inner circle
|
||||||
g.setColor(colorBg);
|
g.setColor(colorBg);
|
||||||
g.fillCircle(w, h3, radiusInner);
|
g.fillCircle(w, h3, radiusInner);
|
||||||
|
@ -917,18 +273,13 @@ function drawInnerCircleAndTriangle(w) {
|
||||||
g.fillPoly([w, h3, w - 15, h3 + radiusOuter + 5, w + 15, h3 + radiusOuter + 5]);
|
g.fillPoly([w, h3, w - 15, h3 + radiusOuter + 5, w + 15, h3 + radiusOuter + 5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function radians(a) {
|
|
||||||
return a * Math.PI / 180;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This draws the actual gauge consisting out of lots of little filled circles
|
* This draws the actual gauge consisting out of lots of little filled circles
|
||||||
*/
|
*/
|
||||||
function drawGauge(cx, cy, percent, color) {
|
let drawGauge = function(cx, cy, percent, color) {
|
||||||
let offset = 15;
|
let offset = 15;
|
||||||
let end = 360 - offset;
|
let end = 360 - offset;
|
||||||
let radius = radiusInner + (circleCount == 3 ? 3 : 2);
|
let radius = radiusOuter+1;
|
||||||
let size = radiusOuter - radiusInner - 2;
|
|
||||||
|
|
||||||
if (percent <= 0) return; // no gauge needed
|
if (percent <= 0) return; // no gauge needed
|
||||||
if (percent > 1) percent = 1;
|
if (percent > 1) percent = 1;
|
||||||
|
@ -938,15 +289,21 @@ function drawGauge(cx, cy, percent, color) {
|
||||||
|
|
||||||
color = getGradientColor(color, percent);
|
color = getGradientColor(color, percent);
|
||||||
g.setColor(color);
|
g.setColor(color);
|
||||||
|
// convert to radians
|
||||||
for (let i = startRotation; i > endRotation - size; i -= size) {
|
startRotation *= Math.PI / 180;
|
||||||
x = cx + radius * Math.sin(radians(i));
|
let amt = Math.PI / 10;
|
||||||
y = cy + radius * Math.cos(radians(i));
|
endRotation = (endRotation * Math.PI / 180) - amt;
|
||||||
g.fillCircle(x, y, size);
|
// all we need to draw is an arc, because we'll fill the center
|
||||||
}
|
let poly = [cx,cy];
|
||||||
|
for (let r = startRotation; r > endRotation; r -= amt)
|
||||||
|
poly.push(
|
||||||
|
cx + radius * Math.sin(r),
|
||||||
|
cy + radius * Math.cos(r)
|
||||||
|
);
|
||||||
|
g.fillPoly(poly);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeCircleText(w, content) {
|
let writeCircleText = function(w, content) {
|
||||||
if (content == undefined) return;
|
if (content == undefined) return;
|
||||||
let font = String(content).length > 4 ? circleFontSmall : String(content).length > 3 ? circleFont : circleFontBig;
|
let font = String(content).length > 4 ? circleFontSmall : String(content).length > 3 ? circleFont : circleFontBig;
|
||||||
g.setFont(font);
|
g.setFont(font);
|
||||||
|
@ -956,26 +313,48 @@ function writeCircleText(w, content) {
|
||||||
g.drawString(content, w, h3);
|
g.drawString(content, w, h3);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWeather() {
|
let getWeather=function() {
|
||||||
let jsonWeather = storage.readJSON('weather.json');
|
let jsonWeather = storage.readJSON('weather.json');
|
||||||
return jsonWeather && jsonWeather.weather ? jsonWeather.weather : undefined;
|
return jsonWeather && jsonWeather.weather ? jsonWeather.weather : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g.clear(1); // clear the whole screen
|
||||||
|
|
||||||
Bangle.setUI({
|
Bangle.setUI({
|
||||||
mode : "clock",
|
mode : "clock",
|
||||||
remove : function() {
|
remove : function() {
|
||||||
// Called to unload all of the clock app
|
// Called to unload all of the clock app (allowing for 'fast load')
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
|
clockInfoMenu.forEach(c => c.remove());
|
||||||
delete Graphics.prototype.setFontRobotoRegular50NumericOnly;
|
delete Graphics.prototype.setFontRobotoRegular50NumericOnly;
|
||||||
delete Graphics.prototype.setFontRobotoRegular21;
|
delete Graphics.prototype.setFontRobotoRegular21;
|
||||||
}});
|
if (!showWidgets) require("widget_utils").show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let clockInfoDraw = (itm, info, options) => {
|
||||||
|
//print("Draw",itm.name,options);
|
||||||
|
drawCircle(options.circlePosition, itm, info);
|
||||||
|
if (options.focus) g.reset().drawRect(options.x, options.y, options.x+options.w-2, options.y+options.h-1)
|
||||||
|
};
|
||||||
|
let clockInfoItems = require("clock_info").load();
|
||||||
|
let clockInfoMenu = [];
|
||||||
|
for(var i=0;i<circleCount; i++) {
|
||||||
|
let w = circlePosX[i];
|
||||||
|
let y = h3-radiusBorder;
|
||||||
|
clockInfoMenu[i] = require("clock_info").addInteractive(clockInfoItems, {
|
||||||
|
x:w-radiusBorder, y:y, w:radiusBorder*2, h:g.getHeight()-(y+1),
|
||||||
|
draw : clockInfoDraw, circlePosition : i+1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
|
if (!showWidgets) require("widget_utils").hide();
|
||||||
|
else Bangle.drawWidgets();
|
||||||
|
|
||||||
// schedule a draw for the next second or minute
|
// schedule a draw for the next second or minute
|
||||||
function queueDraw() {
|
let queueDraw=function() {
|
||||||
let queueMillis = settings.updateInterval * 1000;
|
let queueMillis = settings.updateInterval * 1000;
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = setTimeout(function() {
|
drawTimeout = setTimeout(function() {
|
||||||
|
@ -985,3 +364,4 @@ function queueDraw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
draw();
|
draw();
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
"showWidgets": false,
|
"showWidgets": false,
|
||||||
"weatherCircleData": "humidity",
|
"weatherCircleData": "humidity",
|
||||||
"circleCount": 3,
|
"circleCount": 3,
|
||||||
"circle1": "Bangle/HRM",
|
|
||||||
"circle2": "Bangle/Steps",
|
|
||||||
"circle3": "Bangle/Battery",
|
|
||||||
"circle4": "weather",
|
|
||||||
"circle1color": "green-red",
|
"circle1color": "green-red",
|
||||||
"circle2color": "#0000ff",
|
"circle2color": "#0000ff",
|
||||||
"circle3color": "red-green",
|
"circle3color": "red-green",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "circlesclock",
|
{ "id": "circlesclock",
|
||||||
"name": "Circles clock",
|
"name": "Circles clock",
|
||||||
"shortName":"Circles clock",
|
"shortName":"Circles clock",
|
||||||
"version":"0.18",
|
"version":"0.21",
|
||||||
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
|
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],
|
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],
|
||||||
|
|
|
@ -12,23 +12,6 @@
|
||||||
storage.write(SETTINGS_FILE, settings);
|
storage.write(SETTINGS_FILE, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
//const valuesCircleTypes = ["empty", "steps", "stepsDist", "hr", "battery", "weather", "sunprogress", "temperature", "pressure", "altitude", "timer"];
|
|
||||||
//const namesCircleTypes = ["empty", "steps", "distance", "heart", "battery", "weather", "sun", "temperature", "pressure", "altitude", "timer"];
|
|
||||||
var valuesCircleTypes = ["empty","weather", "sunprogress"];
|
|
||||||
var namesCircleTypes = ["empty","weather", "sun"];
|
|
||||||
clock_info.load().forEach(e=>{
|
|
||||||
if(e.dynamic) {
|
|
||||||
valuesCircleTypes = valuesCircleTypes.concat([e.name+"/"]);
|
|
||||||
namesCircleTypes = namesCircleTypes.concat([e.name]);
|
|
||||||
} else {
|
|
||||||
let values = e.items.map(i=>e.name+"/"+i.name);
|
|
||||||
let names =e.name=="Bangle" ? e.items.map(i=>i.name) : values;
|
|
||||||
valuesCircleTypes = valuesCircleTypes.concat(values);
|
|
||||||
namesCircleTypes = namesCircleTypes.concat(names);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff",
|
const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff",
|
||||||
"#00ffff", "#fff", "#000", "green-red", "red-green", "fg"];
|
"#00ffff", "#fff", "#000", "green-red", "red-green", "fg"];
|
||||||
const namesColors = ["default", "red", "green", "blue", "yellow", "magenta",
|
const namesColors = ["default", "red", "green", "blue", "yellow", "magenta",
|
||||||
|
@ -105,12 +88,6 @@
|
||||||
const menu = {
|
const menu = {
|
||||||
'': { 'title': /*LANG*/'Circle ' + circleId },
|
'': { 'title': /*LANG*/'Circle ' + circleId },
|
||||||
/*LANG*/'< Back': ()=>showMainMenu(),
|
/*LANG*/'< Back': ()=>showMainMenu(),
|
||||||
/*LANG*/'data': {
|
|
||||||
value: valuesCircleTypes.indexOf(settings[circleName]),
|
|
||||||
min: 0, max: valuesCircleTypes.length - 1,
|
|
||||||
format: v => namesCircleTypes[v],
|
|
||||||
onchange: x => save(circleName, valuesCircleTypes[x]),
|
|
||||||
},
|
|
||||||
/*LANG*/'color': {
|
/*LANG*/'color': {
|
||||||
value: valuesColors.indexOf(settings[colorKey]) || 0,
|
value: valuesColors.indexOf(settings[colorKey]) || 0,
|
||||||
min: 0, max: valuesColors.length - 1,
|
min: 0, max: valuesColors.length - 1,
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
After Width: | Height: | Size: 901 B |
|
@ -0,0 +1,32 @@
|
||||||
|
(function() {
|
||||||
|
require("Font4x8Numeric").add(Graphics);
|
||||||
|
return {
|
||||||
|
name: "Bangle",
|
||||||
|
items: [
|
||||||
|
{ name : "Date",
|
||||||
|
get : () => {
|
||||||
|
let d = new Date();
|
||||||
|
let g = Graphics.createArrayBuffer(24,24,1,{msb:true});
|
||||||
|
g.drawImage(atob("FhgBDADAMAMP/////////////////////8AADwAAPAAA8AADwAAPAAA8AADwAAPAAA8AADwAAPAAA8AADwAAP///////"),1,0);
|
||||||
|
g.setFont("6x15").setFontAlign(0,0).drawString(d.getDate(),11,17);
|
||||||
|
return {
|
||||||
|
text : require("locale").dow(d,1).toUpperCase(),
|
||||||
|
img : g.asImage("string")
|
||||||
|
};
|
||||||
|
},
|
||||||
|
show : function() {
|
||||||
|
this.interval = setTimeout(()=>{
|
||||||
|
this.emit("redraw");
|
||||||
|
this.interval = setInterval(()=>{
|
||||||
|
this.emit("redraw");
|
||||||
|
}, 86400000);
|
||||||
|
}, 86400000 - (Date.now() % 86400000));
|
||||||
|
},
|
||||||
|
hide : function() {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.interval = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
})
|
|
@ -0,0 +1,12 @@
|
||||||
|
{ "id": "clkinfocal",
|
||||||
|
"name": "Calendar Clockinfo",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "For clocks that display 'clockinfo' (messages that can be cycled through using the clock_info module) this displays the day of the month in the icon, and the weekday",
|
||||||
|
"icon": "app.png",
|
||||||
|
"type": "clkinfo",
|
||||||
|
"tags": "clkinfo,calendar",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"clkinfocal.clkinfo.js","url":"clkinfo.js"}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1 +1,4 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
|
0.02: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps
|
||||||
|
Add a 'time' clockinfo that also displays a percentage of day left
|
||||||
|
0.03: Change 3rd mode to show the time to next sunrise/sunset time (not actual time)
|
||||||
|
|
|
@ -1,32 +1,75 @@
|
||||||
(function() {
|
(function() {
|
||||||
// get today's sunlight times for lat/lon
|
// get today's sunlight times for lat/lon
|
||||||
var sunrise, sunset;
|
var sunrise, sunset, date;
|
||||||
|
var SunCalc = require("suncalc"); // from modules folder
|
||||||
|
const locale = require("locale");
|
||||||
|
|
||||||
function calculate() {
|
function calculate() {
|
||||||
var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
|
|
||||||
const locale = require("locale");
|
|
||||||
var location = require("Storage").readJSON("mylocation.json",1)||{};
|
var location = require("Storage").readJSON("mylocation.json",1)||{};
|
||||||
location.lat = location.lat||51.5072;
|
location.lat = location.lat||51.5072;
|
||||||
location.lon = location.lon||0.1276;
|
location.lon = location.lon||0.1276; // London
|
||||||
location.location = location.location||"London";
|
date = new Date(Date.now());
|
||||||
var times = SunCalc.getTimes(new Date(), location.lat, location.lon);
|
var times = SunCalc.getTimes(date, location.lat, location.lon);
|
||||||
sunrise = locale.time(times.sunrise,1);
|
sunrise = times.sunrise;
|
||||||
sunset = locale.time(times.sunset,1);
|
sunset = times.sunset;
|
||||||
/* do we want to re-calculate this every day? Or we just assume
|
/* do we want to re-calculate this every day? Or we just assume
|
||||||
that 'show' will get called once a day? */
|
that 'show' will get called once a day? */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
this.interval = setTimeout(()=>{
|
||||||
|
this.emit("redraw");
|
||||||
|
this.interval = setInterval(()=>{
|
||||||
|
this.emit("redraw");
|
||||||
|
}, 60000);
|
||||||
|
}, 60000 - (Date.now() % 60000));
|
||||||
|
}
|
||||||
|
function hide() {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.interval = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "Bangle",
|
name: "Bangle",
|
||||||
items: [
|
items: [
|
||||||
{ name : "Sunrise",
|
{ name : "Sunrise",
|
||||||
get : () => ({ text : sunrise,
|
get : () => { calculate();
|
||||||
img : atob("GBiBAAAAAAAAAAAAAAAYAAA8AAB+AAD/AAAAAAAAAAAAAAAYAAAYAAQYIA4AcAYAYAA8AAB+AAD/AAH/gD///D///AAAAAAAAAAAAA==") }),
|
return { text : locale.time(sunrise,1),
|
||||||
show : calculate, hide : () => {}
|
img : atob("GBiBAAAAAAAAAAAAAAAYAAA8AAB+AAD/AAAAAAAAAAAAAAAYAAAYAAQYIA4AcAYAYAA8AAB+AAD/AAH/gD///D///AAAAAAAAAAAAA==") }},
|
||||||
|
show : show, hide : hide
|
||||||
}, { name : "Sunset",
|
}, { name : "Sunset",
|
||||||
get : () => ({ text : sunset,
|
get : () => { calculate();
|
||||||
img : atob("GBiBAAAAAAAAAAAAAAB+AAA8AAAYAAAYAAAAAAAAAAAAAAAYAAAYAAQYIA4AcAYAYAA8AAB+AAD/AAH/gD///D///AAAAAAAAAAAAA==") }),
|
return { text : locale.time(sunset,1),
|
||||||
show : calculate, hide : () => {}
|
img : atob("GBiBAAAAAAAAAAAAAAB+AAA8AAAYAAAYAAAAAAAAAAAAAAAYAAAYAAQYIA4AcAYAYAA8AAB+AAD/AAH/gD///D///AAAAAAAAAAAAA==") }},
|
||||||
|
show : show, hide : hide
|
||||||
|
}, { name : "Sunrise/set", // Time in day (uses v/min/max to show percentage through day)
|
||||||
|
hasRange : true,
|
||||||
|
get : () => {
|
||||||
|
calculate();
|
||||||
|
let day = true;
|
||||||
|
let d = date.getTime();
|
||||||
|
let dayLength = sunset.getTime()-sunrise.getTime();
|
||||||
|
let timeUntil, timeTotal;
|
||||||
|
if (d < sunrise.getTime()) {
|
||||||
|
day = false; // early morning
|
||||||
|
timePast = sunrise.getTime()-d;
|
||||||
|
timeTotal = 86400000-dayLength;
|
||||||
|
} else if (d > sunset.getTime()) {
|
||||||
|
day = false; // evening
|
||||||
|
timePast = d-sunset.getTime();
|
||||||
|
timeTotal = 86400000-dayLength;
|
||||||
|
} else { // day!
|
||||||
|
timePast = d-sunrise.getTime();
|
||||||
|
timeTotal = dayLength;
|
||||||
|
}
|
||||||
|
let v = Math.round(100 * timePast / timeTotal);
|
||||||
|
let minutesTo = (timeTotal-timePast)/60000;
|
||||||
|
return { text : (minutesTo>90) ? (Math.round(minutesTo/60)+"h") : (Math.round(minutesTo)+"m"),
|
||||||
|
v : v, min : 0, max : 100,
|
||||||
|
img : day ? atob("GBiBAAAYAAAYAAAYAAgAEBwAOAx+MAD/AAH/gAP/wAf/4Af/4Of/5+f/5wf/4Af/4AP/wAH/gAD/AAx+MBwAOAgAEAAYAAAYAAAYAA==") : atob("GBiBAAfwAA/8AAP/AAH/gAD/wAB/wAB/4AA/8AA/8AA/8AAf8AAf8AAf8AAf8AA/8AA/8AA/4AB/4AB/wAD/wAH/gAf/AA/8AAfwAA==")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
show : show, hide : hide
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "clkinfosunrise",
|
{ "id": "clkinfosunrise",
|
||||||
"name": "Sunrise Clockinfo",
|
"name": "Sunrise Clockinfo",
|
||||||
"version":"0.01",
|
"version":"0.03",
|
||||||
"description": "For clocks that display 'clockinfo' (messages that can be cycled through using the clock_info module) this displays sunrise and sunset based on the location from the 'My Location' app",
|
"description": "For clocks that display 'clockinfo' (messages that can be cycled through using the clock_info module) this displays sunrise and sunset based on the location from the 'My Location' app",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "clkinfo",
|
"type": "clkinfo",
|
||||||
|
|
|
@ -6,3 +6,4 @@
|
||||||
0.06: better contrast for light theme, use fg color instead of dithered for ring
|
0.06: better contrast for light theme, use fg color instead of dithered for ring
|
||||||
0.07: Use default Bangle formatter for booleans
|
0.07: Use default Bangle formatter for booleans
|
||||||
0.08: fix idle timer always getting set to true
|
0.08: fix idle timer always getting set to true
|
||||||
|
0.09: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
|
var SunCalc = require("suncalc"); // from modules folder
|
||||||
const storage = require('Storage');
|
const storage = require('Storage');
|
||||||
const locale = require("locale");
|
const locale = require("locale");
|
||||||
const SETTINGS_FILE = "daisy.json";
|
const SETTINGS_FILE = "daisy.json";
|
||||||
|
@ -70,7 +70,7 @@ function getSteps() {
|
||||||
try {
|
try {
|
||||||
return Bangle.getHealthStatus("day").steps;
|
return Bangle.getHealthStatus("day").steps;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (WIDGETS.wpedom !== undefined)
|
if (WIDGETS.wpedom !== undefined)
|
||||||
return WIDGETS.wpedom.getSteps();
|
return WIDGETS.wpedom.getSteps();
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -151,7 +151,7 @@ function prevInfo() {
|
||||||
function clearInfo() {
|
function clearInfo() {
|
||||||
g.setColor(g.theme.bg);
|
g.setColor(g.theme.bg);
|
||||||
//g.setColor(g.theme.fg);
|
//g.setColor(g.theme.fg);
|
||||||
g.fillRect((w/2) - infoWidth, infoLine - infoHeight, (w/2) + infoWidth, infoLine + infoHeight);
|
g.fillRect((w/2) - infoWidth, infoLine - infoHeight, (w/2) + infoWidth, infoLine + infoHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawInfo() {
|
function drawInfo() {
|
||||||
|
@ -202,7 +202,7 @@ function drawClock() {
|
||||||
var mm = da[4].substr(3,2);
|
var mm = da[4].substr(3,2);
|
||||||
var steps = getSteps();
|
var steps = getSteps();
|
||||||
var p_steps = Math.round(100*(steps/10000));
|
var p_steps = Math.round(100*(steps/10000));
|
||||||
|
|
||||||
g.reset();
|
g.reset();
|
||||||
g.setColor(g.theme.bg);
|
g.setColor(g.theme.bg);
|
||||||
g.fillRect(0, 0, w, h);
|
g.fillRect(0, 0, w, h);
|
||||||
|
@ -218,7 +218,7 @@ function drawClock() {
|
||||||
g.drawString(mm, (w/2) + 1, h/2);
|
g.drawString(mm, (w/2) + 1, h/2);
|
||||||
|
|
||||||
drawInfo();
|
drawInfo();
|
||||||
|
|
||||||
// recalc sunrise / sunset every hour
|
// recalc sunrise / sunset every hour
|
||||||
if (drawCount % 60 == 0)
|
if (drawCount % 60 == 0)
|
||||||
updateSunRiseSunSet(new Date(), location.lat, location.lon);
|
updateSunRiseSunSet(new Date(), location.lat, location.lon);
|
||||||
|
@ -254,7 +254,7 @@ function resetHrm() {
|
||||||
Bangle.on('HRM', function(hrm) {
|
Bangle.on('HRM', function(hrm) {
|
||||||
hrmCurrent = hrm.bpm;
|
hrmCurrent = hrm.bpm;
|
||||||
hrmConfidence = hrm.confidence;
|
hrmConfidence = hrm.confidence;
|
||||||
log_debug("HRM=" + hrm.bpm + " (" + hrm.confidence + ")");
|
log_debug("HRM=" + hrm.bpm + " (" + hrm.confidence + ")");
|
||||||
if (infoMode == "ID_HRM" ) drawHrm();
|
if (infoMode == "ID_HRM" ) drawHrm();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ function getGaugeImage(p) {
|
||||||
palette : pal2,
|
palette : pal2,
|
||||||
buffer : require("heatshrink").decompress(atob("AH4A/AH4AChWq1WpqtUFUgpBFYYABoApggQqDFYlVqBVjFYxZfFQorGLLrWCFZbgbVguoBQcFLD8qFQYMHiosDKzoOJFgZYYKwYPLFgZWawARMLDJWCawgAJcAZWYCZ6FCLCkKFQOgCZ8BFYNUFaZWSLAlAQShWQLAiESQQRtTLAKESFQOoFacFQiSCCwArTgCESQSyEUlTZTboyCnQiSCYQiSCYQiSCZQgdAVxwqYQgSwMVwOoFbMFWBquaWCArBVzKwDbRoqaWATcKbQKuaWAbcKbQKuaWAbcKVzqwNFYIqcWATaKVziwDbhDaebhjaebhgrBbTrcCFZDafbheqFcTcHbT7cDFY0CbT7cDqArxhWqwArfgFVqgrHFUDcBFY0qFcdVFY2oFcMFFY2qFclAFYugFcMBFYsCFctQFYuAFcMAFYsKFctUFYoqigEVFeEqFctVFYmoFccFFYmqFc1AcIdQFccBFf4rbGAoAhKQYr/Fa8FFc9UFYYqkgEVFf4r/FYwDDAEZTDFf4r/Ff4rbqorooArBqArlgIr/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4rbqgrlgorCioroAYIr/Ff4r/FbYDDAEZTDFf4r/FYtAFclVFYUBFc9QFf4rZAgoAgKQor/FbFUFccFFYkVFcwFDioFEAD4lFGIorgPogrtWoYAfqorEgIrlqArFAwgAdEg4rlPgqKFADrUHcQorfA4sVA4wAbEY4zHFbh7GRY4AbaY7jBqAqfERArrMBAAZUxNVbkEVFZAJBFcJhRAC6lJFYLcebQIrIBRTaXJhIrhUhLcfD5YLBbjtVFZTceZ5jceJRpkLVyaiLWDpJNFYKwaUIIrMSIKwaDhw6OVx50NFYKwZDZ6waOaCTBQjBGBZZw8CQi4ZBOR6EYeySEYQSCEaQSITDH6BvGIaKEWQSSEEbqQVVQgRYSKwLGUQgRCQKwTFUC4RYQKwSCTDAhEONQTwULAqcNCARWVLAhGMB55YPDhQqDKy4dFFhAMMLCzgFawZWbEI4AIGogAYFZtAFbgsMFTyyGVkBZOKr7gJazoA/AHIA="))
|
buffer : require("heatshrink").decompress(atob("AH4A/AH4AChWq1WpqtUFUgpBFYYABoApggQqDFYlVqBVjFYxZfFQorGLLrWCFZbgbVguoBQcFLD8qFQYMHiosDKzoOJFgZYYKwYPLFgZWawARMLDJWCawgAJcAZWYCZ6FCLCkKFQOgCZ8BFYNUFaZWSLAlAQShWQLAiESQQRtTLAKESFQOoFacFQiSCCwArTgCESQSyEUlTZTboyCnQiSCYQiSCYQiSCZQgdAVxwqYQgSwMVwOoFbMFWBquaWCArBVzKwDbRoqaWATcKbQKuaWAbcKbQKuaWAbcKVzqwNFYIqcWATaKVziwDbhDaebhjaebhgrBbTrcCFZDafbheqFcTcHbT7cDFY0CbT7cDqArxhWqwArfgFVqgrHFUDcBFY0qFcdVFY2oFcMFFY2qFclAFYugFcMBFYsCFctQFYuAFcMAFYsKFctUFYoqigEVFeEqFctVFYmoFccFFYmqFc1AcIdQFccBFf4rbGAoAhKQYr/Fa8FFc9UFYYqkgEVFf4r/FYwDDAEZTDFf4r/Ff4rbqorooArBqArlgIr/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4rbqgrlgorCioroAYIr/Ff4r/FbYDDAEZTDFf4r/FYtAFclVFYUBFc9QFf4rZAgoAgKQor/FbFUFccFFYkVFcwFDioFEAD4lFGIorgPogrtWoYAfqorEgIrlqArFAwgAdEg4rlPgqKFADrUHcQorfA4sVA4wAbEY4zHFbh7GRY4AbaY7jBqAqfERArrMBAAZUxNVbkEVFZAJBFcJhRAC6lJFYLcebQIrIBRTaXJhIrhUhLcfD5YLBbjtVFZTceZ5jceJRpkLVyaiLWDpJNFYKwaUIIrMSIKwaDhw6OVx50NFYKwZDZ6waOaCTBQjBGBZZw8CQi4ZBOR6EYeySEYQSCEaQSITDH6BvGIaKEWQSSEEbqQVVQgRYSKwLGUQgRCQKwTFUC4RYQKwSCTDAhEONQTwULAqcNCARWVLAhGMB55YPDhQqDKy4dFFhAMMLCzgFawZWbEI4AIGogAYFZtAFbgsMFTyyGVkBZOKr7gJazoA/AHIA="))
|
||||||
};
|
};
|
||||||
|
|
||||||
// p90
|
// p90
|
||||||
if (p >= 90 && p < 100) return {
|
if (p >= 90 && p < 100) return {
|
||||||
width : 176, height : 176, bpp : 2,
|
width : 176, height : 176, bpp : 2,
|
||||||
|
@ -410,7 +410,7 @@ function BUTTON(name,x,y,w,h,c,f,tx) {
|
||||||
// if pressed the callback
|
// if pressed the callback
|
||||||
BUTTON.prototype.check = function(x,y) {
|
BUTTON.prototype.check = function(x,y) {
|
||||||
//console.log(this.name + ":check() x=" + x + " y=" + y +"\n");
|
//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)) {
|
if (x>= this.x && x<= (this.x + this.w) && y>= this.y && y<= (this.y + this.h)) {
|
||||||
log_debug(this.name + ":callback\n");
|
log_debug(this.name + ":callback\n");
|
||||||
this.callback();
|
this.callback();
|
||||||
|
@ -472,7 +472,7 @@ function checkIdle() {
|
||||||
warned = false;
|
warned = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hour = (new Date()).getHours();
|
let hour = (new Date()).getHours();
|
||||||
let active = (hour >= 9 && hour < 21);
|
let active = (hour >= 9 && hour < 21);
|
||||||
//let active = true;
|
//let active = true;
|
||||||
|
@ -501,7 +501,7 @@ function buzzer(n) {
|
||||||
|
|
||||||
if (n-- < 1) return;
|
if (n-- < 1) return;
|
||||||
Bangle.buzz(250);
|
Bangle.buzz(250);
|
||||||
|
|
||||||
if (buzzTimeout) clearTimeout(buzzTimeout);
|
if (buzzTimeout) clearTimeout(buzzTimeout);
|
||||||
buzzTimeout = setTimeout(function() {
|
buzzTimeout = setTimeout(function() {
|
||||||
buzzTimeout = undefined;
|
buzzTimeout = undefined;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "daisy",
|
{ "id": "daisy",
|
||||||
"name": "Daisy",
|
"name": "Daisy",
|
||||||
"version":"0.08",
|
"version":"0.09",
|
||||||
"dependencies": {"mylocation":"app"},
|
"dependencies": {"mylocation":"app"},
|
||||||
"description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times",
|
"description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
"custom": "custom.html",
|
"custom": "custom.html",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"espruinoprog.app.js","url":"app.js"},
|
{"name":"espruinoprog.app.js","url":"app.js"},
|
||||||
{"name":"espruinoprog.img","url":"app-icon.js","evaluate":true},
|
{"name":"espruinoprog.img","url":"app-icon.js","evaluate":true}
|
||||||
|
], "data": [
|
||||||
{"name":"espruinoprog.json"}
|
{"name":"espruinoprog.json"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,5 @@
|
||||||
0.04: Add functionality to sort apps manually or alphabetically ascending/descending.
|
0.04: Add functionality to sort apps manually or alphabetically ascending/descending.
|
||||||
0.05: Tweaks to help with memory usage
|
0.05: Tweaks to help with memory usage
|
||||||
0.06: Reduce memory usage
|
0.06: Reduce memory usage
|
||||||
0.07: Allow negative numbers when manual-sorting
|
0.07: Allow negative numbers when manual-sorting
|
||||||
|
0.08: Automatic translation of strings.
|
||||||
|
|
|
@ -3,20 +3,20 @@ const store = require('Storage');
|
||||||
function showMainMenu() {
|
function showMainMenu() {
|
||||||
const mainmenu = {
|
const mainmenu = {
|
||||||
'': {
|
'': {
|
||||||
'title': 'App Manager',
|
'title': /*LANG*/'App Manager',
|
||||||
},
|
},
|
||||||
'< Back': ()=> {load();},
|
'< Back': ()=> {load();},
|
||||||
'Sort Apps': () => showSortAppsMenu(),
|
/*LANG*/'Sort Apps': () => showSortAppsMenu(),
|
||||||
'Manage Apps': ()=> showApps(),
|
/*LANG*/'Manage Apps': ()=> showApps(),
|
||||||
'Compact': () => {
|
/*LANG*/'Compact': () => {
|
||||||
E.showMessage('Compacting...');
|
E.showMessage(/*LANG*/'Compacting...');
|
||||||
try {
|
try {
|
||||||
store.compact();
|
store.compact();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
},
|
},
|
||||||
'Free': {
|
/*LANG*/'Free': {
|
||||||
value: undefined,
|
value: undefined,
|
||||||
format: (v) => {
|
format: (v) => {
|
||||||
return store.getFree();
|
return store.getFree();
|
||||||
|
@ -65,13 +65,13 @@ function eraseData(info) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function eraseApp(app, files,data) {
|
function eraseApp(app, files,data) {
|
||||||
E.showMessage('Erasing\n' + app.name + '...');
|
E.showMessage(/*LANG*/'Erasing\n' + app.name + '...');
|
||||||
var info = store.readJSON(app.id + ".info", 1)||{};
|
var info = store.readJSON(app.id + ".info", 1)||{};
|
||||||
if (files) eraseFiles(info);
|
if (files) eraseFiles(info);
|
||||||
if (data) eraseData(info);
|
if (data) eraseData(info);
|
||||||
}
|
}
|
||||||
function eraseOne(app, files,data){
|
function eraseOne(app, files,data){
|
||||||
E.showPrompt('Erase\n'+app.name+'?').then((v) => {
|
E.showPrompt(/*LANG*/'Erase\n'+app.name+'?').then((v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
Bangle.buzz(100, 1);
|
Bangle.buzz(100, 1);
|
||||||
eraseApp(app, files, data);
|
eraseApp(app, files, data);
|
||||||
|
@ -82,7 +82,7 @@ function eraseOne(app, files,data){
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function eraseAll(apps, files,data) {
|
function eraseAll(apps, files,data) {
|
||||||
E.showPrompt('Erase all?').then((v) => {
|
E.showPrompt(/*LANG*/'Erase all?').then((v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
Bangle.buzz(100, 1);
|
Bangle.buzz(100, 1);
|
||||||
apps.forEach(app => eraseApp(app, files, data));
|
apps.forEach(app => eraseApp(app, files, data));
|
||||||
|
@ -99,11 +99,11 @@ function showAppMenu(app) {
|
||||||
'< Back': () => showApps(),
|
'< Back': () => showApps(),
|
||||||
};
|
};
|
||||||
if (app.hasData) {
|
if (app.hasData) {
|
||||||
appmenu['Erase Completely'] = () => eraseOne(app, true, true);
|
appmenu[/*LANG*/'Erase Completely'] = () => eraseOne(app, true, true);
|
||||||
appmenu['Erase App,Keep Data'] = () => eraseOne(app, true, false);
|
appmenu[/*LANG*/'Erase App,Keep Data'] = () => eraseOne(app, true, false);
|
||||||
appmenu['Only Erase Data'] = () => eraseOne(app, false, true);
|
appmenu[/*LANG*/'Only Erase Data'] = () => eraseOne(app, false, true);
|
||||||
} else {
|
} else {
|
||||||
appmenu['Erase'] = () => eraseOne(app, true, false);
|
appmenu[/*LANG*/'Erase'] = () => eraseOne(app, true, false);
|
||||||
}
|
}
|
||||||
E.showMenu(appmenu);
|
E.showMenu(appmenu);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ function showAppMenu(app) {
|
||||||
function showApps() {
|
function showApps() {
|
||||||
const appsmenu = {
|
const appsmenu = {
|
||||||
'': {
|
'': {
|
||||||
'title': 'Apps',
|
'title': /*LANG*/'Apps',
|
||||||
},
|
},
|
||||||
'< Back': () => showMainMenu(),
|
'< Back': () => showMainMenu(),
|
||||||
};
|
};
|
||||||
|
@ -128,17 +128,17 @@ function showApps() {
|
||||||
menu[app.name] = () => showAppMenu(app);
|
menu[app.name] = () => showAppMenu(app);
|
||||||
return menu;
|
return menu;
|
||||||
}, appsmenu);
|
}, appsmenu);
|
||||||
appsmenu['Erase All'] = () => {
|
appsmenu[/*LANG*/'Erase All'] = () => {
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
'': {'title': 'Erase All'},
|
'': {'title': /*LANG*/'Erase All'},
|
||||||
'Erase Everything': () => eraseAll(list, true, true),
|
/*LANG*/'Erase Everything': () => eraseAll(list, true, true),
|
||||||
'Erase Apps,Keep Data': () => eraseAll(list, true, false),
|
/*LANG*/'Erase Apps,Keep Data': () => eraseAll(list, true, false),
|
||||||
'Only Erase Data': () => eraseAll(list, false, true),
|
/*LANG*/'Only Erase Data': () => eraseAll(list, false, true),
|
||||||
'< Back': () => showApps(),
|
'< Back': () => showApps(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
appsmenu['...No Apps...'] = {
|
appsmenu[/*LANG*/'...No Apps...'] = {
|
||||||
value: undefined,
|
value: undefined,
|
||||||
format: ()=> '',
|
format: ()=> '',
|
||||||
onchange: ()=> {}
|
onchange: ()=> {}
|
||||||
|
@ -150,16 +150,16 @@ function showApps() {
|
||||||
function showSortAppsMenu() {
|
function showSortAppsMenu() {
|
||||||
const sorterMenu = {
|
const sorterMenu = {
|
||||||
'': {
|
'': {
|
||||||
'title': 'App Sorter',
|
'title': /*LANG*/'App Sorter',
|
||||||
},
|
},
|
||||||
'< Back': () => showMainMenu(),
|
'< Back': () => showMainMenu(),
|
||||||
'Sort: manually': ()=> showSortAppsManually(),
|
/*LANG*/'Sort: manually': ()=> showSortAppsManually(),
|
||||||
'Sort: alph. ASC': () => {
|
/*LANG*/'Sort: alph. ASC': () => {
|
||||||
E.showMessage('Sorting:\nAlphabetically\nascending ...');
|
E.showMessage(/*LANG*/'Sorting:\nAlphabetically\nascending ...');
|
||||||
sortAlphabet(false);
|
sortAlphabet(false);
|
||||||
},
|
},
|
||||||
'Sort: alph. DESC': () => {
|
'Sort: alph. DESC': () => {
|
||||||
E.showMessage('Sorting:\nAlphabetically\ndescending ...');
|
E.showMessage(/*LANG*/'Sorting:\nAlphabetically\ndescending ...');
|
||||||
sortAlphabet(true);
|
sortAlphabet(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -169,7 +169,7 @@ function showSortAppsMenu() {
|
||||||
function showSortAppsManually() {
|
function showSortAppsManually() {
|
||||||
const appsSorterMenu = {
|
const appsSorterMenu = {
|
||||||
'': {
|
'': {
|
||||||
'title': 'Sort: manually',
|
'title': /*LANG*/'Sort: manually',
|
||||||
},
|
},
|
||||||
'< Back': () => showSortAppsMenu(),
|
'< Back': () => showSortAppsMenu(),
|
||||||
};
|
};
|
||||||
|
@ -186,7 +186,7 @@ function showSortAppsManually() {
|
||||||
return menu;
|
return menu;
|
||||||
}, appsSorterMenu);
|
}, appsSorterMenu);
|
||||||
} else {
|
} else {
|
||||||
appsSorterMenu['...No Apps...'] = {
|
appsSorterMenu[/*LANG*/'...No Apps...'] = {
|
||||||
value: undefined,
|
value: undefined,
|
||||||
format: ()=> '',
|
format: ()=> '',
|
||||||
onchange: ()=> {}
|
onchange: ()=> {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "files",
|
"id": "files",
|
||||||
"name": "App Manager",
|
"name": "App Manager",
|
||||||
"version": "0.07",
|
"version": "0.08",
|
||||||
"description": "Show currently installed apps, free space, and allow their deletion from the watch",
|
"description": "Show currently installed apps, free space, and allow their deletion from the watch",
|
||||||
"icon": "files.png",
|
"icon": "files.png",
|
||||||
"tags": "tool,system,files",
|
"tags": "tool,system,files",
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New app!
|
0.01: New app!
|
||||||
0.02: Submitted to app loader
|
0.02: Submitted to app loader
|
||||||
|
0.03: Do not invert colors
|
||||||
|
|
|
@ -35,7 +35,7 @@ function drawImage(fileName) {
|
||||||
Bangle.setLCDBrightness(1); // Full brightness
|
Bangle.setLCDBrightness(1); // Full brightness
|
||||||
|
|
||||||
image = eval(storage.read(fileName)); // Sadly, the only reasonable way to do this
|
image = eval(storage.read(fileName)); // Sadly, the only reasonable way to do this
|
||||||
g.clear().reset().drawImage(image, 88, 88, { rotate: angle });
|
g.clear().reset().setBgColor(0).setColor("#fff").drawImage(image, 88, 88, { rotate: angle });
|
||||||
}
|
}
|
||||||
|
|
||||||
setWatch(info => {
|
setWatch(info => {
|
||||||
|
@ -44,9 +44,9 @@ setWatch(info => {
|
||||||
else angle = 0;
|
else angle = 0;
|
||||||
Bangle.buzz();
|
Bangle.buzz();
|
||||||
|
|
||||||
g.clear().reset().drawImage(image, 88, 88, { rotate: angle })
|
g.clear().reset().setBgColor(0).setColor("#fff").drawImage(image, 88, 88, { rotate: angle })
|
||||||
}
|
}
|
||||||
}, BTN1, { repeat: true });
|
}, BTN1, { repeat: true });
|
||||||
|
|
||||||
// We don't load the widgets because there is no reasonable way to unload them
|
// We don't load the widgets because there is no reasonable way to unload them
|
||||||
drawMenu();
|
drawMenu();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "gallery",
|
"id": "gallery",
|
||||||
"name": "Gallery",
|
"name": "Gallery",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "A gallery that lets you view images uploaded with the IDE (see README)",
|
"description": "A gallery that lets you view images uploaded with the IDE (see README)",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
|
|
|
@ -13,3 +13,4 @@
|
||||||
Reconstruct battery voltage by using calibrated batFullVoltage
|
Reconstruct battery voltage by using calibrated batFullVoltage
|
||||||
Averaging for smoothing compass headings
|
Averaging for smoothing compass headings
|
||||||
Save state if route or waypoint has been chosen
|
Save state if route or waypoint has been chosen
|
||||||
|
0.09: Workaround a minifier issue allowing to install gpstrek with minification enabled
|
||||||
|
|
|
@ -259,7 +259,8 @@ let getCompassSlice = function(compassDataSource){
|
||||||
|
|
||||||
|
|
||||||
if (compassDataSource.getPoints){
|
if (compassDataSource.getPoints){
|
||||||
for (let p of compassDataSource.getPoints()){
|
let points = compassDataSource.getPoints(); //storing this in a variable works around a minifier bug causing a problem in the next line: for(let a of a.getPoints())
|
||||||
|
for (let p of points){
|
||||||
g.reset();
|
g.reset();
|
||||||
var bpos = p.bearing - lastDrawnValue;
|
var bpos = p.bearing - lastDrawnValue;
|
||||||
if (bpos>180) bpos -=360;
|
if (bpos>180) bpos -=360;
|
||||||
|
@ -285,7 +286,8 @@ let getCompassSlice = function(compassDataSource){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (compassDataSource.getMarkers){
|
if (compassDataSource.getMarkers){
|
||||||
for (let m of compassDataSource.getMarkers()){
|
let markers = compassDataSource.getMarkers(); //storing this in a variable works around a minifier bug causing a problem in the next line: for(let a of a.getMarkers())
|
||||||
|
for (let m of markers){
|
||||||
g.reset();
|
g.reset();
|
||||||
g.setColor(m.fillcolor);
|
g.setColor(m.fillcolor);
|
||||||
let mpos = m.xpos * width;
|
let mpos = m.xpos * width;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "gpstrek",
|
"id": "gpstrek",
|
||||||
"name": "GPS Trekking",
|
"name": "GPS Trekking",
|
||||||
"version": "0.08",
|
"version": "0.09",
|
||||||
"description": "Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!",
|
"description": "Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"screenshots": [{"url":"screen1.png"},{"url":"screen2.png"},{"url":"screen3.png"},{"url":"screen4.png"}],
|
"screenshots": [{"url":"screen1.png"},{"url":"screen2.png"},{"url":"screen3.png"},{"url":"screen4.png"}],
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
0.02: Includeas the ha.lib.js library that can be used by other apps or clocks.
|
0.02: Includeas the ha.lib.js library that can be used by other apps or clocks.
|
||||||
0.03: Added clkinfo for clocks.
|
0.03: Added clkinfo for clocks.
|
||||||
0.04: Feedback if clkinfo run is called.
|
0.04: Feedback if clkinfo run is called.
|
||||||
0.05: Clkinfo improvements.
|
0.05: Clkinfo improvements.
|
||||||
|
0.06: Updated clkinfo icon.
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
var haItems = {
|
var haItems = {
|
||||||
name: "Home",
|
name: "Home",
|
||||||
img: atob("GBiBAf/////////n///D//+B//8A//48T/wkD/gkD/A8D+AYB8AYA4eZ4QyZMOyZN+fb5+D/B+B+B+A8B+AYB+AYB+AYB+AYB+A8Bw=="),
|
img: atob("GBiBAAAAAAAAAAAAAAAYAAA+AAB+AADD4AHb4APD4Afn8A/n+BxmOD0mnA0ksAwAMA+B8A/D8A/n8A/n8A/n8A/n8AAAAAAAAAAAAA=="),
|
||||||
items: []
|
items: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "ha",
|
"id": "ha",
|
||||||
"name": "HomeAssistant",
|
"name": "HomeAssistant",
|
||||||
"version": "0.05",
|
"version": "0.06",
|
||||||
"description": "Integrates your BangleJS into HomeAssistant.",
|
"description": "Integrates your BangleJS into HomeAssistant.",
|
||||||
"icon": "ha.png",
|
"icon": "ha.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"hourstrike.app.js","url":"app.js"},
|
{"name":"hourstrike.app.js","url":"app.js"},
|
||||||
{"name":"hourstrike.boot.js","url":"boot.js"},
|
{"name":"hourstrike.boot.js","url":"boot.js"},
|
||||||
{"name":"hourstrike.img","url":"app-icon.js","evaluate":true},
|
{"name":"hourstrike.img","url":"app-icon.js","evaluate":true}
|
||||||
|
], "data" : [
|
||||||
{"name":"hourstrike.json","url":"hourstrike.json"}
|
{"name":"hourstrike.json","url":"hourstrike.json"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,3 +8,4 @@
|
||||||
0.08: Don't force backlight on/watch unlocked on Bangle 2
|
0.08: Don't force backlight on/watch unlocked on Bangle 2
|
||||||
0.09: Grey out BPM until confidence is over 50%
|
0.09: Grey out BPM until confidence is over 50%
|
||||||
0.10: Autoscale raw graph to maximum value seen
|
0.10: Autoscale raw graph to maximum value seen
|
||||||
|
0.11: Automatic translation of strings.
|
||||||
|
|
|
@ -37,7 +37,7 @@ function updateHrm(){
|
||||||
var px = g.getWidth()/2;
|
var px = g.getWidth()/2;
|
||||||
g.setFontAlign(0,-1);
|
g.setFontAlign(0,-1);
|
||||||
g.clearRect(0,24,g.getWidth(),80);
|
g.clearRect(0,24,g.getWidth(),80);
|
||||||
g.setFont("6x8").drawString("Confidence "+(hrmInfo.confidence || "--")+"%", px, 70);
|
g.setFont("6x8").drawString(/*LANG*/"Confidence "+(hrmInfo.confidence || "--")+"%", px, 70);
|
||||||
|
|
||||||
updateScale();
|
updateScale();
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ function updateHrm(){
|
||||||
g.setFontVector(40).setColor(hrmInfo.confidence > 50 ? g.theme.fg : "#888").drawString(str,px,45);
|
g.setFontVector(40).setColor(hrmInfo.confidence > 50 ? g.theme.fg : "#888").drawString(str,px,45);
|
||||||
px += g.stringWidth(str)/2;
|
px += g.stringWidth(str)/2;
|
||||||
g.setFont("6x8").setColor(g.theme.fg);
|
g.setFont("6x8").setColor(g.theme.fg);
|
||||||
g.drawString("BPM",px+15,45);
|
g.drawString(/*LANG*/"BPM",px+15,45);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateScale(){
|
function updateScale(){
|
||||||
|
@ -101,7 +101,7 @@ Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.reset().setFont("6x8",2).setFontAlign(0,-1);
|
g.reset().setFont("6x8",2).setFontAlign(0,-1);
|
||||||
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
|
g.drawString(/*LANG*/"Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
|
||||||
countDown();
|
countDown();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "hrm",
|
"id": "hrm",
|
||||||
"name": "Heart Rate Monitor",
|
"name": "Heart Rate Monitor",
|
||||||
"version": "0.10",
|
"version": "0.11",
|
||||||
"description": "Measure your heart rate and see live sensor data",
|
"description": "Measure your heart rate and see live sensor data",
|
||||||
"icon": "heartrate.png",
|
"icon": "heartrate.png",
|
||||||
"tags": "health",
|
"tags": "health",
|
||||||
|
|
|
@ -9,3 +9,7 @@
|
||||||
0.23: Added note to configure position in "my location" if not done yet. Small fixes.
|
0.23: Added note to configure position in "my location" if not done yet. Small fixes.
|
||||||
0.24: Added fast load
|
0.24: Added fast load
|
||||||
0.25: Minor code optimization
|
0.25: Minor code optimization
|
||||||
|
0.26: BJS2: Swipe down to rotate 180 degree
|
||||||
|
0.27: BJS2: Changed swipe down to swipe up
|
||||||
|
0.28: Reverted changes to implementation of 0.25
|
||||||
|
0.29: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps
|
||||||
|
|
|
@ -14,7 +14,6 @@ Provide names and the UTC offsets for up to three other timezones in the app sto
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
## Requests
|
## Requests
|
||||||
|
|
||||||
Please use [the Espruino Forum](http://forum.espruino.com/microcosms/1424/) if you have feature requests or notice bugs.
|
Please use [the Espruino Forum](http://forum.espruino.com/microcosms/1424/) if you have feature requests or notice bugs.
|
||||||
|
|
|
@ -15,7 +15,7 @@ require("Font5x9Numeric7Seg").add(Graphics);
|
||||||
require("FontTeletext10x18Ascii").add(Graphics);
|
require("FontTeletext10x18Ascii").add(Graphics);
|
||||||
|
|
||||||
// Font for single secondary time
|
// Font for single secondary time
|
||||||
const secondaryTimeFontSize = 4;
|
const secondaryTimeFontSize = 4;
|
||||||
const secondaryTimeZoneFontSize = 2;
|
const secondaryTimeZoneFontSize = 2;
|
||||||
|
|
||||||
// Font / columns for multiple secondary times
|
// Font / columns for multiple secondary times
|
||||||
|
@ -39,14 +39,14 @@ const yposWorld = big ? 170 : 120;
|
||||||
const OFFSET_TIME_ZONE = 0;
|
const OFFSET_TIME_ZONE = 0;
|
||||||
const OFFSET_HOURS = 1;
|
const OFFSET_HOURS = 1;
|
||||||
|
|
||||||
var PosInterval = 0;
|
var PosInterval = 0;
|
||||||
|
|
||||||
var offsets = require("Storage").readJSON("hworldclock.settings.json") || [];
|
var offsets = require("Storage").readJSON("hworldclock.settings.json") || [];
|
||||||
|
|
||||||
//=======Sun
|
//=======Sun
|
||||||
setting = require("Storage").readJSON("setting.json",1);
|
setting = require("Storage").readJSON("setting.json",1);
|
||||||
E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ
|
E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ
|
||||||
SunCalc = require("hsuncalc.js");
|
SunCalc = require("suncalc"); // from modules folder
|
||||||
const LOCATION_FILE = "mylocation.json";
|
const LOCATION_FILE = "mylocation.json";
|
||||||
var rise = "read";
|
var rise = "read";
|
||||||
var set = "...";
|
var set = "...";
|
||||||
|
@ -87,7 +87,7 @@ const mockOffsets = {
|
||||||
//offsets = mockOffsets.fourOffsets; // should render in columns
|
//offsets = mockOffsets.fourOffsets; // should render in columns
|
||||||
|
|
||||||
// END TESTING CODE
|
// END TESTING CODE
|
||||||
|
|
||||||
|
|
||||||
// Load settings
|
// Load settings
|
||||||
function loadMySettings() {
|
function loadMySettings() {
|
||||||
|
@ -141,11 +141,9 @@ function getCurrentTimeFromOffset(dt, offset) {
|
||||||
function updatePos() {
|
function updatePos() {
|
||||||
coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":0,"lon":0,"location":"-"}; //{"lat":53.3,"lon":10.1,"location":"Pattensen"};
|
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) {
|
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);
|
times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon);
|
||||||
rise = "^" + times.sunrise.toString().split(" ")[4].substr(0,5);
|
rise = "^" + times.sunrise.toString().split(" ")[4].substr(0,5);
|
||||||
set = "v" + times.sunset.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 {
|
} else {
|
||||||
rise = null;
|
rise = null;
|
||||||
set = null;
|
set = null;
|
||||||
|
@ -179,7 +177,7 @@ function drawSeconds() {
|
||||||
//console.log(seconds);
|
//console.log(seconds);
|
||||||
if (Bangle.isLocked() && secondsMode != "always") seconds = seconds.slice(0, -1) + ':::'; // we use :: as the font does not have an x
|
if (Bangle.isLocked() && secondsMode != "always") seconds = seconds.slice(0, -1) + ':::'; // we use :: as the font does not have an x
|
||||||
//console.log(seconds);
|
//console.log(seconds);
|
||||||
g.drawString(`${seconds}`, xyCenterSeconds, yposTime+14, true);
|
g.drawString(`${seconds}`, xyCenterSeconds, yposTime+14, true);
|
||||||
queueDrawSeconds();
|
queueDrawSeconds();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -196,18 +194,18 @@ function draw() {
|
||||||
let time = da[4].split(":");
|
let time = da[4].split(":");
|
||||||
let hours = time[0],
|
let hours = time[0],
|
||||||
minutes = time[1];
|
minutes = time[1];
|
||||||
|
|
||||||
|
|
||||||
if (_12hour){
|
if (_12hour){
|
||||||
//do 12 hour stuff
|
//do 12 hour stuff
|
||||||
if (hours > 12) {
|
if (hours > 12) {
|
||||||
ampm = "PM";
|
ampm = "PM";
|
||||||
hours = hours - 12;
|
hours = hours - 12;
|
||||||
if (hours < 10) hours = doublenum(hours);
|
if (hours < 10) hours = doublenum(hours);
|
||||||
} else {
|
} else {
|
||||||
ampm = "AM";
|
ampm = "AM";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//g.setFont(font, primaryTimeFontSize);
|
//g.setFont(font, primaryTimeFontSize);
|
||||||
g.setFont("5x9Numeric7Seg",primaryTimeFontSize);
|
g.setFont("5x9Numeric7Seg",primaryTimeFontSize);
|
||||||
|
@ -221,18 +219,18 @@ function draw() {
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
}
|
}
|
||||||
g.drawString(`${hours}:${minutes}`, xyCenter-10, yposTime, true);
|
g.drawString(`${hours}:${minutes}`, xyCenter-10, yposTime, true);
|
||||||
|
|
||||||
// am / PM ?
|
// am / PM ?
|
||||||
if (_12hour){
|
if (_12hour){
|
||||||
//do 12 hour stuff
|
//do 12 hour stuff
|
||||||
//let ampm = require("locale").medidian(new Date()); Not working
|
//let ampm = require("locale").medidian(new Date()); Not working
|
||||||
g.setFont("Vector", 17);
|
g.setFont("Vector", 17);
|
||||||
g.drawString(ampm, xyCenterSeconds, yAmPm, true);
|
g.drawString(ampm, xyCenterSeconds, yAmPm, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (secondsMode != "none") drawSeconds(); // To make sure...
|
if (secondsMode != "none") drawSeconds(); // To make sure...
|
||||||
|
|
||||||
// draw Day, name of month, Date
|
// draw Day, name of month, Date
|
||||||
//DATE
|
//DATE
|
||||||
let localDate = require("locale").date(new Date(), 1);
|
let localDate = require("locale").date(new Date(), 1);
|
||||||
localDate = localDate.substring(0, localDate.length - 5);
|
localDate = localDate.substring(0, localDate.length - 5);
|
||||||
|
@ -251,7 +249,7 @@ function draw() {
|
||||||
|
|
||||||
|
|
||||||
if (offsets.length === 1) {
|
if (offsets.length === 1) {
|
||||||
let date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)];
|
let 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
|
// For a single secondary timezone, draw it bigger and drop time zone to second line
|
||||||
const xOffset = 30;
|
const xOffset = 30;
|
||||||
g.setFont(font, secondaryTimeFontSize).drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
|
g.setFont(font, secondaryTimeFontSize).drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
|
||||||
|
@ -277,7 +275,7 @@ function draw() {
|
||||||
g.setFontAlign(-1, 0).setFont("Vector",12).drawString(`${rise}`, 10, 3 + yposWorld + 3 * 15, true); // draw rise
|
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
|
g.setFontAlign(1, 0).drawString(`${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw set
|
||||||
} else {
|
} else {
|
||||||
g.setFontAlign(-1, 0).setFont("Vector",11).drawString("set city in \'my location\' app!", 10, 3 + yposWorld + 3 * 15, true);
|
g.setFontAlign(-1, 0).setFont("Vector",11).drawString("set city in \'my location\' app!", 10, 3 + yposWorld + 3 * 15, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//debug settings
|
//debug settings
|
||||||
|
@ -287,7 +285,7 @@ function draw() {
|
||||||
//g.drawString(colorWhenDark, xcol2, 3 + yposWorld + 3 * 15, true);
|
//g.drawString(colorWhenDark, xcol2, 3 + yposWorld + 3 * 15, true);
|
||||||
|
|
||||||
queueDraw();
|
queueDraw();
|
||||||
|
|
||||||
if (secondsMode != "none") queueDrawSeconds();
|
if (secondsMode != "none") queueDrawSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +305,7 @@ Bangle.setUI({
|
||||||
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
||||||
drawTimeoutSeconds = undefined;
|
drawTimeoutSeconds = undefined;
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
}});
|
}});
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
@ -328,15 +326,15 @@ if (!Bangle.isLocked()) { // Initial state
|
||||||
if (secondsMode != "none") {
|
if (secondsMode != "none") {
|
||||||
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
||||||
drawTimeoutSeconds = undefined;
|
drawTimeoutSeconds = undefined;
|
||||||
}
|
}
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
draw(); // draw immediately, queue redraw
|
draw(); // draw immediately, queue redraw
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
if (secondsMode == "always") secondsTimeout = 1000;
|
if (secondsMode == "always") secondsTimeout = 1000;
|
||||||
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
|
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
|
||||||
|
|
||||||
if (secondsMode != "none") {
|
if (secondsMode != "none") {
|
||||||
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
||||||
drawTimeoutSeconds = undefined;
|
drawTimeoutSeconds = undefined;
|
||||||
|
@ -350,9 +348,9 @@ if (!Bangle.isLocked()) { // Initial state
|
||||||
updatePos();
|
updatePos();
|
||||||
}
|
}
|
||||||
draw(); // draw immediately, queue redraw
|
draw(); // draw immediately, queue redraw
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Bangle.on('lock',on=>{
|
Bangle.on('lock',on=>{
|
||||||
if (!on) { // UNlocked
|
if (!on) { // UNlocked
|
||||||
|
@ -366,7 +364,7 @@ Bangle.on('lock',on=>{
|
||||||
if (secondsMode != "none") {
|
if (secondsMode != "none") {
|
||||||
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
||||||
drawTimeoutSeconds = undefined;
|
drawTimeoutSeconds = undefined;
|
||||||
}
|
}
|
||||||
if (drawTimeout) clearTimeout(drawTimeout);
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
drawTimeout = undefined;
|
drawTimeout = undefined;
|
||||||
|
|
||||||
|
@ -375,7 +373,7 @@ Bangle.on('lock',on=>{
|
||||||
|
|
||||||
if (secondsMode == "always") secondsTimeout = 1000;
|
if (secondsMode == "always") secondsTimeout = 1000;
|
||||||
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
|
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
|
||||||
|
|
||||||
if (secondsMode != "none") {
|
if (secondsMode != "none") {
|
||||||
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
||||||
drawTimeoutSeconds = undefined;
|
drawTimeoutSeconds = undefined;
|
||||||
|
@ -388,7 +386,7 @@ Bangle.on('lock',on=>{
|
||||||
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
|
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
|
||||||
updatePos();
|
updatePos();
|
||||||
}
|
}
|
||||||
draw(); // draw immediately, queue redraw
|
draw(); // draw immediately, queue redraw
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,298 +0,0 @@
|
||||||
/* Module suncalc.js
|
|
||||||
(c) 2011-2015, Vladimir Agafonkin
|
|
||||||
SunCalc is a JavaScript library for calculating sun/moon position and light phases.
|
|
||||||
https://github.com/mourner/suncalc
|
|
||||||
|
|
||||||
PB: Usage:
|
|
||||||
E.setTimeZone(2); // 1 = MEZ, 2 = MESZ
|
|
||||||
SunCalc = require("suncalc.js");
|
|
||||||
pos = SunCalc.getPosition(Date.now(), 53.3, 10.1);
|
|
||||||
times = SunCalc.getTimes(Date.now(), 53.3, 10.1);
|
|
||||||
rise = times.sunrise; // Date object
|
|
||||||
rise_str = rise.getHours() + ':' + rise.getMinutes(); //hh:mm
|
|
||||||
*/
|
|
||||||
var exports={};
|
|
||||||
|
|
||||||
// shortcuts for easier to read formulas
|
|
||||||
|
|
||||||
var PI = Math.PI,
|
|
||||||
sin = Math.sin,
|
|
||||||
cos = Math.cos,
|
|
||||||
tan = Math.tan,
|
|
||||||
asin = Math.asin,
|
|
||||||
atan = Math.atan2,
|
|
||||||
acos = Math.acos,
|
|
||||||
rad = PI / 180;
|
|
||||||
|
|
||||||
// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
|
|
||||||
|
|
||||||
// date/time constants and conversions
|
|
||||||
|
|
||||||
var dayMs = 1000 * 60 * 60 * 24,
|
|
||||||
J1970 = 2440588,
|
|
||||||
J2000 = 2451545;
|
|
||||||
|
|
||||||
function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
|
|
||||||
function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } // PB: onece removed + 0.5; included it again 4 Jan 2021
|
|
||||||
function toDays(date) { return toJulian(date) - J2000; }
|
|
||||||
|
|
||||||
|
|
||||||
// general calculations for position
|
|
||||||
|
|
||||||
var e = rad * 23.4397; // obliquity of the Earth
|
|
||||||
|
|
||||||
function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); }
|
|
||||||
function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
|
|
||||||
|
|
||||||
function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
|
|
||||||
function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }
|
|
||||||
|
|
||||||
function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; }
|
|
||||||
|
|
||||||
function astroRefraction(h) {
|
|
||||||
if (h < 0) // the following formula works for positive altitudes only.
|
|
||||||
h = 0; // if h = -0.08901179 a div/0 would occur.
|
|
||||||
|
|
||||||
// formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
|
|
||||||
// 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad:
|
|
||||||
return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179));
|
|
||||||
}
|
|
||||||
|
|
||||||
// general sun calculations
|
|
||||||
|
|
||||||
function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
|
|
||||||
|
|
||||||
function eclipticLongitude(M) {
|
|
||||||
|
|
||||||
var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
|
|
||||||
P = rad * 102.9372; // perihelion of the Earth
|
|
||||||
|
|
||||||
return M + C + P + PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sunCoords(d) {
|
|
||||||
|
|
||||||
var M = solarMeanAnomaly(d),
|
|
||||||
L = eclipticLongitude(M);
|
|
||||||
|
|
||||||
return {
|
|
||||||
dec: declination(L, 0),
|
|
||||||
ra: rightAscension(L, 0)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculates sun position for a given date and latitude/longitude
|
|
||||||
|
|
||||||
exports.getPosition = function (date, lat, lng) {
|
|
||||||
|
|
||||||
var lw = rad * -lng,
|
|
||||||
phi = rad * lat,
|
|
||||||
d = toDays(date),
|
|
||||||
|
|
||||||
c = sunCoords(d),
|
|
||||||
H = siderealTime(d, lw) - c.ra;
|
|
||||||
|
|
||||||
return {
|
|
||||||
azimuth: Math.round((azimuth(H, phi, c.dec) / rad + 180) % 360), // PB: converted to deg
|
|
||||||
altitude: Math.round( altitude(H, phi, c.dec) / rad) // PB: converted to deg
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// sun times configuration (angle, morning name, evening name)
|
|
||||||
|
|
||||||
var times = [
|
|
||||||
[-0.833, 'sunrise', 'sunset' ]
|
|
||||||
];
|
|
||||||
|
|
||||||
// calculations for sun times
|
|
||||||
var J0 = 0.0009;
|
|
||||||
|
|
||||||
function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
|
|
||||||
|
|
||||||
function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
|
|
||||||
function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
|
|
||||||
|
|
||||||
function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
|
|
||||||
function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; }
|
|
||||||
|
|
||||||
// returns set time for the given sun altitude
|
|
||||||
function getSetJ(h, lw, phi, dec, n, M, L) {
|
|
||||||
|
|
||||||
var w = hourAngle(h, phi, dec),
|
|
||||||
a = approxTransit(w, lw, n);
|
|
||||||
return solarTransitJ(a, M, L);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// calculates sun times for a given date, latitude/longitude, and, optionally,
|
|
||||||
// the observer height (in meters) relative to the horizon
|
|
||||||
|
|
||||||
exports.getTimes = function (date, lat, lng, height) {
|
|
||||||
|
|
||||||
height = height || 0;
|
|
||||||
|
|
||||||
var lw = rad * -lng,
|
|
||||||
phi = rad * lat,
|
|
||||||
|
|
||||||
dh = observerAngle(height),
|
|
||||||
|
|
||||||
d = toDays(date),
|
|
||||||
n = julianCycle(d, lw),
|
|
||||||
ds = approxTransit(0, lw, n),
|
|
||||||
|
|
||||||
M = solarMeanAnomaly(ds),
|
|
||||||
L = eclipticLongitude(M),
|
|
||||||
dec = declination(L, 0),
|
|
||||||
|
|
||||||
Jnoon = solarTransitJ(ds, M, L),
|
|
||||||
|
|
||||||
i, len, time, h0, Jset, Jrise;
|
|
||||||
|
|
||||||
|
|
||||||
var result = {
|
|
||||||
solarNoon: fromJulian(Jnoon),
|
|
||||||
nadir: fromJulian(Jnoon - 0.5)
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i = 0, len = times.length; i < len; i += 1) {
|
|
||||||
time = times[i];
|
|
||||||
h0 = (time[0] + dh) * rad;
|
|
||||||
|
|
||||||
Jset = getSetJ(h0, lw, phi, dec, n, M, L);
|
|
||||||
Jrise = Jnoon - (Jset - Jnoon);
|
|
||||||
|
|
||||||
result[time[1]] = fromJulian(Jrise);
|
|
||||||
result[time[2]] = fromJulian(Jset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas
|
|
||||||
|
|
||||||
function moonCoords(d) { // geocentric ecliptic coordinates of the moon
|
|
||||||
|
|
||||||
var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude
|
|
||||||
M = rad * (134.963 + 13.064993 * d), // mean anomaly
|
|
||||||
F = rad * (93.272 + 13.229350 * d), // mean distance
|
|
||||||
|
|
||||||
l = L + rad * 6.289 * sin(M), // longitude
|
|
||||||
b = rad * 5.128 * sin(F), // latitude
|
|
||||||
dt = 385001 - 20905 * cos(M); // distance to the moon in km
|
|
||||||
|
|
||||||
return {
|
|
||||||
ra: rightAscension(l, b),
|
|
||||||
dec: declination(l, b),
|
|
||||||
dist: dt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getMoonPosition = function (date, lat, lng) {
|
|
||||||
|
|
||||||
var lw = rad * -lng,
|
|
||||||
phi = rad * lat,
|
|
||||||
d = toDays(date),
|
|
||||||
|
|
||||||
c = moonCoords(d),
|
|
||||||
H = siderealTime(d, lw) - c.ra,
|
|
||||||
h = altitude(H, phi, c.dec),
|
|
||||||
// formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
|
|
||||||
pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H));
|
|
||||||
|
|
||||||
h = h + astroRefraction(h); // altitude correction for refraction
|
|
||||||
|
|
||||||
return {
|
|
||||||
azimuth: azimuth(H, phi, c.dec),
|
|
||||||
altitude: h,
|
|
||||||
distance: c.dist,
|
|
||||||
parallacticAngle: pa
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// calculations for illumination parameters of the moon,
|
|
||||||
// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
|
|
||||||
// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
|
|
||||||
|
|
||||||
getMoonIllumination = function (date) {
|
|
||||||
|
|
||||||
var d = toDays(date || new Date()),
|
|
||||||
s = sunCoords(d),
|
|
||||||
m = moonCoords(d),
|
|
||||||
|
|
||||||
sdist = 149598000, // distance from Earth to Sun in km
|
|
||||||
|
|
||||||
phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)),
|
|
||||||
inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)),
|
|
||||||
angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) -
|
|
||||||
cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra));
|
|
||||||
|
|
||||||
return {
|
|
||||||
fraction: (1 + cos(inc)) / 2,
|
|
||||||
phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI,
|
|
||||||
angle: angle
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function hoursLater(date, h) {
|
|
||||||
return new Date(date.valueOf() + h * dayMs / 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
|
|
||||||
|
|
||||||
getMoonTimes = function (date, lat, lng, inUTC) {
|
|
||||||
var t = new Date(date);
|
|
||||||
if (inUTC) t.setUTCHours(0, 0, 0, 0);
|
|
||||||
else t.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
var hc = 0.133 * rad,
|
|
||||||
h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc,
|
|
||||||
h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx;
|
|
||||||
|
|
||||||
// go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
|
|
||||||
for (var i = 1; i <= 24; i += 2) {
|
|
||||||
h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc;
|
|
||||||
h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc;
|
|
||||||
|
|
||||||
a = (h0 + h2) / 2 - h1;
|
|
||||||
b = (h2 - h0) / 2;
|
|
||||||
xe = -b / (2 * a);
|
|
||||||
ye = (a * xe + b) * xe + h1;
|
|
||||||
d = b * b - 4 * a * h1;
|
|
||||||
roots = 0;
|
|
||||||
|
|
||||||
if (d >= 0) {
|
|
||||||
dx = Math.sqrt(d) / (Math.abs(a) * 2);
|
|
||||||
x1 = xe - dx;
|
|
||||||
x2 = xe + dx;
|
|
||||||
if (Math.abs(x1) <= 1) roots++;
|
|
||||||
if (Math.abs(x2) <= 1) roots++;
|
|
||||||
if (x1 < -1) x1 = x2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roots === 1) {
|
|
||||||
if (h0 < 0) rise = i + x1;
|
|
||||||
else set = i + x1;
|
|
||||||
|
|
||||||
} else if (roots === 2) {
|
|
||||||
rise = i + (ye < 0 ? x2 : x1);
|
|
||||||
set = i + (ye < 0 ? x1 : x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rise && set) break;
|
|
||||||
|
|
||||||
h0 = h2;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = {};
|
|
||||||
|
|
||||||
if (rise) result.rise = hoursLater(t, rise);
|
|
||||||
if (set) result.set = hoursLater(t, set);
|
|
||||||
|
|
||||||
if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "hworldclock",
|
"id": "hworldclock",
|
||||||
"name": "Hanks World Clock",
|
"name": "Hanks World Clock",
|
||||||
"shortName": "Hanks World Clock",
|
"shortName": "Hanks World Clock",
|
||||||
"version": "0.25",
|
"version": "0.29",
|
||||||
"description": "Current time zone plus up to three others",
|
"description": "Current time zone plus up to three others",
|
||||||
"allow_emulator":true,
|
"allow_emulator":true,
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
@ -15,11 +15,10 @@
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"hworldclock.app.js","url":"app.js"},
|
{"name":"hworldclock.app.js","url":"app.js"},
|
||||||
{"name":"hworldclock.img","url":"hworldclock-icon.js","evaluate":true},
|
{"name":"hworldclock.img","url":"hworldclock-icon.js","evaluate":true},
|
||||||
{"name":"hworldclock.settings.js","url":"settings.js"},
|
{"name":"hworldclock.settings.js","url":"settings.js"}
|
||||||
{"name":"hsuncalc.js","url":"hsuncalc.js"}
|
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
{"name":"hworldclock.settings.json"},
|
{"name":"hworldclock.settings.json"},
|
||||||
{"name":"hworldclock.json"}
|
{"name":"hworldclock.json"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -622,12 +622,12 @@ s.pl = {};
|
||||||
endPerfLog("fullDraw", true);
|
endPerfLog("fullDraw", true);
|
||||||
|
|
||||||
if (!Bangle.uiRemove){
|
if (!Bangle.uiRemove){
|
||||||
setUi();
|
setUi(); // Calls Bangle.setUI() (this comment also fixes lint warning)
|
||||||
let orig = Bangle.drawWidgets;
|
let orig = Bangle.drawWidgets;
|
||||||
Bangle.drawWidgets = ()=>{};
|
Bangle.drawWidgets = ()=>{};
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets = orig;
|
Bangle.drawWidgets = orig;
|
||||||
require("widget_utils").swipeOn();
|
require("widget_utils").swipeOn(0);
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
}
|
}
|
||||||
}).catch((e)=>{
|
}).catch((e)=>{
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
0.03: Positioning of marker now takes the height of the widget field into account.
|
0.03: Positioning of marker now takes the height of the widget field into account.
|
||||||
0.04: Fix issue if going back without typing.
|
0.04: Fix issue if going back without typing.
|
||||||
0.05: Keep drag-function in ram, hopefully improving performance and input reliability somewhat.
|
0.05: Keep drag-function in ram, hopefully improving performance and input reliability somewhat.
|
||||||
|
0.06: Support input of numbers and uppercase characters.
|
||||||
|
|
|
@ -4,6 +4,10 @@ A library that provides the ability to input text by swiping PalmOS Graffiti-sty
|
||||||
|
|
||||||
To get a legend of available characters, just tap the screen.
|
To get a legend of available characters, just tap the screen.
|
||||||
|
|
||||||
|
To switch between the input of alphabetic and numeric characters tap the widget which displays either "123" or "ABC".
|
||||||
|
|
||||||
|
To switch between lowercase and uppercase characters do an up swipe.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
|
@ -1,47 +1,68 @@
|
||||||
|
exports.INPUT_MODE_ALPHA = 0;
|
||||||
|
exports.INPUT_MODE_NUM = 1;
|
||||||
|
|
||||||
/* To make your own strokes, type:
|
/* To make your own strokes, type:
|
||||||
|
|
||||||
Bangle.on('stroke',print)
|
Bangle.on('stroke',print)
|
||||||
|
|
||||||
on the left of the IDE, then do a stroke and copy out the Uint8Array line
|
on the left of the IDE, then do a stroke and copy out the Uint8Array line
|
||||||
*/
|
*/
|
||||||
exports.getStrokes = function(cb) {
|
exports.getStrokes = function(mode, cb) {
|
||||||
cb("a", new Uint8Array([58, 159, 58, 155, 62, 144, 69, 127, 77, 106, 86, 90, 94, 77, 101, 68, 108, 62, 114, 59, 121, 59, 133, 61, 146, 70, 158, 88, 169, 107, 176, 124, 180, 135, 183, 144, 185, 152]));
|
if (mode === exports.INPUT_MODE_ALPHA) {
|
||||||
cb("b", new Uint8Array([51, 47, 51, 77, 56, 123, 60, 151, 65, 163, 68, 164, 68, 144, 67, 108, 67, 76, 72, 43, 104, 51, 121, 74, 110, 87, 109, 95, 131, 117, 131, 140, 109, 152, 88, 157]));
|
cb("a", new Uint8Array([58, 159, 58, 155, 62, 144, 69, 127, 77, 106, 86, 90, 94, 77, 101, 68, 108, 62, 114, 59, 121, 59, 133, 61, 146, 70, 158, 88, 169, 107, 176, 124, 180, 135, 183, 144, 185, 152]));
|
||||||
cb("c", new Uint8Array([153, 62, 150, 62, 145, 62, 136, 62, 123, 62, 106, 65, 85, 70, 65, 75, 50, 82, 42, 93, 37, 106, 36, 119, 36, 130, 40, 140, 49, 147, 61, 153, 72, 156, 85, 157, 106, 158, 116, 158]));
|
cb("b", new Uint8Array([51, 47, 51, 77, 56, 123, 60, 151, 65, 163, 68, 164, 68, 144, 67, 108, 67, 76, 72, 43, 104, 51, 121, 74, 110, 87, 109, 95, 131, 117, 131, 140, 109, 152, 88, 157]));
|
||||||
cb("d", new Uint8Array([57, 178, 57, 176, 55, 171, 52, 163, 50, 154, 49, 146, 47, 135, 45, 121, 44, 108, 44, 97, 44, 85, 44, 75, 44, 66, 44, 58, 44, 48, 44, 38, 46, 31, 48, 26, 58, 21, 75, 20, 99, 26, 120, 35, 136, 51, 144, 70, 144, 88, 137, 110, 124, 131, 106, 145, 88, 153]));
|
cb("c", new Uint8Array([153, 62, 150, 62, 145, 62, 136, 62, 123, 62, 106, 65, 85, 70, 65, 75, 50, 82, 42, 93, 37, 106, 36, 119, 36, 130, 40, 140, 49, 147, 61, 153, 72, 156, 85, 157, 106, 158, 116, 158]));
|
||||||
cb("e", new Uint8Array([150, 72, 141, 69, 114, 68, 79, 69, 48, 77, 32, 81, 31, 85, 46, 91, 73, 95, 107, 100, 114, 103, 83, 117, 58, 134, 66, 143, 105, 148, 133, 148, 144, 148]));
|
cb("d", new Uint8Array([57, 178, 57, 176, 55, 171, 52, 163, 50, 154, 49, 146, 47, 135, 45, 121, 44, 108, 44, 97, 44, 85, 44, 75, 44, 66, 44, 58, 44, 48, 44, 38, 46, 31, 48, 26, 58, 21, 75, 20, 99, 26, 120, 35, 136, 51, 144, 70, 144, 88, 137, 110, 124, 131, 106, 145, 88, 153]));
|
||||||
cb("f", new Uint8Array([157, 52, 155, 52, 148, 52, 137, 52, 124, 52, 110, 52, 96, 52, 83, 52, 74, 52, 67, 52, 61, 52, 57, 52, 55, 52, 52, 52, 52, 54, 52, 58, 52, 64, 54, 75, 58, 97, 59, 117, 60, 130]));
|
cb("e", new Uint8Array([150, 72, 141, 69, 114, 68, 79, 69, 48, 77, 32, 81, 31, 85, 46, 91, 73, 95, 107, 100, 114, 103, 83, 117, 58, 134, 66, 143, 105, 148, 133, 148, 144, 148]));
|
||||||
cb("g", new Uint8Array([160, 66, 153, 62, 129, 58, 90, 56, 58, 57, 38, 65, 31, 86, 43, 125, 69, 152, 116, 166, 145, 154, 146, 134, 112, 116, 85, 108, 97, 106, 140, 106, 164, 106]));
|
cb("f", new Uint8Array([157, 52, 155, 52, 148, 52, 137, 52, 124, 52, 110, 52, 96, 52, 83, 52, 74, 52, 67, 52, 61, 52, 57, 52, 55, 52, 52, 52, 52, 54, 52, 58, 52, 64, 54, 75, 58, 97, 59, 117, 60, 130]));
|
||||||
cb("h", new Uint8Array([58, 50, 58, 55, 58, 64, 58, 80, 58, 102, 58, 122, 58, 139, 58, 153, 58, 164, 58, 171, 58, 177, 58, 179, 58, 181, 58, 180, 58, 173, 58, 163, 59, 154, 61, 138, 64, 114, 68, 95, 72, 84, 80, 79, 91, 79, 107, 82, 123, 93, 137, 111, 145, 130, 149, 147, 150, 154, 150, 159]));
|
cb("g", new Uint8Array([160, 66, 153, 62, 129, 58, 90, 56, 58, 57, 38, 65, 31, 86, 43, 125, 69, 152, 116, 166, 145, 154, 146, 134, 112, 116, 85, 108, 97, 106, 140, 106, 164, 106]));
|
||||||
cb("i", new Uint8Array([89, 48, 89, 49, 89, 51, 89, 55, 89, 60, 89, 68, 89, 78, 89, 91, 89, 103, 89, 114, 89, 124, 89, 132, 89, 138, 89, 144, 89, 148, 89, 151, 89, 154, 89, 156, 89, 157, 89, 158]));
|
cb("h", new Uint8Array([58, 50, 58, 55, 58, 64, 58, 80, 58, 102, 58, 122, 58, 139, 58, 153, 58, 164, 58, 171, 58, 177, 58, 179, 58, 181, 58, 180, 58, 173, 58, 163, 59, 154, 61, 138, 64, 114, 68, 95, 72, 84, 80, 79, 91, 79, 107, 82, 123, 93, 137, 111, 145, 130, 149, 147, 150, 154, 150, 159]));
|
||||||
cb("j", new Uint8Array([130, 57, 130, 61, 130, 73, 130, 91, 130, 113, 130, 133, 130, 147, 130, 156, 130, 161, 130, 164, 130, 166, 129, 168, 127, 168, 120, 168, 110, 168, 91, 167, 81, 167, 68, 167]));
|
cb("i", new Uint8Array([89, 48, 89, 49, 89, 51, 89, 55, 89, 60, 89, 68, 89, 78, 89, 91, 89, 103, 89, 114, 89, 124, 89, 132, 89, 138, 89, 144, 89, 148, 89, 151, 89, 154, 89, 156, 89, 157, 89, 158]));
|
||||||
cb("k", new Uint8Array([149, 63, 147, 68, 143, 76, 136, 89, 126, 106, 114, 123, 100, 136, 86, 147, 72, 153, 57, 155, 45, 152, 36, 145, 29, 131, 26, 117, 26, 104, 27, 93, 30, 86, 35, 80, 45, 77, 62, 80, 88, 96, 113, 116, 130, 131, 140, 142, 145, 149, 148, 153]));
|
cb("j", new Uint8Array([130, 57, 130, 61, 130, 73, 130, 91, 130, 113, 130, 133, 130, 147, 130, 156, 130, 161, 130, 164, 130, 166, 129, 168, 127, 168, 120, 168, 110, 168, 91, 167, 81, 167, 68, 167]));
|
||||||
cb("l", new Uint8Array([42, 55, 42, 59, 42, 69, 44, 87, 44, 107, 44, 128, 44, 143, 44, 156, 44, 163, 44, 167, 44, 169, 45, 170, 49, 170, 59, 169, 76, 167, 100, 164, 119, 162, 139, 160, 163, 159]));
|
cb("k", new Uint8Array([149, 63, 147, 68, 143, 76, 136, 89, 126, 106, 114, 123, 100, 136, 86, 147, 72, 153, 57, 155, 45, 152, 36, 145, 29, 131, 26, 117, 26, 104, 27, 93, 30, 86, 35, 80, 45, 77, 62, 80, 88, 96, 113, 116, 130, 131, 140, 142, 145, 149, 148, 153]));
|
||||||
cb("m", new Uint8Array([49, 165, 48, 162, 46, 156, 44, 148, 42, 138, 42, 126, 42, 113, 43, 101, 45, 91, 47, 82, 49, 75, 51, 71, 54, 70, 57, 70, 61, 74, 69, 81, 75, 91, 84, 104, 94, 121, 101, 132, 103, 137, 106, 130, 110, 114, 116, 92, 125, 75, 134, 65, 139, 62, 144, 66, 148, 83, 151, 108, 155, 132, 157, 149]));
|
cb("l", new Uint8Array([42, 55, 42, 59, 42, 69, 44, 87, 44, 107, 44, 128, 44, 143, 44, 156, 44, 163, 44, 167, 44, 169, 45, 170, 49, 170, 59, 169, 76, 167, 100, 164, 119, 162, 139, 160, 163, 159]));
|
||||||
cb("n", new Uint8Array([50, 165, 50, 160, 50, 153, 50, 140, 50, 122, 50, 103, 50, 83, 50, 65, 50, 52, 50, 45, 50, 43, 52, 52, 57, 67, 66, 90, 78, 112, 93, 131, 104, 143, 116, 152, 127, 159, 135, 160, 141, 150, 148, 125, 154, 96, 158, 71, 161, 56, 162, 49]));
|
cb("m", new Uint8Array([49, 165, 48, 162, 46, 156, 44, 148, 42, 138, 42, 126, 42, 113, 43, 101, 45, 91, 47, 82, 49, 75, 51, 71, 54, 70, 57, 70, 61, 74, 69, 81, 75, 91, 84, 104, 94, 121, 101, 132, 103, 137, 106, 130, 110, 114, 116, 92, 125, 75, 134, 65, 139, 62, 144, 66, 148, 83, 151, 108, 155, 132, 157, 149]));
|
||||||
cb("o", new Uint8Array([107, 58, 104, 58, 97, 61, 87, 68, 75, 77, 65, 88, 58, 103, 54, 116, 53, 126, 55, 135, 61, 143, 75, 149, 91, 150, 106, 148, 119, 141, 137, 125, 143, 115, 146, 104, 146, 89, 142, 78, 130, 70, 116, 65, 104, 62]));
|
cb("n", new Uint8Array([50, 165, 50, 160, 50, 153, 50, 140, 50, 122, 50, 103, 50, 83, 50, 65, 50, 52, 50, 45, 50, 43, 52, 52, 57, 67, 66, 90, 78, 112, 93, 131, 104, 143, 116, 152, 127, 159, 135, 160, 141, 150, 148, 125, 154, 96, 158, 71, 161, 56, 162, 49]));
|
||||||
cb("p", new Uint8Array([52, 59, 52, 64, 54, 73, 58, 88, 61, 104, 65, 119, 67, 130, 69, 138, 71, 145, 71, 147, 71, 148, 71, 143, 70, 133, 68, 120, 67, 108, 67, 97, 67, 89, 68, 79, 72, 67, 83, 60, 99, 58, 118, 58, 136, 63, 146, 70, 148, 77, 145, 84, 136, 91, 121, 95, 106, 97, 93, 97, 82, 97]));
|
cb("o", new Uint8Array([107, 58, 104, 58, 97, 61, 87, 68, 75, 77, 65, 88, 58, 103, 54, 116, 53, 126, 55, 135, 61, 143, 75, 149, 91, 150, 106, 148, 119, 141, 137, 125, 143, 115, 146, 104, 146, 89, 142, 78, 130, 70, 116, 65, 104, 62]));
|
||||||
cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67]));
|
cb("p", new Uint8Array([29, 47, 29, 55, 29, 75, 29, 110, 29, 145, 29, 165, 29, 172, 29, 164, 30, 149, 37, 120, 50, 91, 61, 74, 72, 65, 85, 61, 103, 61, 118, 63, 126, 69, 129, 76, 130, 87, 126, 98, 112, 108, 97, 114, 87, 116]));
|
||||||
cb("r", new Uint8Array([53, 49, 53, 62, 53, 91, 53, 127, 53, 146, 53, 147, 53, 128, 53, 94, 53, 69, 62, 44, 82, 42, 94, 50, 92, 68, 82, 85, 77, 93, 80, 102, 95, 119, 114, 134, 129, 145, 137, 150]));
|
cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67]));
|
||||||
cb("s", new Uint8Array([159, 72, 157, 70, 155, 68, 151, 66, 145, 63, 134, 60, 121, 58, 108, 56, 96, 55, 83, 55, 73, 55, 64, 56, 57, 60, 52, 65, 49, 71, 49, 76, 50, 81, 55, 87, 71, 94, 94, 100, 116, 104, 131, 108, 141, 114, 145, 124, 142, 135, 124, 146, 97, 153, 70, 157, 52, 158]));
|
cb("r", new Uint8Array([53, 49, 53, 62, 53, 91, 53, 127, 53, 146, 53, 147, 53, 128, 53, 94, 53, 69, 62, 44, 82, 42, 94, 50, 92, 68, 82, 85, 77, 93, 80, 102, 95, 119, 114, 134, 129, 145, 137, 150]));
|
||||||
cb("t", new Uint8Array([45, 55, 48, 55, 55, 55, 72, 55, 96, 55, 120, 55, 136, 55, 147, 55, 152, 55, 155, 55, 157, 55, 158, 56, 158, 60, 156, 70, 154, 86, 151, 102, 150, 114, 148, 125, 148, 138, 148, 146]));
|
cb("s", new Uint8Array([159, 72, 157, 70, 155, 68, 151, 66, 145, 63, 134, 60, 121, 58, 108, 56, 96, 55, 83, 55, 73, 55, 64, 56, 57, 60, 52, 65, 49, 71, 49, 76, 50, 81, 55, 87, 71, 94, 94, 100, 116, 104, 131, 108, 141, 114, 145, 124, 142, 135, 124, 146, 97, 153, 70, 157, 52, 158]));
|
||||||
cb("u", new Uint8Array([35, 52, 35, 59, 35, 73, 35, 90, 36, 114, 38, 133, 42, 146, 49, 153, 60, 157, 73, 158, 86, 156, 100, 152, 112, 144, 121, 131, 127, 114, 132, 97, 134, 85, 135, 73, 136, 61, 136, 56]));
|
cb("t", new Uint8Array([45, 55, 48, 55, 55, 55, 72, 55, 96, 55, 120, 55, 136, 55, 147, 55, 152, 55, 155, 55, 157, 55, 158, 56, 158, 60, 156, 70, 154, 86, 151, 102, 150, 114, 148, 125, 148, 138, 148, 146]));
|
||||||
cb("v", new Uint8Array([36, 55, 37, 59, 40, 68, 45, 83, 51, 100, 58, 118, 64, 132, 69, 142, 71, 149, 73, 156, 76, 158, 77, 160, 77, 159, 80, 151, 82, 137, 84, 122, 86, 111, 90, 91, 91, 78, 91, 68, 91, 63, 92, 61, 97, 61, 111, 61, 132, 61, 150, 61, 162, 61]));
|
cb("u", new Uint8Array([35, 52, 35, 59, 35, 73, 35, 90, 36, 114, 38, 133, 42, 146, 49, 153, 60, 157, 73, 158, 86, 156, 100, 152, 112, 144, 121, 131, 127, 114, 132, 97, 134, 85, 135, 73, 136, 61, 136, 56]));
|
||||||
cb("w", new Uint8Array([33, 58, 34, 81, 39, 127, 44, 151, 48, 161, 52, 162, 57, 154, 61, 136, 65, 115, 70, 95, 76, 95, 93, 121, 110, 146, 119, 151, 130, 129, 138, 84, 140, 56, 140, 45]));
|
cb("v", new Uint8Array([36, 55, 37, 59, 40, 68, 45, 83, 51, 100, 58, 118, 64, 132, 69, 142, 71, 149, 73, 156, 76, 158, 77, 160, 77, 159, 80, 151, 82, 137, 84, 122, 86, 111, 90, 91, 91, 78, 91, 68, 91, 63, 92, 61, 97, 61, 111, 61, 132, 61, 150, 61, 162, 61]));
|
||||||
cb("x", new Uint8Array([56, 63, 56, 67, 57, 74, 60, 89, 66, 109, 74, 129, 85, 145, 96, 158, 107, 164, 117, 167, 128, 164, 141, 155, 151, 140, 159, 122, 166, 105, 168, 89, 170, 81, 170, 73, 169, 66, 161, 63, 141, 68, 110, 83, 77, 110, 55, 134, 47, 145]));
|
cb("w", new Uint8Array([25, 46, 25, 82, 25, 119, 33, 143, 43, 153, 60, 147, 73, 118, 75, 91, 76, 88, 85, 109, 96, 134, 107, 143, 118, 137, 129, 112, 134, 81, 134, 64, 134, 55]));
|
||||||
cb("y", new Uint8Array([42, 56, 42, 70, 48, 97, 62, 109, 85, 106, 109, 90, 126, 65, 134, 47, 137, 45, 137, 75, 127, 125, 98, 141, 70, 133, 65, 126, 92, 137, 132, 156, 149, 166]));
|
cb("x", new Uint8Array([56, 63, 56, 67, 57, 74, 60, 89, 66, 109, 74, 129, 85, 145, 96, 158, 107, 164, 117, 167, 128, 164, 141, 155, 151, 140, 159, 122, 166, 105, 168, 89, 170, 81, 170, 73, 169, 66, 161, 63, 141, 68, 110, 83, 77, 110, 55, 134, 47, 145]));
|
||||||
cb("z", new Uint8Array([29, 62, 35, 62, 43, 62, 63, 62, 87, 62, 110, 62, 125, 62, 134, 62, 138, 62, 136, 63, 122, 68, 103, 77, 85, 91, 70, 107, 59, 120, 50, 132, 47, 138, 43, 143, 41, 148, 42, 151, 53, 155, 80, 157, 116, 158, 146, 158, 163, 158]));
|
cb("y", new Uint8Array([30, 41, 30, 46, 30, 52, 30, 63, 30, 79, 33, 92, 38, 100, 47, 104, 54, 107, 66, 105, 79, 94, 88, 82, 92, 74, 94, 77, 96, 98, 96, 131, 94, 151, 91, 164, 85, 171, 75, 171, 71, 162, 74, 146, 84, 130, 95, 119, 106, 113]));
|
||||||
|
cb("z", new Uint8Array([29, 62, 35, 62, 43, 62, 63, 62, 87, 62, 110, 62, 125, 62, 134, 62, 138, 62, 136, 63, 122, 68, 103, 77, 85, 91, 70, 107, 59, 120, 50, 132, 47, 138, 43, 143, 41, 148, 42, 151, 53, 155, 80, 157, 116, 158, 146, 158, 163, 158]));
|
||||||
|
cb("SHIFT", new Uint8Array([100, 160, 100, 50]));
|
||||||
|
} else if (mode === exports.INPUT_MODE_NUM) {
|
||||||
|
cb("0", new Uint8Array([82, 50, 76, 50, 67, 50, 59, 50, 50, 51, 43, 57, 38, 68, 34, 83, 33, 95, 33, 108, 34, 121, 42, 136, 57, 148, 72, 155, 85, 157, 98, 155, 110, 149, 120, 139, 128, 127, 134, 119, 137, 114, 138, 107, 138, 98, 138, 88, 138, 77, 137, 71, 134, 65, 128, 60, 123, 58]));
|
||||||
|
cb("1", new Uint8Array([100, 50, 100, 160]));
|
||||||
|
cb("2", new Uint8Array([40, 79, 46, 74, 56, 66, 68, 58, 77, 49, 87, 45, 100, 45, 111, 46, 119, 50, 128, 58, 133, 71, 130, 88, 120, 106, 98, 128, 69, 150, 50, 162, 42, 167, 43, 168, 58, 169, 78, 170, 93, 170, 103, 170, 109, 170]));
|
||||||
|
cb("3", new Uint8Array([47, 65, 51, 60, 57, 56, 65, 51, 74, 47, 84, 45, 93, 45, 102, 45, 109, 46, 122, 51, 129, 58, 130, 65, 127, 74, 120, 85, 112, 92, 107, 96, 112, 101, 117, 105, 125, 113, 128, 123, 127, 134, 122, 145, 108, 156, 91, 161, 70, 163, 55, 163]));
|
||||||
|
cb("4", new Uint8Array([37, 58, 37, 60, 37, 64, 37, 69, 37, 75, 37, 86, 37, 96, 37, 105, 37, 112, 37, 117, 37, 122, 37, 126, 37, 128, 38, 129, 40, 129, 45, 129, 48, 129, 53, 129, 67, 129, 85, 129, 104, 129, 119, 129, 129, 129, 136, 129]));
|
||||||
|
cb("5", new Uint8Array([142, 60, 119, 60, 79, 60, 45, 60, 37, 64, 37, 86, 37, 103, 47, 107, 66, 106, 81, 103, 97, 103, 116, 103, 129, 108, 131, 130, 122, 152, 101, 168, 85, 172, 70, 172, 59, 172]));
|
||||||
|
cb("6", new Uint8Array([136, 54, 135, 49, 129, 47, 114, 47, 89, 54, 66, 66, 50, 81, 39, 95, 35, 109, 34, 128, 38, 145, 52, 158, 81, 164, 114, 157, 133, 139, 136, 125, 132, 118, 120, 115, 102, 117, 85, 123]));
|
||||||
|
cb("7", new Uint8Array([47, 38, 48, 38, 53, 38, 66, 38, 85, 38, 103, 38, 117, 38, 125, 38, 129, 38, 134, 41, 135, 47, 135, 54, 135, 66, 131, 93, 124, 126, 116, 149, 109, 161, 105, 168]));
|
||||||
|
cb("8", new Uint8Array([122, 61, 102, 61, 83, 61, 60, 61, 47, 62, 45, 78, 58, 99, 84, 112, 105, 122, 118, 134, 121, 149, 113, 165, 86, 171, 59, 171, 47, 164, 45, 144, 50, 132, 57, 125, 67, 117, 78, 109, 87, 102, 96, 94, 105, 86, 113, 85]));
|
||||||
|
cb("9", new Uint8Array([122, 58, 117, 55, 112, 51, 104, 51, 95, 51, 86, 51, 77, 51, 68, 51, 60, 51, 54, 56, 47, 64, 46, 77, 46, 89, 46, 96, 51, 103, 64, 109, 74, 110, 83, 110, 94, 107, 106, 102, 116, 94, 124, 84, 127, 79, 128, 78, 128, 94, 128, 123, 128, 161, 128, 175]));
|
||||||
|
}
|
||||||
cb("\b", new Uint8Array([183, 103, 182, 103, 180, 103, 176, 103, 169, 103, 159, 103, 147, 103, 133, 103, 116, 103, 101, 103, 85, 103, 73, 103, 61, 103, 52, 103, 38, 103, 34, 103, 29, 103, 27, 103, 26, 103, 25, 103, 24, 103]));
|
cb("\b", new Uint8Array([183, 103, 182, 103, 180, 103, 176, 103, 169, 103, 159, 103, 147, 103, 133, 103, 116, 103, 101, 103, 85, 103, 73, 103, 61, 103, 52, 103, 38, 103, 34, 103, 29, 103, 27, 103, 26, 103, 25, 103, 24, 103]));
|
||||||
cb(" ", new Uint8Array([39, 118, 40, 118, 41, 118, 44, 118, 47, 118, 52, 118, 58, 118, 66, 118, 74, 118, 84, 118, 94, 118, 104, 117, 114, 116, 123, 116, 130, 116, 144, 116, 149, 116, 154, 116, 158, 116, 161, 116, 163, 116]));
|
cb(" ", new Uint8Array([39, 118, 40, 118, 41, 118, 44, 118, 47, 118, 52, 118, 58, 118, 66, 118, 74, 118, 84, 118, 94, 118, 104, 117, 114, 116, 123, 116, 130, 116, 144, 116, 149, 116, 154, 116, 158, 116, 161, 116, 163, 116]));
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.input = function(options) {
|
exports.input = function(options) {
|
||||||
options = options||{};
|
options = options||{};
|
||||||
|
let input_mode = exports.INPUT_MODE_ALPHA;
|
||||||
var text = options.text;
|
var text = options.text;
|
||||||
if ("string"!=typeof text) text="";
|
if ("string"!=typeof text) text="";
|
||||||
|
|
||||||
Bangle.strokes = {};
|
function setupStrokes() {
|
||||||
exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
Bangle.strokes = {};
|
||||||
|
exports.getStrokes(input_mode, (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
||||||
|
}
|
||||||
|
setupStrokes();
|
||||||
|
|
||||||
var flashToggle = false;
|
var flashToggle = false;
|
||||||
const R = Bangle.appRect;
|
const R = Bangle.appRect;
|
||||||
|
@ -49,6 +70,9 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
||||||
var Rx2;
|
var Rx2;
|
||||||
var Ry1;
|
var Ry1;
|
||||||
var Ry2;
|
var Ry2;
|
||||||
|
let flashInterval;
|
||||||
|
let shift = false;
|
||||||
|
let lastDrag;
|
||||||
|
|
||||||
function findMarker(strArr) {
|
function findMarker(strArr) {
|
||||||
if (strArr.length == 0) {
|
if (strArr.length == 0) {
|
||||||
|
@ -101,10 +125,13 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
|
if (flashInterval) clearInterval(flashInterval);
|
||||||
|
flashInterval = undefined;
|
||||||
|
|
||||||
g.reset();
|
g.reset();
|
||||||
g.clearRect(R).setColor("#f00");
|
g.clearRect(R).setColor("#f00");
|
||||||
var n=0;
|
var n=0;
|
||||||
exports.getStrokes((id,s) => {
|
exports.getStrokes(input_mode, (id,s) => {
|
||||||
var x = n%6;
|
var x = n%6;
|
||||||
var y = (n-x)/6;
|
var y = (n-x)/6;
|
||||||
s = g.transformVertices(s, {scale:0.16, x:R.x+x*30-4, y:R.y+y*30-4});
|
s = g.transformVertices(s, {scale:0.16, x:R.x+x*30-4, y:R.y+y*30-4});
|
||||||
|
@ -114,39 +141,87 @@ exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) );
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isInside(rect, e) {
|
||||||
|
return e.x>=rect.x && e.x<rect.x+rect.w
|
||||||
|
&& e.y>=rect.y && e.y<=rect.y+rect.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStrokeInside(rect, stroke) {
|
||||||
|
for(let i=0; i < stroke.length; i+=2) {
|
||||||
|
if (!isInside(rect, {x: stroke[i], y: stroke[i+1]})) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function strokeHandler(o) {
|
function strokeHandler(o) {
|
||||||
//print(o);
|
//print(o);
|
||||||
if (!flashInterval)
|
if (!flashInterval)
|
||||||
flashInterval = setInterval(() => {
|
flashInterval = setInterval(() => {
|
||||||
flashToggle = !flashToggle;
|
flashToggle = !flashToggle;
|
||||||
draw();
|
draw(false);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
if (o.stroke!==undefined) {
|
if (o.stroke!==undefined && o.xy.length >= 6 && isStrokeInside(R, o.xy)) {
|
||||||
var ch = o.stroke;
|
var ch = o.stroke;
|
||||||
if (ch=="\b") text = text.slice(0,-1);
|
if (ch=="\b") text = text.slice(0,-1);
|
||||||
else text += ch;
|
else if (ch==="SHIFT") { shift=!shift; Bangle.drawWidgets(); }
|
||||||
g.clearRect(R);
|
else text += shift ? ch.toUpperCase() : ch;
|
||||||
}
|
}
|
||||||
|
lastDrag = undefined;
|
||||||
|
g.clearRect(R);
|
||||||
flashToggle = true;
|
flashToggle = true;
|
||||||
draw();
|
draw(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switches between alphabetic and numeric input
|
||||||
|
function cycleInput() {
|
||||||
|
input_mode++;
|
||||||
|
if (input_mode > exports.INPUT_MODE_NUM) input_mode = 0;
|
||||||
|
shift = false;
|
||||||
|
setupStrokes();
|
||||||
|
show();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
Bangle.on('stroke',strokeHandler);
|
Bangle.on('stroke',strokeHandler);
|
||||||
g.reset().clearRect(R);
|
g.reset().clearRect(R);
|
||||||
show();
|
show();
|
||||||
draw(false);
|
draw(false);
|
||||||
var flashInterval;
|
|
||||||
|
// Create Widget to switch between alphabetic and numeric input
|
||||||
|
WIDGETS.kbswipe={
|
||||||
|
area:"tl",
|
||||||
|
width: 36, // 3 chars, 6*2 px/char
|
||||||
|
draw: function() {
|
||||||
|
g.reset();
|
||||||
|
g.setFont("6x8:2x3");
|
||||||
|
g.setColor("#f00");
|
||||||
|
if (input_mode === exports.INPUT_MODE_ALPHA) {
|
||||||
|
g.drawString(shift ? "ABC" : "abc", this.x, this.y);
|
||||||
|
} else if (input_mode === exports.INPUT_MODE_NUM) {
|
||||||
|
g.drawString("123", this.x, this.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return new Promise((resolve,reject) => {
|
return new Promise((resolve,reject) => {
|
||||||
var l;//last event
|
|
||||||
Bangle.setUI({mode:"custom", drag:e=>{
|
Bangle.setUI({mode:"custom", drag:e=>{
|
||||||
"ram";
|
"ram";
|
||||||
if (l) g.reset().setColor("#f00").drawLine(l.x,l.y,e.x,e.y);
|
if (isInside(R, e)) {
|
||||||
l = e.b ? e : 0;
|
if (lastDrag) g.reset().setColor("#f00").drawLine(lastDrag.x,lastDrag.y,e.x,e.y);
|
||||||
},touch:() => {
|
lastDrag = e.b ? e : 0;
|
||||||
if (flashInterval) clearInterval(flashInterval);
|
}
|
||||||
flashInterval = undefined;
|
},touch:(n,e) => {
|
||||||
show();
|
if (WIDGETS.kbswipe && isInside({x: WIDGETS.kbswipe.x, y: WIDGETS.kbswipe.y, w: WIDGETS.kbswipe.width, h: 24}, e)) {
|
||||||
|
// touch inside widget
|
||||||
|
cycleInput();
|
||||||
|
} else if (isInside(R, e)) {
|
||||||
|
// touch inside app area
|
||||||
|
show();
|
||||||
|
}
|
||||||
}, back:()=>{
|
}, back:()=>{
|
||||||
|
delete WIDGETS.kbswipe;
|
||||||
Bangle.removeListener("stroke", strokeHandler);
|
Bangle.removeListener("stroke", strokeHandler);
|
||||||
if (flashInterval) clearInterval(flashInterval);
|
if (flashInterval) clearInterval(flashInterval);
|
||||||
Bangle.setUI();
|
Bangle.setUI();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "kbswipe",
|
{ "id": "kbswipe",
|
||||||
"name": "Swipe keyboard",
|
"name": "Swipe keyboard",
|
||||||
"version":"0.05",
|
"version":"0.06",
|
||||||
"description": "A library for text input via PalmOS style swipe gestures (beta!)",
|
"description": "A library for text input via PalmOS style swipe gestures (beta!)",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type":"textinput",
|
"type":"textinput",
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Lato
|
||||||
|
|
||||||
|
A simple clock with the Lato font, with fast load and clock_info
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
This clock is a Lato version of Simplest++. Simplest++ provided the
|
||||||
|
smallest example of a clock that supports 'fast load' and 'clock
|
||||||
|
info'. Lato takes this one step further and adds the lovely Lato
|
||||||
|
font. The clock is derived from Simplest++ and inspired by the
|
||||||
|
Pastel Clock.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
* When the screen is unlocked, tap at the bottom of the csreen on the information text.
|
||||||
|
It should change color showing it is selected.
|
||||||
|
|
||||||
|
* Swipe up or down to cycle through the info screens that can be displayed
|
||||||
|
when you have finished tap again towards the centre of the screen to unselect.
|
||||||
|
|
||||||
|
* Swipe left or right to change the type of info screens displayed (by default
|
||||||
|
there is only one type of data so this will have no effect)
|
||||||
|
|
||||||
|
* Settings are saved automatically and reloaded along with the clock.
|
||||||
|
|
||||||
|
## About Clock Info's
|
||||||
|
|
||||||
|
* The clock info modules enable all clocks to add the display of information to the clock face.
|
||||||
|
|
||||||
|
* The default clock_info module provides a display of battery %, Steps, Heart Rate and Altitude.
|
||||||
|
|
||||||
|
* Installing the [Sunrise ClockInfo](https://banglejs.com/apps/?id=clkinfosunrise) adds Sunrise and Sunset times into the list of info's.
|
||||||
|
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
* [What is Fast Load and how does it work](http://www.espruino.com/Bangle.js+Fast+Load)
|
||||||
|
|
||||||
|
* [Clock Info Tutorial](http://www.espruino.com/Bangle.js+Clock+Info)
|
||||||
|
|
||||||
|
* [How to load modules through the IDE](https://github.com/espruino/BangleApps/blob/master/modules/README.md)
|
||||||
|
|
||||||
|
|
||||||
|
## With Thanks
|
||||||
|
|
||||||
|
* Gordon for support
|
||||||
|
* David Peer for his work on BW Clock
|
||||||
|
|
||||||
|
|
||||||
|
Written by: [Hugh Barney](https://github.com/hughbarney) For support
|
||||||
|
and discussion please post in the [Bangle JS
|
||||||
|
Forum](http://forum.espruino.com/microcosms/1424/)
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwkA/4A/AH4AUiIAKCxXzC5c/C5PyC5cvC8PxC5cfLxNEABhgI+gXNp4X3//+9wAK96PJC6/zC5bvKC6//C5YWKC4nUoMUpoXS8lDn/zmlOC6NCA4ckC6Hkl4HD+QwCC5c+LoIsCoSKBMIPjC5tD//0olEp//mgXNmMRiYuBC4JjBBAYAK+MRj//CwIABBAgXkI5AXOiRyBC4J8BkIXN+dEoKnFiNEAYIXNa4sUC59EJAIACkIHBC5iMCoMTn/zmIuBSQIXODAMRAAKqDABikCAAqqBC8i8CAArCBC/n0C49PC5oA/AH4AIA=="))
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"id": "lato",
|
||||||
|
"name": "Lato",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "A Lato Font clock with fast load and clock_info",
|
||||||
|
"readme": "README.md",
|
||||||
|
"icon": "app.png",
|
||||||
|
"screenshots": [{"url":"screenshot3.png"}],
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"lato.app.js","url":"app.js"},
|
||||||
|
{"name":"lato.img","url":"icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
|
@ -7,6 +7,7 @@
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "launch",
|
"type": "launch",
|
||||||
|
"default": true,
|
||||||
"tags": "tool,system,launcher",
|
"tags": "tool,system,launcher",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
|
@ -79,3 +79,6 @@
|
||||||
Move widget to widmessage
|
Move widget to widmessage
|
||||||
0.56: Fix handling of music messages
|
0.56: Fix handling of music messages
|
||||||
0.57: Fix "unread Timeout" = off (previously defaulted to 60s)
|
0.57: Fix "unread Timeout" = off (previously defaulted to 60s)
|
||||||
|
0.58: Fast load messages without writing to flash
|
||||||
|
Don't write messages to flash until the app closes
|
||||||
|
0.59: Ensure we do write messages if messages app can't be fast loaded (see #2373)
|
||||||
|
|
|
@ -48,6 +48,11 @@ to the clock. */
|
||||||
var unreadTimeout;
|
var unreadTimeout;
|
||||||
/// List of all our messages
|
/// List of all our messages
|
||||||
var MESSAGES = require("messages").getMessages();
|
var MESSAGES = require("messages").getMessages();
|
||||||
|
if (Bangle.MESSAGES) {
|
||||||
|
// fast loading messages
|
||||||
|
Bangle.MESSAGES.forEach(m => require("messages").apply(m, MESSAGES));
|
||||||
|
delete Bangle.MESSAGES;
|
||||||
|
}
|
||||||
|
|
||||||
var onMessagesModified = function(type,msg) {
|
var onMessagesModified = function(type,msg) {
|
||||||
if (msg.handled) return;
|
if (msg.handled) return;
|
||||||
|
@ -105,7 +110,6 @@ function showMapMessage(msg) {
|
||||||
layout.render();
|
layout.render();
|
||||||
function back() { // mark as not new and return to menu
|
function back() { // mark as not new and return to menu
|
||||||
msg.new = false;
|
msg.new = false;
|
||||||
saveMessages();
|
|
||||||
layout = undefined;
|
layout = undefined;
|
||||||
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:0});
|
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:0});
|
||||||
}
|
}
|
||||||
|
@ -140,7 +144,6 @@ function showMusicMessage(msg) {
|
||||||
openMusic = false;
|
openMusic = false;
|
||||||
var wasNew = msg.new;
|
var wasNew = msg.new;
|
||||||
msg.new = false;
|
msg.new = false;
|
||||||
saveMessages();
|
|
||||||
layout = undefined;
|
layout = undefined;
|
||||||
if (wasNew) checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:0,openMusic:0});
|
if (wasNew) checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:0,openMusic:0});
|
||||||
else checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
else checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
||||||
|
@ -223,24 +226,20 @@ function showMessageSettings(msg) {
|
||||||
},
|
},
|
||||||
/*LANG*/"Delete" : () => {
|
/*LANG*/"Delete" : () => {
|
||||||
MESSAGES = MESSAGES.filter(m=>m.id!=msg.id);
|
MESSAGES = MESSAGES.filter(m=>m.id!=msg.id);
|
||||||
saveMessages();
|
|
||||||
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
||||||
},
|
},
|
||||||
/*LANG*/"Mark Unread" : () => {
|
/*LANG*/"Mark Unread" : () => {
|
||||||
msg.new = true;
|
msg.new = true;
|
||||||
saveMessages();
|
|
||||||
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
||||||
},
|
},
|
||||||
/*LANG*/"Mark all read" : () => {
|
/*LANG*/"Mark all read" : () => {
|
||||||
MESSAGES.forEach(msg => msg.new = false);
|
MESSAGES.forEach(msg => msg.new = false);
|
||||||
saveMessages();
|
|
||||||
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
||||||
},
|
},
|
||||||
/*LANG*/"Delete all messages" : () => {
|
/*LANG*/"Delete all messages" : () => {
|
||||||
E.showPrompt(/*LANG*/"Are you sure?", {title:/*LANG*/"Delete All Messages"}).then(isYes => {
|
E.showPrompt(/*LANG*/"Are you sure?", {title:/*LANG*/"Delete All Messages"}).then(isYes => {
|
||||||
if (isYes) {
|
if (isYes) {
|
||||||
MESSAGES = [];
|
MESSAGES = [];
|
||||||
saveMessages();
|
|
||||||
}
|
}
|
||||||
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
||||||
});
|
});
|
||||||
|
@ -295,7 +294,7 @@ function showMessage(msgid) {
|
||||||
}
|
}
|
||||||
function goBack() {
|
function goBack() {
|
||||||
layout = undefined;
|
layout = undefined;
|
||||||
msg.new = false; saveMessages(); // read mail
|
msg.new = false; // read mail
|
||||||
cancelReloadTimeout(); // don't auto-reload to clock now
|
cancelReloadTimeout(); // don't auto-reload to clock now
|
||||||
checkMessages({clockIfNoMsg:1,clockIfAllRead:0,showMsgIfUnread:0,openMusic:openMusic});
|
checkMessages({clockIfNoMsg:1,clockIfAllRead:0,showMsgIfUnread:0,openMusic:openMusic});
|
||||||
}
|
}
|
||||||
|
@ -303,7 +302,7 @@ function showMessage(msgid) {
|
||||||
];
|
];
|
||||||
if (msg.positive) {
|
if (msg.positive) {
|
||||||
buttons.push({type:"btn", src:atob("GRSBAAAAAYAAAcAAAeAAAfAAAfAAAfAAAfAAAfAAAfBgAfA4AfAeAfAPgfAD4fAA+fAAP/AAD/AAA/AAAPAAADAAAA=="), cb:()=>{
|
buttons.push({type:"btn", src:atob("GRSBAAAAAYAAAcAAAeAAAfAAAfAAAfAAAfAAAfAAAfBgAfA4AfAeAfAPgfAD4fAA+fAAP/AAD/AAA/AAAPAAADAAAA=="), cb:()=>{
|
||||||
msg.new = false; saveMessages();
|
msg.new = false;
|
||||||
cancelReloadTimeout(); // don't auto-reload to clock now
|
cancelReloadTimeout(); // don't auto-reload to clock now
|
||||||
Bangle.messageResponse(msg,true);
|
Bangle.messageResponse(msg,true);
|
||||||
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:openMusic});
|
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:openMusic});
|
||||||
|
@ -312,7 +311,7 @@ function showMessage(msgid) {
|
||||||
if (msg.negative) {
|
if (msg.negative) {
|
||||||
if (buttons.length) buttons.push({width:32}); // nasty hack...
|
if (buttons.length) buttons.push({width:32}); // nasty hack...
|
||||||
buttons.push({type:"btn", src:atob("FhaBADAAMeAB78AP/4B/fwP4/h/B/P4D//AH/4AP/AAf4AB/gAP/AB/+AP/8B/P4P4fx/A/v4B//AD94AHjAAMA="), cb:()=>{
|
buttons.push({type:"btn", src:atob("FhaBADAAMeAB78AP/4B/fwP4/h/B/P4D//AH/4AP/AAf4AB/gAP/AB/+AP/8B/P4P4fx/A/v4B//AD94AHjAAMA="), cb:()=>{
|
||||||
msg.new = false; saveMessages();
|
msg.new = false;
|
||||||
cancelReloadTimeout(); // don't auto-reload to clock now
|
cancelReloadTimeout(); // don't auto-reload to clock now
|
||||||
Bangle.messageResponse(msg,false);
|
Bangle.messageResponse(msg,false);
|
||||||
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:openMusic});
|
checkMessages({clockIfNoMsg:1,clockIfAllRead:1,showMsgIfUnread:1,openMusic:openMusic});
|
||||||
|
|
|
@ -18,9 +18,25 @@ exports.listener = function(type, msg) {
|
||||||
if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true;
|
if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true;
|
||||||
else return;
|
else return;
|
||||||
}
|
}
|
||||||
require("messages").save(msg);
|
if (Bangle.load === load || !Bangle.uiRemove) {
|
||||||
|
// no fast loading: store message to flash
|
||||||
|
/* FIXME: Maybe we need a better way of deciding if an app will
|
||||||
|
be fast loaded than just hard-coding a Bangle.uiRemove check.
|
||||||
|
Bangle.load could return a bool (as the load doesn't happen immediately). */
|
||||||
|
require("messages").save(msg);
|
||||||
|
} else {
|
||||||
|
if (!Bangle.MESSAGES) Bangle.MESSAGES = [];
|
||||||
|
Bangle.MESSAGES.push(msg);
|
||||||
|
}
|
||||||
|
const saveToFlash = () => {
|
||||||
|
// save messages from RAM to flash after all, if we decide not to launch app
|
||||||
|
if (!Bangle.MESSAGES) return;
|
||||||
|
Bangle.MESSAGES.forEach(m => require("messages").save(m));
|
||||||
|
delete Bangle.MESSAGES;
|
||||||
|
}
|
||||||
msg.handled = true;
|
msg.handled = true;
|
||||||
if ((msg.t!=="add" || !msg.new) && (type!=="music")) { // music always has t:"modify"
|
if ((msg.t!=="add" || !msg.new) && (type!=="music")) { // music always has t:"modify"
|
||||||
|
saveToFlash();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +51,11 @@ exports.listener = function(type, msg) {
|
||||||
exports.messageTimeout = setTimeout(function() {
|
exports.messageTimeout = setTimeout(function() {
|
||||||
delete exports.messageTimeout;
|
delete exports.messageTimeout;
|
||||||
if (type!=="music") {
|
if (type!=="music") {
|
||||||
if (!loadMessages) return require("messages").buzz(msg.src); // no opening the app, just buzz
|
if (!loadMessages) {
|
||||||
|
// not opening the app, just buzz
|
||||||
|
saveToFlash();
|
||||||
|
return require("messages").buzz(msg.src);
|
||||||
|
}
|
||||||
if (!quiet && unlockWatch) {
|
if (!quiet && unlockWatch) {
|
||||||
Bangle.setLocked(false);
|
Bangle.setLocked(false);
|
||||||
Bangle.setLCDPower(1); // turn screen on
|
Bangle.setLCDPower(1); // turn screen on
|
||||||
|
@ -51,9 +71,11 @@ exports.listener = function(type, msg) {
|
||||||
*/
|
*/
|
||||||
exports.open = function(msg) {
|
exports.open = function(msg) {
|
||||||
if (msg && msg.id && !msg.show) {
|
if (msg && msg.id && !msg.show) {
|
||||||
// store which message to load
|
|
||||||
msg.show = 1;
|
msg.show = 1;
|
||||||
require("messages").save(msg, {force: 1});
|
if (Bangle.load === load) {
|
||||||
|
// no fast loading: store message to load in flash
|
||||||
|
require("messages").save(msg, {force: 1});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bangle.load((msg && msg.new && msg.id!=="music") ? "messagegui.new.js" : "messagegui.app.js");
|
Bangle.load((msg && msg.new && msg.id!=="music") ? "messagegui.new.js" : "messagegui.app.js");
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"id": "messagegui",
|
"id": "messagegui",
|
||||||
"name": "Message UI",
|
"name": "Message UI",
|
||||||
"version": "0.57",
|
"shortName": "Messages",
|
||||||
|
"version": "0.59",
|
||||||
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
@ -9,6 +10,7 @@
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
"dependencies" : { "messageicons":"module" },
|
"dependencies" : { "messageicons":"module" },
|
||||||
"provides_modules": ["messagegui"],
|
"provides_modules": ["messagegui"],
|
||||||
|
"default": true,
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"messagegui","url":"lib.js"},
|
{"name":"messagegui","url":"lib.js"},
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.01: Moved message icons from messages into standalone library
|
0.01: Moved message icons from messages into standalone library
|
||||||
|
0.02: Added several new icons and colors
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
{
|
{
|
||||||
"id": "messageicons",
|
"id": "messageicons",
|
||||||
"name": "Message Icons",
|
"name": "Message Icons",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "Library containing a list of icons and colors for apps",
|
"description": "Library containing a list of icons and colors for apps",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"tags": "tool,system",
|
"tags": "tool,system",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
"provides_modules" : ["messageicons"],
|
"provides_modules" : ["messageicons"],
|
||||||
|
"default": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"messageicons","url":"lib.js"}
|
{"name":"messageicons","url":"lib.js"}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
0.55: Moved messages library into standalone library
|
0.55: Moved messages library into standalone library
|
||||||
0.56: Fix handling of music messages
|
0.56: Fix handling of music messages
|
||||||
|
0.57: Optimize saving empty message list
|
||||||
|
0.58: show/hide "messages" widget directly, instead of through library stub
|
||||||
|
|
|
@ -125,9 +125,10 @@ exports.openGUI = function(msg) {
|
||||||
* @param {boolean} show
|
* @param {boolean} show
|
||||||
*/
|
*/
|
||||||
exports.toggleWidget = function(show) {
|
exports.toggleWidget = function(show) {
|
||||||
if (!require("Storage").read("messagewidget")) return; // "messagewidget" module is missing!
|
if (!global.WIDGETS || !WIDGETS["messages"]) return; // widget is missing!
|
||||||
if (show) require("messagewidget").show();
|
const method = WIDGETS["messages"][show ? "show" : "hide"];
|
||||||
else require("messagewidget").hide();
|
/* if (typeof(method)!=="function") return; // widget must always have show()+hide(), fail hard rather than hide problems */
|
||||||
|
method.apply(WIDGETS["messages"]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -135,7 +136,8 @@ exports.toggleWidget = function(show) {
|
||||||
* @param {array} messages Messages to save
|
* @param {array} messages Messages to save
|
||||||
*/
|
*/
|
||||||
exports.write = function(messages) {
|
exports.write = function(messages) {
|
||||||
require("Storage").writeJSON("messages.json", messages.map(m => {
|
if (!messages.length) require("Storage").erase("messages.json");
|
||||||
|
else require("Storage").writeJSON("messages.json", messages.map(m => {
|
||||||
// we never want to save saved/handled status to file;
|
// we never want to save saved/handled status to file;
|
||||||
delete m.saved;
|
delete m.saved;
|
||||||
delete m.handled;
|
delete m.handled;
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
{
|
{
|
||||||
"id": "messages",
|
"id": "messages",
|
||||||
"name": "Messages",
|
"name": "Messages",
|
||||||
"version": "0.56",
|
"version": "0.58",
|
||||||
"description": "Library to handle, load and store message events received from Android/iOS",
|
"description": "Library to handle, load and store message events received from Android/iOS",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"tags": "tool,system",
|
"tags": "tool,system",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
"provides_modules" : ["messages"],
|
"provides_modules" : ["messages"],
|
||||||
"dependencies" : { "messagegui":"module","messagewidget":"module" },
|
"dependencies" : {
|
||||||
|
"messagegui":"module",
|
||||||
|
"message":"widget"
|
||||||
|
},
|
||||||
|
"default": true,
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"messages","url":"lib.js"},
|
{"name":"messages","url":"lib.js"},
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: Initial version
|
0.01: Initial version
|
||||||
0.02: Use default Bangle formatter for booleans
|
0.02: Use default Bangle formatter for booleans
|
||||||
|
0.03: Drop duplicate alarm widget
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "noteify",
|
"id": "noteify",
|
||||||
"name": "Noteify",
|
"name": "Noteify",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Write notes using an onscreen keyboard and use them as custom messages for alarms or timers.",
|
"description": "Write notes using an onscreen keyboard and use them as custom messages for alarms or timers.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm",
|
"tags": "tool,alarm",
|
||||||
|
@ -9,8 +9,7 @@
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"noteify.app.js","url":"app.js"},
|
{"name":"noteify.app.js","url":"app.js"},
|
||||||
{"name":"noteify.img","url":"app-icon.js","evaluate":true},
|
{"name":"noteify.img","url":"app-icon.js","evaluate":true}
|
||||||
{"name":"noteify.wid.js","url":"widget.js"}
|
|
||||||
],
|
],
|
||||||
"data": [{"name":"noteify.json"}],
|
"data": [{"name":"noteify.json"}],
|
||||||
"dependencies": {"scheduler":"type","textinput":"type"},
|
"dependencies": {"scheduler":"type","textinput":"type"},
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
WIDGETS["alarm"]={area:"tl",width:0,draw:function() {
|
|
||||||
if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
|
||||||
},reload:function() {
|
|
||||||
// don't include library here as we're trying to use as little RAM as possible
|
|
||||||
WIDGETS["alarm"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==false)) ? 24 : 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
WIDGETS["alarm"].reload();
|
|
|
@ -19,3 +19,4 @@
|
||||||
0.16: make check_idle boolean setting work properly with new B2 menu
|
0.16: make check_idle boolean setting work properly with new B2 menu
|
||||||
0.17: Use default Bangle formatter for booleans
|
0.17: Use default Bangle formatter for booleans
|
||||||
0.18: fix idle option always getting defaulted to true
|
0.18: fix idle option always getting defaulted to true
|
||||||
|
0.19: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "pastel",
|
"id": "pastel",
|
||||||
"name": "Pastel Clock",
|
"name": "Pastel Clock",
|
||||||
"shortName": "Pastel",
|
"shortName": "Pastel",
|
||||||
"version": "0.18",
|
"version": "0.19",
|
||||||
"description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times",
|
"description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times",
|
||||||
"icon": "pastel.png",
|
"icon": "pastel.png",
|
||||||
"dependencies": {"mylocation":"app","weather":"app"},
|
"dependencies": {"mylocation":"app","weather":"app"},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
|
var SunCalc = require("suncalc"); // from modules folder
|
||||||
require("f_latosmall").add(Graphics);
|
require("f_latosmall").add(Graphics);
|
||||||
const storage = require('Storage');
|
const storage = require('Storage');
|
||||||
const locale = require("locale");
|
const locale = require("locale");
|
||||||
|
@ -85,7 +85,7 @@ function getSteps() {
|
||||||
try {
|
try {
|
||||||
return Bangle.getHealthStatus("day").steps;
|
return Bangle.getHealthStatus("day").steps;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (WIDGETS.wpedom !== undefined)
|
if (WIDGETS.wpedom !== undefined)
|
||||||
return WIDGETS.wpedom.getSteps();
|
return WIDGETS.wpedom.getSteps();
|
||||||
else
|
else
|
||||||
return '???';
|
return '???';
|
||||||
|
@ -181,12 +181,12 @@ function drawClock() {
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
var da = d.toString().split(" ");
|
var da = d.toString().split(" ");
|
||||||
var time = da[4].substr(0,5);
|
var time = da[4].substr(0,5);
|
||||||
|
|
||||||
var hh = da[4].substr(0,2);
|
var hh = da[4].substr(0,2);
|
||||||
var mm = da[4].substr(3,2);
|
var mm = da[4].substr(3,2);
|
||||||
var day = da[0];
|
var day = da[0];
|
||||||
var month_day = da[1] + " " + da[2];
|
var month_day = da[1] + " " + da[2];
|
||||||
|
|
||||||
// fix hh for 12hr clock
|
// fix hh for 12hr clock
|
||||||
var h2 = "0" + parseInt(hh) % 12 || 12;
|
var h2 = "0" + parseInt(hh) % 12 || 12;
|
||||||
if (parseInt(hh) > 12)
|
if (parseInt(hh) > 12)
|
||||||
|
@ -215,12 +215,12 @@ function drawClock() {
|
||||||
g.reset();
|
g.reset();
|
||||||
g.setColor(g.theme.bg);
|
g.setColor(g.theme.bg);
|
||||||
g.fillRect(Bangle.appRect);
|
g.fillRect(Bangle.appRect);
|
||||||
|
|
||||||
// draw a grid like graph paper
|
// draw a grid like graph paper
|
||||||
if (settings.grid && process.env.HWVERSION !=1) {
|
if (settings.grid && process.env.HWVERSION !=1) {
|
||||||
g.setColor("#0f0");
|
g.setColor("#0f0");
|
||||||
for (var gx=20; gx <= w; gx += 20)
|
for (var gx=20; gx <= w; gx += 20)
|
||||||
g.drawLine(gx, 30, gx, h - 24);
|
g.drawLine(gx, 30, gx, h - 24);
|
||||||
for (var gy=30; gy <= h - 24; gy += 20)
|
for (var gy=30; gy <= h - 24; gy += 20)
|
||||||
g.drawLine(0, gy, w, gy);
|
g.drawLine(0, gy, w, gy);
|
||||||
}
|
}
|
||||||
|
@ -238,7 +238,7 @@ function drawClock() {
|
||||||
g.drawString( (w_wind.split(' ').slice(0, 2).join(' ')), (w/2) + 6, 24 + ((y - 24)/2));
|
g.drawString( (w_wind.split(' ').slice(0, 2).join(' ')), (w/2) + 6, 24 + ((y - 24)/2));
|
||||||
// display first 2 words of the wind string eg '4 mph'
|
// display first 2 words of the wind string eg '4 mph'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.font == "Architect")
|
if (settings.font == "Architect")
|
||||||
g.setFontArchitect();
|
g.setFontArchitect();
|
||||||
else if (settings.font == "GochiHand")
|
else if (settings.font == "GochiHand")
|
||||||
|
@ -253,7 +253,7 @@ function drawClock() {
|
||||||
g.setFontSpecialElite();
|
g.setFontSpecialElite();
|
||||||
else
|
else
|
||||||
g.setFontLato();
|
g.setFontLato();
|
||||||
|
|
||||||
g.setFontAlign(1,-1); // right aligned
|
g.setFontAlign(1,-1); // right aligned
|
||||||
g.drawString(hh, x - 6, y);
|
g.drawString(hh, x - 6, y);
|
||||||
g.setFontAlign(-1,-1); // left aligned
|
g.setFontAlign(-1,-1); // left aligned
|
||||||
|
@ -310,7 +310,7 @@ function BUTTON(name,x,y,w,h,c,f,tx) {
|
||||||
// if pressed the callback
|
// if pressed the callback
|
||||||
BUTTON.prototype.check = function(x,y) {
|
BUTTON.prototype.check = function(x,y) {
|
||||||
//console.log(this.name + ":check() x=" + x + " y=" + y +"\n");
|
//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)) {
|
if (x>= this.x && x<= (this.x + this.w) && y>= this.y && y<= (this.y + this.h)) {
|
||||||
log_debug(this.name + ":callback\n");
|
log_debug(this.name + ":callback\n");
|
||||||
this.callback();
|
this.callback();
|
||||||
|
@ -366,7 +366,7 @@ function checkIdle() {
|
||||||
warned = false;
|
warned = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hour = (new Date()).getHours();
|
let hour = (new Date()).getHours();
|
||||||
let active = (hour >= 9 && hour < 21);
|
let active = (hour >= 9 && hour < 21);
|
||||||
//let active = true;
|
//let active = true;
|
||||||
|
@ -397,7 +397,7 @@ function buzzer(n) {
|
||||||
|
|
||||||
if (n-- < 1) return;
|
if (n-- < 1) return;
|
||||||
Bangle.buzz(250);
|
Bangle.buzz(250);
|
||||||
|
|
||||||
if (buzzTimeout) clearTimeout(buzzTimeout);
|
if (buzzTimeout) clearTimeout(buzzTimeout);
|
||||||
buzzTimeout = setTimeout(function() {
|
buzzTimeout = setTimeout(function() {
|
||||||
buzzTimeout = undefined;
|
buzzTimeout = undefined;
|
||||||
|
|
|
@ -4,3 +4,6 @@
|
||||||
Addict.
|
Addict.
|
||||||
0.04: New layout.
|
0.04: New layout.
|
||||||
0.05: Add widget field, tweak layout.
|
0.05: Add widget field, tweak layout.
|
||||||
|
0.06: Add compatibility with Fastload Utils.
|
||||||
|
0.07: Remove just the specific listeners to not interfere with Quick Launch
|
||||||
|
when fastloading.
|
||||||
|
|
|
@ -1,77 +1,20 @@
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], mimetype:"", data:"", package:"", class:"", target:"", extra:{someKey:"someValueOrString"}}));
|
Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], mimetype:"", data:"", package:"", class:"", target:"", extra:{someKey:"someValueOrString"}}));
|
||||||
|
|
||||||
Podcast Addict is developed by Xavier Guillemane and can be downloaded on Google Play Store: https://play.google.com/store/apps/details?id=com.bambuna.podcastaddict&hl=en_US&gl=US
|
Podcast Addict is developed by Xavier Guillemane and can be downloaded on Google Play Store: https://play.google.com/store/apps/details?id=com.bambuna.podcastaddict&hl=en_US&gl=US
|
||||||
|
|
||||||
Podcast Addict can be controlled through the sending of remote commands called 'Intents'.
|
How to use intents to control Podcast Addict: https://podcastaddict.com/faq/130
|
||||||
Some 3rd parties apps specialized in task automation will then allow you to control Podcast Addict. For example, you will be able to wake up to the sound of your playlist or to start automatically playing when some NFC tag has been detected.
|
|
||||||
In Tasker, you just need to copy/paste one of the following intent in the task Action field ("Misc" action type then select "Send Itent") .
|
|
||||||
If you prefer Automate It, you can use the Podcast Addict plugin that will save you some configuration time (https://play.google.com/store/apps/details?id=com.smarterapps.podcastaddictplugin )
|
|
||||||
Before using an intent make sure to set the following:
|
|
||||||
Package: com.bambuna.podcastaddict
|
|
||||||
Class (UPDATE intent only): com.bambuna.podcastaddict.receiver.PodcastAddictBroadcastReceiver
|
|
||||||
Class (every other intent): com.bambuna.podcastaddict.receiver.PodcastAddictPlayerReceiver
|
|
||||||
Here are the supported commands (Intents) :
|
|
||||||
com.bambuna.podcastaddict.service.player.toggle – Toggle the playlist
|
|
||||||
com.bambuna.podcastaddict.service.player.stop – Stop the player and release its resources
|
|
||||||
com.bambuna.podcastaddict.service.player.play – Start playing the playlist
|
|
||||||
com.bambuna.podcastaddict.service.player.pause – Pause the playlist
|
|
||||||
com.bambuna.podcastaddict.service.player.nexttrack – Start playing next track
|
|
||||||
com.bambuna.podcastaddict.service.player.previoustrack – Start playing previous track
|
|
||||||
com.bambuna.podcastaddict.service.player.jumpforward – Jump 30s forward
|
|
||||||
com.bambuna.podcastaddict.service.player.jumpbackward – Jump 15s backward
|
|
||||||
com.bambuna.podcastaddict.service.player.1xspeed - Disable the variable playback speed
|
|
||||||
com.bambuna.podcastaddict.service.player.1.5xspeed – Force the playback speed at 1.5x
|
|
||||||
com.bambuna.podcastaddict.service.player.2xspeed – Force the playback speed at 2.0x
|
|
||||||
com.bambuna.podcastaddict.service.player.stoptimer – Disable the timer
|
|
||||||
com.bambuna.podcastaddict.service.player.15mntimer – Set the timer at 15 minutes
|
|
||||||
com.bambuna.podcastaddict.service.player.30mntimer – Set the timer at 30 minutes
|
|
||||||
com.bambuna.podcastaddict.service.player.60mntimer – Set the timer at 1 hour
|
|
||||||
com.bambuna.podcastaddict.service.update – Trigger podcasts update
|
|
||||||
com.bambuna.podcastaddict.openmainscreen – Open the app on the Main screen
|
|
||||||
com.bambuna.podcastaddict.openplaylist – Open the app on the Playlist screen
|
|
||||||
com.bambuna.podcastaddict.openplayer – Open the app on the Player screen
|
|
||||||
com.bambuna.podcastaddict.opennewepisodes – Open the app on the New episodes screen
|
|
||||||
com.bambuna.podcastaddict.opendownloadedepisodes – Open the app on the Downloaded episodes screen
|
|
||||||
com.bambuna.podcastaddict.service.player.playfirstepisode – Start playing the first episode in the playlist
|
|
||||||
com.bambuna.podcastaddict.service.player.customspeed – Select playback speed
|
|
||||||
In order to use this intent you need to pass a float argument called "arg1". Valid values are within [0.1, 5.0]
|
|
||||||
com.bambuna.podcastaddict.service.player.customtimer – Start a custom timer
|
|
||||||
In order to use this intent you need to pass an int argument called "arg1" containing the number of minutes. Valid values are within [1, 1440]
|
|
||||||
com.bambuna.podcastaddict.service.player.deletecurrentskipnexttrack – Delete the current episode and skip to the next one. It behaves the same way as long pressing on the player >| button, but doesn't display any confirmation popup.
|
|
||||||
com.bambuna.podcastaddict.service.player.deletecurrentskipprevioustrack – Delete the current episode and skip to the previous one. It behaves the same way as long pressing on the player |< button, but doesn't display any confirmation popup.
|
|
||||||
com.bambuna.podcastaddict.service.player.boostVolume – Toggle the Volume Boost audio effect
|
|
||||||
You can pass a, optional boolean argument called "arg1" in order to create a ON or OFF button for the volume boost. Without this parameter the app will just toggle the current value
|
|
||||||
com.bambuna.podcastaddict.service.player.quickBookmark – Creates a bookmark at the current playback position so you can easily retrieve it later.
|
|
||||||
com.bambuna.podcastaddict.service.download.pause – Pause downloads
|
|
||||||
com.bambuna.podcastaddict.service.download.resume – Resume downloads
|
|
||||||
com.bambuna.podcastaddict.service. download.toggle – Toggle downloads
|
|
||||||
com.bambuna.podcastaddict.service.player.favorite – Mark the current episode playing as favorite.
|
|
||||||
com.bambuna.podcastaddict.openplaylist – Open the app on the Playlist screen
|
|
||||||
You can pass an optional string argument called "arg1" in order to select the playlist to open. Without this parameter the app will open the current playlist
|
|
||||||
Here's how it works:
|
|
||||||
##AUDIO## will open the Audio playlist screen
|
|
||||||
##VIDEO## will open the Video playlist screen
|
|
||||||
##RADIO## will open the Radio screen
|
|
||||||
Any other argument will be used as a CATEGORY name. The app will then open this category under the playlist CUSTOM tab
|
|
||||||
You can pass an optional boolean argument called "arg2" in order to select if the app UI should be opened. Without this parameter the playlist will be displayed
|
|
||||||
You can pass an optional boolean argument called "arg3" in order to select if the app should start playing the selected playlist. Without this parameter the playback won't start
|
|
||||||
Since v2020.3
|
|
||||||
com.bambuna.podcastaddict.service.full_backup – Trigger a full backup of the app data (relies on the app automatic backup settings for the folder and the # of backup to keep)
|
|
||||||
This task takes a lot of resources and might take up to a minute to complete, so please avoid using the app at the same time
|
|
||||||
Since v2020.15
|
|
||||||
com.bambuna.podcastaddict.service.player.toggletimer – This will toggle the Sleep Timer using the last duration and parameter used in the app.
|
|
||||||
Since v2020.16
|
|
||||||
com.bambuna.podcastaddict.service.player.togglespeed – This will toggle the Playback speed for the episode currently playing (alternate between selected speed and 1.0x).
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var R;
|
let R;
|
||||||
var backToMenu = false;
|
let widgetUtils = require("widget_utils");
|
||||||
var dark = g.theme.dark; // bool
|
let backToMenu = false;
|
||||||
|
let dark = g.theme.dark; // bool
|
||||||
|
|
||||||
// The main layout of the app
|
// The main layout of the app
|
||||||
function gfx() {
|
let gfx = function() {
|
||||||
//Bangle.drawWidgets();
|
widgetUtils.hide();
|
||||||
R = Bangle.appRect;
|
R = Bangle.appRect;
|
||||||
marigin = 8;
|
marigin = 8;
|
||||||
// g.drawString(str, x, y, solid)
|
// g.drawString(str, x, y, solid)
|
||||||
|
@ -106,19 +49,19 @@ function gfx() {
|
||||||
|
|
||||||
g.setFontAlign(1, 1, 0);
|
g.setFontAlign(1, 1, 0);
|
||||||
g.drawString("Speed", R.x + R.w - 2*marigin, R.y + R.h - 2*marigin);
|
g.drawString("Speed", R.x + R.w - 2*marigin, R.y + R.h - 2*marigin);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Touch handler for main layout
|
// Touch handler for main layout
|
||||||
function touchHandler(_, xy) {
|
let touchHandler = function(_, xy) {
|
||||||
x = xy.x;
|
x = xy.x;
|
||||||
y = xy.y;
|
y = xy.y;
|
||||||
len = (R.w<R.h+1)?(R.w/3):(R.h/3);
|
len = (R.w<R.h+1)?(R.w/3):(R.h/3);
|
||||||
|
|
||||||
// doing a<b+1 seemed faster than a<=b, also using a>b-1 instead of a>b.
|
// doing a<b+1 seemed faster than a<=b, also using a>b-1 instead of a>=b.
|
||||||
if ((R.x-1<x && x<R.x+len) && (R.y-1<y && y<R.y+len)) {
|
if ((R.x-1<x && x<R.x+len) && (R.y-1<y && y<R.y+len)) {
|
||||||
//Menu
|
//Menu
|
||||||
Bangle.removeAllListeners("touch");
|
Bangle.removeListener("touch", touchHandler);
|
||||||
Bangle.removeAllListeners("swipe");
|
Bangle.removeListener("swipe", swipeHandler);
|
||||||
backToMenu = true;
|
backToMenu = true;
|
||||||
E.showMenu(paMenu);
|
E.showMenu(paMenu);
|
||||||
} else if ((R.x-1<x && x<R.x+len) && (R.y2-len<y && y<R.y2+1)) {
|
} else if ((R.x-1<x && x<R.x+len) && (R.y2-len<y && y<R.y2+1)) {
|
||||||
|
@ -127,13 +70,13 @@ function touchHandler(_, xy) {
|
||||||
gadgetbridgeWake();
|
gadgetbridgeWake();
|
||||||
} else if ((R.x2-len<x && x<R.x2+1) && (R.y-1<y && y<R.y+len)) {
|
} else if ((R.x2-len<x && x<R.x2+1) && (R.y-1<y && y<R.y+len)) {
|
||||||
//Srch
|
//Srch
|
||||||
Bangle.removeAllListeners("touch");
|
Bangle.removeListener("touch", touchHandler);
|
||||||
Bangle.removeAllListeners("swipe");
|
Bangle.removeListener("swipe", swipeHandler);
|
||||||
E.showMenu(searchMenu);
|
E.showMenu(searchMenu);
|
||||||
} else if ((R.x2-len<x && x<R.x2+1) && (R.y2-len<y && y<R.y2+1)) {
|
} else if ((R.x2-len<x && x<R.x2+1) && (R.y2-len<y && y<R.y2+1)) {
|
||||||
//Speed
|
//Speed
|
||||||
Bangle.removeAllListeners("touch");
|
Bangle.removeListener("touch", touchHandler);
|
||||||
Bangle.removeAllListeners("swipe");
|
Bangle.removeListener("swipe", swipeHandler);
|
||||||
E.showMenu(speedMenu);
|
E.showMenu(speedMenu);
|
||||||
} else if ((R.x-1<x && x<R.x+len) && (R.y+R.h/2-len/2<y && y<R.y+R.h/2+len/2)) {
|
} else if ((R.x-1<x && x<R.x+len) && (R.y+R.h/2-len/2<y && y<R.y+R.h/2+len/2)) {
|
||||||
//Previous
|
//Previous
|
||||||
|
@ -145,43 +88,51 @@ function touchHandler(_, xy) {
|
||||||
//play/pause
|
//play/pause
|
||||||
btMsg("service", standardCls, "player.toggle");
|
btMsg("service", standardCls, "player.toggle");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Swipe handler for main layout, used to jump backward and forward within a podcast episode.
|
// Swipe handler for main layout, used to jump backward and forward within a podcast episode.
|
||||||
function swipeHandler(LR, _) {
|
let swipeHandler = function(LR, _) {
|
||||||
if (LR==-1) {
|
if (LR==-1) {
|
||||||
btMsg("service", standardCls, "player.jumpforward");
|
btMsg("service", standardCls, "player.jumpforward");
|
||||||
}
|
}
|
||||||
if (LR==1) {
|
if (LR==1) {
|
||||||
btMsg("service", standardCls, "player.jumpbackward");
|
btMsg("service", standardCls, "player.jumpbackward");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Navigation input on the main layout
|
// Navigation input on the main layout
|
||||||
function setUI() {
|
let setUI = function() {
|
||||||
// Bangle.setUI code from rigrig's smessages app for volume control: https://git.tubul.net/rigrig/BangleApps/src/branch/personal/apps/smessages/app.js
|
// Bangle.setUI code from rigrig's smessages app for volume control: https://git.tubul.net/rigrig/BangleApps/src/branch/personal/apps/smessages/app.js
|
||||||
Bangle.setUI(
|
Bangle.setUI(
|
||||||
{mode : "updown", back : load},
|
{mode : "updown",
|
||||||
ud => {
|
remove : ()=>{
|
||||||
if (ud) Bangle.musicControl(ud>0 ? "volumedown" : "volumeup");
|
Bangle.removeListener("touch", touchHandler);
|
||||||
}
|
Bangle.removeListener("swipe", swipeHandler);
|
||||||
|
clearWatch(buttonHandler);
|
||||||
|
widgetUtils.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ud => {
|
||||||
|
if (ud) Bangle.musicControl(ud>0 ? "volumedown" : "volumeup");
|
||||||
|
}
|
||||||
);
|
);
|
||||||
Bangle.on("touch", touchHandler);
|
Bangle.on("touch", touchHandler);
|
||||||
Bangle.on("swipe", swipeHandler);
|
Bangle.on("swipe", swipeHandler);
|
||||||
}
|
let buttonHandler = setWatch(()=>{load();}, BTN, {edge:'falling'});
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The functions for interacting with Android and the Podcast Addict app
|
The functions for interacting with Android and the Podcast Addict app
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pkg = "com.bambuna.podcastaddict";
|
let pkg = "com.bambuna.podcastaddict";
|
||||||
standardCls = pkg + ".receiver.PodcastAddictPlayerReceiver";
|
let standardCls = pkg + ".receiver.PodcastAddictPlayerReceiver";
|
||||||
updateCls = pkg + ".receiver.PodcastAddictBroadcastReceiver";
|
let updateCls = pkg + ".receiver.PodcastAddictBroadcastReceiver";
|
||||||
speed = 1.0;
|
let speed = 1.0;
|
||||||
|
|
||||||
simpleSearch = "";
|
let simpleSearch = "";
|
||||||
|
|
||||||
function simpleSearchTerm() { // input a simple search term without tags, overrides search with tags (artist and track)
|
let simpleSearchTerm = function() { // input a simple search term without tags, overrides search with tags (artist and track)
|
||||||
require("textinput").input({
|
require("textinput").input({
|
||||||
text: simpleSearch
|
text: simpleSearch
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
|
@ -189,9 +140,9 @@ function simpleSearchTerm() { // input a simple search term without tags, overri
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
E.showMenu(searchMenu);
|
E.showMenu(searchMenu);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
function searchPlayWOTags() { //make a search and play using entered terms
|
let searchPlayWOTags = function() { //make a search and play using entered terms
|
||||||
searchString = simpleSearch;
|
searchString = simpleSearch;
|
||||||
Bluetooth.println(JSON.stringify({
|
Bluetooth.println(JSON.stringify({
|
||||||
t: "intent",
|
t: "intent",
|
||||||
|
@ -203,9 +154,9 @@ function searchPlayWOTags() { //make a search and play using entered terms
|
||||||
},
|
},
|
||||||
flags: ["FLAG_ACTIVITY_NEW_TASK"]
|
flags: ["FLAG_ACTIVITY_NEW_TASK"]
|
||||||
}));
|
}));
|
||||||
}
|
};
|
||||||
|
|
||||||
function gadgetbridgeWake() {
|
let gadgetbridgeWake = function() {
|
||||||
Bluetooth.println(JSON.stringify({
|
Bluetooth.println(JSON.stringify({
|
||||||
t: "intent",
|
t: "intent",
|
||||||
target: "activity",
|
target: "activity",
|
||||||
|
@ -213,15 +164,15 @@ function gadgetbridgeWake() {
|
||||||
package: "gadgetbridge",
|
package: "gadgetbridge",
|
||||||
class: "nodomain.freeyourgadget.gadgetbridge.activities.WakeActivity"
|
class: "nodomain.freeyourgadget.gadgetbridge.activities.WakeActivity"
|
||||||
}));
|
}));
|
||||||
}
|
};
|
||||||
|
|
||||||
// For stringing together the action for Podcast Addict to perform
|
// For stringing together the action for Podcast Addict to perform
|
||||||
function actFn(actName, activOrServ) {
|
let actFn = function(actName, activOrServ) {
|
||||||
return "com.bambuna.podcastaddict." + (activOrServ == "service" ? "service." : "") + actName;
|
return "com.bambuna.podcastaddict." + (activOrServ == "service" ? "service." : "") + actName;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Send the intent message to Gadgetbridge
|
// Send the intent message to Gadgetbridge
|
||||||
function btMsg(activOrServ, cls, actName, xtra) {
|
let btMsg = function(activOrServ, cls, actName, xtra) {
|
||||||
|
|
||||||
Bluetooth.println(JSON.stringify({
|
Bluetooth.println(JSON.stringify({
|
||||||
t: "intent",
|
t: "intent",
|
||||||
|
@ -231,22 +182,20 @@ function btMsg(activOrServ, cls, actName, xtra) {
|
||||||
target: "broadcastreceiver",
|
target: "broadcastreceiver",
|
||||||
extra: xtra
|
extra: xtra
|
||||||
}));
|
}));
|
||||||
}
|
};
|
||||||
|
|
||||||
// Get back to the main layout
|
// Get back to the main layout
|
||||||
function backToGfx() {
|
let backToGfx = function() {
|
||||||
E.showMenu();
|
E.showMenu();
|
||||||
g.clear();
|
g.clear();
|
||||||
g.reset();
|
g.reset();
|
||||||
Bangle.removeAllListeners("touch");
|
|
||||||
Bangle.removeAllListeners("swipe");
|
|
||||||
setUI();
|
setUI();
|
||||||
gfx();
|
gfx();
|
||||||
backToMenu = false;
|
backToMenu = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Podcast Addict Menu
|
// Podcast Addict Menu
|
||||||
var paMenu = {
|
let paMenu = {
|
||||||
"": {
|
"": {
|
||||||
title: " ",
|
title: " ",
|
||||||
back: backToGfx
|
back: backToGfx
|
||||||
|
@ -271,7 +220,7 @@ var paMenu = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var controlMenu = {
|
let controlMenu = {
|
||||||
"": {
|
"": {
|
||||||
title: " ",
|
title: " ",
|
||||||
back: () => {if (backToMenu) E.showMenu(paMenu);
|
back: () => {if (backToMenu) E.showMenu(paMenu);
|
||||||
|
@ -310,7 +259,7 @@ var controlMenu = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var speedMenu = {
|
let speedMenu = {
|
||||||
"": {
|
"": {
|
||||||
title: " ",
|
title: " ",
|
||||||
back: () => {if (backToMenu) E.showMenu(paMenu);
|
back: () => {if (backToMenu) E.showMenu(paMenu);
|
||||||
|
@ -333,7 +282,7 @@ var speedMenu = {
|
||||||
//"Slower" : ()=>{speed-=0.1; speed=((speed<0.1)?0.1:speed); btMsg("service",standardCls,"player.customspeed",{arg1:speed});},
|
//"Slower" : ()=>{speed-=0.1; speed=((speed<0.1)?0.1:speed); btMsg("service",standardCls,"player.customspeed",{arg1:speed});},
|
||||||
};
|
};
|
||||||
|
|
||||||
var searchMenu = {
|
let searchMenu = {
|
||||||
"": {
|
"": {
|
||||||
title: " ",
|
title: " ",
|
||||||
|
|
||||||
|
@ -356,7 +305,7 @@ var searchMenu = {
|
||||||
"Simpler search and play" : searchPlayWOTags,
|
"Simpler search and play" : searchPlayWOTags,
|
||||||
};
|
};
|
||||||
|
|
||||||
var navigationMenu = {
|
let navigationMenu = {
|
||||||
"": {
|
"": {
|
||||||
title: " ",
|
title: " ",
|
||||||
back: () => {if (backToMenu) E.showMenu(paMenu);
|
back: () => {if (backToMenu) E.showMenu(paMenu);
|
||||||
|
@ -372,4 +321,6 @@ var navigationMenu = {
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
setUI();
|
setUI();
|
||||||
|
widgetUtils.hide();
|
||||||
gfx();
|
gfx();
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "podadrem",
|
"id": "podadrem",
|
||||||
"name": "Podcast Addict Remote",
|
"name": "Podcast Addict Remote",
|
||||||
"shortName": "PA Remote",
|
"shortName": "PA Remote",
|
||||||
"version": "0.05",
|
"version": "0.07",
|
||||||
"description": "Control Podcast Addict on your android device.",
|
"description": "Control Podcast Addict on your android device.",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"presentor.app.js","url":"app.js"},
|
{"name":"presentor.app.js","url":"app.js"},
|
||||||
{"name":"presentor.img","url":"app-icon.js","evaluate":true},
|
{"name":"presentor.img","url":"app-icon.js","evaluate":true}
|
||||||
|
], "data": [
|
||||||
{"name":"presentor.json","url":"settings.json"}
|
{"name":"presentor.json","url":"settings.json"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
0.01: first release
|
0.01: first release
|
||||||
0.02: added option to buzz on prime, with settings
|
0.02: added option to buzz on prime, with settings
|
||||||
|
0.03: added option to debug settings and test fw 2.15.93 load speed ups
|
||||||
|
0.04: changed icon
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Prime Time Lato (clock)
|
# Prime Lato (clock)
|
||||||
|
|
||||||
A watchface that displays time and its prime factors in the Lato font.
|
A watchface that displays time and its prime factors in the Lato font.
|
||||||
For example when the time is 21:05, the prime factors are 5,421.
|
For example when the time is 21:05, the prime factors are 5,421.
|
||||||
|
|
|
@ -2,7 +2,8 @@ const h = g.getHeight();
|
||||||
const w = g.getWidth();
|
const w = g.getWidth();
|
||||||
const SETTINGS_FILE = "primetimelato.json";
|
const SETTINGS_FILE = "primetimelato.json";
|
||||||
let settings;
|
let settings;
|
||||||
|
let setStr = 'U';
|
||||||
|
|
||||||
Graphics.prototype.setFontLato = function(scale) {
|
Graphics.prototype.setFontLato = function(scale) {
|
||||||
// Actual height 41 (43 - 3)
|
// Actual height 41 (43 - 3)
|
||||||
this.setFontCustom(
|
this.setFontCustom(
|
||||||
|
@ -28,6 +29,22 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
|
||||||
function loadSettings() {
|
function loadSettings() {
|
||||||
settings = require("Storage").readJSON(SETTINGS_FILE,1)||{};
|
settings = require("Storage").readJSON(SETTINGS_FILE,1)||{};
|
||||||
settings.buzz_on_prime = (settings.buzz_on_prime === undefined ? false : settings.buzz_on_prime);
|
settings.buzz_on_prime = (settings.buzz_on_prime === undefined ? false : settings.buzz_on_prime);
|
||||||
|
settings.debug = (settings.debug === undefined ? false : settings.debug);
|
||||||
|
|
||||||
|
switch(settings.buzz_on_prime) {
|
||||||
|
case true:
|
||||||
|
setStr = 'T';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case false:
|
||||||
|
setStr = 'F';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case undefined:
|
||||||
|
default:
|
||||||
|
setStr = 'U';
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a list of prime factors of n and outputs them as a string, if n is prime outputs "Prime Time!"
|
// creates a list of prime factors of n and outputs them as a string, if n is prime outputs "Prime Time!"
|
||||||
|
@ -69,9 +86,16 @@ function draw() {
|
||||||
g.setColor(0,0,0);
|
g.setColor(0,0,0);
|
||||||
g.fillRect(Bangle.appRect);
|
g.fillRect(Bangle.appRect);
|
||||||
|
|
||||||
|
g.setColor(100,100,100);
|
||||||
|
|
||||||
|
if (settings.debug) {
|
||||||
|
g.setFontLatoSmall();
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
g.drawString(setStr, w/2, h/4);
|
||||||
|
}
|
||||||
|
|
||||||
g.setFontLato();
|
g.setFontLato();
|
||||||
g.setFontAlign(0, 0);
|
g.setFontAlign(0, 0);
|
||||||
g.setColor(100,100,100);
|
|
||||||
g.drawString(timeStr, w/2, h/2);
|
g.drawString(timeStr, w/2, h/2);
|
||||||
|
|
||||||
g.setFontLatoSmall();
|
g.setFontLatoSmall();
|
||||||
|
|
Before Width: | Height: | Size: 710 B After Width: | Height: | Size: 1.4 KiB |