1
0
Fork 0
jeffmer 2020-12-28 11:01:25 +00:00
commit 57907b0e6f
14 changed files with 489 additions and 262 deletions

View File

@ -1492,7 +1492,7 @@
"name": "Digital Assistant, not EDITH",
"shortName": "DANE",
"icon": "app.png",
"version": "0.11",
"version": "0.15",
"description": "A Watchface inspired by Tony Stark's EDITH and based on https://arwes.dev/",
"tags": "clock",
"type": "clock",
@ -1513,7 +1513,7 @@
"name": "DANE Touch Launcher",
"shortName":"DANE Toucher",
"icon": "app.png",
"version":"0.03",
"version":"0.07",
"description": "Touch enable left to right launcher in the style of the DANE Watchface",
"tags": "tool,system,launcher",
"type":"launch",
@ -2307,7 +2307,7 @@
"name": "World Clock - 4 time zones",
"shortName":"World Clock",
"icon": "app.png",
"version":"0.02",
"version":"0.03",
"description": "Current time zone plus up to four others",
"tags": "clock",
"type" : "clock",
@ -2491,5 +2491,17 @@
{"name":"dtlaunch.app.js","url":"app.js"},
{"name":"dtlaunch.img","url":"app-icon.js","evaluate":true}
]
},
{ "id": "HRV",
"name": "HRV monitor app",
"shortName":"HRV monitor",
"icon": "hrv.png",
"version":"0.01",
"description": "Heart Rate Variability app",
"tags": "",
"storage": [
{"name":"HRV.app.js","url":"app.js"},
{"name":"HRV.img","url":"app-icon.js","evaluate":true}
]
}
]

1
apps/HRV/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkEIf4A/ACUf+IXV///C/4X/C+jOIC5sBBxAIHgcgFwgXPFAUQAohHOFAQRCAoQdCC5QoCCQQuKC4woCCYQuKC4ooC+UvDQi/NFAUggRKEC5guDAoIwCFxIXEFwaqHC5YoGj4uKC4woKI5cxCyDvHC/4X/C/4XqAB3xC4s/DCAXFh4XWGCBHGAH4A/ABg"))

205
apps/HRV/app.js Normal file
View File

@ -0,0 +1,205 @@
var file = require("Storage").open("HR_log.csv", "w");
file.write(""); //reset log
file = require("Storage").open("HR_log.csv", "a");
//debugging or analysis files
var cutoff_file = require("Storage").open("cuttoff.csv", "w");
var peaks_file = require("Storage").open("peaks.csv", "w");
var debugging = true;
var first_signals = 0; // ignore the first several signals
var heartrate = [];
var BPM_array = [];
var raw_HR_array = new Float32Array(1024);
var alternate_array = new Float32Array(3072);
var pulse_array = [];
var pulsecount = 0;
var cutoff_threshold = 0.5;
var sample_frequency = 51.6;
var gap_threshold = 0.15;
var hr_min = 40;
var hr_max = 160;
g.setFontAlign(0, 0); // center font
g.setFont("6x8", 2);
function storeMyData(data, file_type) {
log = raw_HR_array;
// shift elements backwards - note the 4, because a Float32 is 4 bytes
log.set(new Float32Array(log.buffer, 4 /*bytes*/));
// add ad final element
log[log.length - 1] = data;
}
function average(samples) {
var sum = 0;
for (var i = 0; i < samples.length; i++) {
sum += parseFloat(samples[i]);
}
var avg = sum / samples.length;
return avg;
}
function StandardDeviation(data) {
var m = average(data);
return Math.sqrt(data.reduce(function (sq, n) {
return sq + Math.pow(n - m, 2);
}, 0) / (data.length - 1));
}
function turn_off() {
Bangle.setHRMPower(0);
g.clear();
g.drawString("processing 1/5", 120, 120);
rolling_average(raw_HR_array,5);
g.clear();
g.drawString("processing 2/5", 120, 120);
upscale();
g.clear();
g.drawString("processing 3/5", 120, 120);
rolling_average(alternate_array,5);
g.clear();
g.drawString("processing 4/5", 120, 120);
apply_cutoff();
if(debugging)
for (let i = 0; i < 256; i++) {
cutoff_file.write(alternate_array[i] + "," + "\n");
}
find_peaks();
if(debugging)
for (let i = 0; i < pulse_array.length; i++) {
peaks_file.write(pulse_array[i] + "," + "\n");
}
g.clear();
g.drawString("processing 5/5", 120, 120);
calculate_HRV();
}
function bernstein(A, B, C, D, E, t) {
s = 1 - t;
x = (A * Math.pow(s, 4)) + (B * 4 * Math.pow(s, 3) * t) + (C * 6 * s * s * t * t)
+ (D * 4 * s * Math.pow(t, 3)) + (E * Math.pow(t, 4));
return x;
}
function upscale() {
var index = 0;
for (let i = raw_HR_array.length - 1; i > 5; i -= 5) {
p0 = raw_HR_array[i];
p1 = raw_HR_array[i - 1];
p2 = raw_HR_array[i - 2];
p3 = raw_HR_array[i - 3];
p4 = raw_HR_array[i - 4];
for (let T = 0; T < 100; T += 10) {
x = T / 100;
D = bernstein(p0, p1, p2, p3, p4, x);
alternate_array[index] = D;
index++;
}
}
}
function rolling_average(values, count) {
temp_array = [];
for (let i = 0; i < values.length; i++) {
temp_array = [];
for (let x = 0; x < count; x++)
temp_array.push(values[i + x]);
values[i] = average(temp_array);
}
}
function apply_cutoff() {
var x;
for (let i = 0; i < alternate_array.length; i++) {
x = alternate_array[i];
if (x < cutoff_threshold)
x = cutoff_threshold;
alternate_array[i] = x;
}
}
function find_peaks() {
var previous;
var previous_slope = 0;
var slope;
var gap_size = 0;
var temp_array = [];
for (let i = 0; i < alternate_array.length; i++) {
if (previous == null)
previous = alternate_array[i];
slope = alternate_array[i] - previous;
if (slope * previous_slope < 0) {
if (gap_size > 30) {
pulse_array.push(gap_size);
gap_size = 0;
}
}
else {
gap_size++;
}
previous_slope = slope;
previous = alternate_array[i];
}
}
function calculate_HRV() {
var gap_average = average(pulse_array);
var temp_array = [];
var gap_max = (1 + gap_threshold) * gap_average;
var gap_min = (1 - gap_threshold) * gap_average;
for (let i = 0; i < pulse_array.length; i++) {
if (pulse_array[i] > gap_min && pulse_array[i] < gap_max)
temp_array.push(pulse_array[i]);
}
gap_average = average(temp_array);
var calculatedHR = (sample_frequency*60)/(gap_average/2);
g.flip();
g.clear();
//var display_stdv = StandardDeviation(pulse_array).toFixed(1);
var HRV = (StandardDeviation(temp_array) * (1 / (sample_frequency * 2) * 1000)).toFixed(0);
g.drawString("HRV:" + HRV + "\nHR:" + calculatedHR.toFixed(0)
+"\nSample Count:" + temp_array.length, 120, 120);
Bangle.buzz(500,1);
}
g.clear();
g.drawString("preparing", 120, 120);
Bangle.setHRMPower(1);
Bangle.on('HRM', function (hrm) {
g.flip();
if (first_signals < 5) {
g.clear();
g.drawString("setting up...\nremain still " + first_signals * 20 + "%", 120, 120);
first_signals++;
}
else {
BPM_array = hrm.raw;
if(hrm.bpm > hr_min && hrm.bpm < hr_max)
heartrate.push(hrm.bpm);
if (pulsecount < 7) {
for (let i = 0; i < 256; i++) {
file.write(BPM_array[i]+","+"\n");
storeMyData(BPM_array[i], 0);
}
g.clear();
g.drawString("logging: " + ((pulsecount/6)*100).toFixed(0) + "%", 120, 120);
}
if(pulsecount == 6)
turn_off();
pulsecount++;
}
});

BIN
apps/HRV/hrv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

View File

@ -6,4 +6,8 @@
0.08: Removed Image, Reduced RAM usage
0.09: Added Unix Time
0.10: Added Counter, Added Battery Display
0.11: Code Refactoring, Improved Description
0.11: Code Refactoring, Improved Description
0.12: Move code to Arwes Module
0.13: Improve icon
0.14: Switch Icon back to 8bit web palette to fix it
0.15: Hotfix: Remove var declaration from app image

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("l8wxH+AH4A/AH4A/AFUvl8Cu4AEgUCBQIrfFQMRAAe/Aw4xbDYIlBiUS7AjCAAY5BBYMSiJkBGC4sCicTiRQJHoUSCAIwBF6sv30SikUiRMMMIISD7AvTl/YiYtPF40TF6R4BicVFqAWDF4MViaPRIwQWTF4O/IwiKRCoMRUiZHEDJ5cXJAxeOOQuQhQuShWQJIe/JJkviIuC74tTFwORRqKLD+3cmVLpsLFZtNAANKhXeDYKNOu4uEmdlDwVNBoNlsoDDmoKBhYQChcyFycVFwOTFwJcBpomBhYjCmouBAwYMCmZdBa4d3FyonBKoIoCAwIECLooucEIIjCRIYuFms1Lqq7CFwS7DLQQsDhYrBHIZdHXZkCdQpQDXoIQDFwIDBeoQQCpYuSl8RFwMT70KCRYAIhUSFwMTiMvFxm/CQUSFyp5Did3Fxi8DOBwuLDSEv7ETfoRCNDI13DIMT34ZPIYSgOaxJ3SIgZeTC7COBdgMCC58vOoakWiQvQFoQTBFqgvEiURF5gRDOKIdIDwMRiO/axMCBoMRLQItXF4Z9B7F3BxF37BZBAAQnRIYobDMAKqIl5aDAA5zJFwaCBAA6PBFxQQEAAYKBFxjSCU4IECA4YuJCAoAEFx0UikTAAIEBAwQuKCIoADFxsCI5RdiUAoAEVgIVJABRDHAH4A/AH4A/ADAA="))
require("heatshrink").decompress(atob("l8wxH+AH4A/AH4A/AFVCnk2m0wAAc2nk8oQrhg0AAAUGmAGFGQIxaFgMMlYABEYYACLYI5BBgMAmAwYoU2gAtCmwpBNRIPClcwF6paElZMMGAISDhk8LacwFqAvHSAItWRIKgQR6qmXoTvCIwQVUUiaOEgxHOLgkMLiJeVCYs3oYuSoc3JKM8gASCg8+FqX+oguEgCnMRYkHotkstlFZoPBstGnwbDaxouEhFF2eJAAIwCx+JAYIKBoNlAQIQCsk/g68QmARDmVGx9BJ4ImDAYNBGQJYBAwIICoFFFzYhCFAVlAgQBCLoVlF0CSDGoYFEoNGoguRXYguB2aACQYYsDAgK+Cx4NBLqbqGWALpCdYQABYAIDBFQP+MIVkLos8Fxc8gARCg9ECRYAInwuDgAuMoUGFwc9Fyk9LgcwoQTMRoifMDTk8LwZCOPA0wDIUGDJ5DDUBrWLDCBEEmxeRC7clngXPoR1DUjIvOFrIbDd4MlO5k8IIUGRKQvGngwBgAeBfxINDT6AvKAAM8g0wBxEwgwrBAAQnROAIUDDYbBBVRFCQ4QAHO4IuMhk2OoIABAYcwgAuIgEwCIIAFM4JzJFwbSBABAuKCpQuZngUGngu/AA6xBAAKmGAAJEJABRDHAH4A/AH4A/ADAA=="))

View File

@ -1,3 +1,7 @@
var d = require("dane_arwes");
var Arwes = d.default();
const font = "6x8";
const timeFontSize = 4;
const unixTimeFontSize = 2;
@ -7,112 +11,14 @@ const yOffset = 23;
const width = g.getWidth();
const height = g.getHeight();
const xyCenter = width / 2 + 4;
const cornerSize = 14;
const cornerOffset = 3;
const borderWidth = 1;
const yposTime = 27 + yOffset;
const yposDate = 65 + yOffset + 12;
const yposCounter = 58 + yOffset + 35 + 40;
const mainColor = "#26dafd";
const mainColorDark = "#029dbb";
// const mainColorLight = "#8bebfe";
const secondaryColor = "#df9527";
const secondaryColorDark = "#8b5c15";
// const secondaryColorLight = "#ecc180";
const success = "#00ff00";
// const successDark = "#000900";
// const successLight = "#060f06";
const alert = "#ff0000";
// const alertDark = "#090000";
// const alertLight = "#0f0606";
let count = 100;
let oldCount = count;
function drawTopLeftCorner(x, y) {
g.setColor(mainColor);
const x1 = x - cornerOffset;
const y1 = y - cornerOffset;
g.fillRect(x1, y1, x1 + cornerSize, y1 + cornerSize);
g.setColor("#000000");
g.fillRect(x, y, x + cornerSize - cornerOffset, y + cornerSize - cornerOffset);
}
function drawTopRightCorner(x, y) {
g.setColor(mainColor);
const x1 = x + cornerOffset;
const y1 = y - cornerOffset;
g.fillRect(x1, y1, x1 - cornerSize, y1 + cornerSize);
g.setColor("#000000");
g.fillRect(x, y, x - cornerSize - cornerOffset, y + cornerSize - cornerOffset);
}
function drawBottomLeftCorner(x, y) {
g.setColor(mainColor);
const x1 = x - cornerOffset;
const y1 = y + cornerOffset;
g.fillRect(x1, y1, x1 + cornerSize, y1 - cornerSize);
g.setColor("#000000");
g.fillRect(x, y, x + cornerSize - cornerOffset, y - cornerSize + cornerOffset);
}
function drawBottomRightCorner(x, y) {
g.setColor(mainColor);
const x1 = x + cornerOffset;
const y1 = y + cornerOffset;
g.fillRect(x1, y1, x1 - cornerSize, y1 - cornerSize);
g.setColor("#000000");
g.fillRect(x, y, x - cornerSize + cornerOffset, y - cornerSize + cornerOffset);
}
function drawFrame(x1, y1, x2, y2) {
drawTopLeftCorner(x1, y1);
drawTopRightCorner(x2, y1);
drawBottomLeftCorner(x1, y2);
drawBottomRightCorner(x2, y2);
g.setColor(mainColorDark);
g.drawRect(x1, y1, x2, y2);
g.setColor("#000000");
g.fillRect(x1 + borderWidth, y1 + borderWidth, x2 - borderWidth, y2 - borderWidth);
}
function drawTopFrame(x1, y1, x2, y2) {
drawBottomLeftCorner(x1, y2);
drawBottomRightCorner(x2, y2);
g.setColor(mainColorDark);
g.drawRect(x1, y1, x2, y2);
g.setColor("#000000");
g.fillRect(x1 + borderWidth, y1 + borderWidth, x2 - borderWidth, y2 - borderWidth);
}
function drawFrameNoCorners(x1, y1, x2, y2) {
g.setColor(mainColorDark);
g.drawRect(x1, y1, x2, y2);
g.setColor("#000000");
g.fillRect(x1 + borderWidth, y1 + borderWidth, x2 - borderWidth, y2 - borderWidth);
}
// function drawBottomFrame(x1,y1,x2,y2) {
// drawTopLeftCorner(x1,y1);
// drawTopRightCorner(x2,y1);
// g.setColor(mainColorDark);
// g.drawRect(x1,y1,x2,y2);
// g.setColor("#000000");
// g.fillRect(x1+borderWidth,y1+borderWidth,x2-borderWidth,y2-borderWidth);
// }
// function getUTCTime(d) {
// return d.toUTCString().split(' ')[4].split(':').map(function(d){return Number(d);});
// }
function drawTimeText(d) {
const da = d.toString().split(" ");
// var dutc = getUTCTime(d);
@ -121,19 +27,19 @@ function drawTimeText(d) {
const hours = time[0],
minutes = time[1],
seconds = time[2];
g.setColor(mainColor);
g.setColor(Arwes.C.color.primary.base);
g.setFont(font, timeFontSize);
g.drawString(`${hours}:${minutes}:${seconds}`, xyCenter, yposTime, true);
const unix = Math.round(d.getTime());
g.setFont(font, unixTimeFontSize);
g.setColor(secondaryColor);
g.setColor(Arwes.C.color.secondary.base);
g.drawString(`${unix}`, xyCenter, yposTime + 22, true);
g.setFont(font, smallFontSize);
}
function drawDateText(d) {
g.setColor(mainColor);
g.setColor(Arwes.C.color.primary.base);
g.setFont(font, dateFontSize);
g.drawString(`${d.getDate()}.${d.getMonth() + 1}.${d.getFullYear()}`, xyCenter, yposDate, true);
}
@ -144,7 +50,7 @@ function drawCounterText() {
g.setColor("#000000");
g.fillRect(37, 58 + yOffset + 36, 203, 58 + 80 + yOffset + 34);
g.setFontAlign(0, 0);
g.setColor(alert);
g.setColor(Arwes.C.color.alert.base);
g.setFont(font, 8);
g.drawString(`${count}`, xyCenter, yposCounter, true);
@ -153,35 +59,41 @@ function drawCounterText() {
function levelColor(l) {
// no icon -> brightest green to indicate charging, even when showing percentage
if (Bangle.isCharging()) return success;
if (l >= 50) return success;
if (l >= 15) return secondaryColorDark;
return alert;
if (Bangle.isCharging()) return Arwes.C.color.success.base;
if (l >= 50) return Arwes.C.color.success.base;
if (l >= 15) return Arwes.C.color.secondary.dark;
return Arwes.C.color.alert.base;
}
function drawBattery() {
const l = E.getBattery(), c = levelColor(l);
count = l;
const xl = 45 + l * (194 - 46) / 100;
g.clearRect(46, 58 + 80 + yOffset + 37, 193, height - 5);
g.setColor(c).fillRect(46, 58 + 80 + yOffset + 37, xl, height - 5);
}
function updateCounter() {
drawBattery();
drawCounterText();
}
function drawClock() {
// main frame
drawFrame(3, 10 + yOffset, width - 3, height - 3);
Arwes.drawFrame(3, 10 + yOffset, width - 3, height - 3);
// time frame
drawTopFrame(20, 10 + yOffset, 220, 58 + yOffset);
Arwes.drawFrameBottomCorners(20, 10 + yOffset, 220, 58 + yOffset);
// date frame
drawTopFrame(28, 58 + yOffset, 212, 58 + yOffset + 35);
Arwes.drawFrameBottomCorners(28, 58 + yOffset, 212, 58 + yOffset + 35);
// counter frame
drawTopFrame(36, 58 + yOffset + 35, 204, 58 + 80 + yOffset + 35);
Arwes.drawFrameBottomCorners(36, 58 + yOffset + 35, 204, 58 + 80 + yOffset + 35);
// battery frame
drawFrameNoCorners(44, 58 + 80 + yOffset + 35, 196, height - 3);
Arwes.drawFrameNoCorners(44, 58 + 80 + yOffset + 35, 196, height - 3);
updateCounter();
updateClock();
// const img = makeImg();
@ -193,8 +105,7 @@ function updateClock() {
const date = new Date();
drawTimeText(date);
drawDateText(date);
drawCounterText();
drawBattery();
}
@ -224,5 +135,6 @@ setWatch(Bangle.showLauncher, BTN2, {repeat: false, edge: "falling"});
// refesh every 100 milliseconds
setInterval(updateClock, 500);
setInterval(updateCounter, 1000);

View File

@ -1,3 +1,7 @@
0.01: Fork Toucher and change looks to match with DANE Watchface
0.02: Add Frames
0.03: Add LowRes Support
0.03: Add LowRes Support
0.04: Move code to Arwes Module
0.05: Add icon
0.06: remove app image as it is unused
0.07: Bump version number for change to apps.json causing 404 on upload

View File

@ -1,99 +1,10 @@
var d = require("dane_arwes");
var Arwes = d.default();
const yOffset = 23;
const width = g.getWidth();
const height = g.getHeight();
const xyCenter = width / 2 + 4;
const cornerSize = 14;
const cornerOffset = 3;
const borderWidth = 1;
const mainColor = "#26dafd";
const mainColorDark = "#029dbb";
// const mainColorLight = "#8bebfe";
const secondaryColor = "#df9527";
const secondaryColorDark = "#8b5c15";
// const secondaryColorLight = "#ecc180";
const success = "#00ff00";
// const successDark = "#000900";
// const successLight = "#060f06";
const alert = "#ff0000";
// const alertDark = "#090000";
// const alertLight = "#0f0606";
function drawTopLeftCorner(x, y) {
g.setColor(mainColor);
const x1 = x - cornerOffset;
const y1 = y - cornerOffset;
g.fillRect(x1, y1, x1 + cornerSize, y1 + cornerSize);
g.setColor("#000000");
g.fillRect(x, y, x + cornerSize - cornerOffset, y + cornerSize - cornerOffset);
}
function drawTopRightCorner(x, y) {
g.setColor(mainColor);
const x1 = x + cornerOffset;
const y1 = y - cornerOffset;
g.fillRect(x1, y1, x1 - cornerSize, y1 + cornerSize);
g.setColor("#000000");
g.fillRect(x, y, x - cornerSize - cornerOffset, y + cornerSize - cornerOffset);
}
function drawBottomLeftCorner(x, y) {
g.setColor(mainColor);
const x1 = x - cornerOffset;
const y1 = y + cornerOffset;
g.fillRect(x1, y1, x1 + cornerSize, y1 - cornerSize);
g.setColor("#000000");
g.fillRect(x, y, x + cornerSize - cornerOffset, y - cornerSize + cornerOffset);
}
function drawBottomRightCorner(x, y) {
g.setColor(mainColor);
const x1 = x + cornerOffset;
const y1 = y + cornerOffset;
g.fillRect(x1, y1, x1 - cornerSize, y1 - cornerSize);
g.setColor("#000000");
g.fillRect(x, y, x - cornerSize + cornerOffset, y - cornerSize + cornerOffset);
}
function drawFrame(x1, y1, x2, y2) {
drawTopLeftCorner(x1, y1);
drawTopRightCorner(x2, y1);
drawBottomLeftCorner(x1, y2);
drawBottomRightCorner(x2, y2);
g.setColor(mainColorDark);
g.drawRect(x1, y1, x2, y2);
g.setColor("#000000");
g.fillRect(x1 + borderWidth, y1 + borderWidth, x2 - borderWidth, y2 - borderWidth);
}
function drawFrameNoCorners(x1, y1, x2, y2) {
g.setColor(mainColorDark);
g.drawRect(x1, y1, x2, y2);
g.setColor("#000000");
g.fillRect(x1 + borderWidth, y1 + borderWidth, x2 - borderWidth, y2 - borderWidth);
}
function drawTopFrame(x1, y1, x2, y2) {
drawBottomLeftCorner(x1, y2);
drawBottomRightCorner(x2, y2);
g.setColor(mainColorDark);
g.drawRect(x1, y1, x2, y2);
g.setColor("#000000");
g.fillRect(x1 + borderWidth, y1 + borderWidth, x2 - borderWidth, y2 - borderWidth);
}
function drawBottomFrame(x1,y1,x2,y2) {
drawTopLeftCorner(x1,y1);
drawTopRightCorner(x2,y1);
g.setColor(mainColorDark);
g.drawRect(x1,y1,x2,y2);
g.setColor("#000000");
g.fillRect(x1+borderWidth,y1+borderWidth,x2-borderWidth,y2-borderWidth);
}
const Storage = require("Storage");
const filename = 'dane_tcr.json';
@ -160,7 +71,7 @@ const APPS = getApps();
function noIcon(x, y, scale){
if(scale < 0.2) return;
g.setColor(alert);
g.setColor(Arwes.C.color.alert.base);
g.setFontAlign(0,0);
g.setFont('6x8',settings.highres ? 6:3);
g.drawString('x_x', x+1.5, y);
@ -203,11 +114,11 @@ function render(){
const h = (settings.highres ?8:6)*fontSize
const w = ((settings.highres ?6:2)*fontSize)*app.name.length
if(settings.hightres)
drawFrame(HALF-w, HALF-h, HALF+w, HALF+h);
Arwes.drawFrame(HALF-w, HALF-h, HALF+w, HALF+h);
else
drawFrame(HALF-w-2, HALF-h, HALF+w, HALF+h);
Arwes.drawFrame(HALF-w-2, HALF-h, HALF+w, HALF+h);
g.setFont(font, fontSize);
g.setColor(alert);
g.setColor(Arwes.C.color.alert.base);
g.setFontAlign(0,0);
g.drawString(app.name, HALF, HALF);
return;
@ -225,9 +136,9 @@ function render(){
const imageScale = settings.highres ? scale*2 : scale;
if(settings.hightres)
drawFrame(x-rescale-5, y-rescale-5, x+rescale+5, y+rescale+5);
Arwes.drawFrame(x-rescale-5, y-rescale-5, x+rescale+5, y+rescale+5);
else
drawFrame(x-rescale-2-2, y-rescale-1, x+rescale+2, y+rescale+1);
Arwes.drawFrame(x-rescale-2-2, y-rescale-1, x+rescale+2, y+rescale+1);
@ -248,10 +159,10 @@ function render(){
const h = (settings.highres ?8:6)*fontSize
const w = ((settings.highres ?6:2)*fontSize)*10//app.name.length
if(settings.highres)
drawFrame(36, HEIGHT/4*3-(fontSize*8), 204, HEIGHT/4*3+(fontSize*8));
Arwes.drawFrame(36, HEIGHT/4*3-(fontSize*8), 204, HEIGHT/4*3+(fontSize*8));
else
drawTopFrame(HALF-w-2, HEIGHT/4*3-h, HALF+w, HEIGHT/4*3+h);
g.setColor(mainColor);
Arwes.drawFrameBottomCorners(HALF-w-2, HEIGHT/4*3-h, HALF+w, HEIGHT/4*3+h);
g.setColor(Arwes.C.color.primary.base);
g.setFont(font, fontSize);
g.setFontAlign(0,0);
g.drawString(app.name, HALF, HEIGHT/4*3);
@ -262,10 +173,10 @@ function render(){
const version = app.version ? app.version : '0.00';
const info = type+' v'+version;
const textWidth = (info.length*(6*1.5))
drawTopFrame(HALF-textWidth/2, 210-(1.5*8)-2, HALF+textWidth/2, 210+(1.5*8)-2);
Arwes.drawFrameBottomCorners(HALF-textWidth/2, 210-(1.5*8)-2, HALF+textWidth/2, 210+(1.5*8)-2);
g.setFontAlign(0,1);
g.setFont('6x8', 1.5);
g.setColor(secondaryColor);
g.setColor(Arwes.C.color.secondary.base);
g.drawString(info, HALF, 210, { scale: scale });
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,2 +1,3 @@
0.01: First try
0.02: Update custom.html for refactor; add README
0.03: Update for larger secondary timezone display (#610)

View File

@ -1,31 +1,75 @@
/* jshint esversion: 6 */
const timeFontSize = 6;
const dateFontSize = 3;
const gmtFontSize = 2;
// Font for primary time and date
const primaryTimeFontSize = 6;
const primaryDateFontSize = 3;
// Font for single secondary time
const secondaryTimeFontSize = 4;
const secondaryTimeZoneFontSize = 2;
// Font / columns for multiple secondary times
const secondaryRowColFontSize = 2;
const xcol1 = 10;
const xcol2 = g.getWidth() - xcol1;
const font = "6x8";
const xyCenter = g.getWidth() / 2;
const xcol1=10;
const xcol2=g.getWidth()-xcol1;
const yposTime = 75;
const yposDate = 130;
//const yposYear = 175;
//const yposGMT = 220;
const yposWorld=170;
const yposWorld = 170;
const OFFSET_TIME_ZONE = 0;
const OFFSET_HOURS = 1;
var offsets = require("Storage").readJSON("worldclock.settings.json");
var offsets = require("Storage").readJSON("worldclock.settings.json") || [];
// TESTING CODE
// Used to test offset array values during development.
// Uncomment to override secondary offsets value
// const mockOffsets = {
// zeroOffsets: [],
// oneOffset: [["UTC", 0]],
// twoOffsets: [
// ["Tokyo", 9],
// ["UTC", 0],
// ],
// fourOffsets: [
// ["Tokyo", 9],
// ["UTC", 0],
// ["Denver", -7],
// ["Miami", -5],
// ],
// fiveOffsets: [
// ["Tokyo", 9],
// ["UTC", 0],
// ["Denver", -7],
// ["Chicago", -6],
// ["Miami", -5],
// ],
// };
// Uncomment one at a time to test various offsets array scenarios
// offsets = mockOffsets.zeroOffsets; // should render nothing below primary time
// offsets = mockOffsets.oneOffset; // should render larger in two rows
// offsets = mockOffsets.twoOffsets; // should render two in columns
// offsets = mockOffsets.fourOffsets; // should render in columns
// offsets = mockOffsets.fiveOffsets; // should render first four in columns
// END TESTING CODE
// Check settings for what type our clock should be
//var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"];
var secondInterval = undefined;
var secondInterval;
function doublenum(x) {
return x<10? "0"+x : ""+x;
return x < 10 ? "0" + x : "" + x;
}
function offset(dt,offset) {
return new Date(dt.getTime() + (offset*60*60*1000));
function getCurrentTimeFromOffset(dt, offset) {
return new Date(dt.getTime() + offset * 60 * 60 * 1000);
}
function drawSimpleClock() {
@ -33,43 +77,60 @@ function drawSimpleClock() {
var d = new Date();
var da = d.toString().split(" ");
g.reset(); // default draw styles
// default draw styles
g.reset();
// drawSting centered
g.setFontAlign(0, 0);
// draw time
var time = da[4].substr(0, 5).split(":");
var hours = time[0], minutes = time[1];
var hours = time[0],
minutes = time[1];
g.setFont(font, timeFontSize);
g.setFont(font, primaryTimeFontSize);
g.drawString(`${hours}:${minutes}`, xyCenter, yposTime, true);
// draw Day, name of month, Date
var date = [da[0], da[1], da[2]].join(" ");
g.setFont(font, dateFontSize);
g.setFont(font, primaryDateFontSize);
g.drawString(date, xyCenter, yposDate, true);
// draw year
//g.setFont(font, dateFontSize);
//g.drawString(d.getFullYear(), xyCenter, yposYear, true);
// set gmt to UTC+0
var gmt = new Date(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
// draw gmt
//console.log(d.getTimezoneOffset());//offset to GMT in minutes
var gmt = new Date(d.getTime()+(d.getTimezoneOffset()*60*1000));
//gmt is now UTC+0
for (var i=0; i<offsets.length; i++) {
g.setFont(font, gmtFontSize);
dx=offset(gmt,offsets[i][1]);
hours=doublenum(dx.getHours());
minutes=doublenum(dx.getMinutes());
g.setFontAlign(-1, 0);
g.drawString(offsets[i][0],xcol1,yposWorld+i*15, true);
g.setFontAlign(1, 0);
g.drawString(`${hours}:${minutes}`, xcol2, yposWorld+i*15, true);
//g.drawString(gmt, xyCenter, yposWorld, true);
}
// Loop through offset(s) and render
offsets.forEach((offset, index) => {
dx = getCurrentTimeFromOffset(gmt, offset[OFFSET_HOURS]);
hours = doublenum(dx.getHours());
minutes = doublenum(dx.getMinutes());
if (offsets.length === 1) {
// For a single secondary timezone, draw it bigger and drop time zone to second line
const xOffset = 30;
g.setFont(font, secondaryTimeFontSize);
g.drawString(`${hours}:${minutes}`, xyCenter, yposTime + 100, true);
g.setFont(font, secondaryTimeZoneFontSize);
g.drawString(offset[OFFSET_TIME_ZONE], xyCenter, yposTime + 130, true);
// draw Day, name of month, Date
g.setFont(font, secondaryTimeZoneFontSize);
g.drawString(date, xyCenter, yposDate, true);
} else if (index < 4) {
// For > 1 extra timezones, render as columns / rows
g.setFont(font, secondaryRowColFontSize);
g.setFontAlign(-1, 0);
g.drawString(
offset[OFFSET_TIME_ZONE],
xcol1,
yposWorld + index * 15,
true
);
g.setFontAlign(1, 0);
g.drawString(`${hours}:${minutes}`, xcol2, yposWorld + index * 15, true);
}
});
}
// clean app screen
@ -78,11 +139,11 @@ Bangle.loadWidgets();
Bangle.drawWidgets();
// refesh every 15 sec when screen is on
Bangle.on('lcdPower',on=>{
Bangle.on("lcdPower", (on) => {
if (secondInterval) clearInterval(secondInterval);
secondInterval = undefined;
if (on) {
secondInterval = setInterval(drawSimpleClock, 15E3);
secondInterval = setInterval(drawSimpleClock, 15e3);
drawSimpleClock(); // draw immediately
}
});
@ -90,9 +151,8 @@ Bangle.on('lcdPower',on=>{
// draw now and every 15 sec until display goes off
drawSimpleClock();
if (Bangle.isLCDOn()) {
secondInterval = setInterval(drawSimpleClock, 15E3);
secondInterval = setInterval(drawSimpleClock, 15e3);
}
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });

116
modules/dane_arwes.js Normal file
View File

@ -0,0 +1,116 @@
/* Copyright (c) 2020 OmegaRogue. See the file LICENSE for copying permission. */
/*
Graphics Functions based on the React Sci-Fi UI Framework Arwes
*/
var C = {
cornerSize: 14, // description
cornerOffset: 3, // description
borderWidth: 1 // description
};
function Arwes(cornerSize, cornerOffset) {
this.cornerSize = cornerSize;
this.cornerOffset = cornerOffset;
}
/** 'public' constants here */
Arwes.prototype.C = {
color: {
primary: {
base: "#26dafd",
light: "#8bebfe",
dark: "#029dbb"
},
secondary: {
base: "#df9527",
light: "#ecc180",
dark: "#8b5c15"
},
header: {
base: "#a1ecfb",
light: "#fff",
dark: "#3fd8f7"
},
control: {
base: "#acf9fb",
light: "#fff",
dark: "#4bf2f6"
},
success: {
base: "#00ff00",
light: "#6f6",
dark: "#090"
},
alert: {
base: "#ff0000",
light: "#f66",
dark: "#900"
},
disabled: {
base: "#999999",
light: "#ccc",
dark: "#666"
}
}
};
function drawCorner(obj, x, y, n) {
g.setColor(obj.C.color.primary.base);
let s1 = (n&1)?1:-1, s2 = (n&2)?1:-1;
const x1 = x + obj.cornerOffset * s1;
const y1 = y + obj.cornerOffset * s2;
g.fillRect(x1, y1, x - obj.cornerSize*s1 + obj.cornerOffset*s1, y);
g.fillRect(x1, y1, x, y - obj.cornerSize*s2 + obj.cornerOffset*s2);
}
Arwes.prototype.drawFrameNoCorners = function (x1, y1, x2, y2) {
g.setColor(this.C.color.primary.dark);
g.drawRect(x1, y1, x2, y2);
}
Arwes.prototype.drawFrame = function (x1, y1, x2, y2) {
drawCorner(this, x1, y1, 0);
drawCorner(this, x2, y1, 1);
drawCorner(this, x1, y2, 2);
drawCorner(this, x2, y2, 3);
this.drawFrameNoCorners(x1, y1, x2, y2);
}
Arwes.prototype.drawFrameBottomCorners = function (x1, y1, x2, y2) {
drawCorner(this, x1, y2, 2);
drawCorner(this, x2, y2, 3);
this.drawFrameNoCorners(x1, y1, x2, y2);
}
Arwes.prototype.drawFrameTopCorners = function (x1, y1, x2, y2) {
drawCorner(this, x1, y1, 0);
drawCorner(this, x2, y1, 1);
this.drawFrameNoCorners(x1, y1, x2, y2);
}
Arwes.prototype.drawFrameLeftCorners = function (x1, y1, x2, y2) {
drawCorner(this, x1, y1, 0);
drawCorner(this, x1, y2, 2);
this.drawFrameNoCorners(x1, y1, x2, y2);
}
Arwes.prototype.drawFrameRightCorners = function (x1, y1, x2, y2) {
drawCorner(this, x2, y1, 1);
drawCorner(this, x2, y2, 3);
this.drawFrameNoCorners(x1, y1, x2, y2);
}
exports.create = function (cornerSize, cornerOffset) {
return new Arwes(cornerSize, cornerOffset);
};
exports.default = function () {
return new Arwes(C.cornerSize, C.cornerOffset);
};

1
modules/dane_arwes.min.js vendored Normal file
View File

@ -0,0 +1 @@
function Arwes(a,b){this.cornerSize=a,this.cornerOffset=b}function drawCorner(a,d,e,i){g.setColor(a.C.color.primary.base);let b=i&1?1:-1,c=i&2?1:-1;const f=d+a.cornerOffset*b;const h=e+a.cornerOffset*c;g.fillRect(f,h,d-a.cornerSize*b+a.cornerOffset*b,e),g.fillRect(f,h,d,e-a.cornerSize*c+a.cornerOffset*c)}var C={cornerSize:14,cornerOffset:3,borderWidth:1};Arwes.prototype.C={color:{primary:{base:'#26dafd',light:'#8bebfe',dark:'#029dbb'},secondary:{base:'#df9527',light:'#ecc180',dark:'#8b5c15'},header:{base:'#a1ecfb',light:'#fff',dark:'#3fd8f7'},control:{base:'#acf9fb',light:'#fff',dark:'#4bf2f6'},success:{base:'#00ff00',light:'#6f6',dark:'#090'},alert:{base:'#ff0000',light:'#f66',dark:'#900'},disabled:{base:'#999999',light:'#ccc',dark:'#666'}}},Arwes.prototype.drawFrameNoCorners=function(a,b,c,d){g.setColor(this.C.color.primary.dark),g.drawRect(a,b,c,d)},Arwes.prototype.drawFrame=function(a,b,c,d){drawCorner(this,a,b,0),drawCorner(this,c,b,1),drawCorner(this,a,d,2),drawCorner(this,c,d,3),this.drawFrameNoCorners(a,b,c,d)},Arwes.prototype.drawFrameBottomCorners=function(b,d,c,a){drawCorner(this,b,a,2),drawCorner(this,c,a,3),this.drawFrameNoCorners(b,d,c,a)},Arwes.prototype.drawFrameTopCorners=function(b,a,c,d){drawCorner(this,b,a,0),drawCorner(this,c,a,1),this.drawFrameNoCorners(b,a,c,d)},Arwes.prototype.drawFrameLeftCorners=function(a,b,d,c){drawCorner(this,a,b,0),drawCorner(this,a,c,2),this.drawFrameNoCorners(a,b,d,c)},Arwes.prototype.drawFrameRightCorners=function(d,b,a,c){drawCorner(this,a,b,1),drawCorner(this,a,c,3),this.drawFrameNoCorners(d,b,a,c)},exports.create=function(a,b){return new Arwes(a,b)},exports.default=function(){return new Arwes(C.cornerSize,C.cornerOffset)}